832401221_WuHaodong_First Assignment——Front-End and Back-End Separated Calculator System
Front-End and Back-End Separation Calculator System
Project Information
| Item | Information |
|---|---|
| Student ID | 832401221 |
| Project | Front-End and Back-End Separation Calculator System |
| Frontend | HTML / CSS / JavaScript |
| Backend | Python / Flask |
| Database | SQLite |
| Frontend Deployment | GitHub Pages |
| Backend Deployment | Railway |
Repository & Deployment Links
GitHub Repositories
Frontend Repository:
https://github.com/hodden2026/832401221_calculator_frontend
Backend Repository:
https://github.com/hodden2026/832401221_calculator_backend
Code Style
Frontend Code Style:
https://github.com/hodden2026/832401221_calculator_frontend/blob/main/codestyle.md
Backend Code Style:
https://github.com/hodden2026/832401221_calculator_backend/blob/main/codestyle.md
Public Deployment
Frontend:
https://hodden2026.github.io/832401221_calculator_frontend/
Backend:
https://832401221calculatorbackend-production.up.railway.app/
Table of Contents
- 1. Project Overview
- 2. PSP
- 3. Requirement Analysis
- 4. System Design
- 5. Implementation
- 6. API Design
- 7. Testing
- 8. Deployment
- 9. Screenshots and Demonstration
- 10. Problems Encountered and Solutions
- 11. Summary and Learning
1. Project Overview
本项目实现了一个基于前后端分离架构的计算器系统。
项目由两个独立部分组成:
- Frontend:HTML、CSS、JavaScript
- Backend:Python、Flask、SQLite
前端主要负责:
- 页面展示
- 用户输入
- 按钮交互
- API 请求
- 计算结果展示
- 历史记录展示
- 历史记录搜索
- 删除历史记录
后端主要负责:
- 接收数学表达式
- 表达式合法性检查
- 数学表达式计算
- 异常处理
- 历史记录持久化
- 历史记录查询
- 历史记录删除
本项目最重要的设计原则是:
前端不负责生成最终数学计算结果,所有最终计算均由后端完成。
因此,当后端服务器停止运行时,前端页面虽然仍然能够打开,但是不能独立产生新的有效计算结果。
系统支持的核心功能包括:
- 加法
- 减法
- 乘法
- 除法
- 复合表达式
- 运算优先级
- 括号
- 小数
- 一元正号
- 一元负号
- 非法表达式处理
- 除零异常处理
- 计算历史持久化
- 历史记录查询
- 删除指定历史记录
在完成基本要求以后,还增加了以下扩展功能:
- 键盘快捷键
- 历史记录搜索
- 深色 / 浅色主题切换
- 后端在线状态显示
- 清空全部历史记录
- 响应式页面
- 历史记录数量显示
2. PSP
| PSP2.1 | Estimated Time (min) | Actual Time (min) |
|---|---|---|
| Planning | 30 | 40 |
| · Estimate | 20 | 25 |
| Development | 505 | 630 |
| · Analysis | 40 | 50 |
| · Design Spec | 30 | 40 |
| · Design Review | 20 | 20 |
| · Coding Standard | 30 | 45 |
| · Design | 45 | 60 |
| · Coding | 220 | 270 |
| · Code Review | 30 | 35 |
| · Test | 90 | 110 |
| Reporting | 120 | 145 |
| · Test Report | 40 | 50 |
| · Size Measurement | 20 | 25 |
| · Postmortem & Improvement Plan | 60 | 70 |
| Total | 655 | 815 |
PSP Analysis
本项目最初预计总耗时约为 655 分钟,实际完成时间约为 815 分钟,实际耗时比预计多约 160 分钟。
时间差主要集中在开发、测试以及部署阶段。
在开发阶段,我最初低估了前后端分离、SQLite 数据持久化以及安全表达式解析需要的时间。
特别是在处理以下情况时,需要进行多次测试和修改:
- 运算符优先级
- 括号
- 一元正负号
- 小数
- 非法表达式
- 除零
测试阶段实际耗时也比预计更长。
除了基本的四则运算之外,还需要验证:
- 非法表达式
- 除零
- 历史记录
- 删除历史记录
- 键盘快捷键
- 公网 API
- 前后端联调
部署阶段则需要学习 Railway、Volume、环境变量、Gunicorn 和 GitHub Pages 的配置方法。
通过 PSP 记录可以发现,我最初对于接口联调和部署所需时间的估计偏乐观。
以后进行类似项目时,我会为部署、调试、异常处理和前后端接口联调预留更多时间。
3. Requirement Analysis
3.1 Basic Calculation
系统需要支持四种基本运算:
+
-
*
/
例如:
12+8
10-7
6*8
20/4
最终数学结果全部由后端计算。
3.2 Compound Expressions
系统需要支持多个运算符组成的复合表达式,并正确处理运算优先级。
例如:
1+2*3
正确结果应该为:
7
而:
(1+2)*3
正确结果应该为:
9
因此系统需要支持:
- 运算优先级
- 括号
- 复合数学表达式
系统还支持:
-5+8
3*-2
1.5+2.3
这些表达式分别测试:
- 一元负号
- 负数操作数
- 小数计算
3.3 Error Handling
后端需要对非法输入进行正确处理。
例如:
10/0
系统返回:
Division by zero
而非法表达式:
1++*2
返回:
Invalid expression
这样可以避免错误输入导致后端程序崩溃。
3.4 Calculation History
每次成功完成计算以后,系统都会把计算历史保存到后端数据库。
每条记录包括:
id
expression
result
created_at
历史记录的正式数据源是 SQLite。
前端不会使用 LocalStorage 保存正式的计算历史。
因此用户刷新页面以后,前端仍然可以从后端重新获取已经保存的历史记录。
3.5 Delete History
用户可以删除指定的历史记录。
前端发送:
DELETE /api/history/{id}
后端从 SQLite 数据库中真正删除对应数据。
删除完成以后,前端再次请求:
GET /api/history
获取数据库最新状态。
4. System Design
4.1 Functional Structure Diagram

