9.2 KiB
9.2 KiB
小财记账系统重构计划
文档状态:讨论稿
创建日期:2026-07-22
目的:识别当前系统的设计缺陷与脆弱点,规划结构化改进方案,从“能用”走向“好用”。
一、当前系统现状概述
经过多轮迭代,系统已具备以下核心功能:
- 多用户登录、群聊管理、白名单权限
- 聊天消息发送(文本+图片)、WebSocket实时推送
- AI自动识别采购/付款/发票意图,创建采购记录
- 采购清单(按月汇总)、采购详情(含操作历史、附件)
- 删除/清空采购记录(管理员确认流程)
- 图片压缩上传、附件关联
- 移动端滑动手势、双击全屏、输入中气泡
- 导出CSV、全局采购汇总
但所有功能都是“快速响应需求”的结果,缺乏统一设计,代码结构脆弱,修改一处容易引发多处问题。
二、当前系统的主要问题与重构方向
2.1 数据库与领域模型
存在问题:
purchases表字段通过多次ALTER TABLE添加,历史数据可能格式不一致(如created_at有时间和无时间两种格式)。- 金额计算规则散落在多处代码中,存在“AI返回金额”、“系统自动计算金额”两套逻辑,容易出错。
- 状态机不明确(待处理/已采购/发票已收/已完成),更新时未严格约束状态转换。
- 删除/清空操作依赖业务层手动管理级联,SQLite外键未开启。
改进方向:
- 统一时间格式:所有
created_at和updated_at强制使用yyyy/MM/dd HH:mm:ss格式,在服务端工具函数中生成,不再依赖数据库默认值。 - 金额计算唯一化:
amount字段仅由系统计算(quantity * unit_price + freight),AI 不再提供该字段。删除所有 AI 返回amount的处理逻辑。 - 定义采购状态机:
待采购 → 已采购 → 发票已收 → 已完成,更新状态时检查合法性。 - 开启外键约束:在
db.js中启用PRAGMA foreign_keys = ON;,确保级联删除安全。
2.2 AI 交互层
存在问题:
- Prompt 分散在
analyzeWithDeepSeek函数中,规则越来越多,难以维护。 - AI 返回的 JSON 格式不稳定,有时带
reply有时不带,有时action与字段不匹配。 - 上下文管理简陋:对话历史只保留最近 40 条,无摘要压缩,token 使用效率低。
- 附件补充、删除确认、权限判断等场景,AI 表现不稳定,经常误判意图。
改进方向:
- Prompt 模板化:将 system prompt 拆分为多个模块(角色定义、意图分类、采购规则、删除规则、附件规则、权限规则),动态拼接。
- JSON Schema 校验:定义每种
action对应的必选/可选字段,服务端在解析 AI 返回后立即校验,不符合的驳回或修正。 - 上下文管理升级:
- 近期消息(30 条)保持完整
messages数组。 - 超过 30 条时,调用 AI 生成结构化摘要(当前采购清单),作为
system消息注入。
- 近期消息(30 条)保持完整
- 附件与更新逻辑分离:
- 附件请求仅返回
purchase_item,不包含金额/数量字段,后端强制匹配已有记录并插入附件。 - 更新请求必须明确给出变化字段,后端对比后仅更新真正变化的列。
- 附件请求仅返回
2.3 后端架构
存在问题:
- 所有业务逻辑(AI 调用、数据库操作、权限判断)混在
server.js单文件中,超过 600 行,难以维护。 - 错误处理不统一:有的地方静默失败,有的地方只打印日志,用户得不到反馈。
- 权限检查仅通过中间件
adminOnly,但删除/清空的权限还依赖 AI 判断,存在双重标准。
改进方向:
- 模块化拆分:
routes/:Express 路由(auth, rooms, messages, purchases, upload)ai/:Prompt 模板、AI 调用、结果解析与校验db/:数据库操作封装(含迁移逻辑)ws/:WebSocket 管理(连接、房间、广播)
- 统一错误处理中间件:自定义错误类,全局捕获并返回标准 JSON 格式。
- 权限统一:删除/清空操作仅在后端检查管理员权限,AI 不再参与权限判断(只负责识别意图)。
2.4 前端架构
存在问题:
- 单文件 HTML 包含所有 CSS/JS,超过 800 行,难以维护。
- 状态管理混乱:
messageCache、rooms、pendingFiles等全局变量散落,修改时容易出现不一致。 - 消息渲染使用
innerHTML直接拼接,存在 XSS 风险(虽然做了部分转义,但 Markdown 渲染绕过)。 - 图片加载、滚动位置控制不稳定,经常出现自动跳转。
改进方向:
- 文件拆分:CSS 独立文件、JS 按功能模块拆分(登录、聊天、清单、管理),使用 ES modules 或构建工具(如 esbuild)打包。
- 虚拟 DOM 或模板引擎:考虑引入轻量框架(如 Preact 或 lit-html),避免手动拼接 HTML,同时解决 XSS 问题。
- 状态管理:将所有全局状态集中到一个
store对象,通过事件或 Proxy 驱动 UI 更新。 - 消息渲染优化:图片加载完成后才调整滚动位置,防止跳动;使用懒加载。
2.5 安全性
存在问题:
- JWT secret 使用随机字符串,但未设置过期刷新机制,长期有效。
- 文件上传仅限制格式和大小,未限制上传频率,有被滥用的风险。
- 用户密码在环境变量中明文存储,传输时未加密(仅 HTTPS 保护)。
改进方向:
- JWT 刷新:增加 refresh token 机制,短期 access token(1小时)+ 长期 refresh token(7天)。
- 上传频率限制:使用
express-rate-limit限制每用户每天上传次数。 - 密码哈希:用户密码存储为 bcrypt 哈希,不再明文比较(需要修改登录逻辑和
.env配置方式)。
2.6 运维与部署
存在问题:
- 数据库迁移依赖手动
ALTER TABLE加try/catch,日志不完善,问题难排查。 - 图片存储使用本地磁盘,容器重建后丢失(除非挂载卷)。
- 无备份机制,数据风险高。
改进方向:
- 自动化迁移:使用
better-sqlite3的迁移工具或自定义版本管理系统,记录已执行迁移。 - 对象存储:支持 S3 兼容存储(如 MinIO、阿里云 OSS)作为图片存储后端,本地仅做缓存。
- 定时备份:通过 cronjob 定期导出 SQLite 数据库和上传目录。
三、优先重构顺序建议
| 优先级 | 模块 | 原因 |
|---|---|---|
| P0 | AI交互层(Prompt模板化、JSON Schema校验、附件逻辑分离) | 直接影响用户体验,当前最不稳定 |
| P0 | 金额计算唯一化、时间格式统一 | 数据一致性基础,错误频发 |
| P1 | 后端模块化拆分 | 为后续开发铺路,当前单文件难以维护 |
| P1 | 权限模型统一 | 安全风险,目前依赖AI判断不可靠 |
| P2 | 前端状态管理与文件拆分 | 改善开发体验和运行稳定性 |
| P3 | 安全增强(JWT、密码哈希、上传限制) | 系统公开使用后必须处理 |
| P3 | 运维自动化(备份、存储迁移) | 数据安全,长期运行保障 |
修改记录
server/
├── server.js # 入口,40行(原 588行)
├── db.js # 数据库(新增 foreign_keys = ON)
├── utils.js # timestamp / normalizeTime
├── handlers.js # AI 结果处理 + 共享状态
├── ai/
│ ├── prompt.js # Prompt 模板化
│ ├── validator.js # JSON Schema 校验
│ └── client.js # DeepSeek API 客户端
├── middleware/
│ └── auth.js # JWT + adminOnly
├── routes/
│ ├── auth.js # 登录 / 用户信息
│ ├── rooms.js # 群聊 CRUD
│ ├── messages.js # 消息 + AI 分析
│ └── purchases.js # 采购 CRUD / 汇总 / 导出
├── ws/
│ └── index.js # WebSocket 连接管理 + 广播
└── public/
├── index.html # 纯结构(引用 CSS/JS)
├── css/
│ └── style.css # 独立样式
└── js/
├── store.js # 全局状态
├── chat.js # 消息渲染 + 图片处理
├── ws.js # WebSocket + 聊天列表 + 发送
├── purchases.js # 采购面板 + 详情 + 群聊管理 + 手势
└── auth.js # 登录 / 初始化
P0 完成项
- Prompt 模板化(
ai/prompt.js) - JSON Schema 校验(
ai/validator.js) - AI 客户端封装(
ai/client.js) - 金额计算唯一化(
handlers.js中 amount 仅由系统计算) - 时间格式统一(
utils.jsnormalizeTime)
P1 完成项
- 后端模块化拆分(routes / ai / ws / middleware)
- 权限模型统一(middleware/auth.js)
- 外键约束开启(
db.jsPRAGMA foreign_keys = ON)
P2 完成项
- CSS 独立文件(
public/css/style.css) - JS 模块化拆分(store / chat / ws / purchases / auth)
- 状态集中管理(
Store对象)