generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) wiki_page: Architecture-and-Code-Map wiki_url: https://git.ilapage.cn/chengma/cmsp/wiki/Architecture-and-Code-Map.- wiki_revision: c070f41cdae8b495e381dfd571500274697a0677 synchronized_at: 2026-09-29T08:52:08Z # 架构与代码地图 ## 本地列表分页条数(2026-09-29) 商品列表 n-pagination 启用 show-size-picker,提供每页20/50/100/200/500条;初始/重置仍20。changePageSize 更新 query.pageSize、清空旧勾选,调用 search 回到第一页,其他筛选条件保留。仅此次页大小切换明确清空勾选,普通搜索/翻页继续原选择行为。 store.ListProducts 接受1—500的 pageSize,空/非正数/大于500仍回退20;参数化 LIMIT/OFFSET、排序与同条件 COUNT 保持原样。这里是本地商品列表,与 erpgo 商品同步每页200、任务并发及上传流程无关。选择500增加当前页行和视频摘要读取,不自动勾选或启动任务。 ## 商品列表下载状态筛选(2026-09-29) `ProductListView.vue` 的初始/重置查询默认为在售中(NORMAL)、缺少视频(missing)和未下载(not_downloaded)。下载状态下拉复用现有 n-select,提供全部、未下载(含失败)、待下载、下载中、已下载、下载失败;用户点击搜索应用条件,沿用原页码重置和筛选栏换行;普通搜索/翻页保持原有勾选行为。 `store.ProductQuery.DownloadStatus` 的 JSON 名称为 `downloadStatus`,空值保持旧调用查全部下载状态。`buildWhere` 把 not_downloaded 映射到 `download_status IN (pending, failed)`,单状态等值查询,所有值参数化,与其他条件 AND 组合;COUNT 和分页列表使用同一 WHERE。not_downloaded 仅是查询条件,不写入数据库状态。筛选不调用外部接口、不更改 SQLite 表结构或商品/视频/任务/上传状态。 ## 图搜原始商品链接(2026-09-29) `parseImageSearchResult` 读取已通过真实响应确认的 `itemsArray[].auctionURL`,写入 `SimilarItem.URL`。`SimilarItem.DetailURL` 在 Go 侧校验 http/https、精确商品域名 `item.taobao.com` / `detail.tmall.com`、`/item.htm` 路径及唯一且匹配的 `id`,拒绝用户信息、其他端口、片段、错误编码和不可信站点;协议相对地址补 https。保留原链接查询参数与顺序,不记录完整链接或参数值。只有响应缺少链接时兼容原 ID 拼接方式;非法原链接候选跳过,不访问也不改拼旧地址,其他合法候选照常返回;若有非法链接且全部候选都不可用,则明确报错,不能记为正常无结果。 `SelectVideo` 校验链接后传整个 `SimilarItem` 给 `ExtractDetailVideos`,详情导航使用原链接;选中的 `SimilarItem.URL` 同时用于 CDN 下载 Referer。原有串行候选、同批 ID 缓存、登录/访问异常停止门和 SQLite 状态保持不变。这里仍是浏览器直接导航到返回的商品链接:MTOP 搜索没有生成淘宝搜索结果页,也没有模拟结果卡片点击或为详情导航额外设置 Referer。 ## 淘宝访问异常停止与候选复用(2026-09-29) `App.prepareVideoFetch` 建立专属 Chrome 连接;`prepareVideoFetchOnPage` 调用本批 `taobao.LoginGuard.Check`:首次实际处理调用 `CheckLogin` 打开「我的淘宝」深度检查,后续商品调用 `CheckCurrentLogin` 读取当前 URL/标题/有限正文和 Cookie 名称,不发送检查导航。图搜首页、MTOP 返回和详情页中的明确访问异常均映射到任务全局停止门,签名算法及请求参数不变。 `internal/taobao/candidates.go` 的 `SelectVideo` 串行检查不同淘宝商品 ID,仅调用详情页快速守卫,不在候选间导航到「我的淘宝」。正常详情 URL/空结果在一次批次内复用,异常不缓存,批次结束清空;不缓存页面正文、Cookie 或签名。缓存是临时查询结果,SQLite 仍是任务状态唯一事实来源。 `task.Runner` 保持原 `login_required` 终态以兼容现有事件,用 `stopReason` 区分 `login_required`、`risk_blocked`、`risk_suspected`。访问异常停止后不继续候选或商品;候选等待可取消,已开始的视频下载仍完成后停止。 找到视频准备下载时才写 `download_status=running`。深度检查、图搜、详情风控停止及取消等待不覆盖商品原状态、视频记录和上传状态。批量跳过下载完成商品;单商品入口有成功记录且本地文件非空时复用,不打开 Chrome。 ## 当前查询调用链(2026-09-28) 本节描述已实现的店铺/商品查询路径,优先于本页历史目标设计中的直连查询描述。视频上传与淘宝流程沿用原实现。 ```text 参数设置 → internal/config(erpgo 服务地址、API Key) RefreshShops → internal/erpgo.Client.ListShops → GET /api/v1/integrations/huohanhan/shops → internal/store.ReplaceShops(同一事务替换本地店铺缓存) DownloadProductData → internal/erpgo.Client.DownloadAllProducts → GET /api/v1/integrations/huohanhan/products(指定店铺,串行分页) → 完整校验并按内部商品 id 去重 → internal/store.SyncProducts(商品和诊断同一事务) → 前端刷新本地商品列表 ``` 主要入口是 app.go 的 RefreshShops、DownloadProductData;internal/erpgo/client.go 负责白名单转换、稳定错误码及分页完整性;internal/store/product.go 的 SyncProducts 使用与诊断仓储共用的事务辅助函数,不改变 SQLite 表结构。 查询只读取货憨憨现有数据,不触发 Shopee 同步;不自动回退直连。任何请求/校验失败不返回可落库的部分数据;数据库失败回滚商品与诊断。下载、上传、视频记录和断点状态保留,不依据列表删除商品。internal/huohanhan 仍用于现有视频上传,原客户端代码保留用于回归与回退。 ## 当前实现状态 **历史设计基线截至 2026-09-02;当前查询实现见上方 2026-09-28 专节。** 本页的目录结构和执行路径是已确认的目标设计,不是既有实现。阅读时必须区分: - **当前事实**:仓库有 DevHarness 骨架、文档镜像,以及 Go 骨架(Wails 入口 + config / store / logx 三个包,见下方代码地图)。淘宝与货憨憨的业务能力尚未实现,目前只存在于仓库外的两份 Python 参考实现。 - **目标规范**:本页描述的 Go 目录、执行路径和边界。 Go 骨架建立后,本页必须改写为对当前代码的描述,并在工单记录变更。不允许用目标描述宣称现有能力。 ## 项目定位 cmsp 是运行在使用者本机的单机桌面程序,没有服务端,没有多用户,没有权限模型。它是三个外部系统之间的搬运工: ```text 货憨憨 ERP --查询--> erpgo --拉取--> cmsp(本机 SQLite) --上传--> 货憨憨 ERP --同步--> Shopee | +--以图搜、抓视频--> 淘宝(专属 Chrome) ``` 程序不直接访问 Shopee。所有写入 Shopee 的内容都经由货憨憨 ERP。 ## 代码地图 目标目录结构: ```text cmsp/ ├─ main.go Wails 应用入口 ├─ app.go 暴露给前端的方法,只做参数校验和转发 ├─ internal/ │ ├─ config/ 参数设置的读取、校验与持久化 │ ├─ store/ SQLite 打开、迁移与仓储;商品、任务、视频、认证状态 │ ├─ erpgo/ 店铺/商品只读查询、字段转换与分页校验 │ ├─ huohanhan/ 货憨憨 ERP 客户端 │ │ ├─ auth.go 登录、验证码 OCR、认证状态复用与失效重登 │ │ ├─ client.go 统一请求、401 重试 │ │ ├─ product.go 商品查询与分页 │ │ └─ upload.go 素材上传与商品视频批量更新 │ ├─ taobao/ 淘宝流程,结构见设计规范 │ │ ├─ chrome_manager.go 专属 Chrome 的端口扫描、启动、归属校验、复用与关闭 │ │ ├─ cdp_client.go CDP 连接、导航与在页面上下文执行 JavaScript │ │ ├─ login_service.go Cookie 检查与服务端深度登录检查 │ │ ├─ image_service.go 主图读取与缩放 │ │ ├─ sign_service.go pcSign 与 MTOP sign 生成 │ │ ├─ image_search.go 调用 MTOP 以图搜接口并解析商品 │ │ ├─ detail_video.go 商品详情页视频地址提取与去重 │ │ └─ models.go 登录状态与商品结构体 │ ├─ downloader/ 视频下载队列、并发控制、重试与 ffprobe 校验 │ └─ task/ 任务编排、状态机、进度事件、断点续传 └─ frontend/ └─ src/ ├─ views/ProductsView 商品数据页:表格、多选、同步/下载/上传按钮、进度与日志 └─ views/SettingsView 参数设置页:货憨憨账号、Chrome 路径与用户数据目录、下载目录、并发数 ``` 界面为两个标签页:**商品数据**与**参数设置**。 前端不直接实现业务判断。登录是否有效、任务能否开始、按钮是否可用,都由 Go 侧返回的状态决定。 ## 两条主要执行路径 ### 路径一:货憨憨商品同步与视频上传 ```text 使用者点击「同步数据」 → app.go 校验参数 → internal/erpgo 使用 X-API-Key 调用查询接口 → 串行分页并完整校验指定 Shopee 店铺在售商品 → internal/store.SyncProducts 同一事务保存商品和诊断 → 事件推送进度,前端刷新表格 使用者勾选商品点击「上传视频」 → internal/task 逐个取出本地视频文件 → internal/huohanhan/upload 上传素材取得线上地址 → internal/huohanhan/upload 批量更新商品视频 → internal/store 更新上传状态 → 事件推送进度 ``` 上传链路的接口细节尚未确认,见[需求总览](09-product-requirements-overview.md)的未决项。 ### 路径二:淘宝以图搜与视频下载 ```text 使用者勾选商品点击「下载视频」 → internal/taobao/chrome_manager 准备或复用专属 Chrome → internal/taobao/login_service 执行服务端深度登录检查 ├─ 无效 → 返回 E_LOGIN_REQUIRED,中断整批,提示重新登录 └─ 有效 ↓ → 对每个商品: internal/taobao/image_service 读取主图并缩放 internal/taobao/sign_service 生成 pcSign 与 MTOP sign internal/taobao/image_search 在淘宝页面上下文请求 MTOP,解析同款商品 internal/taobao/detail_video 逐个打开同款详情页,提取并去重视频地址 internal/downloader 下载 MP4,ffprobe 校验,写入本地目录 internal/store 逐条落库,商品间随机等待 → 事件推送进度 ``` 每个新启动或手动恢复的批次只在首次实际访问淘宝时做深度登录检查;后续商品在当前页面检查。`LoginGuard` 仅记录本批首次深度检查通过标志,不缓存凭据或服务器登录有效结论。单商品、独立图搜各自首次深度检查;显式登录检查按钮仍可深度检查。登录失效及访问异常是全局停止条件,不是单个商品的失败。 详细的登录检查、签名和接口约定见[业务规则与术语](03-business-rules-and-glossary.md)的「淘宝登录」与「淘宝以图搜」两节。 原先仓库内的 `Wails专属Chrome登录与淘宝以图搜流程.md` 已于 2026-09-02 由负责人删除,其中的稳定结论已并入上述业务规则页,不要再引用那个文件名。 ## 不可破坏的边界 - **淘宝登录必须由使用者手动完成。** 程序只负责打开登录页和检查结果,不自动填写密码、不绕过验证码、不绕过安全验证。 - **淘宝登录态只存在于专属 Chrome Profile。** 程序不读取、不复制、不落盘、不上传 Cookie;只从当前会话读取 `_m_h5_tk` 用于计算签名,且不缓存、不长期复用、不写入日志。 - **同一个 Chrome Profile 不得同时启动两个实例。** 启动前必须校验 PID 存在、进程命令属于本程序专属 Profile、CDP 端口可访问且存在 `page` 类型目标。 - **登录失效是全局停止门。** 必须中断整批任务并保留断点,不得记为单商品失败后继续,也不得自动反复请求 MTOP 接口。 - **货憨憨的写操作必须显式确认。** 上传素材、批量修改商品、删除素材都属于写操作,调用方必须明确表达意图,不得作为查询流程的副作用发生。 - **凭据不进入仓库。** 账号、密码、token、Cookie、Chrome Profile 内容不得出现在代码、日志、工单、Wiki 和提交中。 - **SQLite 是任务状态的唯一事实来源。** 不允许再用 JSON 文件维护第二份任务状态。 - **前端不做安全判断。** 按钮禁用只是提示,真正的校验必须在 Go 侧执行。