4.2 System Architecture Diagram

4.3 Front-End / Back-End Separation Design
整个项目最重要的设计思想是前端与后端职责分离。
前端主要负责:
- 接收用户输入
- 页面显示
- 发送 API 请求
- 显示后端返回的计算结果
- 展示历史记录
- 搜索历史记录
- 发送删除请求
后端主要负责:
- 验证表达式
- 解析表达式
- 数学计算
- 错误处理
- 保存历史
- 查询历史
- 删除历史
- 返回 JSON
例如用户输入:
1+2*3
前端只发送:
{
"expression": "1+2*3"
}
后端完成计算后返回:
{
"success": true,
"expression": "1+2*3",
"result": 7
}
最后前端负责把:
7
显示给用户。
因此,如果后端服务停止,前端不能独立生成新的有效计算结果。
5. Implementation
5.1 Frontend Implementation
前端主要由三个核心文件组成:
index.html
style.css
script.js
index.html
负责:
- 页面总体结构
- Calculator 区域
- 数字按钮
- 运算符按钮
- 表达式显示区域
- 结果区域
- 历史记录区域
- 搜索框
- 主题切换按钮
- 后端状态区域
style.css
负责:
- 页面整体布局
- Calculator 样式
- History 样式
- CSS Grid
- Flexbox
- 深色主题
- 浅色主题
- 响应式布局
script.js
负责:
- 用户输入
- 鼠标按钮事件
- 键盘快捷键
- Fetch API
- 后端状态检查
- 计算结果显示
- 历史查询
- 历史删除
- 历史搜索
- 清空历史
- 主题切换
5.2 Backend Implementation
后端主要由以下模块组成:
app.py
database.py
services/calculator_service.py
app.py
负责:
- Flask 应用
- HTTP API
- Request 处理
- JSON Response
- HTTP Status Code
- CORS
database.py
负责:
- SQLite 数据库连接
- 初始化数据库
- 插入历史记录
- 查询历史记录
- 删除指定记录
- 清空历史记录
services/calculator_service.py
负责:
- AST 表达式解析
- 运算符验证
- 数学计算
- 表达式异常处理
通过模块划分,使 HTTP、数据库和数学计算逻辑保持相对独立。
5.3 Safe Expression Parser
本项目没有使用:
eval()
或者:
exec()
直接执行用户输入。
后端首先使用:
tree = ast.parse(
expression,
mode="eval",
)
将表达式解析成 Python AST。
允许的二元运算符:
BINARY_OPERATORS = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv,
}
允许的一元运算符:
UNARY_OPERATORS = {
ast.UAdd: operator.pos,
ast.USub: operator.neg,
}
表达式处理过程:
User Expression
↓
ast.parse()
↓
Check AST Node
↓
Is Allowed?
↙ ↘
Yes No
↓ ↓
Calculate Reject
例如:
1+2*3
可以正常计算。
而函数调用、属性访问以及其他不允许的 Python 语法不会作为合法数学表达式执行。
5.4 Calculation API
计算接口:
POST /api/calculate
核心代码:
@app.route(
"/api/calculate",
methods=["POST"],
)
def calculate():
data = request.get_json(
silent=True
)
if not data:
return jsonify({
"success": False,
"message":
"JSON request body is required",
}), 400
if "expression" not in data:
return jsonify({
"success": False,
"message":
"Expression is required",
}), 400
expression = data["expression"]
try:
result = calculate_expression(
expression
)
history_id = add_history(
expression,
result,
)
return jsonify({
"success": True,
"expression": expression,
"result": result,
"history_id": history_id,
}), 200
except ExpressionError as error:
return jsonify({
"success": False,
"message": str(error),
}), 400
处理流程为:
Receive Expression
↓
Validate Input
↓
Calculate
↓
Save History
↓
Return JSON
只有成功完成的计算才会写入数据库。
5.5 SQLite Persistence
项目使用 SQLite 保存历史记录。
数据库表结构:
CREATE TABLE IF NOT EXISTS calculation_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
expression TEXT NOT NULL,
result TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
每次成功计算以后:
INSERT INTO calculation_history (
expression,
result
)
VALUES (?, ?)
项目使用参数化 SQL,而不是将用户数据直接拼接进 SQL 字符串。
历史记录包括:
id
expression
result
created_at
本地运行时数据库默认位于:
calculator_backend/calculator.db
Railway 部署环境使用:
DATABASE_PATH=/data/calculator.db
从而让数据库路径可以根据运行环境进行配置。
5.6 History Query and Deletion
查询全部历史:
GET /api/history
删除指定历史:
DELETE /api/history/{id}
清空历史:
DELETE /api/history
删除记录以后,前端重新调用:
GET /api/history
获取后端数据库的最新状态。
因此删除功能不只是从网页上隐藏记录,而是真正执行后端数据库删除。
5.7 Frontend API Communication
前端使用 Fetch API 与 Flask 通信。
例如计算:
const response = await fetch(
`${API_BASE_URL}/api/calculate`,
{
method: "POST",
headers: {
"Content-Type":
"application/json"
},
body: JSON.stringify({
expression: expression
})
}
);
公网部署后的 API 地址:
const API_BASE_URL =
"https://832401221calculatorbackend-production.up.railway.app";
最终公网结构为:
GitHub Pages Frontend
↓
HTTPS Request
↓
Railway Flask Backend
↓
SQLite Database
5.8 Extended Features
Keyboard Shortcuts
系统支持:
| Key | Function |
|---|---|
0-9 | Number input |
+ | Addition |
- | Subtraction |
* | Multiplication |
/ | Division |
( ) | Parentheses |
. | Decimal point |
Enter | Calculate |
Backspace | Delete last character |
Escape | Clear calculator |
History Search
历史记录支持根据以下内容实时搜索:
- Expression
- Result
- Calculation time
搜索操作只改变前端显示,不会修改数据库。
Dark / Light Theme
系统支持:
Light Theme
Dark Theme
LocalStorage 只用于保存主题偏好。
计算历史不会存储在 LocalStorage 中。
Backend Status
前端调用:
GET /api/health
检测后端状态。
后端在线时显示:
后端在线
无法连接时显示:
后端离线
6. API Design
| Method | Endpoint | Function |
|---|---|---|
| GET | /api/health | Check backend status |
| POST | /api/calculate | Calculate expression |
| GET | /api/history | Get calculation history |
| DELETE | /api/history/{id} | Delete specified history |
| DELETE | /api/history | Clear all history |
6.1 Health Check
Request:
GET /api/health
Response:
{
"success": true,
"status": "online"
}
6.2 Calculate Expression
Request:
POST /api/calculate
Body:
{
"expression": "(1+2)*3"
}
Response:
{
"success": true,
"expression": "(1+2)*3",
"result": 9,
"history_id": 1
}
6.3 Division by Zero
Input:
10/0
Response:
{
"success": false,
"message": "Division by zero"
}
6.4 Invalid Expression
Input:
1++*2
Response:
{
"success": false,
"message": "Invalid expression"
}
7. Testing
项目使用 Python unittest 测试后端核心表达式计算逻辑。
运行命令:
python -m unittest discover -s tests -v
测试覆盖:
- Addition
- Subtraction
- Multiplication
- Division
- Operator precedence
- Parentheses
- Decimal numbers
- Unary positive number
- Unary negative number
- Negative operand
- Division by zero
- Invalid expression
- Empty expression
- Unsupported power operator
- Dangerous function call
- Boolean input
测试通过以后显示:
Ran 16 tests
OK
7.1 Manual Test Cases
| Test | Expression | Expected Result |
|---|---|---|
| Addition | 12+8 | 20 |
| Subtraction | 10-7 | 3 |
| Multiplication | 6*8 | 48 |
| Division | 20/4 | 5 |
| Operator Precedence | 1+2*3 | 7 |
| Parentheses | (1+2)*3 | 9 |
| Decimal | 1.5+2.3 | 3.8 |
| Unary Negative | -5+8 | 3 |
| Negative Operand | 3*-2 | -6 |
| Division by Zero | 10/0 | Error |
| Invalid Expression | 1++*2 | Error |
所有核心测试均得到了预期结果。
8. Deployment
项目使用前端和后端分别部署的方式。
8.1 Frontend Deployment
Frontend Repository:
https://github.com/hodden2026/832401221_calculator_frontend
Deployment Platform:
GitHub Pages
Public URL:
https://hodden2026.github.io/832401221_calculator_frontend/
8.2 Backend Deployment
Backend Repository:
https://github.com/hodden2026/832401221_calculator_backend
Deployment Platform:
Railway
Public URL:
https://832401221calculatorbackend-production.up.railway.app
生产环境使用:
gunicorn app:app
运行 Flask。
Railway 中数据库路径设置为:
DATABASE_PATH=/data/calculator.db
数据库数据存储在挂载目录中。
9. Screenshots and Demonstration
Screenshot 1 — Public Calculator Homepage

