
这是需求与信息架构设计稿。图中的范围代表当时的设计目标,本文以当前代码和本次测试结果为准。
一条清晰的主运行线
程序从 main.py 进入 momo_app.app.main。启动层处理运行环境、单实例锁、日志轮转、未捕获异常和 faulthandler,随后加载主题并创建主窗口。把这些事情放在入口,是为了避免页面模块各自处理生命周期,最后出现“窗口关了,线程还在”或“异常只在控制台闪一下”的情况。
主窗口只负责壳层和跨页面协调。真正的业务能力落在几个明确模块里:
main.py
└─ momo_app.app
├─ main_window.py 导航、托盘、提醒、全局拖放
├─ datastore.py SQLite/WAL、迁移、CRUD、备份恢复
├─ pages/records_page.py 项目、记录、资料库、待办、时间线
├─ pages/notes_page.py 独立便签与筛选
├─ pages/stats_page.py 项目和时间统计
├─ pages/reimbursements_page.py
├─ floating.py 快捷记录与大输入窗口
├─ expense_export.py Excel/WPS 导出
└─ live_updates.py SSE 刷新
momo_api.server 局域网 HTTP API、鉴权与 Webhook
这份结构还不是终点。records_page.py 已经增长到 8,391 行,是目前最明确的重构信号。它把项目空间、记录、附件、待办、资料库和大量交互都放在一起,短期便于快速迭代,长期则需要继续拆出视图模型和领域服务。
SQLite/WAL 为什么适合这个阶段
早期版本使用 JSON,优点是直观,缺点也很快出现:局部更新需要改写整份文件,崩溃恢复和并发访问很难建立稳定预期。当前数据层使用 SQLite,包含 meta、projects、records、settings 四张表和五个显式索引。
连接初始化会设置:
PRAGMA journal_mode=WAL;
PRAGMA synchronous=FULL;
PRAGMA foreign_keys=ON;
PRAGMA busy_timeout=10000;
PRAGMA wal_autocheckpoint=1000;
PRAGMA quick_check;
这些参数并不神秘。WAL 让读取和短写入更容易共存,FULL 用性能换更稳妥的落盘行为,外键检查防止关系静默损坏,busy timeout 给短暂锁竞争一个明确等待窗口,启动检查则尽早暴露数据问题。旧 JSON 会先校验再迁移;备份恢复继续兼容旧格式,避免一次存储升级切断历史数据。

API 是受控的扩展面
momo_api.server 使用 ThreadingHTTPServer。当前实现的重点不是把桌面功能全部搬到 Web,而是开放少量高价值入口:健康检查、项目/待办读取、幂等待办创建、签名 Webhook、SSE 事件流,以及手机端报销录入。
网络层要求读写授权;局域网模式必须显式启用,并设置足够强的令牌;请求会检查私有或回环网段、CORS 白名单和请求体大小;Webhook 同时验证签名和事件 ID,避免重放。创建待办支持 Idempotency-Key,因为网络重试不该制造两条相同任务。
仓库中的 OpenAPI 文档有 48 个路径模板、68 个操作。它更接近接口蓝图,覆盖面明显大于当前服务器实现。把“设计过”写成“已经做完”会让后续维护失去基准,所以我在统计页和文章里始终把两者分开。
实时更新不该变成全局刷新
SSE 到达桌面端后,最粗暴的方式是让所有页面全部刷新。小数据时看不出问题,数据量上来以后,界面抖动、焦点丢失和重复查询会一起出现。当前实现把事件类型和页面更新关联起来,只在需要时刷新对应数据,并在后台任务结束时清理对象。
这也是后续重构的方向:让“谁拥有状态、谁能修改、谁负责通知”逐步固定下来。桌面应用的复杂度通常不在算法,而在大量看似独立的交互同时发生时,系统仍能解释自己为什么处于当前状态。
本次可重复验证
本次只读审计运行了以下检查:
python -m compileall -q momo_api momo_app tests tools
结果:COMPILEALL_OK
python -m unittest tests.test_reimbursements tests.test_momo_api -v
结果:Ran 16 tests in 11.440s; OK
python -m momo_app.self_check
结果:火阳 自检通过
通过测试只说明这些已覆盖路径在本次环境中表现符合预期。仓库目前没有覆盖率报告,因此我不会给出一个看起来精确、实际不存在的覆盖率百分比。
评论区