按初级程序员可读、可维护的目标重写项目文档,并定案三项设计决策。 新增 - docs/client/00-getting-started.md 上手指南:装环境、跑起来、常见报错 - docs/client/00-glossary.md 术语表:Outbox、幂等、不可逆阶段等 - docs/templates/task.md 工单与归档模板 - client/requirements.txt 固定依赖版本 - .gitignore 屏蔽 data/、打包产物和调试产物 设计定案 - 本地库只存已领取任务,不再镜像 Admin 任务池;已完成任务永久保留 - 去掉租约、心跳和状态回查;Admin 接口从 5 个降到 3 个 (本项目人工付款,重复下单只产生未付款订单,由人工审核处理) - 打包采用 PyInstaller one-dir:launcher.exe + app/ + data/,便携模式 文档改进 - 补齐可直接抄的代码模板:Worker 线程、事务写库、幂等键、增量加载、错误提示、路径解析 - 状态机由 ASCII 图改为完整转换表,并补充崩溃恢复规则 - 消除文档与现有代码的冲突,02 新增“现状与目标差异”清单 - 待确认事项一律给出临时默认值,避免阻塞开发 - AGENTS.md 新增“小改动直通”,工单必填项由 8 项压缩到 6 项 说明:Gitea 尚未配置,本次无对应工单号;AGENTS.md §0 的 Gitea 信息待补。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
117 lines
9.3 KiB
Markdown
117 lines
9.3 KiB
Markdown
# 00 术语表
|
||
|
||
- 文档状态:基线草案
|
||
- 用途:其他文档里出现的专业词,在这里能查到一句话解释
|
||
|
||
看文档时遇到不认识的词,先查这里。**不要靠猜**——这些词大多和数据安全、不重复下单直接相关,猜错会写出真出问题的代码。
|
||
|
||
## 1. 最重要的三个
|
||
|
||
这三个词撑起了整个项目的可靠性设计,先看懂它们。本节最后还解释了一件事:
|
||
为什么本项目**没有**别的任务系统常见的"租约"和"心跳"。
|
||
|
||
### 幂等(idempotent)
|
||
|
||
**一句话:** 同一个操作做一次和做十次,结果一样。
|
||
|
||
**为什么要有:** 网络会超时。你给 Admin 发了"采购成功",但没收到回复——你不知道 Admin 到底收到没有。如果直接重发,Admin 可能记两笔。幂等就是让重发变安全:每次提交都带一个**幂等键**(`Idempotency-Key`),Admin 看到相同的键就知道"这条我处理过了",直接返回上次的结果,不会重复记账。
|
||
|
||
**在本项目里:** 幂等键存在 `outbox_events.idempotency_key`,格式见 [03 数据模型](03-data-model.md) §5.2。
|
||
|
||
### Outbox(待发件箱)
|
||
|
||
**一句话:** 一张本地表,结果先写进这张表,再由后台慢慢发给 Admin。
|
||
|
||
**为什么要有:** 手机上已经下单了,这一步**不可撤销**。如果这时候网断了,直接把结果丢掉,就等于白下单一次还没人知道。所以流程必须是:先把结果落到本地数据库(连同 Outbox 记录,在同一个事务里),再由后台反复重试发送。发不出去就一直留着,程序重启也还在。
|
||
|
||
**关键区别:** Outbox 重试的是**"提交这个结果"**,不是**"重新做一遍这个任务"**。发送失败只会重发消息,绝不会再去手机上点一次下单。
|
||
|
||
**在本项目里:** 表 `outbox_events`,见 [03 数据模型](03-data-model.md) §5。
|
||
|
||
### 不可逆阶段标记
|
||
|
||
**一句话:** 数据库里的一个时间戳,意思是"这个任务我已经点过下单了,撤不回来了"。
|
||
|
||
**为什么要有:** 程序可能在下单那一刻崩溃或断电。重启后它不知道刚才那单到底成没成——
|
||
这时候如果傻乎乎地重跑一遍,就会买两次。所以规则是:**点下单之前先把这个标记写进数据库**。
|
||
重启后只要看到它有值,就只准去"我的订单"里核对,绝不准重新下单。
|
||
|
||
**为什么它现在特别重要:** 本项目没有租约和心跳(见下方说明),
|
||
防重复下单**全靠它**。改动相关代码要格外小心。
|
||
|
||
**在本项目里:** `task_runs.irreversible_action_at`,规则见 [03 数据模型](03-data-model.md) §7.3。
|
||
|
||
### 为什么没有"租约"和"心跳"
|
||
|
||
你可能在别的任务系统里见过这两个词——Admin 发一个限时凭证证明"这个任务归你",
|
||
Client 定期发心跳续期,过期就得停手。**本项目故意没有这套东西。**
|
||
|
||
原因:本项目不自动付款,采购止于创建订单。就算两台 Client 撞车各下一单,
|
||
产生的也只是重复的**未付款**订单,人工审核时不付即可。为这点代价引入租约、心跳、
|
||
过期判断和一整套中断逻辑,不划算。
|
||
|
||
Admin 想知道某台 Client 是不是卡死了,用"领取后超时重派"就行,Client 不参与。
|
||
完整理由见 [04 接口契约](04-admin-api-contract.md) §1.1。
|
||
|
||
## 2. 数据和接口
|
||
|
||
| 词 | 一句话解释 | 在本项目里指 |
|
||
|---|---|---|
|
||
| 分配 | Admin 决定某个任务归哪台 Client 做。**分配 ≠ 领取**:分配是 Admin 单方面指定,领取是 Client 主动调 `claim` 去拿 | 见 [04](04-admin-api-contract.md) §5.1 |
|
||
| 退避 / backoff | 重试失败后逐次拉长等待时间(1秒、2秒、4秒……),别把服务端打垮 | Outbox 重试和领取轮询,见 [04](04-admin-api-contract.md) §8 |
|
||
| DTO | 只用来在系统之间传数据的简单对象,没有业务逻辑 | Admin 接口收发的那些数据结构 |
|
||
| Repository | 专门放数据库读写代码的一层。**别的地方不许直接写 SQL** | `src/infrastructure/db/` 下的类 |
|
||
| Gateway | 隔离外部系统的一层。换实现(Mock ↔ 真 HTTP)时,上层代码不用改 | `AdminGateway` / `MockAdminGateway` |
|
||
| 状态机 | 规定"什么状态能变成什么状态"的一张表,不在表里的变化就是 bug | 任务状态转换,见 [03](03-data-model.md) §7 |
|
||
| 便携模式 | 程序和它的数据放在同一个文件夹里,整个拷走就能用,不往系统目录写东西 | `data/` 在 `launcher.exe` 旁边,见 [03](03-data-model.md) §2.1 |
|
||
| frozen | Python 判断"我现在是不是打包成 exe 在跑"的标志 | `getattr(sys, "frozen", False)`,见 [03](03-data-model.md) §2.2 |
|
||
| one-dir / one-file | PyInstaller 的两种打包形态:one-dir 产出一个文件夹,one-file 挤成单个 exe。**本项目用 one-dir**,好排查 | 见 [01 需求](01-requirements.md) §8.1 |
|
||
| WAL | SQLite 的一种工作模式,好处是"读"和"写"可以同时进行,不互相锁死 | 建库时 `PRAGMA journal_mode = WAL` |
|
||
| 事务 | 一组数据库操作,要么全部成功,要么全部不生效 | 写任务结果 + 写 Outbox 必须在一个事务里 |
|
||
| schema_version | JSON 数据的版本号。以后结构变了,靠它认出旧数据 | `pdd_data` 和 `admin_payload` 的第一个字段 |
|
||
|
||
## 3. 界面和线程
|
||
|
||
| 词 | 一句话解释 | 在本项目里指 |
|
||
|---|---|---|
|
||
| Qt 主线程 | 唯一允许创建和修改界面控件的线程。**在这个线程里做耗时的事,界面就会卡死转圈** | 见 [02 架构](02-architecture.md) §5 |
|
||
| Worker | 放在后台线程里干耗时活儿(联网、点手机、读大文件)的对象 | `TaskWorker`,模板见 [02](02-architecture.md) §5.1 |
|
||
| 信号 / signal | Qt 里跨线程传消息的机制。后台线程算完了,用信号通知主线程去更新界面 | 后台结果回主线程的唯一正规途径 |
|
||
| 模型/视图 | 表格分成两半:数据在"模型"里,显示在"视图"里。好处是几万行也不卡 | `TaskTableModel` + `TableView` |
|
||
| 增量加载 | 表格先只加载 50 行,用户滚到底了再去数据库拿下一批 | `canFetchMore` / `fetchMore`,见 [05](05-ui-specification.md) §5.3 |
|
||
| 委托 / delegate | 告诉表格"这一列要画成按钮/链接"的画笔对象。**比给每个格子塞一个真控件省资源得多** | "详情"那一列 |
|
||
| InfoBar | Fluent 风格的横条提示。可以停着不消失,还能带按钮 | 可恢复错误的标准提示方式 |
|
||
| 模态对话框 | 弹出来必须先处理它、否则动不了主窗口的窗口。**很打断人,只在必须做决定时用** | 高危确认 |
|
||
| DPI / 显示缩放 | Windows 里"文字和应用的大小"那个百分比设置 | 需验证 100%~200% |
|
||
| 高对比度 | Windows 的一种无障碍主题,颜色极简 | 需验证界面不能只靠颜色区分状态 |
|
||
|
||
## 4. 自动化和安全
|
||
|
||
| 词 | 一句话解释 | 在本项目里指 |
|
||
|---|---|---|
|
||
| uiautomator2 | 用 Python 控制安卓手机点击、滑动、读屏幕的库 | 采集和采购的执行手段 |
|
||
| 控件树 / hierarchy | 手机当前这一屏所有元素的结构化描述(XML),能看到每个元素的文字和坐标 | `device.dump_hierarchy()` 的返回值 |
|
||
| adb | 电脑和安卓手机通信的命令行工具 | 连设备用 |
|
||
| SKU | 一个具体的、可以单独下单的商品组合。比如"黑色 + L 码"是一个 SKU,"黑色 + M 码"是另一个 | `pdd_data.skus`,每个 SKU 有自己的价格 |
|
||
| 规格维度 | 选择商品时的一类选项,比如"颜色"是一个维度,"尺码"是另一个维度 | `pdd_data.dimensions` |
|
||
| 不可逆阶段 | 一旦跨过去就撤不回来的操作阶段(真的把订单提交出去了) | 进入前必须先写 `irreversible_action_at`,见本文 §1 |
|
||
| 演练模式 / dry_run | 前面步骤全做(选规格、选数量、算价格),**就是不点最后那个下单按钮** | 默认模式,见 [01](01-requirements.md) §8 |
|
||
| 人工处理 / manual_review | 程序判断不了,停下来等人看。**绝不自己猜着往下做** | 任务状态之一,比如查到多个可能的订单时 |
|
||
| 订单核对 / reconcile | 程序崩了、不确定到底下单成功没有,去"我的订单"里找证据 | 崩溃恢复的唯一正确做法,**不是重新下单** |
|
||
| Artifact / 诊断产物 | 出错时留下的截图、控件树 XML、步骤日志,用来事后查原因 | 存在文件里,数据库只存路径 |
|
||
| 脱敏 | 把敏感信息(token、Cookie、手机号、地址)从日志和截图里抹掉再保存 | 写 Artifact 前必做 |
|
||
| 门禁 | 一组必须全部满足才允许做某事的条件清单 | 真实下单门禁,见 [06](06-quality-security.md) §3 |
|
||
| 契约测试 | 用同一套测试去考 Mock 和真实 HTTP 两种实现,确保它们行为一致 | `AdminGateway` 的两个实现都要过 |
|
||
|
||
## 5. 流程相关
|
||
|
||
| 词 | 一句话解释 |
|
||
|---|---|
|
||
| 大工单 / Epic | 一个完整产品目标的总纲,下面挂很多小工单 |
|
||
| MVP | 最小可用版本。先做出来能用的一小块,而不是一次做完所有功能 |
|
||
| 单元任务工单 | 实际干活的最小单位,一个人一次能做完、能单独测、能单独回退 |
|
||
| 归档 | 任务做完后,把结论写成 `docs/task/<工单号>-<名称>.md` 存起来 |
|
||
| 基线文档 | `docs/client/` 下这几份,记录长期稳定的规则。改它们要谨慎 |
|
||
|
||
流程细节见仓库根目录 [AGENTS.md](../../AGENTS.md)。
|