说明:
该截图展示了公网部署后的计算器首页。
页面中可以看到:
- 计算器区域
- 历史记录区域
- 后端状态
前端通过 GitHub Pages 公网访问,并连接 Railway 后端。
Screenshot 2 — Basic Arithmetic

说明:
该截图展示基本四则运算。
系统支持:
+
-
×
÷
用户输入表达式以后,前端把表达式发送到 Flask 后端。
最终计算结果由后端生成并返回。
Screenshot 3 — Operator Precedence

输入:
1+2*3
结果:
7
说明后端能够正确处理数学运算优先级。
乘法会优先于加法进行计算。
Screenshot 4 — Parentheses

输入:
(1+2)*3
结果:
9
说明表达式解析器能够正确处理括号。
Screenshot 5 — Negative Number

输入:
3*-2
结果:
-6
说明后端支持一元负号,并且能够正确处理负数作为运算操作数。
Screenshot 6 — Decimal Calculation

输入:
1.5+2.3
结果:
3.8
说明系统支持小数计算。
Screenshot 7 — Division by Zero

输入:
10/0
后端不会发生程序崩溃,而是返回:
Division by zero
说明系统能够正确处理除零异常。
Screenshot 8 — Invalid Expression

输入:
1++*2
后端返回:
Invalid expression
说明系统能够识别非法表达式,并返回明确的错误信息。
Screenshot 9 — History, Search and Delete

