docs: establish ShopHelm harness coding baseline
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
# AI 开发入口
|
||||
|
||||
> ShopHelm coding agent 的固定入口。硬性实现规则见 [`05-coding-rules.md`](05-coding-rules.md)。
|
||||
|
||||
## 一句话定位
|
||||
|
||||
ShopHelm 是面向小团队跨境卖家的 Windows 本地运营工作台。
|
||||
|
||||
首发闭环是:管理店铺和独立 Chrome profile,维护商品草稿与简单图片合成,复用多语言客服话术并记录跟进。
|
||||
|
||||
## 必读顺序
|
||||
|
||||
每次写代码前完整读取:
|
||||
|
||||
1. [`../AGENTS.md`](../AGENTS.md)
|
||||
2. [`01-vision.md`](01-vision.md)
|
||||
3. [`02-requirements.md`](02-requirements.md)
|
||||
4. [`03-tech-stack.md`](03-tech-stack.md)
|
||||
5. [`04-architecture.md`](04-architecture.md)
|
||||
6. [`05-coding-rules.md`](05-coding-rules.md)
|
||||
7. 与任务相关的 [`api.md`](api.md) 和 [`routes.md`](routes.md)
|
||||
8. [`06-tasks.md`](06-tasks.md)
|
||||
9. [`../progress.md`](../progress.md)
|
||||
10. [`current-state.md`](current-state.md)
|
||||
|
||||
## 固定开工流程
|
||||
|
||||
1. `pwd`,确认根目录是 `D:\OPC\shop_helm`。
|
||||
2. 读取 `progress.md` 和 `current-state.md`。
|
||||
3. 若有 `.git`,运行 `git status --short --branch` 和 `git log --oneline -5`。
|
||||
4. 若有 `go.mod`,运行 `./init.ps1`;若没有,只领取 T-001。
|
||||
5. 若基线测试或构建失败,先记录并修复基线。
|
||||
6. 在 `06-tasks.md` 中领取第一个 `TODO` 且依赖均为 `DONE` 的任务。
|
||||
7. 把任务改为 `DOING` 后再编辑代码。
|
||||
|
||||
## 当前阶段
|
||||
|
||||
当前处于“文档基线完成、生产代码尚未初始化”阶段。
|
||||
|
||||
实施顺序:
|
||||
|
||||
1. Phase 0:Go、Gio、SQLite 工程地基。
|
||||
2. Phase 1:先验证 Gio 表格/表单、Chrome profile 启动、图片预览与导出三个高风险点。
|
||||
3. Phase 2:业务阶段一,多店铺运营台。
|
||||
4. Phase 3:业务阶段四,商品运营助手。
|
||||
5. Phase 4:业务阶段五,客服助手。
|
||||
6. Phase 5:稳定性、完整验收和 Windows 打包。
|
||||
|
||||
## 领取和完成规则
|
||||
|
||||
- 一轮只领取一个任务。
|
||||
- 依赖未完成不跳步。
|
||||
- `DOING` 同一时间最多一个。
|
||||
- 验收必须使用任务中列出的命令和可观察步骤。
|
||||
- 只有代码、测试、文档和手工 smoke 全部符合时才能标 `DONE`。
|
||||
- 完成后追加 `progress.md`,覆盖更新 `current-state.md`,再执行 `clean-state-checklist.md`。
|
||||
- 多 agent 并发时改用 [`tasks/README.md`](tasks/README.md),执行记录写入各自任务文件。
|
||||
|
||||
## 首发范围
|
||||
|
||||
首发必须交付:
|
||||
|
||||
- 今日工作台。
|
||||
- 店铺 CRUD、筛选、快捷入口、独立 Chrome profile、普通无认证代理、运行状态、备份恢复。
|
||||
- 商品草稿、文案模板、价格计算、检查清单、CSV/XLSX、图片素材。
|
||||
- 一张底图加一张叠加图的预览、位置/缩放/透明度调整及 PNG/JPG 导出。
|
||||
- 多语言客服话术、一键复制、买家问题与跟进提醒。
|
||||
|
||||
首发明确不做:
|
||||
|
||||
- 平台账号密码保存。
|
||||
- 代理账号密码自动注入。
|
||||
- 自动登录、验证码处理、批量发消息、批量改商品。
|
||||
- 反指纹、防关联、绕过风控或批量爬取。
|
||||
- 完整订单、库存、财务、广告和团队权限系统。
|
||||
- Photoshop 式多图层、滤镜、抠图和复杂排版。
|
||||
|
||||
## 事实来源
|
||||
|
||||
业务和技术事实只信:
|
||||
|
||||
- 本仓库 `docs/` 中的权威文档。
|
||||
- 已应用的 SQLite migration。
|
||||
- `go.mod` / `go.sum` 中已锁定的依赖。
|
||||
- 当前代码、测试和真实运行结果。
|
||||
- 第三方平台的官方文档;使用前需在任务中记录具体来源和访问日期。
|
||||
|
||||
不把聊天记忆、旧导出文件、浏览器页面猜测、临时实验或未引用草稿当作当前事实。
|
||||
|
||||
## 常见任务入口
|
||||
|
||||
做 Gio 页面:
|
||||
|
||||
1. 查 `02-requirements.md` 的需求 ID。
|
||||
2. 查 `routes.md` 的页面职责。
|
||||
3. 查 `api.md` 的服务调用。
|
||||
4. 遵守 `04-architecture.md` 的 UI 异步边界。
|
||||
|
||||
做 SQLite:
|
||||
|
||||
1. 查 `04-architecture.md` 的数据模型。
|
||||
2. 新增不可变 migration。
|
||||
3. 同步 repository、`api.md` 和相关测试。
|
||||
|
||||
做 Chrome 启动:
|
||||
|
||||
1. 查 `api.md` 的 `BrowserLauncher` 合约。
|
||||
2. 只使用外部 Chrome 和独立 profile。
|
||||
3. 检查 profile 占用、启动参数、进程状态和错误映射。
|
||||
|
||||
做图片:
|
||||
|
||||
1. Gio 只负责交互和预览。
|
||||
2. 正式导出走独立 CPU 合成管线。
|
||||
3. 校验预览与导出几何参数一致,且不覆盖原图。
|
||||
|
||||
## 标准命令
|
||||
|
||||
T-001 完成前,`./init.ps1` 会因缺少 `go.mod` 主动失败。
|
||||
|
||||
T-001 完成后:
|
||||
|
||||
```powershell
|
||||
./init.ps1
|
||||
go run ./cmd/shophelm
|
||||
go test ./...
|
||||
go vet ./...
|
||||
go build -o build/shophelm.exe ./cmd/shophelm
|
||||
```
|
||||
|
||||
改动 Gio 交互、Chrome 进程、备份恢复或图片导出时,还需执行对应任务的 Windows 手工 smoke。
|
||||
@@ -0,0 +1,59 @@
|
||||
# 项目愿景
|
||||
|
||||
## 一、核心目标
|
||||
|
||||
店小航 ShopHelm 要解决跨境卖家在多个店铺之间反复找账号、找浏览器环境、找商品资料和找客服话术的问题。
|
||||
|
||||
> 让小团队运营人员从一个本地工作台进入正确店铺环境,完成商品资料整理和客服跟进,减少混店、漏事和重复劳动。
|
||||
|
||||
它不是完整 ERP,也不是规避平台风控的浏览器工具,而是围绕日常运营动作组织信息和入口的桌面工作台。
|
||||
|
||||
## 二、目标用户
|
||||
|
||||
- **店铺运营人员**:同时维护多个平台或站点,需要快速进入正确店铺、整理商品并处理买家问题。
|
||||
- **小团队负责人**:需要知道店铺负责人、最近维护状态、待处理事项,并沉淀可交接的模板。
|
||||
- **个人跨境卖家**:希望用一个本地、低部署成本的工具替代零散表格、书签和临时文件夹。
|
||||
|
||||
首发不面向大型企业复杂组织,也不提供多人实时协作。
|
||||
|
||||
## 三、产品原则
|
||||
|
||||
- **每日工作优先**:第一屏直接呈现近期店铺、今日待办、待优化商品和客服跟进。
|
||||
- **人工可控优先**:影响平台数据或用户文件的动作必须可见、可确认、可追踪。
|
||||
- **本地优先**:首发无需云账号,业务数据保存在用户电脑。
|
||||
- **平台合规优先**:官方 API 高于文件导入导出,文件流程高于辅助 RPA。
|
||||
- **资料沉淀优先**:店铺、商品、图片和话术使用结构化关联,支持交接和复用。
|
||||
- **小步交付**:按店铺、商品、客服顺序完成闭环,不同时铺开订单、库存、财务和广告。
|
||||
- **原始数据安全**:不明文保存密码,不自动删除 profile,不覆盖原始图片。
|
||||
|
||||
## 四、核心价值
|
||||
|
||||
| 价值 | 用户得到的结果 |
|
||||
| --- | --- |
|
||||
| 找对店 | 从店铺记录直接打开对应的独立 Chrome profile 和后台入口 |
|
||||
| 少重复 | 商品文案、图片素材、价格计算和客服话术可以复用 |
|
||||
| 不漏事 | 今日工作台聚合店铺待办、商品待优化和客服跟进 |
|
||||
| 可交接 | 店铺负责人、模板、备注和处理状态集中保存 |
|
||||
| 可控 | 自动化保持辅助性质,关键操作由用户确认 |
|
||||
|
||||
## 五、成功标准
|
||||
|
||||
首发成功不以功能数量衡量,而以用户是否愿意每天打开衡量:
|
||||
|
||||
- 至少 20 个店铺可以稳定管理和分别打开。
|
||||
- 用户无需在文件夹和书签中寻找 profile 与后台链接。
|
||||
- 商品草稿、模板和图片素材能够按店铺归档。
|
||||
- 常见 Logo、角标或水印叠加无需打开复杂设计软件。
|
||||
- 客服人员能快速找到并复制合适话术,按时处理跟进。
|
||||
- 重启应用后业务数据、状态和关联不丢失。
|
||||
|
||||
## 六、非目标
|
||||
|
||||
- 不做浏览器指纹伪装、环境伪装或“防关联”承诺。
|
||||
- 不做自动绕验证码、自动批量登录、刷单、刷评或未经确认的批量发送。
|
||||
- 不做大规模抓取 Shopee、Lazada、Amazon 等平台页面。
|
||||
- 不做完整订单中心、WMS、财务结算、广告投放和利润分析。
|
||||
- 不做 Photoshop 级图片编辑器。
|
||||
- 不在首发提供云同步、多人权限和移动端。
|
||||
|
||||
具体范围与验收标准见 [`02-requirements.md`](02-requirements.md)。
|
||||
@@ -0,0 +1,215 @@
|
||||
# 需求
|
||||
|
||||
> 本文只定义产品需要什么以及如何验收。技术实现和字段存储方式见 [`04-architecture.md`](04-architecture.md)。
|
||||
|
||||
## 一、业务现状
|
||||
|
||||
| 项 | 当前事实 |
|
||||
| --- | --- |
|
||||
| 用户 | 个人卖家或小团队运营人员,同时维护多个跨境店铺 |
|
||||
| 常见痛点 | 店铺入口、Chrome 会话、商品资料、图片、话术和待办分散 |
|
||||
| 当前项目 | 空项目,尚无生产代码和历史数据 |
|
||||
| 首发平台 | Windows 10/11 x64 桌面 |
|
||||
| 首发平台业务 | 字段支持多平台,功能和示例优先覆盖 Shopee |
|
||||
| 数据来源 | 用户手工录入、本地文件、CSV/XLSX;平台官方 API 后置 |
|
||||
| 网络边界 | 核心数据管理离线可用;打开 Chrome 或显式代理检测时才联网 |
|
||||
|
||||
## 二、用户角色
|
||||
|
||||
- **店铺运营**:维护店铺、商品、图片、客服记录和个人待办。
|
||||
- **团队负责人**:在同一台电脑查看负责人、状态、模板和处理记录。
|
||||
- **未登录用户**:首发没有 ShopHelm 云账号,启动本机应用即可使用。
|
||||
|
||||
首发没有应用内角色权限。平台后台权限仍由 Chrome 中的平台账号决定。
|
||||
|
||||
## 三、发布顺序
|
||||
|
||||
1. 业务阶段一:多店铺运营台。
|
||||
2. 业务阶段四:商品运营助手。
|
||||
3. 业务阶段五:客服助手。
|
||||
|
||||
工程上先完成地基和高风险原型,再依上述业务顺序交付。
|
||||
|
||||
## 四、首发功能需求
|
||||
|
||||
### 4.1 今日工作台
|
||||
|
||||
| ID | 需求 | 优先级 |
|
||||
| --- | --- | --- |
|
||||
| DASH-001 | 展示最近打开的店铺,并可直接再次打开 | P0 |
|
||||
| DASH-002 | 展示今天到期和已逾期的店铺待办、客服跟进 | P0 |
|
||||
| DASH-003 | 展示待上架、需优化的商品和最近图片导出 | P0 |
|
||||
| DASH-004 | 提供新增店铺、商品、客服跟进和图片预览入口 | P0 |
|
||||
|
||||
### 4.2 多店铺运营台
|
||||
|
||||
| ID | 需求 | 优先级 |
|
||||
| --- | --- | --- |
|
||||
| STORE-001 | 新增、查看、编辑、归档店铺 | P0 |
|
||||
| STORE-002 | 维护平台、国家/站点、店铺名、账号标识、负责人、标签、状态和备注 | P0 |
|
||||
| STORE-003 | 按关键词、平台、国家、负责人、标签和状态筛选 | P0 |
|
||||
| STORE-004 | 每个店铺绑定唯一 Chrome profile 目录和起始地址 | P0 |
|
||||
| STORE-005 | 一键用绑定 profile 打开外部 Chrome,并更新最后打开时间 | P0 |
|
||||
| STORE-006 | 显示未运行、启动中、运行中、已退出和启动失败状态 | P0 |
|
||||
| STORE-007 | profile 已被占用时阻止重复启动,并给出可操作提示 | P0 |
|
||||
| STORE-008 | 维护订单、商品、营销、客服、数据等快捷链接,并用当前店铺 profile 打开 | P0 |
|
||||
| STORE-009 | 可选配置 HTTP/HTTPS/SOCKS 普通无认证代理 | P0 |
|
||||
| STORE-010 | 维护店铺待办及到期时间 | P0 |
|
||||
| STORE-011 | 备份并恢复本地业务数据和配置引用 | P0 |
|
||||
| STORE-012 | 批量打开、分组视图和快捷键 | P2 |
|
||||
|
||||
归档店铺不得自动删除 Chrome profile。首发不支持需要用户名/密码认证的代理自动注入。
|
||||
|
||||
### 4.3 商品运营助手
|
||||
|
||||
| ID | 需求 | 优先级 |
|
||||
| --- | --- | --- |
|
||||
| PROD-001 | 新增、查看、编辑、归档商品草稿 | P0 |
|
||||
| PROD-002 | 维护店铺、商品名、SKU、成本、售价、币种、库存、链接、状态和备注 | P0 |
|
||||
| PROD-003 | 管理标题模板和描述模板 | P0 |
|
||||
| PROD-004 | 维护上架检查项,并显示未完成项 | P0 |
|
||||
| PROD-005 | 根据明确输入计算收入、成本和基础利润参考,不把结果称为财务利润 | P0 |
|
||||
| PROD-006 | CSV 和 XLSX 导入/导出商品草稿,导入前显示校验结果 | P0 |
|
||||
| PROD-007 | 关联主图、详情图、Logo、角标和水印等本地图片素材 | P0 |
|
||||
| IMG-001 | 选择一张底图和一张叠加图进行预览 | P0 |
|
||||
| IMG-002 | 调整叠加图位置、缩放和透明度,并可恢复默认值 | P0 |
|
||||
| IMG-003 | 选择输出尺寸与 PNG/JPG 格式后导出合成图 | P0 |
|
||||
| IMG-004 | 导出记录关联回商品,且默认不覆盖任何输入文件 | P0 |
|
||||
| PROD-008 | 多语言草稿、关键词库和标题候选 | P1 |
|
||||
| IMG-005 | 保存图片模板、批量套用和批量导出 | P2 |
|
||||
|
||||
首发图片工具只支持“单底图 + 单叠加图”。旋转、混合模式、滤镜、抠图、文字排版和任意多图层不在范围内。
|
||||
|
||||
### 4.4 客服助手
|
||||
|
||||
| ID | 需求 | 优先级 |
|
||||
| --- | --- | --- |
|
||||
| CS-001 | 新增、查看、编辑、归档客服话术 | P0 |
|
||||
| CS-002 | 按售前、物流、退款、退货、催发货、评价、缺货、尺码、售后等分类 | P0 |
|
||||
| CS-003 | 维护语言、标题、正文和使用次数 | P0 |
|
||||
| CS-004 | 一键复制话术正文,并给出成功或失败反馈 | P0 |
|
||||
| CS-005 | 记录买家问题、店铺、订单标识、问题类型、状态、下次跟进时间和备注 | P0 |
|
||||
| CS-006 | 按店铺、状态和到期时间筛选跟进记录 | P0 |
|
||||
| CS-007 | 支持变量预览、多语言回复草稿和超时提醒 | P1 |
|
||||
| CS-008 | 对接官方聊天 API 或辅助打开聊天页 | P2 |
|
||||
|
||||
首发不读取真实聊天消息,也不自动发送回复。
|
||||
|
||||
### 4.5 设置和数据安全
|
||||
|
||||
| ID | 需求 | 优先级 |
|
||||
| --- | --- | --- |
|
||||
| SET-001 | 配置 Chrome 可执行文件、profile 根目录和默认图片导出目录 | P0 |
|
||||
| SET-002 | 配置默认货币、显示语言和日志目录 | P0 |
|
||||
| SEC-001 | SQLite、日志、导出文件和测试数据中不出现平台明文密码 | P0 |
|
||||
| SEC-002 | 所有外部文件路径在使用前校验存在性、类型和可读写权限 | P0 |
|
||||
| SEC-003 | 删除、恢复、覆盖和批量动作必须有明确影响说明和确认 | P0 |
|
||||
| AUDIT-001 | 记录店铺打开、备份恢复、导入和图片导出结果,不记录秘密 | P1 |
|
||||
|
||||
## 五、核心用户流程
|
||||
|
||||
### 5.1 打开正确店铺
|
||||
|
||||
1. 用户在店铺列表搜索或筛选目标店铺。
|
||||
2. 用户点击打开或某个快捷入口。
|
||||
3. 系统检查 Chrome 路径、profile 和代理配置。
|
||||
4. 若 profile 已占用,系统阻止重复启动并说明原因。
|
||||
5. 若可启动,外部 Chrome 使用该店铺 profile 打开目标地址。
|
||||
6. 系统显示运行状态并记录最后打开时间。
|
||||
|
||||
### 5.2 整理商品并导出主图
|
||||
|
||||
1. 用户创建或导入商品草稿。
|
||||
2. 用户补充价格、SKU、模板和检查项。
|
||||
3. 用户关联底图与 Logo/角标/水印。
|
||||
4. 用户在预览中调整位置、缩放和透明度。
|
||||
5. 用户选择输出尺寸和格式。
|
||||
6. 系统导出新文件并将记录关联到商品,原图保持不变。
|
||||
|
||||
### 5.3 复用话术并跟进
|
||||
|
||||
1. 用户按分类和语言找到话术。
|
||||
2. 用户一键复制并在平台页面手工发送。
|
||||
3. 用户记录买家问题和下次跟进时间。
|
||||
4. 到期记录出现在今日工作台。
|
||||
5. 用户更新处理状态直至解决。
|
||||
|
||||
## 六、首发验收标准
|
||||
|
||||
### 6.1 店铺
|
||||
|
||||
- 可创建至少 20 条店铺记录,重启应用后仍存在。
|
||||
- 两个店铺使用不同 profile 目录启动 Chrome,登录会话和缓存目录不相同。
|
||||
- 同一 profile 运行中再次打开时,不启动第二个实例,并显示占用提示。
|
||||
- 快捷入口使用绑定 profile 打开配置的 URL。
|
||||
- 配置无认证代理后,Chrome 启动参数包含对应代理;无代理时不附加代理参数。
|
||||
- 归档店铺后默认列表不显示,但 profile 目录仍存在且可恢复记录。
|
||||
|
||||
### 6.2 商品和图片
|
||||
|
||||
- 可手工创建商品,也可从有效 CSV/XLSX 导入;无效行会列出行号和原因,不部分静默导入。
|
||||
- 价格计算结果能追溯到全部输入,不使用隐含汇率或费率。
|
||||
- 图片预览和导出使用同一组几何参数;基准测试图中叠加层位置误差不超过 1 个输出像素。
|
||||
- PNG 保留预期透明度;JPG 使用明确背景色。
|
||||
- 导出到独立文件,输入图片校验和与内容不变。
|
||||
- 退出并重进应用后,图片素材和导出记录仍关联到原商品。
|
||||
|
||||
### 6.3 客服
|
||||
|
||||
- 可按分类和语言维护话术,重启后不丢失。
|
||||
- 点击复制后,系统剪贴板内容与话术正文一致。
|
||||
- 可创建关联店铺的跟进记录,并在到期日出现在工作台。
|
||||
- 任何流程都不会自动向平台发送消息。
|
||||
|
||||
### 6.4 数据和恢复
|
||||
|
||||
- 在包含店铺、商品、图片引用、话术和跟进的数据库上完成备份。
|
||||
- 在测试目录中恢复备份后,记录数量、关联和设置与备份时一致。
|
||||
- 备份或恢复失败时保留原数据库,不留下被应用当成有效数据的半成品。
|
||||
- 对 SQLite 文件和日志进行文本检查,不含测试输入的明文密码标记。
|
||||
|
||||
## 七、非功能要求
|
||||
|
||||
| 维度 | 要求 |
|
||||
| --- | --- |
|
||||
| 平台 | 首发支持 Windows 10/11 x64 |
|
||||
| 可用性 | 无云账号、无平台 API 时,除外部 Chrome 操作外核心 CRUD 可离线使用 |
|
||||
| 数据一致性 | 外键启用;多表写入使用事务;所有时间按 UTC 存储、按本地时区显示 |
|
||||
| 性能 | 500 条店铺或 5000 条商品数据下,筛选和翻页不冻结 UI;阻塞操作不得在 Gio frame 中执行 |
|
||||
| 可恢复性 | 应用异常退出后数据库可再次打开;运行状态可从真实进程重新核对 |
|
||||
| 可访问性 | 关键动作有文字或标准图标与 tooltip;状态不只依赖颜色表达 |
|
||||
| 可诊断性 | 用户可看到可行动的错误;日志包含错误码和上下文,但不含秘密 |
|
||||
| 语言 | 首发界面为简体中文;业务内容允许多语言 |
|
||||
|
||||
## 八、范围决策
|
||||
|
||||
| 问题 | 当前决策 |
|
||||
| --- | --- |
|
||||
| 产品名 | 店小航 ShopHelm |
|
||||
| 应用形态 | Windows 本地桌面应用 |
|
||||
| UI | Go + Gio |
|
||||
| 数据 | 本地 SQLite 和本地文件引用 |
|
||||
| 应用账号 | 首发不需要 |
|
||||
| 平台密码 | 首发不保存 |
|
||||
| 代理 | 首发只支持无认证代理 |
|
||||
| 平台能力 | 字段支持多平台,优先按 Shopee 场景设计 |
|
||||
| RPA | 不进入首发主链路 |
|
||||
| 图片 | 首发单底图、单叠加图、单张导出 |
|
||||
|
||||
## 九、后续范围
|
||||
|
||||
- 官方 Shopee Open Platform API。
|
||||
- 订单、库存、利润和广告 ROI。
|
||||
- 图片模板与批量导出。
|
||||
- AI 标题、描述、图片和客服建议。
|
||||
- 团队账号、权限、同步与审计。
|
||||
- 在平台允许范围内、人工确认的辅助 RPA。
|
||||
|
||||
## 十、待确认但不阻塞地基
|
||||
|
||||
- 公开发布前采用哪个仍受支持的 Go 版本。
|
||||
- 默认支持哪些 Shopee 站点和后台快捷链接种子。
|
||||
- 价格计算器首发需要哪些用户显式输入的费率。
|
||||
- 首发安装包采用 ZIP 便携版还是安装器。
|
||||
|
||||
这些问题必须在对应实现任务前定稿,不允许 agent 在业务代码中静默猜测。
|
||||
@@ -0,0 +1,101 @@
|
||||
# 技术栈(Tech Stack)
|
||||
|
||||
> 本文是“用什么”的权威来源。系统如何分层见 [`04-architecture.md`](04-architecture.md)。
|
||||
|
||||
## 一、技术栈一览
|
||||
|
||||
| 维度 | 选型 | 状态 | 理由 |
|
||||
| --- | --- | --- | --- |
|
||||
| 操作系统 | Windows 10/11 x64 | 已定 | 首发围绕本地 Chrome、profile 和 Windows 文件系统 |
|
||||
| 语言 | Go;本机基线 `go1.23.0` | 临时已定 | 用户选定 Go;T-001 验证 Gio 兼容性,公开发布前升级到受支持版本 |
|
||||
| 桌面 UI | Gio `gioui.org` | 已定,版本待 T-001 锁定 | 单语言、本地可控;接受自行封装管理台组件 |
|
||||
| UI 组件 | Gio Material + 项目内组件 | 已定 | 只建设 ShopHelm 需要的表格、表单、弹窗和预览画布 |
|
||||
| 扩展组件 | `gioui.org/x/component` | 待原型确认 | API 稳定性较弱;只有 T-101 证明有价值后才引入并锁 commit |
|
||||
| 状态管理 | 页面状态结构体 + application event channel | 已定 | 匹配 Gio immediate-mode,避免额外状态框架 |
|
||||
| 持久化 | SQLite + `database/sql` | 已定 | 本地单用户、备份简单 |
|
||||
| SQLite 驱动 | `modernc.org/sqlite` | 已定 | 避免 Windows CGO 工具链依赖 |
|
||||
| Migration | `embed` 嵌入顺序 SQL + `schema_migrations` | 已定 | 依赖少、版本可审计、离线可用 |
|
||||
| 图片预览 | Gio 画布,必要时 CPU 生成预览帧 | 已定 | 负责交互反馈,不作为正式导出结果 |
|
||||
| 图片导出 | `image`、`image/draw`、`image/png`、`image/jpeg`,缩放用 `golang.org/x/image/draw` | 已定 | 预览和导出解耦,输出分辨率可控 |
|
||||
| 表格文件 | `encoding/csv` + `github.com/xuri/excelize/v2` | 已定 | 满足 CSV/XLSX 人工交换 |
|
||||
| Chrome | `os/exec` 启动系统 Chrome | 已定 | 平台页面不嵌入 Gio,不依赖 WebView |
|
||||
| 密码 | 首发不保存 | 已定 | Chrome profile 保留会话,避免凭证落库 |
|
||||
| 日志 | `log/slog` + 本地文件 sink | 已定 | 结构化、标准库优先;不得记录秘密 |
|
||||
| 配置 | SQLite `settings` + 固定的本地数据目录 | 已定 | 统一备份和恢复 |
|
||||
| 测试 | `go test`、临时 SQLite、fake process/file adapters、Windows 手工 smoke | 已定 | 隔离系统副作用并验证真实集成 |
|
||||
| 发布 | 单机 Windows 构建;安装包形式待 T-503 | 部分待定 | 先保证二进制可运行,再选 ZIP 或安装器 |
|
||||
|
||||
## 二、版本策略
|
||||
|
||||
- 当前机器只有 Go 1.23.0,因此文档基线如实记录该版本,不声称它是最新或仍受支持。
|
||||
- T-001 必须实际运行最小 Gio 窗口并锁定兼容的 Go/Gio 版本,随后同步本文、`go.mod` 和 `current-state.md`。
|
||||
- 依赖首次加入后必须由 `go.mod` / `go.sum` 固定;后续任务不得直接使用浮动 `@latest`。
|
||||
- Go、Gio 或 SQLite 驱动升级必须单独成任务,先跑完整测试和三个高风险 smoke。
|
||||
- 若目标 Gio 版本不支持 Go 1.23.0,优先升级 Go 工具链,不为保留旧版本降级架构或复制旧依赖。
|
||||
|
||||
## 三、关键技术决策
|
||||
|
||||
- **选择 Gio**:保持 Go 单语言和本地部署,但接受 CRUD UI 需要项目内组件建设。
|
||||
- **不选择 Wails**:当前产品方向已选 Gio;只有 T-101 证明核心管理台交互无法达到可用标准时才重新评估。
|
||||
- **外部 Chrome**:真实店铺后台始终由独立 Chrome 进程打开,Gio 只做控制台。
|
||||
- **无远程后端**:首发没有 REST 服务、云账号和同步服务。
|
||||
- **不存平台密码**:账号字段只是用户识别信息;登录由 Chrome profile 会话和用户手工操作承担。
|
||||
- **预览/导出分离**:Gio 显示交互预览,CPU 合成器生成正式 PNG/JPG。
|
||||
- **RPA 后置**:官方 API、CSV/XLSX 和人工操作无法满足且平台允许时,才建立辅助自动化任务。
|
||||
|
||||
## 四、计划依赖
|
||||
|
||||
| 依赖 | 用途 | 引入任务 |
|
||||
| --- | --- | --- |
|
||||
| `gioui.org` | 窗口、布局、输入、Material 组件 | T-001 |
|
||||
| `modernc.org/sqlite` | CGO-free SQLite 驱动 | T-003 |
|
||||
| `golang.org/x/image` | 高质量图片缩放 | T-103 |
|
||||
| `github.com/xuri/excelize/v2` | XLSX 导入导出 | T-304 |
|
||||
|
||||
`gioui.org/x/component`、Windows 专用系统库和日志轮转库均不是预先批准依赖;使用前必须由对应任务验证并更新本文。
|
||||
|
||||
## 五、构建与运行
|
||||
|
||||
当前在 T-001 前没有 `go.mod`,以下命令是初始化后的标准接口:
|
||||
|
||||
| 用途 | 命令 |
|
||||
| --- | --- |
|
||||
| 同步依赖 | `go mod download` |
|
||||
| 本地开发 | `go run ./cmd/shophelm` |
|
||||
| 单元/集成测试 | `go test ./...` |
|
||||
| 竞态检查 | `go test -race ./...` |
|
||||
| 静态检查 | `go vet ./...` |
|
||||
| 格式检查 | `gofmt -l .`,输出必须为空 |
|
||||
| Windows 构建 | `go build -o build/shophelm.exe ./cmd/shophelm` |
|
||||
| 标准入口 | `./init.ps1` |
|
||||
|
||||
是否使用 `-race` 和 Windows GUI linker flags,以 T-001 在目标工具链上的实际结果为准。
|
||||
|
||||
## 六、本地目录约定
|
||||
|
||||
默认运行数据位于:
|
||||
|
||||
```text
|
||||
%LOCALAPPDATA%\ShopHelm\
|
||||
├── data\shophelm.db
|
||||
├── profiles\<store-id>\
|
||||
├── assets\
|
||||
├── backups\
|
||||
└── logs\
|
||||
```
|
||||
|
||||
默认图片导出目录:
|
||||
|
||||
```text
|
||||
%USERPROFILE%\Pictures\ShopHelm\Exports\
|
||||
```
|
||||
|
||||
用户可以在设置中改 profile 根目录、备份目录和导出目录,但迁移或切换目录必须显式确认。
|
||||
|
||||
## 七、依赖纪律
|
||||
|
||||
- 标准库可解决时不加依赖。
|
||||
- 新依赖必须记录用途、许可证、维护状态、替代方案和测试范围。
|
||||
- 同一职责不得并存两套数据库驱动、图片库、日志框架或状态管理方式。
|
||||
- 不因示例代码方便而加入 Web 框架、嵌入浏览器、ORM 或依赖注入框架。
|
||||
- 依赖变更后运行 `go mod tidy`,审查 `go.mod` / `go.sum` 差异并记录验证结果。
|
||||
@@ -0,0 +1,622 @@
|
||||
# 架构设计
|
||||
|
||||
> 本文定义 ShopHelm 的系统结构、模块职责、数据模型和关键数据流。依赖选择见 [`03-tech-stack.md`](03-tech-stack.md),公开调用形状见 [`api.md`](api.md)。
|
||||
|
||||
## 一、架构约束
|
||||
|
||||
- Windows 本地单用户桌面应用,无远程后端。
|
||||
- Gio 负责控制台 UI;店铺后台在外部 Chrome 中运行。
|
||||
- SQLite 是结构化业务数据的唯一持久化事实来源。
|
||||
- 图片原文件按路径引用;应用管理缩略图、合成参数和导出记录。
|
||||
- 平台密码不保存,运行时也不要求输入。
|
||||
- 首发无 CDP、无平台 API、无自动页面操作。
|
||||
- 所有可能阻塞的数据库、文件、图片和进程操作都在 Gio frame 之外执行。
|
||||
|
||||
## 二、系统结构
|
||||
|
||||
```text
|
||||
用户
|
||||
|
|
||||
v
|
||||
Gio Desktop UI
|
||||
|- 今日工作台
|
||||
|- 店铺管理
|
||||
|- 商品与图片
|
||||
|- 客服助手
|
||||
`- 设置
|
||||
|
|
||||
| UI Intent / Application Event
|
||||
v
|
||||
Application Services
|
||||
|- StoreService / DashboardService
|
||||
|- BrowserProfileService / BrowserLaunchService
|
||||
|- ProductService / ImportExportService
|
||||
|- ImageAssetService / ImageComposerService
|
||||
|- ReplyTemplateService / FollowupService
|
||||
`- BackupService / SettingsService
|
||||
|
|
||||
+---------------------------+
|
||||
| |
|
||||
v v
|
||||
Domain + Repository Ports Platform Ports
|
||||
| |- Chrome process adapter
|
||||
v |- Filesystem / clipboard
|
||||
SQLite Repositories `- Clock / path resolver
|
||||
|
|
||||
v
|
||||
%LOCALAPPDATA%\ShopHelm + 用户选择的素材/导出目录
|
||||
```
|
||||
|
||||
依赖方向固定为:
|
||||
|
||||
```text
|
||||
internal/ui
|
||||
-> internal/application
|
||||
-> internal/domain
|
||||
-> repository/platform interfaces
|
||||
|
||||
internal/infrastructure/sqlite
|
||||
-> internal/domain
|
||||
-> repository interfaces
|
||||
|
||||
internal/platform
|
||||
-> platform interfaces
|
||||
```
|
||||
|
||||
`domain` 不得导入 Gio、SQLite driver 或 Windows API。`application` 不得依赖具体 repository 实现。
|
||||
|
||||
## 三、模块职责
|
||||
|
||||
### 3.1 `internal/domain`
|
||||
|
||||
包含:
|
||||
|
||||
- 实体和值对象:Store、BrowserProfile、ProxyConfig、Product、ImageAsset、ImageComposition、ReplyTemplate、CustomerFollowup、Todo。
|
||||
- 枚举和状态转换。
|
||||
- 无 I/O 的校验、价格计算和图片几何计算。
|
||||
- 领域错误,不包含 UI 文案。
|
||||
|
||||
不包含:
|
||||
|
||||
- SQL。
|
||||
- Gio widget。
|
||||
- `exec.Command`。
|
||||
- 文件读写或剪贴板调用。
|
||||
|
||||
### 3.2 `internal/application`
|
||||
|
||||
负责用例编排:
|
||||
|
||||
- 校验输入并调用 repository。
|
||||
- 用事务协调多表写入。
|
||||
- 将平台错误映射为稳定的应用错误码。
|
||||
- 发布 UI 可消费的完成事件。
|
||||
- 维护异步任务的 context、取消和并发边界。
|
||||
|
||||
application service 不直接绘制 UI,不拼 SQL,不依赖全局单例。
|
||||
|
||||
### 3.3 `internal/infrastructure/sqlite`
|
||||
|
||||
负责:
|
||||
|
||||
- 打开数据库并设置 PRAGMA。
|
||||
- 应用嵌入 migration。
|
||||
- 实现 repository interface。
|
||||
- 事务、查询、扫描和约束错误映射。
|
||||
- 备份验证所需的只读打开和完整性检查。
|
||||
|
||||
每次连接至少设置:
|
||||
|
||||
```sql
|
||||
PRAGMA foreign_keys = ON;
|
||||
PRAGMA busy_timeout = 5000;
|
||||
```
|
||||
|
||||
WAL 是否启用由 T-003 用 Windows 实测决定;确定后写入 migration/连接配置和本文,不在不同启动路径中采用不同值。
|
||||
|
||||
### 3.4 `internal/platform/chrome`
|
||||
|
||||
负责:
|
||||
|
||||
- 查找或使用配置的 Chrome 可执行文件。
|
||||
- 规范化 profile 路径。
|
||||
- 检查 ShopHelm 已知进程和 Chrome profile 占用。
|
||||
- 以参数数组启动 Chrome,不经过 shell。
|
||||
- 监听进程退出并发布状态。
|
||||
- 对普通无认证代理生成启动参数。
|
||||
|
||||
该模块不判断店铺业务状态,也不写 SQLite。
|
||||
|
||||
### 3.5 `internal/platform/files`
|
||||
|
||||
负责:
|
||||
|
||||
- 应用数据目录和用户目录解析。
|
||||
- 原子文件写入。
|
||||
- 图片文件探测、校验和与编码。
|
||||
- CSV/XLSX 文件打开和输出。
|
||||
- 备份、恢复临时文件管理。
|
||||
|
||||
### 3.6 `internal/ui`
|
||||
|
||||
负责:
|
||||
|
||||
- Gio 窗口、导航和主题。
|
||||
- 页面状态、输入校验显示、加载/空/错误状态。
|
||||
- 将用户动作转成 application intent。
|
||||
- 消费 application event 并使窗口失效重绘。
|
||||
- 表格、表单、弹窗、toast、状态标识和图片画布等项目内组件。
|
||||
|
||||
frame/layout 函数只能布局、处理已到达的 UI 事件和提交 intent;不能等待数据库、文件、网络或进程。
|
||||
|
||||
## 四、关键数据流
|
||||
|
||||
### 4.1 店铺保存
|
||||
|
||||
```text
|
||||
用户提交表单
|
||||
-> UI 做必填和格式预检查
|
||||
-> StoreService.Validate/Create/Update
|
||||
-> domain 校验枚举、URL、路径和代理
|
||||
-> repository transaction 写 stores/profile/proxy/tags
|
||||
-> 返回 StoreView
|
||||
-> UI 更新列表并显示结果
|
||||
```
|
||||
|
||||
UI 校验用于快速反馈,domain/application 校验才是写入前的权威校验。
|
||||
|
||||
### 4.2 打开店铺 Chrome
|
||||
|
||||
```text
|
||||
用户点击店铺或快捷入口
|
||||
-> BrowserLaunchService 读取 StoreLaunchConfig
|
||||
-> 校验 Chrome、URL、profile、proxy
|
||||
-> ProfileInspector 检查占用
|
||||
-> 状态改为 starting(内存)
|
||||
-> ChromeLauncher.Start(args)
|
||||
-> 记录 PID、last_opened_at 和 launch log
|
||||
-> 状态改为 running
|
||||
-> Wait goroutine 监听退出
|
||||
-> 状态改为 exited/failed
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- `running` 是运行时事实,不以数据库布尔值作为权威来源。
|
||||
- 可以持久化最后 PID、退出码和时间用于诊断,但应用启动时必须重新核对真实进程。
|
||||
- 同一 profile 不允许由 ShopHelm 重复启动。
|
||||
- 归档店铺不杀进程、不删除 profile。
|
||||
- 远程调试端口和 CDP 默认完全关闭。
|
||||
|
||||
标准参数形状:
|
||||
|
||||
```text
|
||||
chrome.exe
|
||||
--user-data-dir=<absolute-profile-dir>
|
||||
--no-first-run
|
||||
--disable-default-apps
|
||||
[--proxy-server=<scheme://host:port>]
|
||||
<target-url>
|
||||
```
|
||||
|
||||
代理认证不通过命令行注入。URL、路径和 host/port 分别校验后再作为独立参数传递。
|
||||
|
||||
### 4.3 图片预览与导出
|
||||
|
||||
```text
|
||||
底图 + 叠加图 + CompositionSpec
|
||||
-> ImageProbe 读取尺寸和格式
|
||||
-> CompositionPlanner 生成目标像素矩形
|
||||
|- UI 预览按 viewport 比例绘制
|
||||
`- Exporter 按输出分辨率 CPU 合成
|
||||
-> 编码临时 PNG/JPG
|
||||
-> fsync / close / 原子改名
|
||||
-> 写 image_exports
|
||||
```
|
||||
|
||||
`CompositionSpec` 使用与画布无关的归一化参数:
|
||||
|
||||
- `center_x`、`center_y`:叠加层中心相对画布宽高的位置,建议范围 `0..1`,允许用户拖出边缘但必须有上限。
|
||||
- `width_ratio`:叠加层宽度 / 画布宽度,保持原始宽高比。
|
||||
- `opacity`:`0..1`。
|
||||
- `canvas_width`、`canvas_height`:正式输出像素尺寸。
|
||||
- `base_fit`:首发固定支持 `contain` 和 `cover`。
|
||||
- `background`:PNG 可透明;JPG 必须是明确 RGBA 颜色,默认白色。
|
||||
|
||||
预览和导出必须调用同一个 `CompositionPlanner`,不得分别实现位置公式。Gio 截图不得作为导出文件。
|
||||
|
||||
### 4.4 CSV/XLSX 导入
|
||||
|
||||
```text
|
||||
用户选择文件
|
||||
-> parser 读取为 ImportRow
|
||||
-> 全量字段校验并生成 Preview
|
||||
-> UI 展示有效数、错误行和原因
|
||||
-> 用户确认
|
||||
-> 单事务写入有效数据
|
||||
-> 返回创建/更新/跳过统计
|
||||
```
|
||||
|
||||
首发不做“遇错继续并静默丢行”。导入策略必须由用户选择:全部有效才导入,或只导入有效行并明确列出跳过项。
|
||||
|
||||
### 4.5 备份与恢复
|
||||
|
||||
备份:
|
||||
|
||||
1. 获取应用级写锁。
|
||||
2. 生成一致性 SQLite 副本到临时路径。
|
||||
3. 对副本运行 `PRAGMA integrity_check`。
|
||||
4. 写入备份元信息后原子改名。
|
||||
5. 释放写锁。
|
||||
|
||||
恢复:
|
||||
|
||||
1. 用户选择备份并明确确认。
|
||||
2. 在测试连接中检查文件头、完整性和 schema 版本。
|
||||
3. 暂停写入并关闭活动数据库连接。
|
||||
4. 先把当前数据库移动为可回滚副本。
|
||||
5. 复制备份到临时路径并原子替换。
|
||||
6. 重新打开、迁移和 smoke;失败则恢复旧数据库。
|
||||
|
||||
备份默认只包含数据库和应用管理的配置,不复制外部 Chrome profile 或外部原图。备份清单必须说明引用文件可能仍在原路径。
|
||||
|
||||
## 五、运行时并发模型
|
||||
|
||||
- Gio window event loop 由 UI 层独占。
|
||||
- application 使用有界 worker 或按操作启动的 goroutine,不创建无限队列。
|
||||
- 每个异步 intent 携带 `context.Context` 和稳定 request ID。
|
||||
- 相同资源的保存操作串行化;按钮在请求进行中显示 busy 并防重复提交。
|
||||
- repository 可供多个 goroutine 使用,但事务对象不跨 goroutine。
|
||||
- Chrome `Wait` 每个已启动进程一个 goroutine,退出后立即释放 registry。
|
||||
- 大图解码、缩放和导出可取消;新预览请求替换旧请求时取消旧任务。
|
||||
- UI 销毁时取消根 context,等待必要的数据库和日志 flush,不强制杀死用户 Chrome。
|
||||
|
||||
## 六、持久化位置
|
||||
|
||||
```text
|
||||
%LOCALAPPDATA%\ShopHelm\
|
||||
├── data\
|
||||
│ `- shophelm.db
|
||||
├── profiles\
|
||||
│ `- <store-id>\
|
||||
├── assets\
|
||||
│ |- imported\
|
||||
│ `- thumbnails\
|
||||
├── backups\
|
||||
└── logs\
|
||||
```
|
||||
|
||||
- 应用管理的 profile 只能建立在配置的 profile 根目录下。
|
||||
- 用户可绑定已有外部 profile,但系统将其标为 `external`,绝不移动或删除。
|
||||
- 原图默认只记录规范化绝对路径;用户选择“复制到素材库”时才复制到 `assets/imported`。
|
||||
- 导出默认写到 `%USERPROFILE%\Pictures\ShopHelm\Exports`。
|
||||
|
||||
## 七、数据模型
|
||||
|
||||
### 7.1 通用规则
|
||||
|
||||
- 主键使用 SQLite `INTEGER PRIMARY KEY`。
|
||||
- 时间以 UTC RFC3339 字符串存储,UI 按本地时区显示。
|
||||
- `created_at`、`updated_at` 由 application 通过注入的 Clock 生成。
|
||||
- 外键始终开启。
|
||||
- 店铺和核心资料使用归档时间 `archived_at`,默认查询排除已归档记录。
|
||||
- 用户可见顺序需要稳定的二级排序:主要排序字段相同后按 `id`。
|
||||
- 不存在 `password`、`cookie`、`token` 或代理秘密列。
|
||||
|
||||
### 7.2 Schema 版本
|
||||
|
||||
```sql
|
||||
CREATE TABLE schema_migrations (
|
||||
version INTEGER PRIMARY KEY,
|
||||
applied_at TEXT NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
migration 文件命名为 `000001_initial.sql`、`000002_*.sql`,只追加不修改。
|
||||
|
||||
### 7.3 店铺域
|
||||
|
||||
```sql
|
||||
CREATE TABLE stores (
|
||||
id INTEGER PRIMARY KEY,
|
||||
platform TEXT NOT NULL,
|
||||
country_code TEXT NOT NULL DEFAULT '',
|
||||
name TEXT NOT NULL,
|
||||
account_label TEXT NOT NULL DEFAULT '',
|
||||
owner TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL,
|
||||
note TEXT NOT NULL DEFAULT '',
|
||||
last_opened_at TEXT,
|
||||
archived_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX ux_stores_identity_active
|
||||
ON stores(platform, country_code, name)
|
||||
WHERE archived_at IS NULL;
|
||||
|
||||
CREATE TABLE browser_profiles (
|
||||
id INTEGER PRIMARY KEY,
|
||||
store_id INTEGER NOT NULL UNIQUE REFERENCES stores(id),
|
||||
profile_dir TEXT NOT NULL UNIQUE,
|
||||
path_kind TEXT NOT NULL,
|
||||
chrome_path TEXT NOT NULL DEFAULT '',
|
||||
start_url TEXT NOT NULL,
|
||||
last_pid INTEGER,
|
||||
last_exit_code INTEGER,
|
||||
last_exited_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE proxy_configs (
|
||||
id INTEGER PRIMARY KEY,
|
||||
store_id INTEGER NOT NULL UNIQUE REFERENCES stores(id),
|
||||
enabled INTEGER NOT NULL DEFAULT 0,
|
||||
scheme TEXT NOT NULL DEFAULT 'http',
|
||||
host TEXT NOT NULL DEFAULT '',
|
||||
port INTEGER,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE store_shortcuts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
store_id INTEGER NOT NULL REFERENCES stores(id),
|
||||
kind TEXT NOT NULL,
|
||||
label TEXT NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
UNIQUE(store_id, kind, label)
|
||||
);
|
||||
|
||||
CREATE TABLE tags (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
CREATE TABLE store_tags (
|
||||
store_id INTEGER NOT NULL REFERENCES stores(id),
|
||||
tag_id INTEGER NOT NULL REFERENCES tags(id),
|
||||
PRIMARY KEY(store_id, tag_id)
|
||||
);
|
||||
```
|
||||
|
||||
`path_kind` 只允许 `managed` 或 `external`。`proxy_configs` 不增加用户名和密码列。
|
||||
|
||||
### 7.4 待办和商品域
|
||||
|
||||
```sql
|
||||
CREATE TABLE todos (
|
||||
id INTEGER PRIMARY KEY,
|
||||
store_id INTEGER REFERENCES stores(id),
|
||||
title TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
due_at TEXT,
|
||||
note TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE products (
|
||||
id INTEGER PRIMARY KEY,
|
||||
store_id INTEGER NOT NULL REFERENCES stores(id),
|
||||
name TEXT NOT NULL,
|
||||
sku TEXT NOT NULL DEFAULT '',
|
||||
cost_minor INTEGER,
|
||||
sale_price_minor INTEGER,
|
||||
currency TEXT NOT NULL DEFAULT '',
|
||||
stock INTEGER,
|
||||
product_url TEXT NOT NULL DEFAULT '',
|
||||
status TEXT NOT NULL,
|
||||
note TEXT NOT NULL DEFAULT '',
|
||||
archived_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX ix_products_store_status
|
||||
ON products(store_id, status, updated_at);
|
||||
|
||||
CREATE TABLE content_templates (
|
||||
id INTEGER PRIMARY KEY,
|
||||
store_id INTEGER REFERENCES stores(id),
|
||||
kind TEXT NOT NULL,
|
||||
language TEXT NOT NULL DEFAULT '',
|
||||
title TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
archived_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE product_check_items (
|
||||
id INTEGER PRIMARY KEY,
|
||||
product_id INTEGER NOT NULL REFERENCES products(id),
|
||||
item_key TEXT NOT NULL,
|
||||
label TEXT NOT NULL,
|
||||
checked INTEGER NOT NULL DEFAULT 0,
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
updated_at TEXT NOT NULL,
|
||||
UNIQUE(product_id, item_key)
|
||||
);
|
||||
```
|
||||
|
||||
金额以最小货币单位整数保存,避免 `REAL` 舍入误差。汇率和平台费率必须是当次计算的显式输入;首发不把计算结果持久化为财务事实。
|
||||
|
||||
### 7.5 图片域
|
||||
|
||||
```sql
|
||||
CREATE TABLE image_assets (
|
||||
id INTEGER PRIMARY KEY,
|
||||
store_id INTEGER REFERENCES stores(id),
|
||||
product_id INTEGER REFERENCES products(id),
|
||||
asset_type TEXT NOT NULL,
|
||||
file_path TEXT NOT NULL,
|
||||
storage_kind TEXT NOT NULL,
|
||||
width INTEGER,
|
||||
height INTEGER,
|
||||
checksum_sha256 TEXT,
|
||||
note TEXT NOT NULL DEFAULT '',
|
||||
missing INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE image_exports (
|
||||
id INTEGER PRIMARY KEY,
|
||||
store_id INTEGER REFERENCES stores(id),
|
||||
product_id INTEGER REFERENCES products(id),
|
||||
base_asset_id INTEGER NOT NULL REFERENCES image_assets(id),
|
||||
overlay_asset_id INTEGER NOT NULL REFERENCES image_assets(id),
|
||||
output_path TEXT NOT NULL,
|
||||
output_format TEXT NOT NULL,
|
||||
output_width INTEGER NOT NULL,
|
||||
output_height INTEGER NOT NULL,
|
||||
composition_json TEXT NOT NULL,
|
||||
checksum_sha256 TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
首发不需要保存 `image_templates`。模板和批量导出进入 Backlog 后新增 migration,不能提前把未使用的通用图层系统塞进 MVP。
|
||||
|
||||
### 7.6 客服域
|
||||
|
||||
```sql
|
||||
CREATE TABLE reply_templates (
|
||||
id INTEGER PRIMARY KEY,
|
||||
category TEXT NOT NULL,
|
||||
language TEXT NOT NULL,
|
||||
title TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
usage_count INTEGER NOT NULL DEFAULT 0,
|
||||
archived_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE customer_followups (
|
||||
id INTEGER PRIMARY KEY,
|
||||
store_id INTEGER NOT NULL REFERENCES stores(id),
|
||||
buyer_label TEXT NOT NULL DEFAULT '',
|
||||
order_ref TEXT NOT NULL DEFAULT '',
|
||||
issue_type TEXT NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
next_followup_at TEXT,
|
||||
note TEXT NOT NULL DEFAULT '',
|
||||
archived_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX ix_followups_due
|
||||
ON customer_followups(status, next_followup_at);
|
||||
```
|
||||
|
||||
`buyer_label` 只保存完成跟进所需的最小标识,不要求保存买家完整个人资料。
|
||||
|
||||
### 7.7 设置与操作记录
|
||||
|
||||
```sql
|
||||
CREATE TABLE settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value_json TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE operation_logs (
|
||||
id INTEGER PRIMARY KEY,
|
||||
action TEXT NOT NULL,
|
||||
entity_type TEXT NOT NULL,
|
||||
entity_id INTEGER,
|
||||
result TEXT NOT NULL,
|
||||
error_code TEXT,
|
||||
detail_json TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
operation log 只记录业务动作摘要、路径的脱敏形式和稳定错误码,不记录话术正文、买家详细信息、账号、Cookie 或命令行秘密。
|
||||
|
||||
## 八、状态枚举
|
||||
|
||||
| 领域 | 合法值 |
|
||||
| --- | --- |
|
||||
| 店铺业务状态 | `active`、`login_required`、`attention`、`paused` |
|
||||
| Chrome 运行状态 | `stopped`、`occupied`、`starting`、`running`、`exited`、`failed` |
|
||||
| 待办 | `pending`、`done`、`cancelled` |
|
||||
| 商品 | `draft`、`pending_listing`、`listed`、`optimize`、`paused` |
|
||||
| 图片素材 | `main`、`detail`、`logo`、`badge`、`watermark` |
|
||||
| 内容模板 | `title`、`description` |
|
||||
| 客服跟进 | `pending`、`in_progress`、`waiting_buyer`、`resolved`、`escalated` |
|
||||
|
||||
数据库约束或 domain 校验必须拒绝其他值。UI 文案与内部枚举通过集中映射,不把中文展示值写入状态列。
|
||||
|
||||
## 九、项目结构
|
||||
|
||||
```text
|
||||
shop_helm/
|
||||
├── cmd/
|
||||
│ `-- shophelm/
|
||||
│ `-- main.go
|
||||
├── internal/
|
||||
│ ├── application/
|
||||
│ ├── domain/
|
||||
│ ├── infrastructure/
|
||||
│ │ `-- sqlite/
|
||||
│ ├── platform/
|
||||
│ │ ├── chrome/
|
||||
│ │ ├── clipboard/
|
||||
│ │ `-- files/
|
||||
│ `-- ui/
|
||||
│ ├── components/
|
||||
│ ├── pages/
|
||||
│ `-- theme/
|
||||
├── migrations/
|
||||
├── assets/
|
||||
├── testdata/
|
||||
├── docs/
|
||||
├── init.ps1
|
||||
├── init.sh
|
||||
├── go.mod
|
||||
└── go.sum
|
||||
```
|
||||
|
||||
Go 测试优先与被测包同目录。跨模块验收夹具放 `testdata/`,不得包含真实账号或隐私数据。
|
||||
|
||||
## 十、错误处理
|
||||
|
||||
- domain 返回可判定的 sentinel/typed errors。
|
||||
- application 映射为 [`api.md`](api.md) 定义的稳定错误码。
|
||||
- infrastructure 附加底层原因供日志诊断,但 UI 不直接展示 SQL、绝对秘密路径或系统堆栈。
|
||||
- UI 显示“发生了什么 + 用户能做什么”,例如“该店铺浏览器环境正在使用,请先关闭对应 Chrome 后重试”。
|
||||
- 可重试操作明确提供重试;不可恢复操作不做无限重试。
|
||||
|
||||
## 十一、测试策略
|
||||
|
||||
| 层 | 测试 |
|
||||
| --- | --- |
|
||||
| domain | 状态校验、价格计算、图片几何的表驱动单元测试 |
|
||||
| application | fake repository/platform adapter,验证编排、事务和错误映射 |
|
||||
| SQLite | 临时目录真实数据库、migration、约束、CRUD、备份恢复 |
|
||||
| Chrome adapter | 参数生成单元测试 + fake executable;真实 Chrome 手工 smoke |
|
||||
| Image composer | 固定小图 golden/pixel 测试、PNG alpha、JPG 背景、原图不变 |
|
||||
| CSV/XLSX | round-trip、错误行、编码和事务行为 |
|
||||
| Gio UI | 页面状态和 intent 单测 + Windows 手工 smoke;不依赖像素脆弱截图作为唯一证据 |
|
||||
|
||||
## 十二、关键风险和闸门
|
||||
|
||||
| 风险 | 必须先通过的闸门 |
|
||||
| --- | --- |
|
||||
| Go 1.23.0 与目标 Gio 不兼容 | T-001 最小窗口在目标 Windows 上运行并锁版本 |
|
||||
| Gio 管理台组件成本过高 | T-101 用真实店铺字段完成表格、筛选和表单原型 |
|
||||
| profile 占用判断不可靠 | T-102 覆盖本应用启动、外部占用、进程退出和脏状态 |
|
||||
| 预览和导出不一致 | T-103 使用同一 CompositionPlanner 通过像素测试 |
|
||||
| SQLite 恢复损坏现有数据 | T-208 在测试目录执行成功和故障注入恢复 |
|
||||
| RPA 导致平台风险 | 首发无 RPA;任何引入必须新建需求与合规任务 |
|
||||
|
||||
高风险闸门未通过时,不继续依赖它的业务任务,也不以 mock 成功替代真实结论。
|
||||
@@ -0,0 +1,176 @@
|
||||
# 编码规则(Coding Rules)
|
||||
|
||||
> 每次改代码前完整读取。本文件是实现硬约束;产品范围以 [`02-requirements.md`](02-requirements.md) 为准,模块事实以 [`04-architecture.md`](04-architecture.md) 和 [`api.md`](api.md) 为准。
|
||||
|
||||
## 0. 黄金法则
|
||||
|
||||
1. **不臆造**:字段、平台规则、路径、依赖和接口必须查仓库事实。
|
||||
2. **一次一事**:只实现当前 `DOING` 任务,不夹带 Backlog 和无关重构。
|
||||
3. **依赖向内**:UI 和基础设施依赖 application/domain,domain 不依赖框架。
|
||||
4. **先验证风险**:Gio 组件、Chrome profile 和图片合成闸门未通过前,不大规模铺业务页面。
|
||||
5. **副作用可控**:进程、文件、数据库、剪贴板和恢复操作必须可观察、可失败、可追踪。
|
||||
6. **完成有证据**:没有测试、构建和任务要求的 smoke,不得标记 `DONE`。
|
||||
|
||||
## 1. 动手前
|
||||
|
||||
- 按 `vision -> requirements -> tech-stack -> architecture -> api/routes -> tasks -> current-state` 建立上下文。
|
||||
- 找到任务对应的需求 ID、服务合约和页面。
|
||||
- 检查是否已有可复用 package、component、repository 和测试夹具。
|
||||
- 运行标准基线并记录当前失败;基线坏时先处理基线。
|
||||
- 查看工作区已有改动,保留不属于本任务的用户修改。
|
||||
- 若任务会改变第三方平台数据、删除用户文件、保存秘密或扩大自动化范围,先停止并要求产品决策。
|
||||
|
||||
## 2. Go 规则
|
||||
|
||||
- package 名短小、全小写、单数优先,不使用 `utils`、`common`、`helpers` 作为无边界杂物包。
|
||||
- 公开标识符只在跨包合约需要时导出;先使用小接口,接口由消费者定义。
|
||||
- 所有 I/O 方法接收 `context.Context`,不把 context 存在 struct 中。
|
||||
- 错误必须包装上下文并保留 `errors.Is/As` 可判定性,不比较底层错误字符串。
|
||||
- 不使用 `panic` 处理可预期业务错误;启动期不可恢复错误可以返回到 `main` 后记录并退出。
|
||||
- 不忽略返回值,不用空 `recover`,不在 library code 中 `os.Exit`。
|
||||
- 时间通过注入的 Clock 获取,测试不依赖真实等待。
|
||||
- 金额不用 `float64` 持久化;用最小货币单位整数,比例/汇率用可精确解析表示。
|
||||
- 路径用 `filepath`,URL 用 `net/url`,CSV/XLSX 用结构化解析器,不手工拆字符串。
|
||||
- 代码必须通过 `gofmt`;注释解释边界和原因,不复述代码。
|
||||
|
||||
## 3. 分层规则
|
||||
|
||||
- `internal/domain`:纯规则、实体和值对象,不导入 Gio、driver、Windows API。
|
||||
- `internal/application`:用例编排和稳定错误码,不写 SQL、不布局 UI。
|
||||
- `internal/infrastructure/sqlite`:repository 和 migration,不出现 UI 文案。
|
||||
- `internal/platform`:进程、路径、剪贴板和文件副作用,不决定业务优先级。
|
||||
- `internal/ui`:页面状态和展示,不直接打开数据库或启动进程。
|
||||
- 不通过全局变量跨层共享数据库、页面状态或 process registry。
|
||||
- 新增跨层调用前先更新 `api.md`;不得为绕过接口而从 UI 直接引用 concrete repository。
|
||||
|
||||
## 4. Gio 规则
|
||||
|
||||
- widget、clickable、editor、list、drag 和页面状态必须跨 frame 持久存在。
|
||||
- layout 函数不得执行 SQL、文件读写、大图解码、网络请求、`Process.Wait` 或阻塞 channel receive。
|
||||
- 用户动作只提交 intent;异步结果通过 event channel 返回并调用 window invalidate。
|
||||
- 每个异步操作有 busy、success、error 和必要的 cancel 状态。
|
||||
- 同一按钮请求进行中防重复提交。
|
||||
- 页面切换保留列表筛选和滚动状态;销毁页面时取消所属 context。
|
||||
- 固定画布、表格、工具栏和图标按钮设置稳定约束,动态文字不得挤压相邻控件。
|
||||
- 关键状态不能只用颜色;图标按钮必须有 tooltip/可访问名称。
|
||||
- 项目内组件只在存在真实复用时抽取,不创建通用 UI DSL。
|
||||
- UI 自动测试不稳定时可以保留手工 smoke,但 domain/application 行为必须有自动测试。
|
||||
|
||||
## 5. SQLite 规则
|
||||
|
||||
- schema 只通过按序 migration 变化,已经提交或发布的 migration 不修改。
|
||||
- 每个连接启用外键和 busy timeout。
|
||||
- 多表写入、导入提交和计数更新使用事务。
|
||||
- repository 查询显式列名,不使用 `SELECT *`。
|
||||
- nullable 数据使用明确类型,不用魔法空字符串混淆“未填写”和有效值,除非 schema 已定义为空字符串语义。
|
||||
- 列表查询有稳定排序、分页边界和必要索引。
|
||||
- 唯一约束、外键约束和 busy 错误映射到稳定应用错误码。
|
||||
- 测试使用临时目录的真实 SQLite,不共享开发数据库。
|
||||
- 不把密码、Cookie、token、代理秘密或图片二进制写进数据库。
|
||||
- 备份恢复前后执行完整性检查;失败必须保留可回滚旧库。
|
||||
|
||||
## 6. Chrome 与进程规则
|
||||
|
||||
- 只能用 `exec.CommandContext` 或经审查的等价 API 和独立参数启动,不使用 `cmd /c`、PowerShell 字符串或 shell 拼接。
|
||||
- Chrome executable、profile 目录和目标 URL 分别校验。
|
||||
- profile 必须是绝对规范路径;managed path 必须位于配置根目录内。
|
||||
- 不使用默认用户 Chrome profile 作为 ShopHelm managed profile。
|
||||
- 同一 profile 占用时返回 `profile_in_use`,不尝试强制解锁或删除 lock 文件。
|
||||
- 不在命令行传密码、Cookie 或代理认证信息。
|
||||
- 不默认加 `--remote-debugging-port`、规避检测或安全降级参数。
|
||||
- 进程退出必须释放 registry;应用重启后不得相信陈旧 PID。
|
||||
- 应用退出默认不杀用户打开的 Chrome;需要关闭能力时必须单独立项和确认。
|
||||
- 测试参数生成时用 fake runner,真实 Chrome smoke 只能使用测试 profile 和非生产账号。
|
||||
|
||||
## 7. 文件、导入和图片规则
|
||||
|
||||
- 任何用户路径先做存在性、文件类型、权限和规范化检查。
|
||||
- 读取外部文件不修改源文件。
|
||||
- 写入先落同目录临时文件,成功 close/sync 后原子改名。
|
||||
- 默认 `overwrite=false`;即使允许覆盖,也绝不能覆盖底图或叠加图。
|
||||
- 图片解码设置尺寸和内存上限,防止异常大文件耗尽内存。
|
||||
- 预览和导出共用 `CompositionPlanner`,不得复制位置/缩放公式。
|
||||
- Gio framebuffer 截图不能作为正式图片输出。
|
||||
- PNG alpha 和 JPG 背景有固定测试。
|
||||
- CSV/XLSX 先 preview 后 commit;文件 fingerprint 改变时拒绝提交。
|
||||
- 导入错误包含行号、字段和原因;不静默修复未知值。
|
||||
- 删除数据库素材关联默认不删除物理文件。
|
||||
|
||||
## 8. 安全、隐私与合规
|
||||
|
||||
- 真实账号、密码、token、Cookie、买家隐私、私有 URL 不进入代码、文档、日志、截图或测试数据。
|
||||
- 账号标识只用于人类识别;日志中仍应最小化或脱敏。
|
||||
- 不实现绕验证码、绕风控、反指纹、刷单、刷评、批量抓取和未经确认的批量消息。
|
||||
- 不把 `user-data-dir` 描述成防关联功能。
|
||||
- 平台规则变化必须以官方来源为准,记录链接、访问日期和影响。
|
||||
- 未来自动化默认具备 dry-run、明确预览、人工确认、速率边界、审计和取消。
|
||||
- 操作日志记录 action、entity、result、error code,不记录完整话术、买家详情或命令秘密。
|
||||
|
||||
## 9. 依赖规则
|
||||
|
||||
- 新依赖前检查标准库和已引入依赖能否满足。
|
||||
- 在 `03-tech-stack.md` 记录用途、版本策略和引入任务。
|
||||
- 运行 `go mod tidy` 后审查直接和间接依赖变化。
|
||||
- 不引入 ORM、Web 框架、嵌入浏览器、依赖注入框架或第二套图片库,除非新任务明确批准。
|
||||
- `gioui.org/x/component` 只有 T-101 通过后才允许保留。
|
||||
- 依赖升级单独提交,不与业务功能混在同一任务。
|
||||
|
||||
## 10. 测试规则
|
||||
|
||||
最低自动验证:
|
||||
|
||||
```powershell
|
||||
go test ./...
|
||||
go vet ./...
|
||||
go build -o build/shophelm.exe ./cmd/shophelm
|
||||
```
|
||||
|
||||
按改动增加:
|
||||
|
||||
- 并发和共享状态:`go test -race ./...`,若目标工具链不支持则记录真实原因。
|
||||
- migration/repository:临时数据库从空库迁移,运行 CRUD、约束和重开测试。
|
||||
- Chrome:参数单测、fake process 生命周期、Windows 真实 Chrome smoke。
|
||||
- 图片:固定夹具 pixel/golden 测试、原图校验和、格式和失败清理。
|
||||
- 导入:CSV/XLSX round-trip、错误行、fingerprint 和事务测试。
|
||||
- 备份:完整性、恢复成功、损坏备份、替换失败回滚测试。
|
||||
- Gio:对应页面手工 smoke,记录窗口尺寸、动作和观察结果。
|
||||
|
||||
测试禁止:
|
||||
|
||||
- 为绿灯删除断言、降低验收或跳过失败用例。
|
||||
- 使用真实用户数据库、真实 Chrome profile 或真实店铺凭证。
|
||||
- 依赖测试执行顺序或固定本机绝对路径。
|
||||
|
||||
## 11. 文档与提交
|
||||
|
||||
- 需求变化先改 `02-requirements.md`,再改代码。
|
||||
- schema 或 service 合约变化同步 `04-architecture.md` 和 `api.md`。
|
||||
- 页面变化同步 `routes.md`。
|
||||
- 版本、依赖和命令变化同步 `03-tech-stack.md`、`00-ai-start-here.md`、`current-state.md` 和 `init.*`。
|
||||
- 每轮实际命令和结果只追加到 `progress.md`。
|
||||
- 提交只包含当前任务相关文件;不重写或撤销用户无关改动。
|
||||
- 提交信息建议:`T-XXX: imperative summary`。
|
||||
|
||||
## 12. 完成定义
|
||||
|
||||
任务标记 `DONE` 前逐项确认:
|
||||
|
||||
- [ ] 需求 ID 和任务验收全部满足。
|
||||
- [ ] 代码遵守模块和副作用边界。
|
||||
- [ ] 自动测试、vet、build 通过。
|
||||
- [ ] 任务要求的手工 smoke 已执行并记录环境。
|
||||
- [ ] 没有真实秘密、隐私或生产路径进入变更。
|
||||
- [ ] 没有无关重构或 Backlog 功能。
|
||||
- [ ] 相关文档、任务状态和 current state 已同步。
|
||||
- [ ] `progress.md` 有真实验证证据。
|
||||
- [ ] `clean-state-checklist.md` 已通过。
|
||||
|
||||
## 13. 绝不
|
||||
|
||||
- 绝不明文保存密码。
|
||||
- 绝不删除或强制解锁 Chrome profile。
|
||||
- 绝不覆盖原始图片作为默认行为。
|
||||
- 绝不在 Gio frame 中等待 I/O。
|
||||
- 绝不把数据库 `running` 字段当真实进程状态。
|
||||
- 绝不静默吞掉导入、恢复或导出中的部分失败。
|
||||
- 绝不因为任务困难就跳过高风险闸门。
|
||||
@@ -0,0 +1,100 @@
|
||||
# 任务看板(Tasks)
|
||||
|
||||
> ShopHelm 单 agent 开发看板。每轮只领取一个 `TODO` 且依赖均为 `DONE` 的任务。
|
||||
|
||||
## 使用规则
|
||||
|
||||
1. 开始前读 `00-ai-start-here.md`、`05-coding-rules.md`、`current-state.md` 和任务相关合约。
|
||||
2. 取编号最小、依赖均 `DONE` 的 `TODO`,先改为 `DOING`。
|
||||
3. 同一时间最多一个 `DOING`,不得跳过技术闸门。
|
||||
4. 验收步骤必须真实执行;只有代码而无命令/观察证据不能 `DONE`。
|
||||
5. 完成后更新本看板、追加 `../progress.md`、覆盖 `current-state.md` 并走 `clean-state-checklist.md`。
|
||||
6. 多 agent 并发时冻结本文,切换到 [`tasks/README.md`](tasks/README.md)。
|
||||
|
||||
状态:`TODO` 待开始 · `DOING` 进行中 · `DONE` 已验证 · `BLOCKED` 有明确阻塞
|
||||
|
||||
## Phase 0 · 工程地基
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-001 | 初始化 Go module 和最小 Gio 窗口 | - | 保留现有 Git 配置;建立 module `shophelm`;锁定兼容 Go/Gio 版本并同步技术文档;`./init.ps1`、`go run ./cmd/shophelm`、`go test ./...`、`go build -o build/shophelm.exe ./cmd/shophelm` 成功;窗口可正常打开和关闭 | TODO |
|
||||
| T-002 | 建立目录、运行路径、配置和日志骨架 | T-001 | SEC-002;目录符合 `04-architecture.md`;默认本地目录可解析/创建;路径解析和设置校验有测试;日志不含环境秘密;应用启动能显示配置错误 | TODO |
|
||||
| T-003 | 接入 SQLite、migration 和 repository 测试基座 | T-002 | 使用 `modernc.org/sqlite`;空库迁移到当前版本;外键/busy timeout 生效;临时库重开测试通过;migration 失败不被标记已应用 | TODO |
|
||||
| T-004 | 建立 application event loop 和生命周期 | T-003 | UI intent 在 frame 外执行;request ID、busy、success/error 事件可用;关闭窗口取消 root context 并关闭数据库;竞态测试或替代验证有记录 | TODO |
|
||||
| T-005 | 实现 AppShell、主题和基础反馈组件 | T-004 | 五项主导航可切换;loading/empty/error/toast/modal/status badge 有稳定演示;小窗口无关键重叠;Windows 手工 smoke 有记录 | TODO |
|
||||
|
||||
## Phase 1 · 高风险技术闸门
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-101 | Gio 管理台表格与表单原型 | T-005 | 用真实店铺字段展示 500 行、固定表头/列、滚动、选中、排序和筛选;新增/编辑表单校验可用;窗口缩放不重叠;决定是否保留 `x/component` 并同步技术文档 | TODO |
|
||||
| T-102 | Chrome profile、进程和代理启动原型 | T-004 | fake runner 验证参数无 shell 注入;两个 managed profile 可分别启动;同 profile 重复打开被阻止;退出后状态释放;无认证代理参数正确;不启用 CDP;真实 Chrome smoke 有记录 | TODO |
|
||||
| T-103 | 图片 CompositionPlanner 与导出原型 | T-004 | 固定底图/叠加图通过位置、缩放、透明度 pixel 测试;预览与导出共享 planner;PNG alpha、JPG 背景、目标存在、输入不变和取消均有测试 | TODO |
|
||||
| T-104 | 技术闸门结论与正式组件归档 | T-101,T-102,T-103 | 原型代码进入正式模块或被删除;Go/Gio/组件/Chrome 占用/图片限制写回架构;三项 smoke 全部可复现;任何未达标项标 `BLOCKED` 并由用户决策是否调整技术路线 | TODO |
|
||||
|
||||
## Phase 2 · 业务阶段一:多店铺运营台
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-201 | 实现店铺域 migration 和 repositories | T-104 | SEC-001;`stores`、profile、proxy、shortcut、tags 表和索引符合架构;CRUD/归档/恢复/唯一约束/事务测试通过;schema 无秘密列 | TODO |
|
||||
| T-202 | 实现店铺列表和 CRUD 页面 | T-201 | STORE-001、STORE-002;新增、查看、编辑、归档和恢复可用;错误保留输入;重启后 20 条测试记录存在;列表默认不显示归档 | TODO |
|
||||
| T-203 | 实现 profile 配置和店铺快捷入口 | T-202 | STORE-004、STORE-008;managed/external 路径规则生效;managed 目录唯一;快捷 URL 校验协议;归档不删除 profile;详情页可打开文件位置 | TODO |
|
||||
| T-204 | 接入 Chrome 启动和运行状态 | T-203,T-102 | STORE-005、STORE-006、STORE-007;启动、占用、退出、失败状态进入 UI;last_opened_at 只在成功启动后更新;应用重启核对陈旧 PID;不强杀 Chrome | TODO |
|
||||
| T-205 | 实现普通代理、筛选和排序 | T-204 | STORE-003、STORE-009;平台/国家/负责人/标签/状态筛选和最近打开排序稳定;代理开启/关闭参数正确;认证字段不存在;500 行操作不冻结 UI | TODO |
|
||||
| T-206 | 实现店铺待办和首版今日工作台 | T-205 | STORE-010、DASH-001、DASH-002;待办 CRUD、到期/逾期计算和最近店铺展示;分区错误不伪装为空;重启后状态保留 | TODO |
|
||||
| T-207 | 实现设置持久化和设置页面 | T-206 | SET-001、SET-002;Chrome、profile、导出目录、默认货币和界面语言可保存并校验;路径切换说明影响;重启后设置保留;密码或代理认证字段不存在 | TODO |
|
||||
| T-208 | 实现数据库备份与恢复 | T-207 | STORE-011、SEC-003;有效备份可恢复全部关联;损坏/高版本备份被拒;替换失败恢复旧库;明确不包含 profile/外部素材;手工故障 smoke 有证据 | TODO |
|
||||
| T-209 | 验收多店铺运营台 | T-208 | 执行 `02-requirements.md` 6.1 和店铺相关 6.4;运行测试/vet/build;用至少两个测试 profile 完成 Windows smoke;发现缺口新建任务,不用降低验收 | TODO |
|
||||
|
||||
## Phase 3 · 业务阶段四:商品运营助手
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-301 | 实现商品、模板和检查项 migration/repositories | T-209 | 表结构、金额整数、状态、索引和外键符合架构;CRUD/归档/检查项事务测试通过;SKU 重复策略定稿并同步需求/schema | TODO |
|
||||
| T-302 | 实现商品草稿列表和 CRUD | T-301 | PROD-001、PROD-002;按店铺/状态/关键词筛选;金额解析错误可见;详情关联店铺;重启后数据保留 | TODO |
|
||||
| T-303 | 实现文案模板、上架检查和价格计算 | T-302 | PROD-003、PROD-004、PROD-005;模板和检查项可复用;计算回显全部输入;空费率无隐含默认;边界/舍入测试通过 | TODO |
|
||||
| T-304 | 实现 CSV/XLSX 商品导入导出 | T-303 | PROD-006;preview 显示行号错误;fingerprint 变化拒绝 commit;两种策略行为明确;CSV/XLSX round-trip 和事务测试通过 | TODO |
|
||||
| T-305 | 实现图片素材库 | T-302 | PROD-007;路径引用/复制到素材库、缩略图和缺失状态可用;删除关联默认不删文件;异常大图被安全拒绝;重启后关联保留 | TODO |
|
||||
| T-306 | 实现图片叠加预览页面 | T-305,T-103 | IMG-001、IMG-002;选择底图/叠加图,拖动、位置、缩放、透明度、重置可用;画布缩放不改变 spec;旧预览请求不会覆盖新结果 | TODO |
|
||||
| T-307 | 实现 PNG/JPG 导出和导出记录 | T-306 | IMG-003、IMG-004;尺寸/格式/背景可选;输出原子写入且默认不覆盖;原图校验和不变;成功记录关联商品;失败不留有效半成品 | TODO |
|
||||
| T-308 | 完成商品工作台集成和阶段验收 | T-304,T-307 | DASH-003 和商品/图片相关 DASH-004、`02-requirements.md` 6.2 全部通过;最近导出/待优化商品可进入详情;测试/vet/build 和图片 Windows smoke 有证据 | TODO |
|
||||
|
||||
## Phase 4 · 业务阶段五:客服助手
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-401 | 实现话术和跟进 migration/repositories | T-308 | 表、状态、到期索引和归档符合架构;CRUD/筛选/重开测试通过;买家字段保持最小化 | TODO |
|
||||
| T-402 | 实现多语言话术库和一键复制 | T-401 | CS-001、CS-002、CS-003、CS-004;分类/语言/关键词筛选;剪贴板成功才增加次数;失败反馈明确;不出现发送按钮 | TODO |
|
||||
| T-403 | 实现买家问题和跟进页面 | T-401 | CS-005、CS-006;关联店铺/订单标识;状态和时间校验;逾期/今日优先;归档和重启行为正确 | TODO |
|
||||
| T-404 | 把客服跟进接入今日工作台 | T-402,T-403 | 客服相关 DASH-004;到期/逾期跟进展示并可进入记录;局部加载失败可重试;系统不读取或发送平台消息 | TODO |
|
||||
| T-405 | 验收客服助手 | T-404 | 执行 `02-requirements.md` 6.3;多语言文本、剪贴板、时区和重启 smoke 通过;测试/vet/build 有证据 | TODO |
|
||||
|
||||
## Phase 5 · 稳定性与发布
|
||||
|
||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| T-501 | 统一错误体验、日志脱敏和运行状态恢复 | T-405 | AUDIT-001;所有稳定错误码有 UI 映射;日志无密码/隐私测试标记;启动时校验素材缺失和 Chrome 陈旧状态;关键操作有可诊断结果 | TODO |
|
||||
| T-502 | 完整首发验收与性能/恢复测试 | T-501 | SEC-001、SEC-002、SEC-003;`02-requirements.md` 6.1-6.4 和非功能要求有逐项证据;500 店铺/5000 商品数据不冻结 frame;备份恢复、异常退出和原图保护通过 | TODO |
|
||||
| T-503 | Windows 可交付构建与运行文档 | T-502 | 决定 ZIP/安装器并记录;干净 Windows 环境可启动;数据目录/升级/卸载不误删 profile 和素材;版本信息可见;完整测试/vet/build 通过 | TODO |
|
||||
|
||||
## 里程碑
|
||||
|
||||
- M0:工程地基可运行。
|
||||
- M1:三个高风险闸门通过,确认继续 Gio。
|
||||
- M2:多店铺运营台可每日使用。
|
||||
- M3:商品资料和单张图片合成闭环完成。
|
||||
- M4:客服话术与跟进闭环完成。
|
||||
- M5:Windows 首发包可交付。
|
||||
|
||||
## Backlog
|
||||
|
||||
- authenticated proxy 的合规实现。
|
||||
- 代理可用性检测和检测历史。
|
||||
- 图片模板、平台尺寸预设、批量套用和批量导出。
|
||||
- 多语言标题/描述和 AI 辅助。
|
||||
- Shopee Open Platform 官方 API。
|
||||
- 在平台许可范围内、人工确认的辅助 RPA。
|
||||
- 订单、库存、利润和广告 ROI。
|
||||
- 云同步、团队成员和权限。
|
||||
- 操作日志浏览与导出。
|
||||
- macOS/Linux 评估。
|
||||
@@ -0,0 +1,47 @@
|
||||
# ShopHelm 项目文档导航
|
||||
|
||||
> 本目录是 ShopHelm 的产品与工程事实来源。AI coding agent 每次从 [`00-ai-start-here.md`](00-ai-start-here.md) 进入。
|
||||
|
||||
## 一句话定位
|
||||
|
||||
店小航 ShopHelm 是面向小团队跨境卖家的 Windows 本地运营工作台,首发跑通“找到正确店铺并打开独立 Chrome 环境 -> 整理商品与图片 -> 复用客服话术并跟进”的日常闭环。
|
||||
|
||||
## 核心文档
|
||||
|
||||
| 文档 | 职责 |
|
||||
| --- | --- |
|
||||
| [`../AGENTS.md`](../AGENTS.md) | 仓库级 agent 规则和安全边界 |
|
||||
| [`00-ai-start-here.md`](00-ai-start-here.md) | 每轮开工、领任务和收尾流程 |
|
||||
| [`01-vision.md`](01-vision.md) | 产品目标、用户、原则和非目标 |
|
||||
| [`02-requirements.md`](02-requirements.md) | 功能范围、优先级、用户故事和验收 |
|
||||
| [`03-tech-stack.md`](03-tech-stack.md) | 技术选型、依赖纪律和命令 |
|
||||
| [`04-architecture.md`](04-architecture.md) | 分层、模块、数据模型和关键数据流 |
|
||||
| [`05-coding-rules.md`](05-coding-rules.md) | Go、Gio、SQLite、文件和进程硬规则 |
|
||||
| [`06-tasks.md`](06-tasks.md) | 单 agent 任务看板和依赖 |
|
||||
| [`api.md`](api.md) | 本地应用服务、事件和错误合约 |
|
||||
| [`routes.md`](routes.md) | 桌面页面、导航状态和组件归属 |
|
||||
| [`current-state.md`](current-state.md) | 当前代码现实和下一个可领取任务 |
|
||||
|
||||
## 执行与质量文档
|
||||
|
||||
| 文档 | 职责 |
|
||||
| --- | --- |
|
||||
| [`../progress.md`](../progress.md) | 只追加的任务执行和验证证据 |
|
||||
| [`clean-state-checklist.md`](clean-state-checklist.md) | 每轮结束前的干净收尾检查 |
|
||||
| [`adoption-checklist.md`](adoption-checklist.md) | 当前空项目从文档进入代码的基线清单 |
|
||||
| [`method-map.md`](method-map.md) | 常见失败模式到修复工件的导航 |
|
||||
| [`evaluator-rubric.md`](evaluator-rubric.md) | 单次交付质量评分 |
|
||||
| [`quality-document.md`](quality-document.md) | 产品域和架构层长期健康度 |
|
||||
| [`tasks/README.md`](tasks/README.md) | 多 agent 时的一任务一文件规则 |
|
||||
|
||||
## 职责边界
|
||||
|
||||
- “做什么、怎么算完成”只在 `02-requirements.md` 定义。
|
||||
- “用什么”只在 `03-tech-stack.md` 定义。
|
||||
- “模块和数据如何组织”只在 `04-architecture.md` 定义。
|
||||
- “公开服务如何调用”只在 `api.md` 定义。
|
||||
- “页面放在哪里”只在 `routes.md` 定义。
|
||||
- “现在代码实际是什么状态”只在 `current-state.md` 维护。
|
||||
- 历史命令和结果只追加到 `../progress.md`。
|
||||
|
||||
同一事实不要在多个文件各写一个版本。需要摘要时链接到权威文档。
|
||||
@@ -0,0 +1,41 @@
|
||||
# 空项目初始化与 Harness 接入清单
|
||||
|
||||
> ShopHelm 当前从空目录起步。本文只记录从“文档基线”进入“可运行代码基线”的一次性准备状态。
|
||||
|
||||
## 文档基线
|
||||
|
||||
- [x] 建立 `AGENTS.md` 和 AI 开工入口。
|
||||
- [x] 固定产品愿景、需求范围和首发验收。
|
||||
- [x] 固定 Go + Gio、SQLite、外部 Chrome 技术路线。
|
||||
- [x] 定义模块边界、数据模型、页面和本地服务合约。
|
||||
- [x] 建立单 agent 任务看板、执行流水和当前状态。
|
||||
- [x] 建立验证、收尾、评审和质量跟踪文档。
|
||||
|
||||
## 代码基线
|
||||
|
||||
- [x] 初始化 Git 仓库并配置远程。
|
||||
- [ ] 初始化 Go module `shophelm`。
|
||||
- [ ] 在目标 Windows 上运行最小 Gio 窗口。
|
||||
- [ ] 锁定 Go/Gio 版本并更新技术文档。
|
||||
- [ ] 建立 `cmd/`、`internal/`、`migrations/` 和测试结构。
|
||||
- [ ] 让 `./init.ps1` 完成依赖同步、测试并打印启动命令。
|
||||
- [ ] 运行 `go test ./...`、`go vet ./...` 和 Windows build。
|
||||
- [ ] 把真实结果追加到 `progress.md` 并更新 `current-state.md`。
|
||||
|
||||
代码基线由 [`T-001`](06-tasks.md) 到 T-005 逐项完成,不在文档初始化任务中顺带生成未经验证的业务代码。
|
||||
|
||||
## 首轮决策闸门
|
||||
|
||||
完成工程基线后,必须在扩展业务页面前验证:
|
||||
|
||||
| 闸门 | 任务 | 通过标准 |
|
||||
| --- | --- | --- |
|
||||
| Gio 管理台可用性 | T-101 | 真实字段表格、筛选和表单在 Windows 上可用 |
|
||||
| Chrome/profile 安全启动 | T-102 | 参数、占用、退出和代理行为可复现 |
|
||||
| 图片预览/导出一致性 | T-103 | 共享几何算法和像素测试通过 |
|
||||
|
||||
任一闸门失败时,先在 T-104 记录证据和替代方案,由用户决定是否调整技术路线,不继续堆业务代码。
|
||||
|
||||
## 完成判据
|
||||
|
||||
当 T-005 完成后,本接入清单视为完成。后续状态只在 `current-state.md`、`06-tasks.md` 和 `progress.md` 维护,不继续修改本文的历史勾选。
|
||||
+550
@@ -0,0 +1,550 @@
|
||||
# 本地模块合约
|
||||
|
||||
> ShopHelm 首发没有 HTTP 后端。本文定义 Gio UI 可调用的 application service、平台 port、事件和错误合约。实现可以拆分文件,但不得另起一套语义不兼容的接口。
|
||||
|
||||
## 一、通用约定
|
||||
|
||||
- 所有可能 I/O 的方法第一个参数是 `context.Context`。
|
||||
- application service 输入输出使用 domain/application DTO,不把 Gio widget 或 SQL row 暴露出去。
|
||||
- ID 使用 `int64`,`0` 表示尚未持久化,外部输入不接受负数。
|
||||
- 时间在 service 边界使用 `time.Time`,持久化时转 UTC RFC3339。
|
||||
- 金额输入先按货币精度解析成整数最小单位;比例和汇率使用十进制字符串或 `big.Rat`,不直接用二进制浮点作为业务事实。
|
||||
- 列表默认 `limit=50`,最大 `200`;必须有稳定的 ID 二级排序。
|
||||
- 写操作成功后返回持久化后的完整 view。
|
||||
- 错误使用稳定 code;底层 cause 只供日志,不直接显示给用户。
|
||||
|
||||
## 二、通用类型
|
||||
|
||||
目标形状:
|
||||
|
||||
```go
|
||||
type SortDirection string
|
||||
|
||||
const (
|
||||
SortAsc SortDirection = "asc"
|
||||
SortDesc SortDirection = "desc"
|
||||
)
|
||||
|
||||
type PageRequest struct {
|
||||
Offset int
|
||||
Limit int
|
||||
SortBy string
|
||||
Direction SortDirection
|
||||
}
|
||||
|
||||
type Page[T any] struct {
|
||||
Items []T
|
||||
Total int
|
||||
Offset int
|
||||
Limit int
|
||||
}
|
||||
|
||||
type ErrorCode string
|
||||
|
||||
type AppError struct {
|
||||
Code ErrorCode
|
||||
Message string
|
||||
FieldErrors map[string]string
|
||||
Retryable bool
|
||||
Cause error
|
||||
}
|
||||
```
|
||||
|
||||
`Message` 是安全的默认用户提示;UI 可以按 `Code` 替换为更具体中文,但不得展示 `Cause`。
|
||||
|
||||
## 三、店铺服务
|
||||
|
||||
### 3.1 输入与视图
|
||||
|
||||
```go
|
||||
type StoreInput struct {
|
||||
Platform string
|
||||
CountryCode string
|
||||
Name string
|
||||
AccountLabel string
|
||||
Owner string
|
||||
Tags []string
|
||||
Status string
|
||||
Note string
|
||||
Profile BrowserProfileInput
|
||||
Proxy ProxyInput
|
||||
}
|
||||
|
||||
type BrowserProfileInput struct {
|
||||
PathKind string // managed | external
|
||||
ProfileDir string
|
||||
ChromePath string
|
||||
StartURL string
|
||||
}
|
||||
|
||||
type ProxyInput struct {
|
||||
Enabled bool
|
||||
Scheme string // http | https | socks4 | socks5
|
||||
Host string
|
||||
Port int
|
||||
}
|
||||
|
||||
type StoreFilter struct {
|
||||
Query string
|
||||
Platforms []string
|
||||
Countries []string
|
||||
Owners []string
|
||||
Tags []string
|
||||
Statuses []string
|
||||
IncludeArchived bool
|
||||
Page PageRequest
|
||||
}
|
||||
|
||||
type StoreView struct {
|
||||
Store domain.Store
|
||||
Profile domain.BrowserProfile
|
||||
Proxy domain.ProxyConfig
|
||||
Tags []string
|
||||
Runtime BrowserRuntimeView
|
||||
ShortcutCount int
|
||||
PendingTodoCount int
|
||||
}
|
||||
```
|
||||
|
||||
`StoreInput` 没有密码、Cookie、token 或代理认证字段。MVP 期间不得添加这些字段的“临时字符串”替代品。
|
||||
|
||||
### 3.2 服务接口
|
||||
|
||||
```go
|
||||
type StoreService interface {
|
||||
List(ctx context.Context, filter StoreFilter) (Page[StoreView], error)
|
||||
Get(ctx context.Context, id int64) (StoreView, error)
|
||||
Create(ctx context.Context, input StoreInput) (StoreView, error)
|
||||
Update(ctx context.Context, id int64, input StoreInput) (StoreView, error)
|
||||
Archive(ctx context.Context, id int64) error
|
||||
Restore(ctx context.Context, id int64) (StoreView, error)
|
||||
}
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- `Create` 对 managed profile 生成 `<profile-root>\<store-id>`,因此可以先事务创建店铺再补 profile;任一步失败整体回滚。
|
||||
- external profile 必须已存在且是目录;managed profile 可由服务创建。
|
||||
- `Archive` 只写 `archived_at`,不关闭 Chrome,不删目录。
|
||||
- `(platform, country, name)` 在未归档记录中重复时返回 `conflict`。
|
||||
|
||||
## 四、快捷入口和待办
|
||||
|
||||
```go
|
||||
type StoreShortcutService interface {
|
||||
List(ctx context.Context, storeID int64) ([]domain.StoreShortcut, error)
|
||||
Create(ctx context.Context, storeID int64, input ShortcutInput) (domain.StoreShortcut, error)
|
||||
Update(ctx context.Context, id int64, input ShortcutInput) (domain.StoreShortcut, error)
|
||||
Delete(ctx context.Context, id int64) error
|
||||
}
|
||||
|
||||
type TodoService interface {
|
||||
List(ctx context.Context, filter TodoFilter) (Page[domain.Todo], error)
|
||||
Create(ctx context.Context, input TodoInput) (domain.Todo, error)
|
||||
Update(ctx context.Context, id int64, input TodoInput) (domain.Todo, error)
|
||||
SetStatus(ctx context.Context, id int64, status string) (domain.Todo, error)
|
||||
Delete(ctx context.Context, id int64) error
|
||||
}
|
||||
```
|
||||
|
||||
快捷链接只接受绝对 `http` 或 `https` URL。`javascript:`、`file:` 和命令协议必须拒绝。
|
||||
|
||||
## 五、Chrome 与 profile 合约
|
||||
|
||||
### 5.1 Application service
|
||||
|
||||
```go
|
||||
type OpenStoreRequest struct {
|
||||
StoreID int64
|
||||
ShortcutID *int64
|
||||
}
|
||||
|
||||
type LaunchResult struct {
|
||||
StoreID int64
|
||||
PID int
|
||||
TargetURL string
|
||||
StartedAt time.Time
|
||||
}
|
||||
|
||||
type BrowserRuntimeView struct {
|
||||
StoreID int64
|
||||
Status string
|
||||
PID int
|
||||
ExitCode *int
|
||||
LastError *AppError
|
||||
ChangedAt time.Time
|
||||
}
|
||||
|
||||
type BrowserLaunchService interface {
|
||||
Open(ctx context.Context, request OpenStoreRequest) (LaunchResult, error)
|
||||
Runtime(ctx context.Context, storeID int64) (BrowserRuntimeView, error)
|
||||
ListRuntime(ctx context.Context) ([]BrowserRuntimeView, error)
|
||||
}
|
||||
```
|
||||
|
||||
`Open` 的副作用:
|
||||
|
||||
- 可能创建 managed profile 目录。
|
||||
- 启动外部 Chrome。
|
||||
- 成功后更新 `last_opened_at` 和最后 PID。
|
||||
- 发布 `browser.status.changed`。
|
||||
|
||||
`Open` 不等待 Chrome 退出。应用退出时默认不结束已打开的 Chrome。
|
||||
|
||||
### 5.2 Platform ports
|
||||
|
||||
```go
|
||||
type LaunchSpec struct {
|
||||
Executable string
|
||||
ProfileDir string
|
||||
TargetURL string
|
||||
ProxyURL string
|
||||
}
|
||||
|
||||
type ProcessHandle interface {
|
||||
PID() int
|
||||
Wait(ctx context.Context) (exitCode int, err error)
|
||||
}
|
||||
|
||||
type ChromeLauncher interface {
|
||||
Start(ctx context.Context, spec LaunchSpec) (ProcessHandle, error)
|
||||
}
|
||||
|
||||
type ProfileInspector interface {
|
||||
Inspect(ctx context.Context, profileDir string) (ProfileUse, error)
|
||||
}
|
||||
|
||||
type ProfileUse struct {
|
||||
Occupied bool
|
||||
PID int
|
||||
Source string // shophelm_registry | chrome_lock | unknown
|
||||
}
|
||||
```
|
||||
|
||||
`ChromeLauncher` 的单元测试必须能注入 fake executable/command runner。只有 platform adapter 可构造 Chrome 参数。
|
||||
|
||||
## 六、商品服务
|
||||
|
||||
```go
|
||||
type ProductInput struct {
|
||||
StoreID int64
|
||||
Name string
|
||||
SKU string
|
||||
Cost string
|
||||
SalePrice string
|
||||
Currency string
|
||||
Stock *int
|
||||
ProductURL string
|
||||
Status string
|
||||
Note string
|
||||
}
|
||||
|
||||
type ProductFilter struct {
|
||||
Query string
|
||||
StoreIDs []int64
|
||||
Statuses []string
|
||||
IncludeArchived bool
|
||||
Page PageRequest
|
||||
}
|
||||
|
||||
type ProductService interface {
|
||||
List(ctx context.Context, filter ProductFilter) (Page[domain.Product], error)
|
||||
Get(ctx context.Context, id int64) (domain.Product, error)
|
||||
Create(ctx context.Context, input ProductInput) (domain.Product, error)
|
||||
Update(ctx context.Context, id int64, input ProductInput) (domain.Product, error)
|
||||
Archive(ctx context.Context, id int64) error
|
||||
Restore(ctx context.Context, id int64) (domain.Product, error)
|
||||
}
|
||||
|
||||
type ContentTemplateService interface {
|
||||
List(ctx context.Context, filter TemplateFilter) (Page[domain.ContentTemplate], error)
|
||||
Create(ctx context.Context, input ContentTemplateInput) (domain.ContentTemplate, error)
|
||||
Update(ctx context.Context, id int64, input ContentTemplateInput) (domain.ContentTemplate, error)
|
||||
Archive(ctx context.Context, id int64) error
|
||||
}
|
||||
|
||||
type ProductChecklistService interface {
|
||||
List(ctx context.Context, productID int64) ([]domain.ProductCheckItem, error)
|
||||
SeedDefaults(ctx context.Context, productID int64) ([]domain.ProductCheckItem, error)
|
||||
SetChecked(ctx context.Context, itemID int64, checked bool) (domain.ProductCheckItem, error)
|
||||
}
|
||||
```
|
||||
|
||||
商品 URL 可以为空;非空时只接受绝对 HTTP(S) URL。SKU 可为空,但同一店铺非空 SKU 重复时 UI 必须警告;是否阻止保存由 T-301 根据用户确认定稿并同步 schema。
|
||||
|
||||
## 七、价格计算
|
||||
|
||||
```go
|
||||
type PriceCalculationInput struct {
|
||||
SalePrice string
|
||||
ProductCost string
|
||||
ShippingCost string
|
||||
OtherCost string
|
||||
PlatformFeeRate string
|
||||
ExchangeRate string
|
||||
Currency string
|
||||
CostCurrency string
|
||||
}
|
||||
|
||||
type PriceCalculationResult struct {
|
||||
Inputs PriceCalculationInput
|
||||
PlatformFee string
|
||||
TotalCost string
|
||||
ReferenceMargin string
|
||||
ReferenceMarginRate string
|
||||
Warnings []string
|
||||
}
|
||||
|
||||
type PriceCalculator interface {
|
||||
Calculate(input PriceCalculationInput) (PriceCalculationResult, error)
|
||||
}
|
||||
```
|
||||
|
||||
规则:
|
||||
|
||||
- 空费率、汇率或费用不能偷偷套默认值;返回校验错误或明确的零值警告。
|
||||
- 除数为零时返回 `validation_failed`。
|
||||
- 输出必须回显输入,界面称其为“基础利润参考”,不称财务报表。
|
||||
|
||||
## 八、商品文件导入导出
|
||||
|
||||
```go
|
||||
type ImportFormat string // csv | xlsx
|
||||
type ImportStrategy string // all_or_nothing | valid_rows_only
|
||||
|
||||
type ProductImportPreview struct {
|
||||
SourcePath string
|
||||
Fingerprint string
|
||||
Format ImportFormat
|
||||
Sheet string
|
||||
ValidRows []ProductImportRow
|
||||
Errors []ImportRowError
|
||||
TotalRows int
|
||||
}
|
||||
|
||||
type ProductImportCommit struct {
|
||||
SourcePath string
|
||||
Fingerprint string
|
||||
Strategy ImportStrategy
|
||||
DefaultStoreID int64
|
||||
}
|
||||
|
||||
type ImportResult struct {
|
||||
Created int
|
||||
Updated int
|
||||
Skipped int
|
||||
}
|
||||
|
||||
type ProductImportExportService interface {
|
||||
PreviewImport(ctx context.Context, path string, format ImportFormat) (ProductImportPreview, error)
|
||||
CommitImport(ctx context.Context, request ProductImportCommit) (ImportResult, error)
|
||||
Export(ctx context.Context, filter ProductFilter, destination string, format ImportFormat) (FileResult, error)
|
||||
}
|
||||
```
|
||||
|
||||
提交时重新计算文件 SHA-256;与 preview fingerprint 不同则返回 `source_changed`,要求重新预览。
|
||||
|
||||
## 九、图片素材与合成
|
||||
|
||||
### 9.1 素材服务
|
||||
|
||||
```go
|
||||
type ImageAssetInput struct {
|
||||
StoreID *int64
|
||||
ProductID *int64
|
||||
AssetType string
|
||||
SourcePath string
|
||||
CopyToLibrary bool
|
||||
Note string
|
||||
}
|
||||
|
||||
type ImageAssetService interface {
|
||||
List(ctx context.Context, filter ImageAssetFilter) (Page[domain.ImageAsset], error)
|
||||
Add(ctx context.Context, input ImageAssetInput) (domain.ImageAsset, error)
|
||||
Update(ctx context.Context, id int64, input ImageAssetInput) (domain.ImageAsset, error)
|
||||
Remove(ctx context.Context, id int64) error
|
||||
RefreshMetadata(ctx context.Context, id int64) (domain.ImageAsset, error)
|
||||
}
|
||||
```
|
||||
|
||||
`Remove` 删除数据库关联;仅当文件是 app-managed 且用户明确勾选删除文件时,才能进入独立确认流程。默认不删文件。
|
||||
|
||||
### 9.2 合成规格
|
||||
|
||||
```go
|
||||
type CompositionSpec struct {
|
||||
BaseAssetID int64
|
||||
OverlayAssetID int64
|
||||
CanvasWidth int
|
||||
CanvasHeight int
|
||||
BaseFit string // contain | cover
|
||||
CenterX float64 // normalized canvas coordinate
|
||||
CenterY float64
|
||||
WidthRatio float64
|
||||
Opacity float64
|
||||
BackgroundRGBA uint32
|
||||
}
|
||||
|
||||
type CompositionPlan struct {
|
||||
Canvas image.Rectangle
|
||||
BaseSource image.Rectangle
|
||||
BaseTarget image.Rectangle
|
||||
OverlaySource image.Rectangle
|
||||
OverlayTarget image.Rectangle
|
||||
Opacity uint8
|
||||
}
|
||||
|
||||
type ExportImageRequest struct {
|
||||
StoreID *int64
|
||||
ProductID *int64
|
||||
Spec CompositionSpec
|
||||
Format string // png | jpg
|
||||
Destination string
|
||||
Overwrite bool
|
||||
}
|
||||
|
||||
type ImageComposerService interface {
|
||||
Plan(ctx context.Context, spec CompositionSpec) (CompositionPlan, error)
|
||||
RenderPreview(ctx context.Context, spec CompositionSpec, maxWidth, maxHeight int) (image.Image, error)
|
||||
Export(ctx context.Context, request ExportImageRequest) (domain.ImageExport, error)
|
||||
}
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- `CanvasWidth`、`CanvasHeight` 必须为正,并受实现中明确的像素/内存上限约束。
|
||||
- `WidthRatio` 大于 0;`Opacity` 在 `0..1`。
|
||||
- `Plan` 是预览和导出的唯一几何算法。
|
||||
- `Overwrite=false` 且目标存在时返回 `destination_exists`。
|
||||
- `Overwrite=true` 仍不得覆盖任一输入素材文件。
|
||||
- 导出成功前不写 `image_exports`;数据库记录失败时删除本次新建输出或报告可恢复的部分失败。
|
||||
|
||||
## 十、客服服务
|
||||
|
||||
```go
|
||||
type ReplyTemplateInput struct {
|
||||
Category string
|
||||
Language string
|
||||
Title string
|
||||
Content string
|
||||
}
|
||||
|
||||
type ReplyTemplateService interface {
|
||||
List(ctx context.Context, filter ReplyTemplateFilter) (Page[domain.ReplyTemplate], error)
|
||||
Create(ctx context.Context, input ReplyTemplateInput) (domain.ReplyTemplate, error)
|
||||
Update(ctx context.Context, id int64, input ReplyTemplateInput) (domain.ReplyTemplate, error)
|
||||
Archive(ctx context.Context, id int64) error
|
||||
Copy(ctx context.Context, id int64) (CopyResult, error)
|
||||
}
|
||||
|
||||
type CustomerFollowupInput struct {
|
||||
StoreID int64
|
||||
BuyerLabel string
|
||||
OrderRef string
|
||||
IssueType string
|
||||
Status string
|
||||
NextFollowupAt *time.Time
|
||||
Note string
|
||||
}
|
||||
|
||||
type CustomerFollowupService interface {
|
||||
List(ctx context.Context, filter FollowupFilter) (Page[domain.CustomerFollowup], error)
|
||||
Create(ctx context.Context, input CustomerFollowupInput) (domain.CustomerFollowup, error)
|
||||
Update(ctx context.Context, id int64, input CustomerFollowupInput) (domain.CustomerFollowup, error)
|
||||
Archive(ctx context.Context, id int64) error
|
||||
}
|
||||
```
|
||||
|
||||
`Copy` 成功写入系统剪贴板后才增加 `usage_count`。剪贴板失败时不增加计数。
|
||||
|
||||
## 十一、工作台
|
||||
|
||||
```go
|
||||
type DashboardQuery struct {
|
||||
Now time.Time
|
||||
RecentStoreLimit int
|
||||
RecentExportLimit int
|
||||
}
|
||||
|
||||
type DashboardView struct {
|
||||
RecentStores []StoreView
|
||||
DueTodos []domain.Todo
|
||||
DueFollowups []domain.CustomerFollowup
|
||||
PendingProducts []domain.Product
|
||||
RecentExports []domain.ImageExport
|
||||
}
|
||||
|
||||
type DashboardService interface {
|
||||
Load(ctx context.Context, query DashboardQuery) (DashboardView, error)
|
||||
}
|
||||
```
|
||||
|
||||
Dashboard 允许各分区分别失败并显示局部错误,但不得把失败分区伪装为空数据。
|
||||
|
||||
## 十二、设置、备份与恢复
|
||||
|
||||
```go
|
||||
type SettingsService interface {
|
||||
Get(ctx context.Context) (domain.Settings, error)
|
||||
Update(ctx context.Context, input SettingsInput) (domain.Settings, error)
|
||||
}
|
||||
|
||||
type BackupService interface {
|
||||
Create(ctx context.Context, destination string) (BackupResult, error)
|
||||
Inspect(ctx context.Context, source string) (BackupManifest, error)
|
||||
Restore(ctx context.Context, source string, confirmation RestoreConfirmation) (RestoreResult, error)
|
||||
}
|
||||
```
|
||||
|
||||
`RestoreConfirmation` 必须包含 UI 展示过的备份 fingerprint 和用户确认标志。恢复期间 application 进入 maintenance 状态,其他写操作返回 `maintenance_mode`。
|
||||
|
||||
## 十三、应用事件
|
||||
|
||||
| 事件 | 触发时机 | 负载 | UI 结果 |
|
||||
| --- | --- | --- | --- |
|
||||
| `operation.started` | 异步 intent 接受 | request ID、operation、entity | 显示 busy,禁用重复提交 |
|
||||
| `operation.completed` | 操作成功 | request ID、result summary | 更新页面并反馈成功 |
|
||||
| `operation.failed` | 操作失败 | request ID、AppError | 保留输入,显示可行动错误 |
|
||||
| `browser.status.changed` | Chrome 启动、运行或退出 | BrowserRuntimeView | 更新店铺行状态 |
|
||||
| `data.restored` | 恢复并重新打开数据库成功 | backup ID、schema version | 清空页面缓存并重载 |
|
||||
| `preview.ready` | 最新图片预览完成 | request ID、preview image | 仅接收当前 request ID 的结果 |
|
||||
|
||||
旧请求的预览事件到达时,UI 根据 request ID 丢弃,避免快速拖动后显示过期画面。
|
||||
|
||||
## 十四、错误码
|
||||
|
||||
| Code | 含义 | 是否可重试 |
|
||||
| --- | --- | --- |
|
||||
| `validation_failed` | 字段或组合不合法 | 修正输入后 |
|
||||
| `not_found` | 资源不存在或已归档 | 否 |
|
||||
| `conflict` | 唯一约束或状态冲突 | 修改输入后 |
|
||||
| `profile_in_use` | Chrome profile 已占用 | 关闭对应进程后 |
|
||||
| `chrome_not_found` | Chrome 路径无效 | 配置后 |
|
||||
| `chrome_start_failed` | 进程启动失败 | 视 cause |
|
||||
| `proxy_invalid` | 代理格式无效 | 修正配置后 |
|
||||
| `source_missing` | 素材或导入文件不存在 | 重新选择 |
|
||||
| `source_changed` | 导入 preview 后文件已变化 | 重新预览 |
|
||||
| `unsupported_format` | 文件或图片格式不支持 | 选择支持格式 |
|
||||
| `destination_exists` | 输出已存在且未允许覆盖 | 改名或确认覆盖 |
|
||||
| `file_access_denied` | 文件权限不足 | 修改目录权限后 |
|
||||
| `database_busy` | SQLite 超时仍被占用 | 是 |
|
||||
| `database_corrupt` | 完整性检查失败 | 使用有效备份 |
|
||||
| `backup_incompatible` | schema 版本不支持 | 升级应用或选其他备份 |
|
||||
| `maintenance_mode` | 恢复期间拒绝写入 | 恢复完成后 |
|
||||
| `cancelled` | 用户取消或 context 结束 | 可重新发起 |
|
||||
| `internal` | 未分类内部错误 | 视日志 |
|
||||
|
||||
新增错误码必须同步 UI 映射和测试,不得把底层错误字符串当稳定合约。
|
||||
|
||||
## 十五、副作用边界
|
||||
|
||||
| 操作 | 允许的副作用 | 禁止的副作用 |
|
||||
| --- | --- | --- |
|
||||
| 保存店铺 | SQLite 写入;创建 managed profile 目录 | 登录平台、删除已有 profile |
|
||||
| 打开店铺 | 启动 Chrome;记录状态 | 注入凭证、开启 CDP、操作网页 |
|
||||
| 导入商品 | 读取用户文件;确认后事务写 SQLite | 修改源文件、静默跳过错误 |
|
||||
| 添加素材 | 读取图片;可选复制到素材库 | 修改原图 |
|
||||
| 导出图片 | 创建新输出;写导出记录 | 用 UI 截图导出、默认覆盖 |
|
||||
| 复制话术 | 写系统剪贴板;增加使用次数 | 自动发送到平台 |
|
||||
| 备份 | 创建一致性数据库副本 | 复制全部 Chrome profile |
|
||||
| 恢复 | 验证后替换数据库并保留回滚副本 | 未确认覆盖、删除外部素材 |
|
||||
@@ -0,0 +1,18 @@
|
||||
# 干净收尾检查清单
|
||||
|
||||
> 每轮会话结束前逐项检查,确保下一轮只依靠仓库即可继续。
|
||||
|
||||
- [ ] 当前只存在一个 `DOING` 任务,或没有进行中任务。
|
||||
- [ ] 当前任务状态与实际完成边界一致,没有无证据的 `DONE`。
|
||||
- [ ] `go test ./...`、`go vet ./...` 和 build 已运行;无法运行时已在 `progress.md` 如实记录。
|
||||
- [ ] 任务要求的 Gio、Chrome、图片、导入或恢复 smoke 已运行并记录环境。
|
||||
- [ ] `progress.md` 已追加真实命令、结果、阻塞和决策。
|
||||
- [ ] `current-state.md` 已覆盖为当前目录、命令、版本、blocker 和下一任务。
|
||||
- [ ] 需求、依赖、schema、合约或页面发生变化时,对应权威文档已同步。
|
||||
- [ ] 没有真实密码、Cookie、token、买家隐私、生产 profile 或私有 URL 进入变更。
|
||||
- [ ] 没有未说明的半成品、临时输出或测试进程残留。
|
||||
- [ ] 测试没有改写用户数据库、外部 profile 或原始图片。
|
||||
- [ ] 工作区中的用户无关改动未被覆盖或回退。
|
||||
- [ ] 如使用 Git,`git status --short` 中每项变化都能解释;必要提交仅包含当前任务。
|
||||
|
||||
任一项不满足时先补齐;确实无法补齐则把任务保留为 `DOING` 或标 `BLOCKED`,记录原因和恢复步骤。
|
||||
@@ -0,0 +1,97 @@
|
||||
# 当前实现状态
|
||||
|
||||
> 可覆盖的仓库现实快照。历史执行和验证结果只追加到 [`../progress.md`](../progress.md)。
|
||||
|
||||
## 当前快照
|
||||
|
||||
- 日期:2026-07-11
|
||||
- 阶段:Harness 文档基线完成,生产代码待初始化
|
||||
- 项目目录:`D:\OPC\shop_helm`
|
||||
- 产品:店小航 ShopHelm,Windows 本地跨境电商运营工作台
|
||||
- 技术路线:Go + Gio + SQLite + 外部 Chrome
|
||||
- 本机 Go:`go1.23.0 windows/amd64`
|
||||
- 本机 Git:`git version 2.49.0.windows.1`
|
||||
- Git 仓库:已初始化,当前为 `main`,已有初始提交并配置 `origin`
|
||||
- Go module:尚未初始化
|
||||
- 生产代码:无
|
||||
- 自动测试:无
|
||||
- Harness 校验:本地链接、占位符、空白、code fence、任务图、P0 追踪、脚本语法和 `git diff --check` 均已通过
|
||||
- 当前 blocker:无;T-001 尚未执行是计划状态,不是阻塞
|
||||
|
||||
## 已存在内容
|
||||
|
||||
| 路径 | 状态 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| `AGENTS.md` | 已有 | 仓库级 agent 规则和安全边界 |
|
||||
| `README.md` | 已有 | 人类入口和项目状态 |
|
||||
| `.git` / `.gitignore` | 已有 | 现有 Git 仓库和 Go ignore 规则,保持用户配置 |
|
||||
| `.gitattributes` | 已有 | 文本统一 LF,二进制素材不做行尾转换 |
|
||||
| `docs/` | 已有 | 产品、技术、架构、任务和质量文档 |
|
||||
| `progress.md` | 已有 | 只追加执行流水 |
|
||||
| `init.ps1` / `init.sh` | 已有 | 计划中的标准 Go 启动入口 |
|
||||
| `cmd/` | 待建 | T-001 创建 Gio 应用入口 |
|
||||
| `internal/` | 待建 | T-002 起创建模块 |
|
||||
| `migrations/` | 待建 | T-003 创建 |
|
||||
| `go.mod` / `go.sum` | 待建 | T-001 创建并锁定版本 |
|
||||
|
||||
## 任务状态
|
||||
|
||||
任务权威来源是 [`06-tasks.md`](06-tasks.md)。
|
||||
|
||||
- 已完成:无生产开发任务。
|
||||
- 正在进行:无。
|
||||
- 下一个可领取任务:T-001 初始化 Go module 和最小 Gio 窗口。
|
||||
|
||||
## 当前可运行内容
|
||||
|
||||
当前只有文档,没有可启动应用。
|
||||
|
||||
```powershell
|
||||
./init.ps1
|
||||
```
|
||||
|
||||
该命令当前会因缺少 `go.mod` 主动退出并提示执行 T-001,这是设计行为。
|
||||
|
||||
T-001 完成后的预期标准命令:
|
||||
|
||||
```powershell
|
||||
./init.ps1
|
||||
go run ./cmd/shophelm
|
||||
go test ./...
|
||||
go vet ./...
|
||||
go build -o build/shophelm.exe ./cmd/shophelm
|
||||
```
|
||||
|
||||
在实际成功运行前,不得把这些预期命令描述为已验证。
|
||||
|
||||
## 已确认决策
|
||||
|
||||
- 首发只支持 Windows 本地桌面。
|
||||
- 使用 Go + Gio,不使用 Wails。
|
||||
- 首发按店铺 -> 商品/图片 -> 客服顺序交付。
|
||||
- SQLite 保存结构化数据,原图按路径引用。
|
||||
- MVP 不保存平台密码,不支持代理认证。
|
||||
- 每个店铺使用独立 Chrome `user-data-dir`,但不提供防关联能力。
|
||||
- 图片预览使用 Gio,正式导出使用独立 CPU 合成管线。
|
||||
- 首发不接平台 API、不做 RPA 和自动页面操作。
|
||||
|
||||
## 下一轮开工
|
||||
|
||||
1. 读取 `AGENTS.md` 和 `docs/00-ai-start-here.md`。
|
||||
2. 在 `docs/06-tasks.md` 把 T-001 改为 `DOING`。
|
||||
3. 保留现有 Git 配置,初始化 module `shophelm`。
|
||||
4. 用本机 Go 运行最小 Gio 窗口;若不兼容,升级到受支持 Go 工具链并记录结论。
|
||||
5. 锁定依赖版本,同步本文和 `docs/03-tech-stack.md`。
|
||||
6. 完成全部验收后才能将 T-001 改为 `DONE`。
|
||||
|
||||
## 维护规则
|
||||
|
||||
以下变化发生时覆盖更新本文件:
|
||||
|
||||
- Git 状态、Go module 或生产目录发生变化。
|
||||
- 实际 Go/Gio 版本确定。
|
||||
- 标准命令首次运行或发生变化。
|
||||
- 任务状态变化。
|
||||
- 新增 blocker 或文档与代码出现差异。
|
||||
|
||||
同时把任务状态同步到 `06-tasks.md`,把真实执行证据追加到 `../progress.md`。
|
||||
@@ -0,0 +1,49 @@
|
||||
# 单次交付评审评分表
|
||||
|
||||
> 任务实现完成、正式接受前使用。它评估一轮交付;长期代码库质量见 [`quality-document.md`](quality-document.md)。
|
||||
|
||||
## 评分
|
||||
|
||||
每项 0-2 分:`0` 不满足,`1` 部分满足,`2` 完全满足。
|
||||
|
||||
| 维度 | 2 分标准 | 分数 | 证据 |
|
||||
| --- | --- | --- | --- |
|
||||
| 需求正确性 | 当前任务对应需求 ID 和全部验收均满足 | | |
|
||||
| 验证真实性 | 自动命令和手工 smoke 均实际运行,结果写入 progress | | |
|
||||
| 范围纪律 | 只修改当前任务需要内容,无 Backlog 和无关重构 | | |
|
||||
| 架构边界 | UI/application/domain/infrastructure/platform 依赖符合文档 | | |
|
||||
| 数据与副作用安全 | 数据库、文件、profile、进程和恢复失败路径均受控 | | |
|
||||
| 安全与合规 | 无秘密泄露、无绕过平台边界、无未经确认自动化 | | |
|
||||
| 交接准备度 | 文档、任务、current state 和下一步足够新会话继续 | | |
|
||||
|
||||
总分:`/14`
|
||||
|
||||
## 强制门槛
|
||||
|
||||
结论为 **Accept** 必须同时满足:
|
||||
|
||||
- 总分至少 12。
|
||||
- “需求正确性”和“验证真实性”均为 2。
|
||||
- 不存在任何 0 分。
|
||||
- 安全硬边界没有违反。
|
||||
- 任务状态、progress 和 current state 已同步。
|
||||
|
||||
任一安全硬边界违反,直接 **Block**,不能用总分抵消。
|
||||
|
||||
## 结论
|
||||
|
||||
- **Accept**:达到门槛,可以将任务保持为 `DONE`。
|
||||
- **Revise**:实现方向可用,但有明确缺口;任务回到 `DOING`,列出修复。
|
||||
- **Block**:存在根本风险、技术闸门失败或需要用户决策;任务标 `BLOCKED`。
|
||||
|
||||
## 评审记录
|
||||
|
||||
- 任务 ID:
|
||||
- 评审日期:
|
||||
- 结论:
|
||||
- 缺失证据:
|
||||
- 必须修复:
|
||||
- 残余风险:
|
||||
- 下次复审条件:
|
||||
|
||||
评审不能靠 agent 自己概括“看起来没问题”。每个 2 分都要引用测试命令、手工步骤、代码位置或文档条目。
|
||||
@@ -0,0 +1,27 @@
|
||||
# 方法对照表
|
||||
|
||||
> 遇到失败模式时先修最相关的工件或边界,不把新规则全部塞进入口文件。
|
||||
|
||||
| 失败模式 | 表现 | 首要修复 | 权威工件 |
|
||||
| --- | --- | --- | --- |
|
||||
| 新会话重新摸索 | 不知道现在能否运行、下一个任务是什么 | 恢复现实快照 | [`current-state.md`](current-state.md) + [`../progress.md`](../progress.md) |
|
||||
| 范围扩散 | 店铺没闭环就开始订单、AI、RPA | 回到需求和任务依赖 | [`02-requirements.md`](02-requirements.md) + [`06-tasks.md`](06-tasks.md) |
|
||||
| Gio 页面卡顿 | 滚动或拖动时 UI 冻结 | 把 I/O 移出 frame,检查事件模型 | [`04-architecture.md`](04-architecture.md) + [`05-coding-rules.md`](05-coding-rules.md) |
|
||||
| UI 状态丢失 | 输入框、按钮或滚动每帧重置 | 持久化页面/widget 状态 | [`routes.md`](routes.md) + [`05-coding-rules.md`](05-coding-rules.md) |
|
||||
| Chrome 混店或重复启动 | profile 参数错误、占用仍启动 | 收紧 launch/profile port 和测试 | [`api.md`](api.md) + T-102 |
|
||||
| 把 profile 当防关联 | 文案承诺规避平台关联 | 回到产品和合规边界 | [`01-vision.md`](01-vision.md) + [`AGENTS.md`](../AGENTS.md) |
|
||||
| 图片预览与导出不一致 | 位置、缩放或透明度改变 | 只保留共享 planner | [`04-architecture.md`](04-architecture.md) + T-103 |
|
||||
| 原图被改写 | 输出路径等于输入或直接原地保存 | 原子新文件和覆盖保护 | [`05-coding-rules.md`](05-coding-rules.md) + [`api.md`](api.md) |
|
||||
| 导入静默丢数据 | 无效行跳过但用户不知道 | preview、fingerprint、事务提交 | [`api.md`](api.md) + T-304 |
|
||||
| 数据恢复后损坏 | 未校验就替换 SQLite | 完整性检查和回滚副本 | [`04-architecture.md`](04-architecture.md) + T-208 |
|
||||
| 代码改了就宣布完成 | 没有测试、build 或 smoke | 绑定完成到证据 | [`06-tasks.md`](06-tasks.md) + [`clean-state-checklist.md`](clean-state-checklist.md) |
|
||||
| 多 agent 抢改看板 | 状态覆盖、ID 冲突 | 切换一任务一文件 | [`tasks/README.md`](tasks/README.md) |
|
||||
| 文档与实现漂移 | schema/API/页面各自演化 | 按变更类型同步权威文档 | [`AGENTS.md`](../AGENTS.md) 的文档同步规则 |
|
||||
| 日志泄露敏感数据 | 错误日志包含账号或路径秘密 | 结构化错误码和脱敏测试 | [`05-coding-rules.md`](05-coding-rules.md) + T-501 |
|
||||
|
||||
## 使用原则
|
||||
|
||||
- 先确认失败模式,再修最小的一项事实、边界或测试。
|
||||
- 修复完成的同一任务同步相关工件。
|
||||
- 同一个事实只有一个权威位置,其他文档只做摘要和链接。
|
||||
- 高风险功能用可复现原型和测试给结论,不靠“理论上可行”。
|
||||
@@ -0,0 +1,50 @@
|
||||
# 代码库质量文档
|
||||
|
||||
> 跟踪 ShopHelm 产品领域和架构层的长期健康度。单次任务输出使用 [`evaluator-rubric.md`](evaluator-rubric.md)。
|
||||
|
||||
## 评级
|
||||
|
||||
- **A**:完整验证通过,边界清楚,测试稳定,新 agent 可直接接手。
|
||||
- **B**:主要验证通过,有少量明确缺口但不影响核心使用。
|
||||
- **C**:部分可用,存在可靠性、测试或可读性缺口。
|
||||
- **D**:不可用,或存在数据/安全/结构性问题。
|
||||
- **N/A**:尚未实现,不参与质量趋势判断。
|
||||
|
||||
## 产品领域
|
||||
|
||||
| 领域 | 评级 | 验证状态 | Agent 可读性 | 测试稳定性 | 关键缺口 | 上次更新 |
|
||||
| --- | --- | --- | --- | --- | --- | --- |
|
||||
| 多店铺运营台 | N/A | 未实现 | 文档已定义 | 无测试 | T-201 至 T-209 | 2026-07-11 |
|
||||
| 商品运营助手 | N/A | 未实现 | 文档已定义 | 无测试 | T-301 至 T-308 | 2026-07-11 |
|
||||
| 图片预览与导出 | N/A | 未实现 | 合约已定义 | 无测试 | T-103、T-305 至 T-307 | 2026-07-11 |
|
||||
| 客服助手 | N/A | 未实现 | 文档已定义 | 无测试 | T-401 至 T-405 | 2026-07-11 |
|
||||
| 数据备份恢复 | N/A | 未实现 | 流程已定义 | 无测试 | T-208 | 2026-07-11 |
|
||||
|
||||
## 架构层
|
||||
|
||||
| 层级 | 评级 | 边界执行 | Agent 可读性 | 关键缺口 | 上次更新 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Harness / 项目文档 | A | 入口和权威来源已建立 | 高 | 随实现持续校准 | 2026-07-11 |
|
||||
| Gio UI | N/A | 未实现 | 目标边界已定义 | T-001、T-101 | 2026-07-11 |
|
||||
| Application / Domain | N/A | 未实现 | 合约已定义 | T-004 起实现 | 2026-07-11 |
|
||||
| SQLite persistence | N/A | 未实现 | schema 草案已定义 | T-003、T-201 起实现 | 2026-07-11 |
|
||||
| Chrome platform adapter | N/A | 未实现 | 副作用边界已定义 | T-102 | 2026-07-11 |
|
||||
| Image pipeline | N/A | 未实现 | 几何与导出边界已定义 | T-103 | 2026-07-11 |
|
||||
|
||||
## 更新规则
|
||||
|
||||
- 每个阶段验收任务完成后更新对应产品领域。
|
||||
- 架构或高风险模块发生实质变化后更新对应架构层。
|
||||
- 评级下降必须写明触发变更和恢复任务。
|
||||
- 没跑验证不能给 A;仅“代码已完成”最高不得超过 C。
|
||||
- 尚未实现用 N/A,不用 D 惩罚计划中的空白。
|
||||
|
||||
## 变更历史
|
||||
|
||||
### 2026-07-11
|
||||
|
||||
- 变更:按 Harness Coding 模板建立 ShopHelm 产品、技术、架构、任务和质量文档。
|
||||
- 提升:聊天中的产品结论转为仓库内可执行事实。
|
||||
- 下降:无。
|
||||
- 新缺口:生产代码、自动测试和真实 Windows smoke 尚未开始。
|
||||
- 下一检查点:T-104 三个高风险技术闸门完成后。
|
||||
+252
@@ -0,0 +1,252 @@
|
||||
# Gio 页面与导航结构
|
||||
|
||||
> ShopHelm 是桌面应用,没有浏览器 URL。本文用稳定的 route ID 定义页面、参数、职责和组件归属。
|
||||
|
||||
## 一、应用框架
|
||||
|
||||
首发窗口由 `AppShell` 组成:
|
||||
|
||||
```text
|
||||
+----------------+---------------------------------------------+
|
||||
| 主导航 | 页面工具栏 |
|
||||
| +---------------------------------------------+
|
||||
| 今日工作台 | |
|
||||
| 店铺管理 | 当前页面内容 |
|
||||
| 商品助手 | |
|
||||
| 客服助手 | |
|
||||
| 设置 | |
|
||||
+----------------+---------------------------------------------+
|
||||
| 全局状态栏:数据库 / 后台操作 / 错误入口 |
|
||||
+--------------------------------------------------------------+
|
||||
```
|
||||
|
||||
主页面不做营销首页。窗口最小尺寸由 T-101 实测确定;低于舒适宽度时侧栏可折叠,但表格和表单不得重叠或截断关键动作。
|
||||
|
||||
## 二、Route 定义
|
||||
|
||||
| Route ID | 参数 | 页面 | 首发 |
|
||||
| --- | --- | --- | --- |
|
||||
| `dashboard` | 无 | 今日工作台 | 是 |
|
||||
| `stores.list` | 可选 filter snapshot | 店铺列表 | 是 |
|
||||
| `stores.new` | 无 | 新增店铺 | 是 |
|
||||
| `stores.detail` | `store_id` | 店铺详情 | 是 |
|
||||
| `stores.edit` | `store_id` | 编辑店铺 | 是 |
|
||||
| `products.list` | 可选 `store_id` | 商品草稿列表 | 是 |
|
||||
| `products.new` | 可选 `store_id` | 新增商品 | 是 |
|
||||
| `products.detail` | `product_id` | 商品详情/编辑 | 是 |
|
||||
| `products.templates` | `kind` | 标题/描述模板 | 是 |
|
||||
| `products.import` | 无 | 商品导入预览 | 是 |
|
||||
| `images.assets` | 可选 `store_id`,`product_id` | 图片素材库 | 是 |
|
||||
| `images.compose` | 可选 `product_id`,`base_asset_id` | 图片叠加预览 | 是 |
|
||||
| `customer.replies` | 可选 category/language | 客服话术库 | 是 |
|
||||
| `customer.followups` | 可选 `store_id`,`status` | 跟进记录 | 是 |
|
||||
| `settings.general` | 无 | 常规和路径设置 | 是 |
|
||||
| `settings.backup` | 无 | 备份与恢复 | 是 |
|
||||
| `settings.logs` | 无 | 日志位置和诊断信息 | P1 |
|
||||
|
||||
无效或已归档 ID 导航到当前页面的 `not_found` 状态,提供返回列表入口,不打开空白窗口。
|
||||
|
||||
## 三、主导航
|
||||
|
||||
主导航固定五项:
|
||||
|
||||
1. 今日工作台。
|
||||
2. 店铺管理。
|
||||
3. 商品助手。
|
||||
4. 客服助手。
|
||||
5. 设置。
|
||||
|
||||
图片素材和图片叠加属于商品助手,不作为一级导航。备份恢复属于设置。
|
||||
|
||||
## 四、页面职责
|
||||
|
||||
### 4.1 今日工作台
|
||||
|
||||
- 最近打开店铺,可直接打开默认入口。
|
||||
- 今日/逾期待办与客服跟进。
|
||||
- 待上架、需优化商品。
|
||||
- 最近图片导出。
|
||||
- 四个新增快捷动作。
|
||||
- 每个分区独立显示 loading、empty 或 error,局部失败不清空其他分区。
|
||||
|
||||
### 4.2 店铺列表
|
||||
|
||||
- 表格列:店铺名、平台、国家/站点、账号标识、负责人、标签、业务状态、Chrome 状态、最后打开、更新时间、操作。
|
||||
- 工具栏:搜索、平台/国家/负责人/标签/状态筛选、排序、新增。
|
||||
- 行操作使用图标按钮并带 tooltip:打开、快捷入口菜单、编辑、归档。
|
||||
- 双击或点击店铺名进入详情;“打开 Chrome”必须是独立明确动作。
|
||||
- 默认排除归档记录。
|
||||
|
||||
### 4.3 店铺新增/编辑
|
||||
|
||||
分区:
|
||||
|
||||
- 基本信息。
|
||||
- Chrome/profile。
|
||||
- 代理。
|
||||
- 快捷入口。
|
||||
- 备注。
|
||||
|
||||
保存前显示字段错误。切换页面时有未保存内容则确认离开。密码字段不得出现。
|
||||
|
||||
### 4.4 店铺详情
|
||||
|
||||
- 顶部显示店铺身份和业务/Chrome 状态。
|
||||
- 主要命令:打开默认入口、打开快捷入口、编辑。
|
||||
- 显示 profile 路径、代理摘要、最后打开、负责人、标签和待办。
|
||||
- profile 路径可复制或在文件管理器中定位;不得提供“一键删除 profile”。
|
||||
|
||||
### 4.5 商品列表和详情
|
||||
|
||||
列表:
|
||||
|
||||
- 按店铺、状态和关键词筛选。
|
||||
- 显示商品名、SKU、店铺、价格、库存、状态、检查进度、更新时间。
|
||||
- 提供新增、导入、导出入口。
|
||||
|
||||
详情:
|
||||
|
||||
- 基础资料和价格。
|
||||
- 标题/描述模板选择。
|
||||
- 上架检查清单。
|
||||
- 图片素材。
|
||||
- 打开图片叠加预览。
|
||||
|
||||
价格计算结果与输入放在同一无嵌套面板中,明确标记为参考值。
|
||||
|
||||
### 4.6 商品导入
|
||||
|
||||
- 第一步选择 CSV/XLSX。
|
||||
- 第二步显示文件摘要、列映射、有效行和错误行。
|
||||
- 第三步选择 `all_or_nothing` 或 `valid_rows_only` 并确认。
|
||||
- 提交后显示创建、更新、跳过统计。
|
||||
|
||||
未经 preview 不允许直接导入。
|
||||
|
||||
### 4.7 图片素材库
|
||||
|
||||
- 显示真实缩略图、类型、所属店铺/商品、尺寸和缺失状态。
|
||||
- 支持按路径引用或复制到 ShopHelm 素材库。
|
||||
- 原图缺失时显示路径和重新定位入口。
|
||||
- 删除关联和删除受管文件是两个不同动作。
|
||||
|
||||
### 4.8 图片叠加预览
|
||||
|
||||
稳定布局:
|
||||
|
||||
```text
|
||||
+----------------------------+----------------------+
|
||||
| | 底图 / 叠加图选择 |
|
||||
| | 位置 X / Y |
|
||||
| 实时预览画布 | 缩放 |
|
||||
| | 透明度 |
|
||||
| | 输出尺寸 / 格式 |
|
||||
| | 导出 |
|
||||
+----------------------------+----------------------+
|
||||
```
|
||||
|
||||
- 画布保持目标宽高比,窗口变化不改变 composition spec。
|
||||
- 拖动叠加图时更新归一化中心点。
|
||||
- 数字输入、滑杆和重置按钮保持同步。
|
||||
- 预览生成中显示稳定 loading,不改变画布尺寸。
|
||||
- 导出成功后显示输出路径和“在文件夹中显示”动作。
|
||||
- 不放多图层列表、滤镜、文字工具或旋转工具。
|
||||
|
||||
窄窗口下控制区移到画布下方,不覆盖画布。
|
||||
|
||||
### 4.9 客服话术
|
||||
|
||||
- 分类、语言、关键词筛选。
|
||||
- 列表显示标题和安全长度的内容预览。
|
||||
- 一键复制是主要动作,并显示反馈。
|
||||
- 新增/编辑使用同页表单或 modal,由 T-101 的组件原型决定,整个应用保持一致。
|
||||
|
||||
### 4.10 客服跟进
|
||||
|
||||
- 表格显示店铺、买家标识、订单标识、问题类型、状态、下次跟进和更新时间。
|
||||
- 默认优先显示逾期、今日到期和未解决记录。
|
||||
- 可快速更新状态。
|
||||
- 不显示“发送消息”按钮。
|
||||
|
||||
### 4.11 设置
|
||||
|
||||
常规设置:
|
||||
|
||||
- Chrome 可执行文件。
|
||||
- managed profile 根目录。
|
||||
- 默认图片导出目录。
|
||||
- 默认货币和界面语言。
|
||||
|
||||
备份设置:
|
||||
|
||||
- 创建备份。
|
||||
- 备份清单和位置。
|
||||
- 检查后恢复。
|
||||
- 清晰说明备份不包含 Chrome profile 和外部素材。
|
||||
|
||||
## 五、项目内组件
|
||||
|
||||
| 组件 | 归属 | 稳定职责 |
|
||||
| --- | --- | --- |
|
||||
| `AppShell` | `ui/components` | 侧栏、工具栏、内容区、全局状态 |
|
||||
| `DataTable` | `ui/components` | 固定列、表头、滚动、选中、排序、行操作 |
|
||||
| `FilterBar` | `ui/components` | 搜索、筛选、重置 |
|
||||
| `FormField` | `ui/components` | 标签、输入、帮助和字段错误 |
|
||||
| `Select` | `ui/components` | 有限枚举选择 |
|
||||
| `Modal` | `ui/components` | 确认和小型编辑流程 |
|
||||
| `Toast` | `ui/components` | 短时成功/失败反馈 |
|
||||
| `StatusBadge` | `ui/components` | 状态文字、图标和颜色 |
|
||||
| `EmptyState` | `ui/components` | 空数据和一个主要动作 |
|
||||
| `ErrorPanel` | `ui/components` | 可行动错误与重试 |
|
||||
| `PathPicker` | `ui/components` | 路径显示、选择和校验 |
|
||||
| `ImagePreviewCanvas` | `ui/components` | 画布、拖动和 viewport 映射 |
|
||||
| `ImageLayerControls` | `ui/components` | 位置、缩放、透明度和重置 |
|
||||
|
||||
组件只抽象已经在至少两个页面出现的稳定交互;首个页面不提前建设通用表单 DSL。
|
||||
|
||||
## 六、导航状态
|
||||
|
||||
```go
|
||||
type Route struct {
|
||||
Name RouteName
|
||||
Params map[string]string
|
||||
}
|
||||
|
||||
type Navigator interface {
|
||||
Push(Route)
|
||||
Replace(Route)
|
||||
Back()
|
||||
}
|
||||
```
|
||||
|
||||
- 应用启动进入 `dashboard`。
|
||||
- 列表进入详情后返回时保留搜索、筛选、排序和滚动位置。
|
||||
- 保存成功后回到来源页或留在详情,由页面任务验收明确;不可每页自行决定不同模式。
|
||||
- 恢复数据库成功后清空历史栈并进入 `dashboard`。
|
||||
- 未保存表单离开时弹确认;没有改动时直接离开。
|
||||
|
||||
## 七、统一页面状态
|
||||
|
||||
每个页面显式维护:
|
||||
|
||||
```text
|
||||
idle -> loading -> ready
|
||||
`-> empty
|
||||
`-> error
|
||||
|
||||
ready -> submitting -> ready/error
|
||||
```
|
||||
|
||||
- loading、empty、error 使用稳定尺寸,避免整个布局跳动。
|
||||
- 请求进行中禁用重复动作,但保留取消或返回能力。
|
||||
- error 保留用户已输入的表单内容。
|
||||
- 删除/归档、覆盖、恢复等不可逆或高影响动作使用确认 modal。
|
||||
|
||||
## 八、UI 文字与图标
|
||||
|
||||
- 命令按钮优先使用 Gio/项目已有标准图标;不手画重复图标。
|
||||
- 图标按钮必须有 tooltip 和可访问名称。
|
||||
- 状态必须同时有文字或图标,不只靠颜色。
|
||||
- 表格内部使用紧凑字号,不使用 hero 级标题。
|
||||
- 固定格式画布、表格列和工具栏必须有稳定尺寸约束。
|
||||
- 页面不放“本功能可以做什么”的宣传说明;只在错误、空状态和确认流程中显示必要操作信息。
|
||||
@@ -0,0 +1,50 @@
|
||||
# 多 agent 一任务一文件
|
||||
|
||||
> 当前默认使用 [`../06-tasks.md`](../06-tasks.md) 单文件看板。只有多个 agent 或人/agent 并发开发时才切换本模式。
|
||||
|
||||
## 切换规则
|
||||
|
||||
1. 把 `docs/06-tasks.md` 冻结为已存在任务的索引,不再并发修改任务行。
|
||||
2. 新任务在本目录使用 `T-<编号>.md`。
|
||||
3. 每个 agent 只编辑自己领取的任务文件和该任务必要的代码。
|
||||
4. 执行记录写入任务文件,不逐任务抢改共享 `progress.md` 和 `current-state.md`。
|
||||
5. 阶段性汇总由单一协调者更新共享文件。
|
||||
|
||||
## 文件名和编号
|
||||
|
||||
- 文件名:`T-101.md`,细分任务可用 `T-101a.md`。
|
||||
- 新编号取 `06-tasks.md` 与本目录现有任务最大编号加一。
|
||||
- 目标文件存在时换下一个编号,不覆盖。
|
||||
- `_template.md` 不参与编号。
|
||||
|
||||
## Frontmatter
|
||||
|
||||
```yaml
|
||||
---
|
||||
id: T-XXX
|
||||
title: 一句话任务名
|
||||
phase: 1
|
||||
deps: []
|
||||
status: TODO
|
||||
created: YYYY-MM-DD
|
||||
owner:
|
||||
---
|
||||
```
|
||||
|
||||
状态只允许 `TODO`、`DOING`、`DONE`、`BLOCKED`。
|
||||
|
||||
## 领取
|
||||
|
||||
- 选择编号最小且依赖全 `DONE` 的 `TODO`。
|
||||
- 设置 `owner` 和 `DOING` 后再开始。
|
||||
- 同一 agent 一次只领取一个。
|
||||
- 发现已有 owner 或状态刚变化时不得覆盖,重新选择任务。
|
||||
|
||||
## 完成
|
||||
|
||||
- 在任务文件记录变更、真实命令、结果、手工 smoke、决策和残余风险。
|
||||
- 全部验收通过后改为 `DONE`。
|
||||
- 验证失败时保持 `DOING`;确有外部决策阻塞时标 `BLOCKED` 并写清恢复条件。
|
||||
- 不以“代码已写”代替证据。
|
||||
|
||||
流程和安全规则的唯一权威源仍是 [`../../AGENTS.md`](../../AGENTS.md)。用户使用 `grill me:` 时只做反方评审,不自动建立或实现任务。
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
id: T-XXX
|
||||
title: 一句话任务名
|
||||
phase: 1
|
||||
deps: []
|
||||
status: TODO
|
||||
created: YYYY-MM-DD
|
||||
owner:
|
||||
---
|
||||
|
||||
## 需求与背景
|
||||
|
||||
对应需求 ID、现象和为什么要做。
|
||||
|
||||
## 方案
|
||||
|
||||
明确修改的模块、数据/服务/页面变化和副作用边界。
|
||||
|
||||
## 验收要点
|
||||
|
||||
- 可执行的自动验证命令。
|
||||
- 可观察的手工 smoke。
|
||||
- 失败路径和安全边界。
|
||||
|
||||
## 边界
|
||||
|
||||
明确本任务不修改和不实现的内容。
|
||||
|
||||
## 执行记录
|
||||
|
||||
- 状态:
|
||||
- 变更:
|
||||
- 验证:
|
||||
- 阻塞:
|
||||
- 决策:
|
||||
- 残余风险:
|
||||
Reference in New Issue
Block a user