623 lines
19 KiB
Markdown
623 lines
19 KiB
Markdown
# 架构设计
|
||
|
||
> 本文定义 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 成功替代真实结论。
|