From e62b691b59b50e49ca5f67a472f3818e96186fb3 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Sat, 11 Jul 2026 17:02:30 +0800 Subject: [PATCH] docs: establish ShopHelm harness coding baseline --- .gitattributes | 12 + AGENTS.md | 110 ++++++ CLAUDE.md | 5 + README.md | 67 +++- docs/00-ai-start-here.md | 131 +++++++ docs/01-vision.md | 59 ++++ docs/02-requirements.md | 215 ++++++++++++ docs/03-tech-stack.md | 101 ++++++ docs/04-architecture.md | 622 ++++++++++++++++++++++++++++++++++ docs/05-coding-rules.md | 176 ++++++++++ docs/06-tasks.md | 100 ++++++ docs/README.md | 47 +++ docs/adoption-checklist.md | 41 +++ docs/api.md | 550 ++++++++++++++++++++++++++++++ docs/clean-state-checklist.md | 18 + docs/current-state.md | 97 ++++++ docs/evaluator-rubric.md | 49 +++ docs/method-map.md | 27 ++ docs/quality-document.md | 50 +++ docs/routes.md | 252 ++++++++++++++ docs/tasks/README.md | 50 +++ docs/tasks/_template.md | 36 ++ init.ps1 | 34 ++ init.sh | 36 ++ progress.md | 33 ++ 25 files changed, 2916 insertions(+), 2 deletions(-) create mode 100644 .gitattributes create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 docs/00-ai-start-here.md create mode 100644 docs/01-vision.md create mode 100644 docs/02-requirements.md create mode 100644 docs/03-tech-stack.md create mode 100644 docs/04-architecture.md create mode 100644 docs/05-coding-rules.md create mode 100644 docs/06-tasks.md create mode 100644 docs/README.md create mode 100644 docs/adoption-checklist.md create mode 100644 docs/api.md create mode 100644 docs/clean-state-checklist.md create mode 100644 docs/current-state.md create mode 100644 docs/evaluator-rubric.md create mode 100644 docs/method-map.md create mode 100644 docs/quality-document.md create mode 100644 docs/routes.md create mode 100644 docs/tasks/README.md create mode 100644 docs/tasks/_template.md create mode 100644 init.ps1 create mode 100755 init.sh create mode 100644 progress.md diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1edf469 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# Keep repository text files on LF so Windows tools do not create full-file +# line-ending diffs. PowerShell accepts LF, and shell scripts require it. +* text=auto eol=lf + +# Binary assets must never receive text conversion. +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.pdf binary +*.zip binary diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2abc982 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,110 @@ +# AGENTS.md + +> ShopHelm 的仓库级 AI coding agent 入口。进入仓库后先读本文,再读 [`docs/00-ai-start-here.md`](docs/00-ai-start-here.md)。 + +## 项目定位 + +ShopHelm(店小航)是面向小团队跨境卖家的 Windows 本地运营工作台。当前只交付三条业务主线: + +1. 多店铺运营台。 +2. 商品运营助手,其中包含单张商品图叠加预览和导出。 +3. 客服话术与跟进助手。 + +项目采用 Go + Gio、SQLite 和外部 Chrome。它不是完整 ERP,不是浏览器“防关联”产品,也不以全自动 RPA 为核心。 + +## 事实优先级 + +发生冲突时按以下顺序处理: + +1. 用户在当前会话中的最新明确指令。 +2. 本文件的仓库级执行与安全规则。 +3. [`docs/02-requirements.md`](docs/02-requirements.md) 的产品范围和验收标准。 +4. [`docs/03-tech-stack.md`](docs/03-tech-stack.md)、[`docs/04-architecture.md`](docs/04-architecture.md)、[`docs/api.md`](docs/api.md) 和 [`docs/routes.md`](docs/routes.md) 的技术事实。 +5. [`docs/06-tasks.md`](docs/06-tasks.md) 的任务拆分。 +6. [`docs/current-state.md`](docs/current-state.md) 和代码、测试所反映的当前现实。 + +若计划文档与可运行代码冲突,先在 `current-state.md` 记录差异,再确认应修文档还是修实现;不得静默选择一边。 + +## 必读顺序 + +每次开始编码前按顺序读取: + +1. `AGENTS.md` +2. `docs/00-ai-start-here.md` +3. `docs/01-vision.md` +4. `docs/02-requirements.md` +5. `docs/03-tech-stack.md` +6. `docs/04-architecture.md` +7. `docs/05-coding-rules.md` +8. `docs/api.md` 和 `docs/routes.md` 中与任务相关的部分 +9. `docs/06-tasks.md` +10. `progress.md` +11. `docs/current-state.md` + +## 固定开工流程 + +1. 用 `pwd` 确认仓库根目录。 +2. 读取 `progress.md` 和 `docs/current-state.md`,恢复当前事实。 +3. 若存在 `.git`,运行 `git status --short --branch` 和 `git log --oneline -5`;若尚未初始化 Git,以 `current-state.md` 为准。 +4. 若存在 `go.mod`,运行 `./init.ps1`;若不存在,只允许领取负责初始化项目的 T-001。 +5. 基线失败时先修基线,不叠加新功能。 +6. 从 `docs/06-tasks.md` 领取第一个依赖均为 `DONE` 的 `TODO` 任务。 + +## 任务纪律 + +- 单 agent 模式下一轮只做一个任务。 +- 开始前把任务改为 `DOING`;同一时间最多一个 `DOING`。 +- 只修改完成该任务所需的代码和文档,不顺手实现 Backlog。 +- 验收和验证全部通过后才能改为 `DONE`。 +- 把真实命令、结果、阻塞和关键决策追加到 `progress.md`,并覆盖更新 `docs/current-state.md`。 +- 会话结束前执行 [`docs/clean-state-checklist.md`](docs/clean-state-checklist.md)。 +- 多 agent 并发时切换到 [`docs/tasks/README.md`](docs/tasks/README.md) 的一任务一文件模式。 + +## 业务与安全硬边界 + +- 密码、Cookie、token、代理密码不得明文进入 SQLite、日志、测试夹具或文档。 +- MVP 不保存平台密码;依靠独立 Chrome profile 保留用户登录会话。 +- `user-data-dir` 只表示会话和缓存隔离,不得宣传或实现“防关联”“反指纹”能力。 +- Chrome 必须通过 `exec.Command` 参数启动,不拼接 shell 命令。 +- 删除或归档店铺时不得自动删除对应 profile 目录。 +- 图片导出默认写入独立目录,不覆盖原图;覆盖必须由用户明确选择并二次确认。 +- 不绕过验证码、平台风控、权限校验、限流或反爬机制。 +- 不实现刷单、刷评、批量未确认发消息、批量爬取或自动修改平台数据。 +- 新增任何会改变第三方平台数据的自动化前,必须先补需求、合规边界、dry-run、人工确认、审计和失败恢复设计。 + +## 工程规则 + +- 标识符、包名和文件名使用英文;产品界面和项目文档默认使用简体中文。 +- 遵守 `internal/domain -> internal/application -> internal/infrastructure/platform -> internal/ui` 的依赖方向。 +- Gio 的 frame/layout 回调中不得做数据库、网络、文件或进程阻塞操作。 +- UI 控件状态必须持久化在页面状态结构中,不得在每帧临时创建导致状态丢失。 +- 数据库 schema 只能通过追加 migration 演进,不得修改已经发布的 migration。 +- 文件和数据库写入需处理部分失败;备份、恢复和图片导出使用临时文件加原子替换。 +- 新增依赖前先更新 `docs/03-tech-stack.md`,说明用途、许可证、替代方案和锁定版本。 +- 默认优先标准库和现有项目组件,不为未来可能需求提前抽象。 + +## 文档同步规则 + +| 变化 | 必须同步 | +| --- | --- | +| 产品范围或验收变化 | `02-requirements.md`、`06-tasks.md` | +| 依赖、Go/Gio 版本或命令变化 | `03-tech-stack.md`、`00-ai-start-here.md`、`current-state.md`、`init.*` | +| 模块边界或数据模型变化 | `04-architecture.md`、`api.md`、相关 migration 和任务 | +| 页面或导航变化 | `routes.md`、相关需求和任务 | +| 代码现实或下一任务变化 | `current-state.md`、`06-tasks.md`、`progress.md` | + +## 对话约定 + +- 用户输入 `grill me:` 或 `grill:` 时,执行反方评审:先查事实,指出需求、技术、商业和合规上的薄弱点,只讨论,不修改代码或文件,除非用户同时明确要求落地修改。 + +## 最低验证 + +代码初始化后,任务结束前至少运行: + +```powershell +go test ./... +go vet ./... +go build -o build/shophelm.exe ./cmd/shophelm +``` + +涉及 Chrome、图片导出、备份恢复或 Gio 交互时,还必须执行任务中写明的手工 smoke,并在 `progress.md` 记录输入、观察结果和环境。 diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..b5ad153 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +# CLAUDE.md + +> Claude Code 的仓库级薄入口。进入本仓库后先读 [`AGENTS.md`](AGENTS.md)。 + +ShopHelm 的权威 agent 规则、产品边界、阅读顺序和验证要求统一维护在 `AGENTS.md`。本文不重复任务流程,避免规则漂移。 diff --git a/README.md b/README.md index c903050..167d0a7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,66 @@ -# shop_helm +# 店小航 ShopHelm -外贸电商助手 \ No newline at end of file +ShopHelm 是面向小团队跨境卖家的 Windows 本地运营工作台。首发版本按以下顺序交付: + +1. 多店铺运营台:管理店铺、独立 Chrome profile、快捷入口和普通代理。 +2. 商品运营助手:管理商品草稿、文案模板、价格、图片素材,并完成单张图片叠加预览与导出。 +3. 客服助手:管理多语言话术、买家问题和跟进提醒。 + +产品不以“防关联”或全自动 RPA 为卖点。不同 `user-data-dir` 只用于隔离本地登录会话;平台操作优先采用官方 API、CSV/XLSX 和人工确认流程。 + +## 当前状态 + +- Harness Coding 文档已初始化。 +- Git 仓库已初始化并配置 `origin`;生产代码和 `go.mod` 尚未初始化。 +- 本机已安装 `go1.23.0 windows/amd64`;第一项开发任务会验证 Gio 兼容性并锁定依赖版本。 +- 下一个任务是 [`T-001`](docs/06-tasks.md):初始化 Go module 和 Go + Gio 最小可运行骨架。 + +当前代码现实以 [`docs/current-state.md`](docs/current-state.md) 为准。 + +## Agent 入口 + +AI coding agent 进入仓库后按此顺序读取: + +1. [`AGENTS.md`](AGENTS.md) +2. [`docs/00-ai-start-here.md`](docs/00-ai-start-here.md) +3. [`docs/05-coding-rules.md`](docs/05-coding-rules.md) +4. [`docs/06-tasks.md`](docs/06-tasks.md) +5. [`progress.md`](progress.md) +6. [`docs/current-state.md`](docs/current-state.md) + +完整文档索引见 [`docs/README.md`](docs/README.md)。 + +## 标准命令 + +T-001 完成后,Windows PowerShell 使用: + +```powershell +./init.ps1 +``` + +脚本执行 `go mod download` 和 `go test ./...`,然后打印开发启动命令。当前没有 `go.mod`,脚本会主动失败并指向 T-001,这是预期状态。 + +常用命令: + +```powershell +go run ./cmd/shophelm +go test ./... +go vet ./... +go build -o build/shophelm.exe ./cmd/shophelm +``` + +## 文档分工 + +| 文件 | 唯一职责 | +| --- | --- | +| [`docs/01-vision.md`](docs/01-vision.md) | 产品为什么做、服务谁、什么不做 | +| [`docs/02-requirements.md`](docs/02-requirements.md) | 用户需求、优先级和可观察验收标准 | +| [`docs/03-tech-stack.md`](docs/03-tech-stack.md) | 技术选型、版本策略和运行命令 | +| [`docs/04-architecture.md`](docs/04-architecture.md) | 模块边界、数据模型和关键数据流 | +| [`docs/api.md`](docs/api.md) | 本地 Go 服务、事件和错误合约 | +| [`docs/routes.md`](docs/routes.md) | Gio 页面、导航状态和组件归属 | +| [`docs/06-tasks.md`](docs/06-tasks.md) | 单 agent 任务看板 | +| [`progress.md`](progress.md) | 只追加的执行与验证证据 | +| [`docs/current-state.md`](docs/current-state.md) | 可覆盖的仓库当前快照 | + +核心原则:需求和架构先成为仓库内事实,代码再按任务逐步实现;没有验证证据的任务不能标记完成。 diff --git a/docs/00-ai-start-here.md b/docs/00-ai-start-here.md new file mode 100644 index 0000000..4f691db --- /dev/null +++ b/docs/00-ai-start-here.md @@ -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。 diff --git a/docs/01-vision.md b/docs/01-vision.md new file mode 100644 index 0000000..c733ce5 --- /dev/null +++ b/docs/01-vision.md @@ -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)。 diff --git a/docs/02-requirements.md b/docs/02-requirements.md new file mode 100644 index 0000000..44a2db8 --- /dev/null +++ b/docs/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 在业务代码中静默猜测。 diff --git a/docs/03-tech-stack.md b/docs/03-tech-stack.md new file mode 100644 index 0000000..57e287e --- /dev/null +++ b/docs/03-tech-stack.md @@ -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\\ +├── assets\ +├── backups\ +└── logs\ +``` + +默认图片导出目录: + +```text +%USERPROFILE%\Pictures\ShopHelm\Exports\ +``` + +用户可以在设置中改 profile 根目录、备份目录和导出目录,但迁移或切换目录必须显式确认。 + +## 七、依赖纪律 + +- 标准库可解决时不加依赖。 +- 新依赖必须记录用途、许可证、维护状态、替代方案和测试范围。 +- 同一职责不得并存两套数据库驱动、图片库、日志框架或状态管理方式。 +- 不因示例代码方便而加入 Web 框架、嵌入浏览器、ORM 或依赖注入框架。 +- 依赖变更后运行 `go mod tidy`,审查 `go.mod` / `go.sum` 差异并记录验证结果。 diff --git a/docs/04-architecture.md b/docs/04-architecture.md new file mode 100644 index 0000000..db5edc7 --- /dev/null +++ b/docs/04-architecture.md @@ -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= + --no-first-run + --disable-default-apps + [--proxy-server=] + +``` + +代理认证不通过命令行注入。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\ +│ `- \ +├── 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 成功替代真实结论。 diff --git a/docs/05-coding-rules.md b/docs/05-coding-rules.md new file mode 100644 index 0000000..8eb6fe6 --- /dev/null +++ b/docs/05-coding-rules.md @@ -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` 字段当真实进程状态。 +- 绝不静默吞掉导入、恢复或导出中的部分失败。 +- 绝不因为任务困难就跳过高风险闸门。 diff --git a/docs/06-tasks.md b/docs/06-tasks.md new file mode 100644 index 0000000..e812723 --- /dev/null +++ b/docs/06-tasks.md @@ -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 评估。 diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..406874d --- /dev/null +++ b/docs/README.md @@ -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`。 + +同一事实不要在多个文件各写一个版本。需要摘要时链接到权威文档。 diff --git a/docs/adoption-checklist.md b/docs/adoption-checklist.md new file mode 100644 index 0000000..57620b0 --- /dev/null +++ b/docs/adoption-checklist.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` 维护,不继续修改本文的历史勾选。 diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..d86eeff --- /dev/null +++ b/docs/api.md @@ -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;任一步失败整体回滚。 +- 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 | +| 恢复 | 验证后替换数据库并保留回滚副本 | 未确认覆盖、删除外部素材 | diff --git a/docs/clean-state-checklist.md b/docs/clean-state-checklist.md new file mode 100644 index 0000000..e077a71 --- /dev/null +++ b/docs/clean-state-checklist.md @@ -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`,记录原因和恢复步骤。 diff --git a/docs/current-state.md b/docs/current-state.md new file mode 100644 index 0000000..9055371 --- /dev/null +++ b/docs/current-state.md @@ -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`。 diff --git a/docs/evaluator-rubric.md b/docs/evaluator-rubric.md new file mode 100644 index 0000000..d1c9f78 --- /dev/null +++ b/docs/evaluator-rubric.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 分都要引用测试命令、手工步骤、代码位置或文档条目。 diff --git a/docs/method-map.md b/docs/method-map.md new file mode 100644 index 0000000..ad9d6bb --- /dev/null +++ b/docs/method-map.md @@ -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 | + +## 使用原则 + +- 先确认失败模式,再修最小的一项事实、边界或测试。 +- 修复完成的同一任务同步相关工件。 +- 同一个事实只有一个权威位置,其他文档只做摘要和链接。 +- 高风险功能用可复现原型和测试给结论,不靠“理论上可行”。 diff --git a/docs/quality-document.md b/docs/quality-document.md new file mode 100644 index 0000000..2e4a3f6 --- /dev/null +++ b/docs/quality-document.md @@ -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 三个高风险技术闸门完成后。 diff --git a/docs/routes.md b/docs/routes.md new file mode 100644 index 0000000..7022fd2 --- /dev/null +++ b/docs/routes.md @@ -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 级标题。 +- 固定格式画布、表格列和工具栏必须有稳定尺寸约束。 +- 页面不放“本功能可以做什么”的宣传说明;只在错误、空状态和确认流程中显示必要操作信息。 diff --git a/docs/tasks/README.md b/docs/tasks/README.md new file mode 100644 index 0000000..a4408de --- /dev/null +++ b/docs/tasks/README.md @@ -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:` 时只做反方评审,不自动建立或实现任务。 diff --git a/docs/tasks/_template.md b/docs/tasks/_template.md new file mode 100644 index 0000000..470bfcc --- /dev/null +++ b/docs/tasks/_template.md @@ -0,0 +1,36 @@ +--- +id: T-XXX +title: 一句话任务名 +phase: 1 +deps: [] +status: TODO +created: YYYY-MM-DD +owner: +--- + +## 需求与背景 + +对应需求 ID、现象和为什么要做。 + +## 方案 + +明确修改的模块、数据/服务/页面变化和副作用边界。 + +## 验收要点 + +- 可执行的自动验证命令。 +- 可观察的手工 smoke。 +- 失败路径和安全边界。 + +## 边界 + +明确本任务不修改和不实现的内容。 + +## 执行记录 + +- 状态: +- 变更: +- 验证: +- 阻塞: +- 决策: +- 残余风险: diff --git a/init.ps1 b/init.ps1 new file mode 100644 index 0000000..3d11cfe --- /dev/null +++ b/init.ps1 @@ -0,0 +1,34 @@ +#!/usr/bin/env pwsh + +# ShopHelm 标准启动与验证入口(Windows PowerShell)。 +# 默认执行依赖同步和基础测试,只打印启动命令。 +# 设置 RUN_START_COMMAND=1 后才会实际启动 Gio 应用。 + +$ErrorActionPreference = "Stop" +Set-Location -Path $PSScriptRoot + +if (-not (Test-Path -LiteralPath "go.mod")) { + Write-Error "尚未找到 go.mod。当前应先完成 docs/06-tasks.md 的 T-001,再运行本脚本。" + exit 2 +} + +$InstallCmd = "go mod download" +$VerifyCmd = "go test ./..." +$StartCmd = "go run ./cmd/shophelm" + +Write-Host "==> 当前目录: $($PWD.Path)" +Write-Host "==> 同步 Go 依赖" +Invoke-Expression $InstallCmd + +Write-Host "==> 运行基础测试" +Invoke-Expression $VerifyCmd + +Write-Host "==> 开发启动命令" +Write-Host " $StartCmd" + +if ($env:RUN_START_COMMAND -eq "1") { + Write-Host "==> 启动 ShopHelm" + Invoke-Expression $StartCmd +} else { + Write-Host "设置 RUN_START_COMMAND=1 可让 init.ps1 在验证通过后启动应用。" +} diff --git a/init.sh b/init.sh new file mode 100755 index 0000000..eb23d99 --- /dev/null +++ b/init.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# ShopHelm 标准启动与验证入口(WSL / Git Bash / macOS / Linux)。 +# Windows 原生开发以 ./init.ps1 为权威入口;本文件保持等价命令。 + +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$ROOT_DIR" + +if [[ ! -f go.mod ]]; then + echo "ERROR: 尚未找到 go.mod。当前应先完成 docs/06-tasks.md 的 T-001。" + exit 2 +fi + +INSTALL_CMD=(go mod download) +VERIFY_CMD=(go test ./...) +START_CMD=(go run ./cmd/shophelm) + +echo "==> 当前目录: $PWD" +echo "==> 同步 Go 依赖" +"${INSTALL_CMD[@]}" + +echo "==> 运行基础测试" +"${VERIFY_CMD[@]}" + +echo "==> 开发启动命令" +printf ' %q' "${START_CMD[@]}" +printf '\n' + +if [[ "${RUN_START_COMMAND:-0}" == "1" ]]; then + echo "==> 启动 ShopHelm" + exec "${START_CMD[@]}" +fi + +echo "设置 RUN_START_COMMAND=1 可让 init.sh 在验证通过后启动应用。" diff --git a/progress.md b/progress.md new file mode 100644 index 0000000..07ccae9 --- /dev/null +++ b/progress.md @@ -0,0 +1,33 @@ +# 执行进度记录 + +> 本文件只追加历史记录。任务状态以 [`docs/06-tasks.md`](docs/06-tasks.md) 为准;当前可运行状态以 [`docs/current-state.md`](docs/current-state.md) 为准。 + +## 记录规则 + +每轮任务完成、部分完成或阻塞时,在文件末尾追加: + +```markdown +## YYYY-MM-DD T-XXX 任务名 + +- 状态:DONE / PARTIAL / BLOCKED +- 变更:修改的文件和行为 +- 验证:真实执行的命令、结果和必要的手工 smoke +- 阻塞:没有则写“无” +- 决策:本轮新增或确认的关键取舍 +- 下一步:下一个可领取任务或需要用户确认的问题 +``` + +没有命令或可观察证据,不得把任务记为 `DONE`。 + +## 执行记录 + + + +## 2026-07-11 DOC-000 建立 ShopHelm Harness Coding 文档 + +- 状态:DONE +- 变更:参考 `D:\github\harness_coding_docs`,建立仓库入口、产品愿景、37 项 P0 需求追踪、Go + Gio 技术栈、分层架构、本地模块合约、Gio 页面结构、编码规则、34 项依赖任务、当前状态和质量文档;补充标准启动脚本与 LF 行尾规则。 +- 验证:本地 Markdown 链接全部可解析;无意外模板占位符和行尾空格;Markdown code fence 成对;34 个任务依赖无缺失/无环且 0 个 `DOING`;37 个 P0 需求 ID 全部映射到任务;`git diff --check` 通过;`bash -n ./init.sh` 通过;`init.ps1` 在缺少 `go.mod` 时按预期非零退出并提示 T-001。 +- 阻塞:生产代码和 `go.mod` 尚不存在,因此没有运行 Go test/build;这是 T-001 的计划范围。 +- 决策:首发固定为 Windows 本地 Go + Gio 应用,按多店铺运营台 -> 商品/单图合成 -> 客服助手推进;不保存平台密码,不做防关联、RPA 主链路和未经确认的平台自动化。 +- 下一步:领取 T-001,初始化 module `shophelm` 和最小 Gio 窗口,实测并锁定 Go/Gio 版本。