第一次作业:前后端分离计算器系统
课程信息说明
| 项目 | 内容 |
|---|---|
| 832402108 | 解皓喆 |
| Assignment Requirements | 前后端分离计算器系统 |
| Objectives of This Assignment | 理解前后端职责划分、HTTP API、表达式解析和数据库持久化 |
| Deadline | 2026 年 10 月 7 日 23:59 |
目录
- 课程信息说明
- Git 仓库和代码规范链接
- PSP 表
- 成品展示
- 设计与实现过程
- 代码说明
- 部署和测试
- 个人总结
Git 仓库和代码规范链接
- Frontend Repository: GitHub - jasonxie2026-fzu/832402108_calculator_frontend · GitHub
- Backend Repository: jasonxie2026-fzu/calculator_backend
- Frontend Code Standard: 832402108_calculator_frontend/codestyle.md at main · jasonxie2026-fzu/832402108_calculator_frontend · GitHub
- Backend Code Standard: jasonxie2026-fzu/calculator_backend
- Frontend Deployment Address: Calculator
- Backend Deployment Address: https://calculator-backend-1-ua8m.onrender.com
PSP 表
| 阶段 | 预计时间(分钟) | 实际时间(分钟) |
|---|---|---|
| 需求分析 | 30 | 20 |
| 系统设计 | 40 | 50 |
| 前端开发 | 90 | 120 |
| 后端开发 | 120 | 60 |
| 表达式计算模块 | 90 | 60 |
| 数据库设计 | 30 | 90 |
| 前后端联调 | 60 | 25 |
| 测试 | 60 | 15 |
| 部署 | 60 | 120 |
| 博客写作 | 90 | 100 |
上表记录了各阶段的实际耗时,用于对比计划与实际的差异。
成品展示
1. 基础计算
前端部署地址为:Calculator
页面采用两栏布局,左侧为计算器,右侧为历史记录。计算结果由后端 API 返回,前端负责显示。
- 图 1:部署后的初始计算器页面
- 图 2:
1 + 2返回3 - 图 3:
1 + 2 * 3返回7 - 图 4:
-5 + 8返回3 - 图 5:
0.5 + 1.25返回1.75
2. 复合表达式
测试1 + 2 * 3时,后端返回7,说明乘法优先于加法。
测试(1 + 2) * 3时,后端返回9,说明括号优先级生效。
3. 错误处理
输入2 / 0后,页面显示Division by zero,说明后端识别了除零错误并返回错误信息。
非法表达式也由后端校验,前端不会自行计算或伪造结果。
4. 历史记录
成功计算后,历史列表显示表达式、结果、时间和删除按钮。刷新页面后,历史记录仍从后端数据库读取。
历史记录功能展示如下:
- 图 6:历史记录列表
- 图 7:刷新后的历史记录
- 图 8:删除指定记录后的历史列表
删除记录的操作可以使用下面两张截图进行前后对比:
图 9:历史记录列表中仍然包含待删除的计算记录,点击该记录右侧的“删除”按钮。
图 10:删除请求完成后,页面重新读取后端历史列表,目标记录不再显示。
5. 取余运算![]()
项目还支持%取余运算。例如输入10 % 3后,后端返回结果1,并将本次计算保存到历史记录中。
图 11:输入10%3后返回结果1,说明后端正确处理了取余运算。
如果只输入%符号而不输入操作数,后端会返回错误提示,前端将错误信息显示在计算器下方。
设计与实现过程
需求分析
本次作业要求实现一个前后端分离的计算器。前端负责用户输入、按钮操作、结果展示和历史展示;后端负责输入校验、表达式解析、计算、异常处理以及数据库操作。
系统架构
浏览器前端 ├── 输入表达式 ├── 发送 HTTP JSON 请求 └── 展示后端返回结果和历史 │ ▼ Node.js 后端 ├── POST /api/calculate ├── GET /api/history ├── DELETE /api/history/:id ├── 表达式解析器 └── SQLite 数据库前端和后端通过 JSON API 通信。前端不计算最终结果,只把表达式发送给后端。
数据库设计
数据库使用 SQLite,表名为calculation_history:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | INTEGER | 主键 |
| expression | TEXT | 用户提交的表达式 |
| result | REAL | 后端计算结果 |
| created_at | TEXT | 计算时间 |
每次计算成功后,后端插入一条记录;页面刷新时重新请求数据库中的历史;删除操作通过后端 DELETE 接口完成。
代码说明
前端请求
前端调用:
fetch(`${API_BASE}/api/calculate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ expression }) });前端只发送表达式,结果使用后端返回的result字段。
后端计算
后端使用递归下降解析器,把表达式拆分为数字、运算符和括号,再按照表达式、项、一元运算和基本值的层级解析。这样可以正确处理运算符优先级、括号、小数以及一元正负号。
项目没有使用eval、exec或其他任意代码执行方式处理用户输入。
错误处理
非法表达式、括号不匹配和除零会返回 HTTP 400 和错误消息,前端将消息显示在计算器下方。
部署和测试
后端公开 API 地址为:
https://calculator-backend-1-ua8m.onrender.com前端部署地址为:
https://eight32402108-calculator-frontend3.onrender.com前端在app.js中通过API_BASE连接后端:
const API_BASE = 'https://calculator-backend-1-ua8m.onrender.com';部署后测试:
- 打开前端公开地址,确认页面显示
API ready。 - 测试
1 + 2 * 3和(1 + 2) * 3。 - 测试非法表达式和除零。
- 刷新页面,确认历史仍然存在。
- 删除一条历史,确认页面更新。
- 直接访问
/api/history,确认后端接口可用。
后端历史接口地址:
https://calculator-backend-1-ua8m.onrender.com/api/history个人总结
这次作业让我更清楚地理解了前端和后端的职责划分。开始时我把注意力放在计算器界面,后来发现真正的难点是让前端只负责交互,把表达式校验和计算放到后端,并且把历史记录持久化到数据库。
实现过程中,表达式解析需要处理运算符优先级、括号和一元负号,不能直接把用户输入当成程序执行。前后端联调时还需要检查请求格式、错误状态和跨域配置。
后续可以继续增加历史搜索、统计信息或科学计算功能。