该截图综合展示历史记录相关功能。
成功计算后:
Expression
Result
Created Time
会被保存到 SQLite 数据库。
前端支持:
- 查看历史记录
- 搜索历史记录
- 删除指定历史记录
删除流程为:
Frontend
↓
DELETE /api/history/{id}
↓
Flask
↓
SQLite
↓
GET /api/history
↓
Refresh Frontend
因此删除操作真正修改了后端数据库中的记录。
Screenshot 10 — Dark Theme

该截图展示系统的深色主题。
用户可以在:
Light Theme
Dark Theme
之间切换。
主题偏好使用 LocalStorage 保存。
LocalStorage 只用于界面主题,不用于保存计算历史。
计算历史仍然全部来自后端 SQLite 数据库。
10. Problems Encountered and Solutions
10.1 PowerShell Virtual Environment Activation
开发初期遇到了 PowerShell ExecutionPolicy 问题。
直接执行:
.venv\Scripts\Activate.ps1
无法正常激活虚拟环境。
为了避免修改 Windows 系统安全策略,最终直接运行虚拟环境中的 Python:
.\.venv\Scripts\python.exe app.py
这样既能够使用正确的 Flask 环境,也不需要改变系统 ExecutionPolicy。
10.2 Safe Expression Calculation
项目中一个比较重要的问题是:
如何在不使用 eval 的情况下计算用户输入的数学表达式?
如果直接执行:
eval(expression)
会存在执行任意 Python 代码的安全风险。
因此项目最终使用:
ast.parse()
解析表达式。
然后只允许以下运算:
Numbers
+
-
*
/
Unary +
Unary -
其他 AST Node 会被拒绝。
这样既能够完成数学表达式计算,也避免直接执行用户输入。
10.3 Front-End / Back-End Communication
本地开发阶段:
Frontend:
http://127.0.0.1:5500
Backend:
http://127.0.0.1:5000
因为两个服务的端口不同,浏览器会认为这是跨域请求。
后端通过 CORS Response Headers 允许前端访问 API。
这样 JavaScript Fetch API 才能正常调用 Flask。
10.4 SQLite File Location
本地开发时数据库默认位于:
calculator_backend/calculator.db
部署 Railway 后,运行环境发生变化。
因此数据库代码增加了:
DATABASE_PATH
环境变量支持。
本地没有设置该变量时,继续使用本地:
calculator.db
Railway 中则设置:
DATABASE_PATH=/data/calculator.db
这样同一份代码能够同时支持:
Local Environment
Railway Environment
10.5 Git and GitHub
根据前后端分离要求,本项目使用两个独立 GitHub Repository:
832401221_calculator_frontend
832401221_calculator_backend
两个仓库分别具有:
README.md
codestyle.md
.gitignore
后端 .gitignore 排除了:
.venv
__pycache__
calculator.db
这样不会把本地虚拟环境、缓存和本地数据库提交到 GitHub。
10.6 Railway Deployment
后端部署过程:
GitHub Backend Repository
↓
Railway Service
↓
requirements.txt
↓
Gunicorn
↓
Flask
↓
Volume / Database Path
↓
Public Domain
生产环境使用:
gunicorn app:app
启动 Flask。
10.7 GitHub Pages Deployment
前端通过 GitHub Pages 部署。
在部署以前,前端 API 地址为:
http://127.0.0.1:5000
公网部署后修改为:
https://832401221calculatorbackend-production.up.railway.app
最终形成完整公网架构:
User
↓
GitHub Pages
↓
Railway Flask Backend
↓
SQLite
11. Summary and Learning
通过本次作业,我第一次比较完整地经历了一个前后端分离 Web 项目的开发过程。
项目开始时,我主要关注:
如何实现一个可以正确进行四则运算的计算器?
但是随着项目逐渐完善,我发现一个完整的软件项目不仅仅是实现计算功能。
还需要考虑:
- Requirement Analysis
- Front-End / Back-End Separation
- API Design
- Input Validation
- Error Handling
- Database Persistence
- Security
- Automated Testing
- Git Version Control
- GitHub Repository
- README
- Code Style
- Deployment
- Documentation
在表达式计算部分,我学习了 Python AST,并理解了为什么不能简单使用 eval() 处理用户输入。
在数据库部分,我学习了 SQLite 的基本操作,并使用参数化 SQL 进行历史记录的新增、查询和删除。
在前端开发过程中,我进一步练习了:
- DOM
- Event Listener
- Fetch API
- async / await
- Keyboard Event
- LocalStorage
- CSS Grid
- Flexbox
- Responsive Design
在后端开发过程中,我进一步理解了:
- Flask Route
- REST API
- HTTP Method
- HTTP Status Code
- JSON Request
- JSON Response
在部署阶段,我学习了:
- GitHub Pages
- Railway
- Gunicorn
- Environment Variables
- Public Domain
- Database Path Configuration
通过整个项目,我逐渐理解:
Frontend
Backend
Database
Testing
Deployment
不是互相独立的几个知识点,而是一个完整软件系统中的不同组成部分。
PSP 记录也让我发现,实际开发时间比最开始预计的时间更长。
其中最容易被低估的部分包括:
- API Integration
- Debugging
- Deployment
- Error Handling
- Environment Configuration
如果以后再次开发类似项目,我会:
- 更早完成需求分析。
- 更早设计 API。
- 更早规划数据库。
- 在开发早期加入自动测试。
- 提前测试部署环境。
- 为前后端联调预留更多时间。
- 在开发过程中持续记录 PSP。
- 减少最后阶段集中修改的风险。
通过完成本次作业,我对一个小型 Web 项目从:
Design
↓
Implementation
↓
Testing
↓
Version Control
↓
Deployment
↓
Documentation
这一完整软件开发流程有了更加清晰和实际的认识。
相关推荐
基于MATLAB的复合弹性模量分析,采用混合法则,结合纤维体积分数变化和图形结果。.zip
1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
基于MATLAB的自适应纯追踪曲线跟踪机器人仿真,采用多路径、自适应速度控制和图形界面可视化。.zip
1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
【数据库优化】通信行业B/O域查询性能提升:基于分区裁剪与覆盖索引的秒级溯源系统实现
内容概要:本文围绕通信行业中海量话单与信令数据的查询性能问题,系统阐述了从全表扫描到秒级响应的数据库优化路径。文章以运营商 cdr_detail 表为案例,揭示了因使用函数操作索引列(如DATE(call_start_time))和深分页(LIMIT offset, size)导致的全表扫描问题,并提出完整的优化方案:通过时间范围RANGE分区实现分区裁剪、构建msisdn与时间的联合覆盖索引、消除索引列函数运算、采用游标分页替代OFFSET等方式,使查询性能从P95 8.3秒降至290毫秒。同时介绍了执行计划分析(EXPLAIN)在调优中的关键作用,并展望了HTAP架构、向量化执行与AI自治优化器等未来方向。; 适合人群:具备一定数据库基础知识,从事通信行业数据开发、DBA或后端研发的技术人员,尤其是面对大规模时序数据查询性能瓶颈的工程师;; 使用场景及目标:①解决营业厅用户详单查询、网间结算对账、VoNR信令分析等高并发、低延迟查询场景下的性能问题;②掌握如何通过分区策略、索引设计和SQL改写实现从全表扫描到精准索引访问的优化落地; 阅读建议:建议结合实际生产环境中的大表查询案例,对照本文的反例与正例进行执行计划对比分析,并在测试环境中实践分区表创建、覆盖索引构建及游标分页改写,深入理解每项优化技巧的实际效果。
石墨提纯全球市场结构与产业发展趋势分析报告(QYResearch最新研究).docx
石墨提纯全球市场结构与产业发展趋势分析报告(QYResearch最新研究)
政府科技管理者如何提升科技成果转化政策的精准匹配与执行效率?.docx
政府科技管理者如何提升科技成果转化政策的精准匹配与执行效率?


