832401221_WuHaodong_First Assignment——Front-End and Back-End Separated Calculator System

832401221_吴浩东 832401221_吴浩东 墨衍会员 · AI 创作全网分发 墨衍智能分发 通过墨衍一键同步至各平台 可同步平台 CSDN微信公众号微博知乎掘金百家号博客园抖音小红书 你的文章也可以这样分发 墨衍会员 ¥399起/年,写一次发全网 我也要 + 加入社区

Front-End and Back-End Separation Calculator System

Project Information

ItemInformation
Student ID832401221
ProjectFront-End and Back-End Separation Calculator System
FrontendHTML / CSS / JavaScript
BackendPython / Flask
DatabaseSQLite
Frontend DeploymentGitHub Pages
Backend DeploymentRailway

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

本项目实现了一个基于前后端分离架构的计算器系统。

项目由两个独立部分组成:

  • Frontend:HTML、CSS、JavaScript
  • Backend:Python、Flask、SQLite

前端主要负责:

  • 页面展示
  • 用户输入
  • 按钮交互
  • API 请求
  • 计算结果展示
  • 历史记录展示
  • 历史记录搜索
  • 删除历史记录

后端主要负责:

  • 接收数学表达式
  • 表达式合法性检查
  • 数学表达式计算
  • 异常处理
  • 历史记录持久化
  • 历史记录查询
  • 历史记录删除

本项目最重要的设计原则是:

前端不负责生成最终数学计算结果,所有最终计算均由后端完成。

因此,当后端服务器停止运行时,前端页面虽然仍然能够打开,但是不能独立产生新的有效计算结果。

系统支持的核心功能包括:

  • 加法
  • 减法
  • 乘法
  • 除法
  • 复合表达式
  • 运算优先级
  • 括号
  • 小数
  • 一元正号
  • 一元负号
  • 非法表达式处理
  • 除零异常处理
  • 计算历史持久化
  • 历史记录查询
  • 删除指定历史记录

在完成基本要求以后,还增加了以下扩展功能:

  • 键盘快捷键
  • 历史记录搜索
  • 深色 / 浅色主题切换
  • 后端在线状态显示
  • 清空全部历史记录
  • 响应式页面
  • 历史记录数量显示

2. PSP

PSP2.1Estimated Time (min)Actual Time (min)
Planning3040
· Estimate2025
Development505630
· Analysis4050
· Design Spec3040
· Design Review2020
· Coding Standard3045
· Design4560
· Coding220270
· Code Review3035
· Test90110
Reporting120145
· Test Report4050
· Size Measurement2025
· Postmortem & Improvement Plan6070
Total655815

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

img


4.2 System Architecture Diagram

img


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

系统支持:

KeyFunction
0-9Number input
+Addition
-Subtraction
*Multiplication
/Division
( )Parentheses
.Decimal point
EnterCalculate
BackspaceDelete last character
EscapeClear calculator

History Search

历史记录支持根据以下内容实时搜索:

  • Expression
  • Result
  • Calculation time

搜索操作只改变前端显示,不会修改数据库。


Dark / Light Theme

系统支持:

Light Theme
Dark Theme

LocalStorage 只用于保存主题偏好。

计算历史不会存储在 LocalStorage 中。


Backend Status

前端调用:

GET /api/health

检测后端状态。

后端在线时显示:

后端在线

无法连接时显示:

后端离线

6. API Design

MethodEndpointFunction
GET/api/healthCheck backend status
POST/api/calculateCalculate expression
GET/api/historyGet calculation history
DELETE/api/history/{id}Delete specified history
DELETE/api/historyClear 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

TestExpressionExpected Result
Addition12+820
Subtraction10-73
Multiplication6*848
Division20/45
Operator Precedence1+2*37
Parentheses(1+2)*39
Decimal1.5+2.33.8
Unary Negative-5+83
Negative Operand3*-2-6
Division by Zero10/0Error
Invalid Expression1++*2Error

所有核心测试均得到了预期结果。


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

img

说明:

该截图展示了公网部署后的计算器首页。

页面中可以看到:

  • 计算器区域
  • 历史记录区域
  • 后端状态

前端通过 GitHub Pages 公网访问,并连接 Railway 后端。


Screenshot 2 — Basic Arithmetic

img

说明:

该截图展示基本四则运算。

系统支持:

+
-
×
÷

用户输入表达式以后,前端把表达式发送到 Flask 后端。

最终计算结果由后端生成并返回。


Screenshot 3 — Operator Precedence

img

输入:

1+2*3

结果:

7

说明后端能够正确处理数学运算优先级。

乘法会优先于加法进行计算。


Screenshot 4 — Parentheses

img

输入:

(1+2)*3

结果:

9

说明表达式解析器能够正确处理括号。


Screenshot 5 — Negative Number

img

输入:

3*-2

结果:

-6

说明后端支持一元负号,并且能够正确处理负数作为运算操作数。


Screenshot 6 — Decimal Calculation

img

输入:

1.5+2.3

结果:

3.8

说明系统支持小数计算。


Screenshot 7 — Division by Zero

img

输入:

10/0

后端不会发生程序崩溃,而是返回:

Division by zero

说明系统能够正确处理除零异常。


Screenshot 8 — Invalid Expression

img

输入:

1++*2

后端返回:

Invalid expression

说明系统能够识别非法表达式,并返回明确的错误信息。


Screenshot 9 — History, Search and Delete

img

该截图综合展示历史记录相关功能。

成功计算后:

Expression
Result
Created Time

会被保存到 SQLite 数据库。

前端支持:

  • 查看历史记录
  • 搜索历史记录
  • 删除指定历史记录

删除流程为:

Frontend
↓
DELETE /api/history/{id}
↓
Flask
↓
SQLite
↓
GET /api/history
↓
Refresh Frontend

因此删除操作真正修改了后端数据库中的记录。


Screenshot 10 — Dark Theme

img

该截图展示系统的深色主题。

用户可以在:

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

如果以后再次开发类似项目,我会:

  1. 更早完成需求分析。
  2. 更早设计 API。
  3. 更早规划数据库。
  4. 在开发早期加入自动测试。
  5. 提前测试部署环境。
  6. 为前后端联调预留更多时间。
  7. 在开发过程中持续记录 PSP。
  8. 减少最后阶段集中修改的风险。

通过完成本次作业,我对一个小型 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最新研究)

行业报告QY

政府科技管理者如何提升科技成果转化政策的精准匹配与执行效率?.docx

政府科技管理者如何提升科技成果转化政策的精准匹配与执行效率?

BIZKEEN
评论
收藏 已收藏
分享
返回顶部