ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

软件工程实践第一次作业:前后端分离计算器系统

软件工程实践第一次作业:前后端分离计算器系统 软件工程实践第一次作业前后端分离计算器系统Course for This Assignment软件工程实践Assignment Requirements前后端分离计算器系统Objectives of This Assignment掌握前后端分离架构、接口开发、数据库持久化、系统设计与博客文档撰写姓名 / 学号吴正杨 / 24127167Other ReferencesCSDN社区作业文档仓库与代码规范链接Frontend Repository:https://github.com/Wzy13850918263/24127167_calculator_frontendBackend Repository:https://github.com/Wzy13850918263/24127167_calculator_backendFrontend Code Standard:https://github.com/Wzy13850918263/24127167_calculator_frontend/blob/main/codestyle.mdBackend Code Standard:https://github.com/Wzy13850918263/24127167_calculator_backend/blob/main/codestyle.md目录PSP 表格需求分析系统设计与架构功能成品展示关键代码解释遇到的问题与学习总结1. PSP 表格PSP2.1Personal Software Process Stages预估耗时分钟实际耗时分钟Planning计划3025· Estimate· 估计任务工时1010Development开发320360· Analysis· 需求分析3025· Design· 系统设计、API、数据库设计4045· Design Review· 设计复审1515· Coding Standard· 代码规范 codestyle 编写1515· Coding· 后端编码逆波兰 数据库接口90110· Coding· 前端页面编码6070· Test· 测试所有功能与异常用例3035Reporting报告150160· Test Report· 测试截图收集4040· Postmortem· 总结、博客撰写90100Total总计5005452. 需求分析本作业要求实现一个前后端完全分离的计算器系统。核心硬性要求所有计算逻辑必须在后端完成。前端只负责收集用户输入的表达式字符串通过 HTTP POST 发给后端禁止前端算好再把结果发给后端存储。计算历史持久化在后端数据库刷新页面历史不能丢失不允许使用浏览器 localStorage。支持复合表达式运算符优先级、括号、小数、一元正负号如-5、3*-2。异常处理非法表达式、除零错误要返回友好提示。严禁使用 eval / exec直接执行用户输入。前端、后端分别建立独立 GitHub 仓库均含 README.md 与 codestyle.md。支持按 id 删除指定历史记录。技术选型后端Python FastAPI轻量、自动生成接口文档数据库SQLite零配置、文件型适合课程作业前端原生 HTML CSS JavaScript无框架专注前后端通信表达式计算手写逆波兰后缀表达式算法不使用 eval3. 系统设计与架构3.1 整体架构浏览器前端 (index.html) │ HTTP / JSON ▼ FastAPI 后端 (main.py) │ ├── 表达式解析模块词法分析 → 中缀转后缀 → 后缀求值 ├── 历史记录模块SQLAlchemy ORM 增删查 ▼ SQLite 数据库 (calculator.db)验证方式停掉后端服务后前端按钮还能点但无法得到任何计算结果说明计算确实发生在后端。3.2 API 接口设计接口方法作用请求 / 返回/api/calculatePOST提交表达式并计算、写入历史请求{expression:(12)*3}返回{success:true,result:9.0}/api/historyGET获取全部历史按时间倒序返回{history:[...]}/api/history/{id}DELETE删除指定 id 的历史记录返回{success:true,msg:删除成功}成功响应样例{success:true,expression:(12)*3,result:9.0}错误响应样例{success:false,message:除数不能为零}3.3 数据库表设计表名calc_history字段类型说明idInteger 主键自增expressionString用户输入的表达式resultFloat计算结果created_atDateTime计算时间默认当前时间3.4 表达式计算模块思路为遵守禁止 eval的要求我用经典的栈 逆波兰三步走词法分析 tokenize把字符串拆成 token 列表特别处理一元负号——当-出现在开头或运算符后时标记为u-一元负号与二元减法区分开。中缀转后缀 infix_to_postfix用运算符栈按优先级u-* / -把中缀表达式转成后缀表达式。后缀求值 calc_postfix遍历后缀表达式遇到数字入栈遇到运算符弹出两个数计算后把结果入栈。除零时抛出ZeroDivisionError。4. 功能成品展示说明以下截图按顺序上传对应我本地博客截图文件夹中的图片4.1 主界面简洁的计算器界面上方为表达式输入框与结果区下方为按钮区最下方为计算历史列表。4.2 基础四则运算输入128后端返回结果 20。4.3 小数运算输入2.5*4结果 10。4.4 括号与运算符优先输入(12)*3结果 9验证括号改变优先级正确。4.5 一元负号输入3*-2结果 -6验证运算符后接负号正确识别。4.6 除零错误处理输入5/0前端显示错误除数不能为零该错误不会写入历史。4.7 非法表达式处理输入括号不匹配的(12提示括号不匹配。4.8 计算历史列表所有成功计算的记录都持久化在后端数据库按时间倒序展示。4.9 刷新页面历史不丢失刷新页面后前端重新调用/api/history从数据库拉取历史依然存在。4.10 删除历史记录点击某条记录的删除按钮后端从数据库真实删除前端列表同步刷新。5. 关键代码解释5.1 前端提交表达式并展示结果前端不做任何计算只把表达式 POST 给后端再根据返回的success字段决定展示结果还是错误信息asyncfunctionsubmitCalc(){constexprinputDom.value.trim();constrespawaitfetch(BACKEND_URL/api/calculate,{method:POST,headers:{Content-Type:application/json},body:JSON.stringify({expression:expr})});constdataawaitresp.json();if(data.success){resultDom.innerText结果 data.result;}else{resultDom.innerText错误data.message;}loadHistory();// 计算后刷新历史}5.2 后端计算接口统一异常处理后端用 try/except 分别捕获除零和其他表达式错误返回统一的 JSON 结构无论成功失败都关闭数据库会话app.post(/api/calculate)defcalc(req:CalcRequest):dbSessionLocal()try:rescalculate_expression(req.expression)recordCalcHistory(expressionreq.expression,resultres)db.add(record);db.commit();db.refresh(record)return{success:True,expression:req.expression,result:res}exceptZeroDivisionError:return{success:False,message:除数不能为零}exceptExceptionase:return{success:False,message:表达式错误str(e)}finally:db.close()5.3 核心一元负号识别词法分析时判断-是一元还是二元这是支持3*-2、-59的关键ifc-:ifi0orexpr[i-1]in(-*/:tokens.append(u-)# 一元负号i1continuetokens.append(c)# 二元减法5.4 历史记录的删除删除接口按 id 查询查不到返回 404查到则从数据库删除并提交app.delete(/api/history/{hid})defdel_history(hid:int):dbSessionLocal()itemdb.query(CalcHistory).filter(CalcHistory.idhid).first()ifnotitem:raiseHTTPException(status_code404,detail记录不存在)db.delete(item);db.commit();db.close()return{success:True,msg:删除成功}6. 遇到的问题与学习总结前后端跨域问题一开始用浏览器直接打开index.htmlfile 协议fetch 后端接口被浏览器 CORS 策略拦截。解决方法是在 FastAPI 里加上CORSMiddleware允许跨域。一元负号的识别最容易出错的是3*-2和开头的-5。一开始没区分一元负号和二元减号导致解析栈里数字和运算符数量对不上。后来在词法分析阶段根据-前面的字符判断标记成独立的u-token 并给最高优先级解决。除零与非法表达式不能让服务崩溃用 try/except 在接口层兜底把异常转成友好的 JSON 错误返回而不是让后端 500。历史持久化的验证一开始容易把历史存在前端内存里刷新就没了。这次每次计算后都写 SQLite、每次进页面都重新拉/api/history确保刷新和重启后端后数据都在。收获真正理解了前后端分离——前端是哑巴只负责展示和发请求后端才是大脑负责计算和存数据。也第一次完整走了一遍写代码 → git 提交 → push 到 GitHub → 写文档的工程流程。后续可优化方向增加科学计算三角函数、开方历史记录分页用 ngrok 把后端暴露到公网方便他人在线访问
返回列表