Frontend/Backend Separated Calculator System — Assignment BlogTable of ContentsFrontend/Backend Separated Calculator System — Assignment Blog1. Course Information2. Git Repository Link and Code Standards Link3. PSP Table4. Presentation of the Finished Product4.1 Main Interface4.2 Addition4.3 Subtraction4.4 Multiplication4.5 Division4.6 Decimal Arithmetic4.7 Operator Precedence4.8 Parentheses4.9 Unary Minus4.10 Division by Zero4.11 Invalid Expression4.12 History Records4.13 Persistence4.14 Delete a Single Record4.15 Clear All4.16 Keyboard Input (Extended Feature)5. Design and Implementation Process5.1 Requirements Analysis5.2 Overall System Architecture5.3 Front-End Design5.4 Back-End Design5.5 API Design5.6 Database Design5.7 Expression Evaluation Algorithm Design5.8 Exception Handling5.9 Frontend/Backend Interaction Process5.10 Deployment Process6. Function Structure Diagram7. Code Explanation (Key Code Design Rationale)7.1 Backend: Expression Parsing (calc.py)7.2 Backend: REST Interface (app.py)7.3 Backend: History CRUD (app.py)7.4 Frontend: Unified Input for Keyboard and Buttons (app.js)7.5 Frontend: History Records (Proof of Persistence)8. Personal Journey and Learnings9. Extended Features (Extra Credit)10. Deployment / Access InformationBackend (public address)Frontend (public address)How to test1. Course InformationCourse for This AssignmentWeb Development TechnologyWeb 开发技术Assignment RequirementsImplement a calculator system with a frontend/backend separated architecture; the backend must do all calculation and persist history in a database; deploy the project and submit the blog, the two GitHub repositories and a publicly accessible address.Objectives of This AssignmentUnderstand the frontend/backend separation architecture, REST API design, database persistence, expression parsing, deployment, and write a complete assignment blog.Other ReferencesAssignment notice: https://bbs.csdn.net/topics/620530837ItemContentStudent王少杰Student ID832401219PlatformWindows Python 3.14 Flask SQLite HTML/CSS/JSDate2026-09-242. Git Repository Link and Code Standards LinkItemLinkFrontend repositoryhttps://github.com/060725/calculator_frontendFrontend code standardhttps://github.com/060725/calculator_frontend/blob/main/codestyle.mdBackend repositoryhttps://github.com/060725/calculator_backendBackend code standardhttps://github.com/060725/calculator_backend/blob/main/codestyle.md3. PSP TablePhaseEstimated (hours)Actual (hours)Requirements analysis0.50.5System design (architecture / API / database)0.50.5Backend: expression parsing calculation module1.52.0Backend: calculation history database module0.50.5Frontend: UI, interaction, keyboard shortcuts2.02.0Frontend/backend integration testing1.01.0Deployment (PythonAnywhere GitHub Pages)0.50.5Blog writing1.01.5Total7.58.54. Presentation of the Finished Product4.1 Main InterfaceThe page shows a dark-themed calculator: the button area is on the left and the history panel is on the right.4.2 AdditionClick128in sequence; the result shows20.4.3 SubtractionAfter clearing, enter15-7; the result shows8.4.4 MultiplicationAfter clearing, enter6×7; the result shows42.4.5 DivisionAfter clearing, enter20÷4; the result shows5.4.6 Decimal ArithmeticAfter clearing, enter3.142.86; the result shows6.4.7 Operator PrecedenceAfter clearing, enter12×3; the result shows7instead of9, proving that multiplication/division have higher precedence than addition/subtraction.4.8 ParenthesesAfter clearing, enter(12)×3; the result shows9, and parentheses have the correct precedence.4.9 Unary MinusAfter clearing, enter3×±2; the result shows-6. You can also type3*-2with the keyboard.4.10 Division by ZeroAfter clearing, enter5÷0; the UI shows a red message: “除数不能为零” (Division by zero is not allowed).4.11 Invalid ExpressionAfter clearing, type onlyand press; the UI shows “表达式无效” (Invalid expression).4.12 History RecordsAfter several calculations, the right panel lists the expressions, results and timestamps in reverse chronological order.4.13 PersistencePressF5to refresh the page; the history records still exist, which proves the data is persisted in the backend database.4.14 Delete a Single RecordClick the×at the top-right of a record; that record is removed from both the list and the database.4.15 Clear AllClick “清空全部” (Clear All) at the top of the panel and confirm; all records are removed.4.16 Keyboard Input (Extended Feature)Type12*3directly with the keyboard and pressEnter; it computes7correctly.Full keyboard support (digits, - * /,( ),Enter,Backspace,Escape) is anextended featurebeyond the basic requirements (see Section 9).5. Design and Implementation Process5.1 Requirements AnalysisThe assignment requires afrontend/backend separatedcalculator system:The frontend provides a graphical interface (dark theme) and supports both mouse clicks and keyboard input;The backend provides an expression-evaluation API and persists history records in a database (SQLite);History records support deleting a single record and clearing all; records must survive page refresh;It must support the four basic operations, decimals, parentheses with precedence, and unary plus/minus;Invalid expressions (e.g., division by zero, a bare operator) must produce friendly error messages;The project must be deployed to a publicly accessible address so that the teaching assistant can verify it.5.2 Overall System Architecture┌─────────────────────────────────┐ ┌─────────────────────────────────┐ │ Browser (Frontend) │ │ Backend Service │ │ calculator_frontend │ HTTP │ calculator_backend │ │ │ ──────► │ │ │ index.html / style.css │ JSON │ app.py REST API │ │ app.js (Fetch calls the API) │ ◄────── │ calc.py Expression parser │ │ · Calculator buttons / keys │ │ SQLite History persistence │ │ · History panel / error msg │ │ (calculator.db) │ └─────────────────────────────────┘ └─────────────────────────────────┘The frontend and backend communicate through a REST API with JSON. The frontend nevertouches the database directly and never computes the result itself — the calculation isalways done on the backend. This is the essence of “frontend/backend separation”.A simple way to verify this: if the backend service is stopped, the frontend can stillaccept input but can no longer obtain any new valid calculation result.5.3 Front-End DesignSingle-page UI: left panel is the calculator button grid, right panel is the history list.Every button carries adata-keyattribute; clicks and keyboard events share one input channel.The display area has three lines: the input expression, the result, and a red error message.After a successful calculation the frontend re-queries the history API to refresh the panel.5.4 Back-End DesignFlask app exposing a small REST API (calculation history CRUD).A hand-writtenrecursive descent parser(calc.py) — noeval/execis used,which satisfies the assignment’s security requirement.CORS is enabled so an independently hosted frontend can call the API cross-origin.SQLite for persistence; awsgi.pyentry is provided for production deployment.5.5 API DesignMethodPathRequest Body / ParamsResponsePOST/api/calculate{expression:12×3}201:{id, expression, result, created_at}GET/api/history—{items:[{id, expression, result, created_at}]}DELETE/api/history/idpath param{ok:true}DELETE/api/history—{ok:true, deleted:n}(optional “clear all”)On success:201with the result, and the record is written to history;On failure (division by zero, invalid expression, etc.):400with a Chinese message in theerrorfield.5.6 Database DesignSQLite database filecalculator.dbwith a single tablehistory:FieldTypeDescriptionidINTEGER PRIMARY KEY AUTOINCREMENTPrimary keyexpressionTEXT NOT NULLThe expression evaluatedresultTEXT NOT NULLThe evaluation resultcreated_atTEXT NOT NULLRecord timeYYYY-MM-DD HH:MM:SSThe table is created automatically on first startup (init_db()), so no manualdatabase initialization is required. History is queried withORDER BY id DESC LIMIT 100,so the records remain visible after a page refresh.5.7 Expression Evaluation Algorithm DesignThe backend uses arecursive descent parser(calc.py):expr : term (( | -) term)* term : factor ((* | / | × | ÷) factor)* factor : ( | -) factor | ( expr ) | numberThe grammar naturally handles “multiplication/division before addition/subtraction”, parentheses, and unary plus/minus;Division by zero raisesValueError(除数不能为零)and a parse failure raisesValueError(表达式无效);Results are formatted uniformly:6.0 → 6,0.30000000000000004 → 0.3.Input is tokenised with a whitelist regex, so arbitrary code can never be executed.5.8 Exception HandlingBackend: business errors raiseValueErrorwith Chinese messages; the API layer mapsthem to400errorfield. Unexpected internal errors are caught and returned as500with a generic message.Frontend: on a non-2xx response, the red message area shows the backend’serrortext; if the backend is unreachable the UI shows “无法连接到后端服务”(Cannot connect to the backend service) instead of a wrong result.5.9 Frontend/Backend Interaction ProcessUser clicks a button / presses a key ↓ Frontend builds the expression string ↓ POST /api/calculate { expression: 12×3 } ↓ Backend validates → parses → calculates → saves to SQLite ↓ 201 { id, expression, result, created_at } ↓ Frontend shows the result and refreshes the history panel (GET /api/history)5.10 Deployment ProcessBackend: deployed withPythonAnywhere(free tier) using thewsgi.pyentrypoint; the Flask app runs behind PythonAnywhere’s web server.Frontend: deployed withGitHub Pages(free static hosting);chooses the production backend address when opened from the deployed domain.Online addresses and test instructions are listed inSection 10.6. Function Structure DiagramFrontend/Backend Separated Calculator System ├── Frontend calculator_frontend │ ├── Calculator UI (dark theme) │ │ ├── Digit / decimal point input │ │ ├── Four basic operator input │ │ ├── Parenthesis input │ │ ├── Sign toggle (±) │ │ ├── Clear (AC) / backspace │ │ └── Evaluate () │ ├── Keyboard shortcuts (extended) │ ├── Error messages (red, division by zero / invalid expression) │ └── History panel │ ├── Shows expression / result / timestamp │ ├── Delete a single record (×) │ └── Clear all (with confirmation) └── Backend calculator_backend ├── POST /api/calculate evaluate expression write history ├── GET /api/history read history ├── DELETE /api/history/id delete one history record ├── DELETE /api/history clear all history └── SQLite persistence7. Code Explanation (Key Code Design Rationale)7.1 Backend: Expression Parsing (calc.py)defparse_term(self):valueself.parse_factor()whileself.peek()in(*,/,×,÷):opself.take()rhsself.parse_factor()ifopin(/,÷):ifrhs0:raiseValueError(除数不能为零)# business error - HTTP 400value/rhselse:value*rhsreturnvalueDesign rationale: The grammar is a three-level recursionexpr → term → factor.Thetermlevel parses afactorfirst and then handles multiplication/division,so the multiplication/division “binds” tighter and naturally has higher precedencethan the addition/subtraction handled by theexprlevel. Thefactorlevel alsohandles parentheses and unary minus, covering cases like(12)×3and3×-2.Noeval/execis used anywhere — the input is parsed with a whitelist token regex.7.2 Backend: REST Interface (app.py)app.route(/api/calculate,methods[POST])defcalculate():expression(request.get_json(silentTrue)or{}).get(expression,).strip()try:resultevaluate(expression)exceptValueErrorasexc:returnjsonify({error:str(exc)}),400curconn.execute(INSERT INTO history (expression, result, created_at) VALUES (?, ?, ?),(expression,result,created_at))conn.commit()returnjsonify({id:cur.lastrowid,expression:expression,result:result,created_at:created_at}),201Design rationale: The endpoint only does “receive expression → validate → evaluate →save to DB → return”. Business errors are uniformly mapped to400 error message, which thefrontend renders as a red message. Parameterized SQL (?placeholders) prevents injection attacks.7.3 Backend: History CRUD (app.py)app.route(/api/history/int:rid,methods[DELETE])defdelete_record(rid):connget_conn()try:conn.execute(DELETE FROM history WHERE id ?,(rid,))conn.commit()finally:conn.close()returnjsonify({ok:True})Design rationale: Deletion goes through the backend API and removes the row from thedatabase for real; the frontend then re-queriesGET /api/historyto refresh the list,so the displayed data always reflects the latest state of the backend database.7.4 Frontend: Unified Input for Keyboard and Buttons (app.js)document.addEventListener(keydown,(event){constkeyevent.key;if(/[0-9]/.test(key))insert(key);elseif(key*)insert(×);elseif(key/){event.preventDefault();insert(÷);}elseif(keyEnter){event.preventDefault();evaluate();}elseif(keyBackspace)backspace();elseif(keyEscape)clearAllInput();});Design rationale: Every button carries adata-keyattribute, and both buttonclicks and keyboard events go through the sameinsert()input channel.*//areautomatically converted to×/÷before being sent to the backend, so clicking withthe mouse and typing with the keyboard behave identically (see screenshot 4.16).This keyboard support is one of the extended features.7.5 Frontend: History Records (Proof of Persistence)asyncfunctionloadHistory(){constresawaitfetch(${API_BASE_URL}/history);constdataawaitres.json();renderHistory(data.items||[]);}Design rationale: History is not stored inlocalStorage; it is read from thebackend SQLite database every time. Therefore the records survive a page refresh(F5), which demonstrates the frontend/backend separation idea that “data ispersisted by the backend” (see screenshot 4.13).8. Personal Journey and LearningsI truly understood frontend/backend separation: the frontend is onlyresponsible for display and interaction, while all calculation and data storageare delegated to the API. The two sides communicate via JSON with clearresponsibilities, so they can be developed and deployed independently.The recursive descent parsergave me a concrete understanding of therelationship between grammar and precedence. Before, I only knew the precedencerules; this time I implemented them with a grammar myself and realized that thelayering ofterm/factoris exactly where precedence comes from.Error handling must reach the frontend: aValueErrorraised in the backendhas to be converted into an HTTP status code plus a user-friendly message, and thefrontend renders it as a red error message — the full chain must be complete.SQLite made persistence painless: no separate database service needed, asingle file does the job. It fits course assignments perfectly and is more thanenough for the CRUD operations on history records.Deployment taught me the difference between local and online environments:the frontend and the backend live on different domains, so CORS must be enabledand the frontend must switch its API address automatically.Through the unified input channel of keyboard shortcuts anddata-key, I merged“clicking” and “typing” into one logic flow and learned the value of abstraction.9. Extended Features (Extra Credit)The following features go beyond the basic requirements and have been implemented anddemonstrated:FeatureDescriptionDemonstrationKeyboard shortcutsFull keyboard input: digits, - * /,( ),Enter,Backspace,EscapeScreenshot 4.16Delete a single history recordEach record has a×button; deletion is executed through the backend APIScreenshot 4.14Clear all history“清空全部” button with a confirmation dialogScreenshot 4.15Red error messagesDivision by zero / invalid expression shown in red, coming from the backendScreenshots 4.10, 4.1110. Deployment / Access InformationBackend (public address)https://060725.pythonanywhere.comFrontend (public address)https://060725.github.io/calculator_frontend/How to testOpen the frontend address in a browser (desktop or mobile).Click or type any expression, e.g.128, then press— the result20is returned by the backend.Check the history panel: records (expression / result / time) are stored in the backend database and surviveF5.Try5÷0— the red message “除数不能为零” appears; trythen— “表达式无效”.To verify separation: stop the backend and press— no new valid result can be computed.
企业数字化 ERP 产品动态
相关推荐
AI编程助手反复读上下文?原理、优化方法与工具实测 从一次深夜改 Bug 的经历说起。我让 AI 编程助手帮忙排查一个登录接口的报错,它先是读了一遍项目结构,又把 package.json、路由文件、配置文件挨个拉进上下文,最后还翻了几个看似无关的工具函数。前后不过十分钟,令牌消耗却抵得上… · 2026/9/26 4:15:52
AI代理安全扫描器OpenClaw实战:行为审计与CI/CD集成 1. 从一条告警说起:AI代理安全为什么突然成了刚需上个月帮一个做跨境电商的朋友排查他们内部工具链的问题,起因特别简单——他们用自动化代理去跑竞品价格监控,结果某天早上发现代理进程全部卡死,日志里反复刷同一行报错ÿ… · 2026/9/26 4:15:52
大模型横向盘点:从闭源到开源,场景化选型与本地部署实战指南 这几年网上关于大模型的榜单和盘点铺天盖地,但说实话,大部分要么是厂商通稿味儿太浓,要么就是把几百个模型名字罗列在一起,看得人头晕,最后也没整明白到底该用哪个。我花了不少时间把全球主流的大模型从闭源到开源捋了… · 2026/9/26 4:15:46
如何考察北京地区生产厂家的彩箱与瓦楞纸箱资质 在北京地区筛选瓦楞纸箱与彩箱生产厂家时,核心在于考察其生产体系的完整性、定制响应的灵活性以及质检标准的稳定性。具备从设计到生产一站式服务能力的企业,通常能针对不同预算提供平衡质感与成本的方案,而非单纯依赖低价竞争或仅承接超大订… · 2026/9/26 6:14:58
ARDM深度解析:Redis可视化客户端的协议感知与生产级设计 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 6:14:52
STM32CubeProgrammer 烧录全攻略:ST-Link、串口与USB下载实战 STM32 开发这几年,工具链的变化其实挺大的。早些年大家烧程序基本就是 Keil MDK 里点一下 Download 按钮,或者用 J-Link 的 J-Flash 单独操作,再老一点用 ST-Link Utility。后来 ST 官方把 ST-Link Utility 停更了,全面转向STM32C… · 2026/9/26 6:14:52
risky-changes 技能剖析:为什么单元测试全绿,改动仍是坏主意? risky-changes 技能剖析:为什么单元测试全绿,改动仍是坏主意? 【免费下载链接】skills access to david ondrejs personal agent skills 项目地址: https://gitcode.com/gh_mirrors/skills46/skills
在 skills(David Ondre… · 2026/9/26 6:14:52
扣子(Coze)实战:从零搭建能干活的Agent工作流 1. 这不是“又一篇Agent教程”,而是我踩了37次坑后整理的实操路线图你搜“Agent入门”时,看到的大多是概念堆砌、框架罗列、API调用示例——讲清楚了“怎么调”,却没人告诉你“为什么这么调”;演示了“能跑通”,但没说… · 2026/9/26 6:14:52
Claude Code模板体系实战:从零搭建可复用的AI协作模板库 最近终于有空把 claude-code-templates 这套模板体系从头到尾重写了一遍。玩 claude-code 也有一阵子了,刚开始我跟大多数人一样,把它当成智能问答终端用,遇到问题直接开问,结果就是每次会话都像跟一个新同事合作:它不… · 2026/9/26 6:14:52
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46