832401306陈子洋 Front-End and Back-End Separation Calculator System
Front-End and Back-End Separation Calculator System
| Item | Current Information |
|---|---|
| Student ID | 832401306(FZU) 24125245(MU) |
| Student Name | Chen Ziyang 陈子洋 |
| Course | EE308FZ Software Engineering |
| Assignment | First Assignment-Front-End and Backend Separation Calculator System |
| Blog Platform | CSDN |
Table of Contents
- Course Information
- Project Links
- PSP Table
- Assignment Description
- Requirement Analysis
- Overall Architecture
- Frontend Design
- Backend Design
- API Design
- Database Design
- Expression Parser Design
- Calculation History Design
- Exception Handling
- Frontend and Backend Communication
- Deployment
- Functional Structure Diagram
- Presentation of Finished Product
- Code Explanation
- Personal Journey and Learnings
1. Course Information
| Field | Content |
|---|---|
| Assignment Requirements | Develop a separated frontend and backend calculator, then provide code, deployment, a blog and presention evidence |
| Objectives | Implement basic and compound calculations, exceptions, exception handling, persistent SQLite history, deletion by ID and public deployment |
| Other References | Teacher provided assignment blog and class announcements |
2. Project Links
Frontend Repository: https://github.com/chenzy3034-ux/832401306_calculator_frontend
Backend Repository: https://github.com/chenzy3034-ux/832401306_calculator_backend
Frontend Code Standard: https://github.com/chenzy3034ux/832401306_calculator_frontend/blob/main/codestyle.md
Backend Code Standard: https://github.com/chenzy3034ux/832401306_calculator_backend/blob/main/codestyle.md
3. PSP Table
The table below lists the typical development activities planned for this project, along with the estimated time for each activity.
| Activity | Estimated min | Actual min |
|---|---|---|
| Requirement analysis | 90 | 75 |
| System design | 120 | 125 |
| Frontend development | 240 | 255 |
| Backend development | 240 | 260 |
| Expression calculation module | 180 | 200 |
| Database design | 120 | 120 |
| Calculation history module | 120 | 140 |
| Frontend and backend integration | 120 | 135 |
| Testing | 180 | 180 |
| Deployment | 180 | 180 |
| Blog writing | 240 | 215 |
| Other work | 90 | 90 |
4. Assignment Description
This assignment requires a calculator system with fully separated frontend and backend applications. The frontend handles expression input, button interaction, result and error display, and history presentation. The backend validates and parses expressions, performs calculations, handles exceptions, and accesses the database.
The project supports addition, subtraction, multiplication, division, decimals, parentheses, operator precedence, unary plus and minus, invalid expression handling, and division by zero handling. Successful calculations are stored in SQLite, and records can be queried and deleted. The frontend neither stores history in localStorage nor calculates final results independently.
5. Requirement Analysis
In this assignment, the calculator system should cover the following requirements:
· Basic calculation. Backend supports +,-,* and /.
· Compound expressions. Precedence, parentheses, decimals, unary signs.
· Invalid input. Restricted tokenizer and parser return controlled errors.
· Division by zero. Evaluator raises a dedicated domain error.
· Persistence history. SQLite file stores every successful calculation.
· History retrieval from the backend.
· Delete the specified records by ID.
6. Overall Architecture
The system is divided into the browser client, HTTP API, business services, and database layers. The browser handles interaction and presentation. FastAPI receives JSON requests and invokes calculation or history services. The SQLite repository provides persistence.
User
↓
React + Vite Frontend
↓ HTTPS / JSON
FastAPI API Routes
↓
Service Layer
├── Expression Calculation Service
│ ↓
│ Tokenizer → Parser → Evaluator
│
└── Calculation History Service
↓
SQLite Database
Module boundaries keep routes, calculation logic, and database access separate. This supports focused testing and keeps parsing and SQL out of API route functions.
7. Frontend Design
The frontend uses React19, Vite8, Javascript and CSS. The calculator component provides the expression field, number button, operators parentheses, decimal point, clear button, and equal button. The HistoryList component presents stored records and handles refresh and delete actions.
· When the expression is empty, the field displays 0.
· Multiplication and division buttons display familiar symbols and are converted to * and / before sending a request.
· Pressing equals calls the backend; React does not calculate the final result.
· The page calls GET/api/history on load and DELETE/api/history/{id} for deletion.
· Failed requests display the safe message returned by the backend.
· When the backend is unavailable, users can still edit the input but cannnot obtain a new valid result.
8. Backend Design
The backend uses Python, FastAPI, Pydantic, Uvicorn and the standard sqlite 3 library. app/api contains HTTP routes, app/services coordinates use cases, app/calculator safely precesses expressions and app/database contains the connection, schema and repository layers.
At application startup, the lifespan handler initializes the database table. Global exception handlers convert request validation errors and unexpected exceptions into a consistent JSON response without exposing a Python stack trace. Allowed CORS origins are configured through an environment variable.
9. API Design
| Method | Endpoint | Purpose |
|---|---|---|
| POST | api/calculate | Validate, calculate, save and return one record |
| GET | api/history | Return stored calculations, newest first |
| DELETE | api/history/{id} | Delete one record by database ID |
| GET | health | Deployment health check |
Example calculation request:
{
"expression": "(1+2)*3"
}
Successful responses place the payload in data. Failed responses place a stable error code and safe message in error.
{
"success": false,
"data": null,
"error": {"code": "INVALID_EXPRESSION", "message": "The expression is invalid"}
}
10. Database Design
The project uses a file based SQLite database rather than browser storage or an in process list.
| Column | Type | Constraint | Meaning |
|---|---|---|---|
| id | INTEGER | PRIMARY KEY AUTOINCREMENT | History record identifier |
| expression | TEXT | NOT NULL | Original API expression |
| result | TEXT | NOT NULL | Decimal calculation result |
| created_at | TEXT | NOT NULL | UTC ISO timestamp |
11. Expression Parser Design
Expression processing follows three explicit stages: tokenizer, parser and evaluator. The input is never executed as Python code.
11.1 Tokenizer
The tokenizer accepts only digits, decimal points, whitespace, +, -, *, /, and parentheses. It constructs numbers with Decimal. Multiple decimal points, an isolated decimal point, or an unknown character raises Invalid expression error.
11.2 Recursive Descent Parser
The parser converts tokens into NumberNode, UnaryNode, and BinaryNode objects. Its grammar is:
expression -> term ((+ | -) term)*
term -> unary ((* | /) unary)*
unary -> (+ | -) unary | primary
primary -> NUMBER | (expression)
11.3 Precedence and Evaluation
The expression level handles addition and subtraction while the term level handles multiplication and division, giving multiplication and division higher precedence. The primary level recursively parses parentheses, and the unary level handles unary plus and minus. The evaluator traverses the AST recursively and checks the right operand before division.
12. Calculation History Design
A successful calculation follows this order: parse the expression, evaluate the result, insert the record into SQLite, and return the stored record. Insertion happens only after evaluation succeeds, so invalid expressions and division by zero do not create history records.
1.POST /api/calculate returns and stores a successful record.
2.GET /api/history returns records in descending created_at and id order.
3.The frontend reloads history on page load and after successful calculations or deletions.
4.DELETE /api/history/{id} removes the row with the specified database ID.
5.Deleting a missing ID returns 404 rather than reporting false success.
13. Exception Handling
| Situation | HTTP | Code | Frontend Message |
|---|---|---|---|
| Invalid expression | 400 | INVALID_EXPRESSION | The expression is invalid |
| Division by zero | 422 | DIVISION_BY_ZERO | Division by is not allowed |
| Missing history ID | 404 | HISTORY_NOT_FOUND | The calculation history record was not found |
| Invalid request body | 422 | INVALID_REQUEST | The request data is invalid |
| Unexpected backend error | 500 | INTERNAL_ERROR | An internal server error occurred |
14. Frontend and Backend Communication
A successful calculation uses the following communication sequence:
User presses =
-> React converts × and ÷ to * and /
-> POST /api/calculate
-> FastAPI validates the request
-> Tokenizer -> Parser -> Evaluator
-> SQLite INSERT
-> JSON response
-> React displays the result
-> React reloads GET /api/history
In development, VITE_API_BASE_URL points to the local backend. The production build sets it to the PythonAnywhere HTTPS address. The backend uses CALCULATOR_CORS_ORIGINS to allow only configured frontend origins.
15. Deployment
15.1 Frontend
The frontend is deployed on GitHub Pages. When the main branch changes, GitHub Actions runs npm ci and npm run build, then publishes dist as the Pages artifact. The Vite base is /832401306_calculator_frontend/.
Public URL: https://chenzy3034-ux.github.io/832401306_calculator_frontend/
15.2 Backend
The backend is deployed on PythonAnywhere as a Uvicorn ASGI web application. Its start command uses environment variables to specify the persistent SQLite file and CORS origin. The platform provides a public HTTPS subdomain.
Backend URL: https://chenzy.pythonanywhere.com/
The public environment has been checked for health, CORS, calculation, history retrieval, deletion, and persistence after a backend reload. The production frontend build and GitHub Pages workflow succeeded, and all 41 backend tests passed.
16. Function Structure Diagram
Calculator System
|-- Frontend
| |-- Expression input and keypad
| |-- Result and error display
| |-- History list and delete action
| `-- Backend API client
|
|-- Backend
| |-- Calculate API
| |-- History APIs
| |-- Calculation service
| |-- History service
| `-- Unified responses and exception handling
|
|-- Expression Processing
| |-- Tokenizer
| |-- Recursive descent parser
| `-- Decimal evaluator
|
`-- Persistence
|-- SQLite connection and initialization
`-- Insert, query, and delete repository
This structure diagram matches the modules in the current repositories. It excludes unimplemented features such as scientific calculation, login, and bulk history deletion.
17. Presentation of Finished Product
17.1 Main calculator page

Figure1. Main calculator page
The initial expression shows 0, the result shows a dash, and the history section is visible.
17.2 Addition

Figure2. Addition: 1+2=3
The backend returns 3 and the successful record appears in history.
17.3 Subtraction

Figure3. Subtractiion: 10-3=7
The backend returns 7.
17.4 Multiplication

Figure4. Multiplication: 4x5=20
The interface shows the multiplication symbol while the API receives an asterisk.
17.5 Division

Figure5. Division: 10/2=5
The interface shows the division symbol while the API receives a slash.
17.6 Decimal

Figure6. Decimal: 3.5+1.2=4.7
This demonstrates decimal parsing and calculation.
17.7 Compound expression

Figure7. Compound expression: 1+2x3=7
This demonstrates multiplication taking precedence over addition.
17.8 Parentheses

Figure8. Parentheses: (1+2)x3=9
This demonstrates parentheses changing the evaluation order.
17.9 Unary minus

Figure9. Unary minus: -5+8=3
This demonstrates unary minus.
17.10 Invalid expression

Figure10. Invalid expression: 1+x2
The page shows "The expression is invalid" and does not add a history record.
17.11 Division by zero

Figure11. Division by zero: 10/0
The page shows "Division by zero is not allowed" and does not add a history record.
17.12 Calculation history

Figure12. Calculation history
The list shows each expression, result, time and Delete button.
17.13 History persistence

Figure13. History persistence
Database records remain after refreshing or reopening the page.
17.14 Delete history

Figure14. Delete history
The selected record -5+8=3 disappears, and the deleted record does not return after refreshing.
18. Code Explanation
18.1 Frontend API Request
The interface uses x and ÷ to make input more intuitive. After the user clicks the equals button, the frontend first converts these symbols into * and /, which are supported by the backend, and then sends the expression.
function toApiExpression(value) {
return value.replaceAll('×', '*').replaceAll('÷', '/')
}
const record = await calculateExpression(toApiExpression(expression))
setResult(record.result)
await loadHistory()
}
calculateExpression sends JSON through an HTTP POST request. The frontend only displays the result returned by the backend and does not calculate the final result by itself.
export function calculateExpression(expression) {
return request('/api/calculate', {
method: 'POST',
body: JSON.stringify({ expression }),
})
}
18.2 Backend Calculate API
POST /api/calculate receives the expression, calls the service layer, and returns the saved history record after a successful calculation. A successful response uses HTTP 201.
history = calculate_and_store(request.expression, repository)
return CalculateSuccessResponse(data=history)
The service layer performs the evaluation before writing the result to the database. Therefore, a failed calculation does not create a history record.
def calculate_and_store(
expression: str,
repository: HistoryRepository,
) -> CalculationHistory:
result = evaluate_expression(expression)
return repository.insert(expression=expression, result=result)
18.3 Expression Parsing
The expression passes through the tokenizer, parser, and evaluator in sequence. The tokenizer converts the input into restricted mathematical tokens, the parser constructs an expression tree, and the evaluator calculates the value of that tree.
def evaluate_expression(expression: str) -> Decimal:
return evaluate(Parser(tokenize(expression)).parse())
This process only handles the numbers and operators defined by the project. It does not execute the user input as Python code.
18.4 Operator Precedence
The parser uses four levels: expression, term, unary, and primary. expression combines only addition and subtraction, while term combines only multiplication and division. Therefore 1+23 becomes 1+(23). Parentheses start a nested expression parse and can override the default order.
18.5 Input Validation
The tokenizer rejects empty expressions and only recognizes numbers, decimal points, whitespace, the four arithmetic operators, and parentheses. Invalid numbers and unknown characters trigger InvalidExpressionError.
if not expression or expression.isspace():
raise InvalidExpressionError("Expression cannot be empty")
The parser also checks whether the expression is complete. For example, if extra tokens remain after parsing, the expression is treated as invalid.
def parse(self) -> ExpressionNode:
expression = self._parse_expression()
if self._current.type is not TokenType.END:
raise InvalidExpressionError("Unexpected token after expression")
return expression
18.6 Error Handling
The calculation API converts invalid expressions and division by zero into clear HTTP status codes and consistent JSON error messages.
except InvalidExpressionError:
return error_response(
status.HTTP_400_BAD_REQUEST,
"INVALID_EXPRESSION",
"The expression is invalid",
)
except DivisionByZeroExpressionError:
return error_response(
status.HTTP_422_UNPROCESSABLE_CONTENT,
"DIVISION_BY_ZERO",
"Division by zero is not allowed",
)
In addition, the global exception handler returns a general message for unexpected backend errors, preventing Python exception stack traces from being exposed to the browser. The shared frontend request function checks both the HTTP status and the success field in the response before displaying the corresponding error message.
18.7 Database Insert
After each successful calculation, the repository uses parameter binding to insert the expression, result, and creation time. The result is stored as text to preserve the representation of the Decimal result.
INSERT INTO calculation_history (expression, result, created_at)
VALUES (?, ?, ?)
After the insertion, the code reads cursor.lastrowid and returns the new record’s id, expression, result, and created_at to the calculation API.
18.8 Database Query
GET /api/history reads records from SQLite. The query sorts the records by creation time and ID in descending order so that newer records appear first.
SELECT id, expression, result, created_at
FROM calculation_history
ORDER BY created_at DESC, id DESC
The frontend requests the history again when the page loads and after a successful calculation or deletion. Therefore, the history is still obtained from the database after the page is refreshed.
18.9 Delete a Specific History Record
The delete operation uses the database record ID and parameter binding to locate the specified row.
cursor = connection.execute(
"DELETE FROM calculation_history WHERE id = ?",
(history_id,),
)
return cursor.rowcount > 0
If rowcount is zero, the service layer raises HistoryNotFoundError, and the API returns HTTP 404. After a successful deletion, the frontend reloads the history list so that the interface remains synchronized with the database.
19. Personal Journey and Learnings
I began this assignment by reviewing the Blog on society and converting its requirements into a checklist. This helped me understand that the task included frontend and backend separation, testing, database persistence, documentation, and deployment.
The most important technical challenge was implementing expression calculation without using eval or exec. I created a tokenizer, recursive descent parser, and evaluator to support operator precedence, parentheses, unary operators, decimals, invalid expressions, and division by zero. This improved my understanding of safe input processing and expression parsing.
I also learned how to separate responsibilities between FastAPI routes, services, calculation modules, and the SQLite repository. Successful calculations are stored in SQLite, while failed calculations do not create history records. The frontend only handles interaction and displays results returned by the backend.
Finally, deploying the frontend to GitHub Pages and the backend to PythonAnywhere helped me understand CORS, environment variables, HTTPS communication, and persistent database storage. Through this project,although there are so many tricky issues, I gained practical experience in developing, testing, integrating, and deploying a complete frontend and backend application.
相关推荐
基于Spring Boot 3 + Vue 3 + Element Plus的现代化宠物救助管理系统设计与实现
本文将详细阐述系统的需求分析、技术选型、架构设计、数据库设计、核心功能模块的实现(包括用户认证、宠物信息管理、救助申请、领养流程等),并提供关键代码片段,最后对系统进行总结与展望。.requestMatchers("/api/auth/login", "/api/auth/register", "/api/public/**").permitAll() // 公开接口。.requestMatchers("/api/admin/**").hasRole("ADMIN") // 管理员接口。
基于MPC+二自由度动力学的车辆轨迹跟踪+横向控制Carsim和Simulink联合仿真(Matlab代码、Simulink仿真实现)
内容概要:本文系统阐述了基于模型预测控制(MPC)与二自由度车辆动力学模型的轨迹跟踪与横向控制方法,通过Carsim与Simulink联合仿真平台实现了Matlab代码与Simulink模型的协同验证。该方法结合MPC算法的滚动时域优化特性与车辆二自由度动力学模型的运动学精度,构建了高精度的路径跟踪控制系统,能够有效提升自动驾驶车辆在复杂工况下的行驶稳定性与轨迹跟随精度。文中详细介绍了控制策略设计、车辆动力学建模、仿真参数配置、联合仿真接口搭建及结果分析全过程,涵盖了状态预测、代价函数构建、约束处理与优化求解等核心环节,适用于智能驾驶中路径规划与跟踪控制系统的研发与验证。; 适合人群:具备车辆动力学、现代控制理论(如最优控制、预测控制)基础知识,熟练掌握Matlab/Simulink与Carsim仿真工具,从事自动驾驶、智能车辆控制算法开发的研究生、科研人员及工程技术人员。; 使用场景及目标:①用于智能网联汽车与无人驾驶系统中横向控制算法的设计与性能验证;②作为MPC在车辆运动控制中应用的教学案例,辅助高校课程实践与科研训练;③支撑高水平学术论文复现、课题申报与工程原型开发。; 阅读建议:建议读者在Carsim与Simulink联合仿真环境下动手复现,重点掌握MPC控制器中预测模型的建立、权重系数调节、约束设定及实时性优化等关键技术,并尝试在不同道路曲率、车速及路面附着条件下进行鲁棒性测试,进一步拓展至非线性MPC或自适应MPC等先进控制策略的研究。
基于线性时变模型预测控制的车辆轨迹跟踪系统研究(Matlab代码、Simulink仿真实现)
内容概要:本文系统研究了基于线性时变模型预测控制(LTV-MPC)的车辆轨迹跟踪系统,利用Matlab与Simulink工具搭建完整的控制系统仿真模型。研究首先建立车辆动力学模型,并通过局部线性化方法将非线性系统转化为时变状态空间模型,进而构建包含状态预测、滚动优化与反馈校正的MPC控制框架。文中详细阐述了目标函数设计、约束条件处理(如输入饱和、状态边界)、预测时域与控制时域设定等关键技术环节,重点分析LTV-MPC在不同行驶工况下对参考轨迹的跟踪精度、稳定性和实时性表现。通过与传统线性MPC的对比仿真,验证了LTV-MPC在应对系统非线性、提升轨迹跟踪性能方面的显著优势,充分展现了其在自动驾驶路径跟踪中的工程应用价值。; 适合人群:具备自动控制理论、车辆动力学及优化算法基础,熟练掌握Matlab/Simulink编程环境,从事智能驾驶决策控制、路径规划、先进驾驶辅助系统(ADAS)研发的科研人员、工程技术人员及研究生。; 使用场景及目标:①掌握车辆轨迹跟踪中MPC控制器的设计与实现方法;②学习如何将非线性车辆模型在线线性化并集成到预测控制框架中;③深入理解LTV-MPC相较于固定参数MPC的性能优势与适用边界;④通过仿真调试优化权重矩阵、预测时域等关键参数,提升实际控制效果;⑤为自动驾驶系统的控制模块开发、学术论文撰写或毕业课题提供可复现的技术方案与仿真平台。; 阅读建议:建议读者结合所提供的Matlab代码与Simulink模型,逐步运行并剖析各功能模块,重点关注车辆模型线性化过程、状态预测模块构建、QP求解器配置及约束处理机制,通过调整工况和控制参数进行对比实验,深入掌握LTV-MPC的调控机理与性能优化方法。
基于扩展卡尔曼滤波(EKF)的激光雷达与雷达异构传感器融合算法研究(Matlab代码实现)
内容概要:本文系统性解读了一套完整的多机器人路径规划(MAPF)MATLAB代码库,聚焦于优先级规划算法的工程实现,提出将求解过程划分为几何解析、拓扑建模、调度优化与可视化呈现四个解耦层次的模块化框架。文章深入阐述了动态边权膨胀、节点预留历史、几何惩罚驱动的局部同步机制、Petri网双阶段优化以及双坐标系统可视化等核心技术设计,实现了空间路径规划与时间冲突消解的有效分离,在保障多项式时间复杂度的前提下显著提升了路径规划的成功率与计算效率。该框架不仅具备良好的可复现性和工程扩展性,还为MAPF领域的算法研究、性能评估与教学实践提供了系统化的参考范例。; 适合人群:具备一定MATLAB编程基础,从事机器人路径规划、智能交通、自动化控制、多智能体系统等相关领域的科研人员、工程技术人员及研究生。; 使用场景及目标:① 深入理解并复现多智能体路径规划中优先级规划与冲突消解的核心机制;② 学习如何将复杂的MAPF问题分解为几何、拓扑、调度与呈现的模块化工程实现方法;③ 借鉴动态边权惩罚、Petri网建模、局部同步优化等创新设计思路,以提升自身路径规划算法的鲁棒性与效率。; 阅读建议:建议读者结合文中提供的MATLAB代码与MovingAI标准地图数据集,严格按照章节顺序逐步实现各模块功能,并通过内置的动画演示工具直观观察算法执行过程,重点关注各模块间的接口设计、关键参数(如惩罚权重、同步阈值)的调优策略及其对整体性能的影响。
【脉冲多普勒雷达信号处理】基于匹配滤波与慢时间傅里叶变换的距离速度联合估计理论及模糊特性研究(Matlab代码实现)
内容概要:本文系统性解读了一套完整的多机器人路径规划(MAPF)MATLAB代码库,重点围绕优先级规划算法展开,提出将求解过程划分为几何、拓扑、调度与呈现四个解耦层次的框架设计。该框架采用动态边权膨胀策略提升路径灵活性,利用节点预留历史实现高效冲突查询,并通过几何惩罚驱动的局部同步机制优化时间消解效率。此外,代码库还引入Petri网整数规划作为对比路径,支持双坐标系统与全流程可视化,具备良好的可复现性与可扩展性,适用于教学与科研参考。; 适合人群:具备一定MATLAB编程基础,从事机器人路径规划、智能调度或自动化系统研究的科研人员及研究生。; 使用场景及目标:①学习多机器人路径规划中优先级规划的核心思想与工程实现;②掌握动态边权膨胀、时间同步与冲突消解等关键技术的设计逻辑;③复现并改进现有算法,用于仓储物流、无人机编队等实际应用场景的研究与仿真验证。; 阅读建议:建议按文中所述的数据流顺序逐步阅读各模块内容,重点关注各层之间的接口设计与信息传递机制,结合提供的可视化工具调试与观察算法行为,以深入理解框架的整体架构与关键创新点。


