37 Commits
Author SHA1 Message Date
QiuSWandClaude Opus 5 c6bafd968e feat: 批量上传视频,以磁盘为准并按货憨憨规则本地预检 (#19)
解除 #17 的单商品限制,改为勾选多个一次上传,逐个串行处理。

选定文件的方式彻底改了:不再查 videos 表,改为扫描
<video_dir>/<蝦皮ID>/*.mp4 按文件名升序取第一个。原因是使用者可能手工把
别处来的视频放进目录,那类文件在 videos 表里没有记录;而且库与磁盘的漂移
无法根绝(手动挪文件、换 video_dir、外部程序写入),以磁盘为事实来源更稳。

上传前用 ffprobe 按货憨憨的要求本地预检,不合规的跳过而不是白传:

  大小  <= 30 MB
  时长  10.0 <= duration <= 60.0,严格比较不取整
  格式  容器为 mp4
  像素  宽 <= 1280 且高 <= 1280

时长必须严格判定:本地已有的 49365131605_1.mp4 是 9.985011 秒,
差 0.015 秒卡在门槛下,任何取整都会把它误判为合规。
ProbeResult 相应增加 Width / Height。

每个商品四条分支,任一分支结束后继续下一个:

  远端已有视频且勾选 > 1  -> existing,跳过,不发任何写请求
  子目录没有 mp4          -> missing,跳过
  有文件但不合规          -> invalid,last_error 写明违反哪条及具体数值
  合规                    -> 上传,done / failed

覆盖策略:batchUpdateShopProductVideo 是覆盖语义且不可恢复,所以批量永不
覆盖远端已有视频,单个(勾选 == 1)保持 #17 的红字警告加二次确认后可覆盖。
判断依据是实时读取的远端状态,不用本地 video_diagnosis——后者只在
「下载数据」时刷新,可能已经过期。

这三个新状态只是上一次尝试的结果记录,不构成断点:批量只处理当次勾选的
商品,不会因为状态是 missing/invalid/existing/failed 就把商品排除在外,
每次点上传都重新扫盘、重新预检、重新读远端。这是为了避免重蹈
video_status='none' 被断点永久跳过的覆辙(见 #15)。

上传成功后按 product_id + local_path 写入或更新 videos 记录,
source_item 留空(磁盘还原不出来自哪个淘宝同款),不使用 ReplaceVideos。

按负责人明确决定,不做容量门禁,仅保留确认框中的静态提示。

测试全部用 httptest 假服务器,未向真实货憨憨发出任何请求。
真机验证尚未进行。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 17:08:12 +08:00
QiuSWandClaude Opus 5 16e4f0faf8 fix: 视频记录不再提前清空,启动重置残留的 running 状态 (#18)
真机使用时发现 55066525387 的子目录里有视频文件,界面上「目录」却不可点。
查明是取视频跑到一半程序被重启,videos 表里该商品的记录已被删除、
新记录还没写回,磁盘有文件而库里无记录。

缺陷一:prepareVideoFetch 在开始下载前先做了一次
ReplaceVideos(product.ID, nil, ...)。而 work.Finalize 里本来就有一次
ReplaceVideos(product.ID, records, now),ReplaceVideos 的语义就是按
product_id 全量替换。前面那次并不能少做任何事,唯一效果是把「文件已下载
但记录已被删」的窗口从零扩大到整个下载过程,任务停止、程序重启和崩溃
都会落进去。删掉它。

原注释说明作者考虑过这个风险,但只防住了登录失效一种情况。

行为变化是期望的:取视频在下载阶段之前失败时,旧的视频记录会保留而不是
被清空。那些文件确实还在磁盘上,「这次没找到视频」由 products.video_status
表达,不该靠删 videos 记录来表达。

缺陷二:进程退出后 download_status / upload_status 仍停留在 running,
但没有任何任务在跑,是个不会自愈的假状态。启动时在同一事务内把这两个字段
的 running 重置为 pending,只动 running,不碰 video_status 和 videos 表。
重置失败只记 Warn,不中断启动。

按负责人要求未对 cmsp.db 做任何手工订正。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 16:46:40 +08:00
QiuSWandClaude Opus 5 0fe24f70f4 fix: 回读不能只看 video 字段,会把成功的上传误判为失败 (#17)
真机验证时 55066525387 报「回读商品视频失败:货憨憨尚未显示已关联的视频」,
但视频其实已经上传成功。两个写接口都返回了 SUCCESS,是回读判据写错了。

video 字段装的是已经在 Shopee 上生效的视频,不是刚设置的那个。货憨憨推送
到 Shopee 是异步的,保存成功后 video 必然还是空的,视频这时在 tempVideoUrl
和 videoUploadIdStr 里。证据是 payloads/huohanhan_save_product_info.har 的
第 3 个请求——一次保存成功后立刻发起的 getDetail,那里 video=[] 而
tempVideoUrl 有值。这条证据本来就在仓库里,之前定判据时没有用上。

HasShopProductVideo 改为 CheckShopProductVideo,返回四个字段:

  videoFailReason 非空                    → 失败,把原因写进 last_error
  video 非空                              → 已在 Shopee 生效
  tempVideoUrl 或 videoUploadIdStr 非空   → 已提交,等待同步,判为成功
  四者皆空                                → 确实没设上,失败

upload_status=done 的语义随之明确为「已提交并回读确认」,
而不是「Shopee 已可见」——后者是异步结果,本地无法立刻断言,
日志会分别提示这两种情况。

确认框的覆盖警告改用 Confirmed():等待同步中的视频同样会被覆盖,
只看 video 会漏掉这种情况,导致该警告不显示。

同时订正 55066525387 被误判写成的 failed 状态。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 16:02:27 +08:00
QiuSWandClaude Opus 5 d1149b629d feat: 视频上传回货憨憨并关联商品,首版限单商品 (#17)
接口契约来自 payloads/huohanhan_batch_save_product_video.har,两步:

  POST product/material/uploadFiles              multipart
       files / isLocalFile=true / fileType=1  →  bean[0] 是 COS 地址
  POST product/batchEdit/batchUpdateShopProductVideo   json
       [{id, platformShopId, videoUrl}]        裸数组,只有三个字段

id 是货憨憨内部记录 ID(products.id),不是蝦皮商品 ID。

不采用 common-product/updateProductItem 那条路:它要回传整份商品共 56 个
字段 26589 字节,而 getDetail 返回的结构无法直接回传,model[].priceInfo
的值就对不上——那是价格数据,照抄会写坏真实店铺且不可逆。
batchUpdateShopProductVideo 只改 videoUrl 一个字段。

这是本项目第一个会往真实店铺写、且删不回来的功能,所以:

- 只有显式点击「上传数据」才触发,不作为任何查询流程的副作用
- 必须二次确认,列出蝦皮 ID、商品名、本地文件名与大小
- 该商品已有视频时额外红字警告会覆盖且无法恢复(接口是覆盖语义,
  videoUrl 传空即删除)
- 首版一次只能一个商品,拦截在 Go 侧,前端只做提示
- 任一步非 SUCCESS 立即停止,不自动重试

关联成功后必须回读 getShopItemInfoPage 确认 records[0].video 非空,
才写 upload_status=done。接口返回 SUCCESS 不等于视频真的挂上了。

容量判断整体延后:payloads 下所有 HAR 都没有 getMaterialSize 的证据,
路径与返回结构均属推测,不凭猜测实现。本次只在确认框放一行静态提示,
待补抓包后另开工单。

测试全部用 httptest 假服务器,未向真实货憨憨发出任何请求。
真机验证尚未进行。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 15:19:34 +08:00
QiuSWandClaude Opus 5 6cc334ab92 fix: 取视频链路与 Python 参考实现对齐,识别风控降级 (#15)
淘宝会在登录仍然有效的情况下停止下发视频资源:不跳登录页、不出验证码,
详情页里就是没有 mp4 地址。原实现识别不了,把它当成「这个同款没视频」,
继续跑满 20 个同款加重风控,最后写 video_status='none',
而断点规则会永久跳过 none,导致账号恢复后这批商品也不再重试。

与参考实现对齐:

- 详情页等待 4 秒改为 8 秒,提为 detail_wait_seconds
- 每个同款详情页前做一次 my_itaobao 深度守卫,在两次详情页之间插入
  正常页面访问;守卫等待提为 guard_wait_seconds
- 详情页后补一次 Cookie 级快速守卫,复用 judgeLogin
- 淘宝首页导航等待 3 秒改为 6 秒。签名请求在该页面上下文里 fetch,
  页面没加载完就发可能带不上 Cookie
- 下载失败重试,5xx 与连接错误重试 download_retries 次,
  4xx 和 ffprobe 校验失败不重试
- 每次下载尝试各有独立的 180 秒超时。此前超时套在整个重试循环外面,
  大视频首次跑到一半失败后剩余预算不足,重试等于不生效

新增风控降级识别:连续 risk_empty_threshold 个同款打开成功但取不到视频时,
判为疑似风控,中断整批任务并保持商品 pending,绝不写入 none。
停止原因通过 stopReason 枚举传给前端,不依赖中文文本分支。

数据订正提供显式按钮,不写进 migrations。刻意不做日期过滤:
synced_at 记录的是商品数据何时从货憨憨拉取,与 video_status 何时被写成
none 无关,每次「下载数据」都会把它刷成当天。某商品是否需要重做的长期
答案来自货憨憨每次全量拉取覆盖的 video_diagnosis,不来自本地时间戳。

淘宝与 Chrome 相关逻辑由假实现覆盖,真机验证尚未进行。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 14:29:03 +08:00
QiuSWandClaude Opus 5 ce729cd1c8 fix: 数据目录跟随配置文件位置,避免视频被 build -clean 删掉
DefaultVideoDir 原先按 exe 所在目录算。wails dev 跑的是
build\bin\cmsp-dev.exe,视频就下载进了 build\bin\运行数据\视频,
而 `wails build -clean` 会清空该目录——已下载的视频会被一起删掉。

改为新增 DataRoot(),规则是「配置文件在哪,数据就在哪」:

  打包后双击 exe   config.yaml 在 exe 旁边   数据也在 exe 旁边
  开发模式         config.yaml 在项目根目录   数据也在项目根目录

与 config.yaml、cmsp.db 已有的查找规则保持一致。
补测试断言视频目录不得落在 build\bin 下。

同时把 .gitignore 补上「浏览器运行状态.json」:该文件含 PID、
调试端口和本机 profile 路径,属于本机运行状态,不该进仓库。

负责人本机的既有数据已一并迁移:5 个视频文件与浏览器状态文件从
build\bin 移到项目根目录,config.yaml 的 video_dir 与数据库中
4 条 videos.local_path 同步更新,迁移后无失效路径。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 11:14:15 +08:00
QiuSWandClaude Opus 5 f79208cc5c feat: 批量取视频,队列、进度与全局停止门 (#14)
由 Codex (gpt-5.6-sol) 实施,Claude 审核。MVP2 最后一块。

internal/task/task.go
- 串行阶段(Prepare)一次一个商品,全程持有同一个 CDP:
  登录深度检查 → 图搜 → 逐个同款提取视频地址。
  并发只作用于下载阶段(纯 HTTP + ffprobe,不碰 CDP)。
  这样既避免多协程踩同一个浏览器页面,也不抬高淘宝风控概率。
- 登录失效走独立的 ErrLoginRequired,触发后立即终止整批,
  不再启动后续商品,不自动重试也不自动重新登录
- 其它错误属于单商品失败,记录后继续处理下一个
- 断点复用商品自身的 download_status,不另建断点文件或断点表,
  因此不会出现断点与数据库不一致

app.go 抽出单商品核心供两处复用,未复制逻辑。

审核补测(跑完删除,未入库),五项全部通过:
- 登录失效发生在第 3 个商品时,后续 D/E/F 一个都没被处理
- Concurrency 设为 4 时 Prepare 峰值仍为 1,串行确实生效
- 下载阶段峰值恰为 2,并发上限真的起作用且确实并行
- 连续 5 个普通失败仍跑完全部,状态为 completed
- 停止后可以重新启动,不会被永久锁死

硬性验收项全部通过:前端无 window.go 等绕过生成绑定的写法
(前三个工单连续出现,本次未再出现);internal 未 import wails;
无新增依赖。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 10:58:55 +08:00
QiuSWandClaude Opus 5 e71559e2f6 feat: 视频按蝦皮商品 ID 分子目录存放,文件名以蝦皮商品 ID 为前缀
原先所有视频平铺在视频目录下,后续别的程序无法按商品区分。改为:

  <视频目录>/<蝦皮商品ID>/<蝦皮商品ID>_<序号>.mp4
  例:运行数据\视频\40583431295\40583431295_1.mp4

- 子目录名和文件名前缀都用蝦皮商品 ID(itemId),不是货憨憨的内部
  记录 ID,因为后续程序对接的是 Shopee 侧的标识
- 文件名同时带前缀,是为了文件被单独挪走或和别的商品混在一起时
  仍能认出归属
- 视频来自哪个淘宝同款不再体现在文件名里,该信息记录在
  videos.source_item,需要回溯来源时查库

安全处理:目录名直接来自货憨憨返回的商品 ID,正常是纯数字但不能假定。
新增 SafeDirName 只保留字母数字与减号下划线;并加固 safeID,
让 ".." 这类全部字符被替换后的输入回落为 unknown,而不是生成
"__" 这种无意义目录名。有测试覆盖路径穿越、Windows 非法字符等输入。

GetVideoSummary 的目录改为取实际文件所在目录而非拼接,
这样分目录之前下载的旧文件(平铺在根目录)点「目录」也能正确打开。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 10:37:27 +08:00
QiuSWandClaude Opus 5 46378db86b feat: 商品主图可点击查看原图,支持右键另存为
- 表格主图列的缩略图可点击,弹出原图弹窗,鼠标指针改为 zoom-in
- main.go 打开 EnableDefaultContextMenu,这样在弹窗图片上右键才有
  「图片另存为」。代价是整个界面右键都会出现浏览器菜单,其中包含
  「检查」,同事可能误点开发者工具;权衡后仍然打开,因为另存图片是
  实际需要,误点开发者工具关掉即可,没有破坏性。同时移除 main.go 中
  已不再成立的「关掉右键检查」注释。
- 弹窗另配一个「另存为」按钮:WebView2 的原生右键菜单在个别机器上
  不给图片选项,这条路径不依赖浏览器菜单
- app.go 新增 SaveImageAs:只接受 http/https,避免变成可读取本地
  任意文件的入口;30 秒超时、20 MB 上限、空响应判失败
- 原图限制在 70vh 内并可滚动,避免大图撑出屏幕

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 10:24:18 +08:00
QiuSWandClaude Opus 5 73f5688ab1 feat: 单商品全链路,提取详情视频并下载到本地 (#13)
由 Codex (gpt-5.6-sol) 实施,Claude 审核。R4a,仍只处理单个商品。

internal/taobao/detail.go
- 从同款详情页 HTML 正则提取 mp4 地址,还原 / 与 \/ 转义
- 只保留 cloud.video.taobao.com 与 cloudvideocdn 的地址
- 去重按(主机、路径、查询串)三元组,忽略 http/https 差异

internal/downloader
- 先写 .part,ffprobe 校验通过后才改名为正式文件
- ffprobe 找不到时报明确中文错误,不静默跳过校验——跳过等于允许
  损坏文件流入不可逆的上传环节
- 请求带 Referer(同款商品页)与浏览器 UA,否则淘宝 CDN 可能拒绝
- 目标文件已存在且非空时跳过

internal/store/video.go
- 视频仓储,按商品全量替换
- source_item 记录实际采用的淘宝同款商品 ID(Q3 的落库要求)

同款选择按 Q3 结论:按图搜顺序依次尝试,第一个有视频的即采用,
不做人工确认;详情页之间按配置随机等待降低风控概率;
登录失效立即停止,不继续尝试下一个同款。

app.go 的 OpenFolder 用 EvalSymlinks + filepath.Rel 校验目标必须在
配置的 VideoDir 之内,避免成为可打开任意目录的入口。

审核补测(跑完删除,未入库):下载中途断开、HTTP 403/404/500、
空响应三种情况均不得留下正式文件或 .part,实测全部通过;
Referer 与 UA 确实发出。

审核期修正:Codex 又用 window.go.main.App 绕过生成绑定三处
(FetchVideosForProduct、OpenFolder、GetVideoSummary),已重新生成
绑定并改为正常 import。这是第三个工单出现同一问题。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 10:04:20 +08:00
QiuSWandClaude Opus 5 e8db874c04 feat: 单个商品以图搜同款 (#12)
由 Codex (gpt-5.6-sol) 实施,Claude 审核。R3a,先验证签名算法是否仍有效。

签名与参考实现字节级一致(审核时用固定随机值和时间戳对拍):

  pcSign      44 字节    一致
  params    1151 字节    一致
  requestText 1390 字节  一致
  MTOP sign   32 字节    一致
  token 提取             一致

Go 特有的陷阱已避开:签名是对 JSON 字符串做哈希,键顺序会改变结果。
Python 的 dict 按插入顺序序列化,而 Go 的 encoding/json 对 map 按键名
字母序排序,用 map 会产出完全不同的 JSON 导致签名必错,且淘宝只返回
含糊的业务错误码,极难定位。实现用 struct 按声明顺序输出,并用
json.Encoder + SetEscapeHTML(false) 对齐 Python 的 ensure_ascii=False。
四个价格字段用空指针输出真正的 null。有专门测试断言键顺序与字节,
防止后人改回 map。

internal/taobao/cdp.go 新增 CookieValue,仅用于读取 _m_h5_tk 计算签名。
AGENTS.md 明确允许该用途;全项目仅 imagesearch.go 调用一次,用完即弃,
不落库、不写日志、不返回前端。

以图搜前先做登录深度检查,未通过即停止,不发起 MTOP 请求,
避免抬高风控概率。

前端:表格操作列新增「搜同款」,结果以弹窗展示缩略图、标题、价格、
店铺、销量,点标题用系统浏览器打开淘宝商品页。

本工单不含批量、队列、并发与落库,那些属于 R3b。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 09:39:58 +08:00
QiuSWandClaude Opus 5 e7c3c6849c fix: 中文 profile 路径导致 Chrome 归属校验永远失败 (#9)
负责人点「打开淘宝登录」报错:
「Chrome进程不属于配置的专属用户数据目录,已将其关闭」

根因是编码不一致。processCommandLine 用 PowerShell 读取进程命令行,
PowerShell 的标准输出使用系统 ANSI 代码页(简体中文 Windows 是 GBK),
而 Go 字符串是 UTF-8。专属 profile 路径含中文
(电商视频自动下载工具\淘宝浏览器\默认账号),两边字节不同,
strings.Index 永远匹配不上,于是 Chrome 刚启动就被判为不属于本 profile
并被杀掉。

单元测试没能发现,因为测试里比较的两个字符串都是 UTF-8。

改为让 PowerShell 先把命令行转成 UTF-8 的 Base64,Go 侧再解码。
不用 [Console]::OutputEncoding=UTF8 是因为那依赖控制台状态;
Base64 只含 ASCII,任何区域设置下都可靠。

实测三种方式:
  原样读取        含中文路径 false   ← 修复前
  设 UTF8 编码    含中文路径 true
  Base64 传输     含中文路径 true    ← 采用

已用真实运行中的 Chrome 进程验证:归属判断返回 true。
补一条防回归测试,覆盖含中文的 profile 以及编码不一致的情形。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 09:22:53 +08:00
QiuSWandClaude Opus 5 656b7a8c49 feat: 专属 Chrome 管理与淘宝登录检查 (#9)
由 Codex (gpt-5.6-sol) 实施,Claude 审核。MVP2 第一块。

internal/taobao/chrome.go
- EnsureBrowser 按四项条件判断能否复用已有实例:PID 存在、进程命令行
  属于本程序专属 profile、CDP 端口可访问、存在 page 类型目标
- 启动前遍历整个端口段,发现已有 Chrome 但状态文件丢失时报错而不是
  再起一个,避免同一 profile 出现两个实例损坏登录数据
- CloseBrowser 必须先校验 PID 归属才允许 taskkill,防止误杀使用者
  自己的 Chrome

internal/taobao/cdp.go
- 基于 gorilla/websocket 的最小 CDP 客户端(该依赖原本就在模块图里,
  由 Wails 引入,此处仅从 indirect 提为直接依赖)
- CookieNames 的结构体只声明 Name 和 Domain,Cookie 的值根本不会
  进入内存,比"取到但不使用"更彻底

internal/taobao/login.go
- 两层判定:四个必需 Cookie 齐全,且我的淘宝页面标题与正文不含
  六个阻断词
- 昵称只作展示,不参与判定。这是对参考 Python 实现的一处有意偏离:
  参考实现要求解析出昵称才算有效,但该解析依赖淘宝页面文案,
  淘宝一改版就会永远解析不到,导致登录永远判为无效、全局停止门
  永久触发、工具彻底不可用。放宽的代价可控:误判为已登录时后续
  MTOP 请求会失败,那条链路本就有 token 失效处理。

app.go 只在点击按钮时启动 Chrome,startup 不自动启动。

前端:设置页两个按钮接真实方法并显示检查结果;侧栏淘宝状态灯
监听 taobao:status 事件。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 09:13:40 +08:00
QiuSWandClaude Opus 5 5bcfea24ba feat: 状态默认选中在售中;明确不清理已下架商品
- 「状态」筛选默认值改回 NORMAL(在售中)
- 「类型」筛选默认 missing(缺少视频),两者配合后,打开程序即为
  「在售中且缺少视频」的商品,正是需要去淘宝搜视频的工作对象

已下架商品不清理(负责人 2026-09-03 决定):货憨憨那边删除或下架的
商品,本地保留不删。因此本地总数可能大于店铺当前商品数——负责人
本机为 5459 对 4935,多出的 524 条即已消失的商品。这是预期行为,
已写入文件头注释,避免后来人自作主张加自动清理。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 08:50:07 +08:00
QiuSWandClaude Opus 5 948945d188 feat: 诊断筛选标签改为「类型」并默认选中缺少视频
- 标签「诊断」改为「类型」
- 默认值由「全部」改为「缺少视频」:这些才是需要拿主图去淘宝
  搜视频的商品,是本工具的主要工作对象,打开即可直接干活
- 「重置」按钮同样回到「缺少视频」,与默认值保持一致

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-03 08:47:57 +08:00
QiuSWandClaude Opus 5 2e8a01460d fix: 状态筛选默认改为全部;修正 build.bat 产生垃圾目录
1. 商品列表的「状态」筛选默认值由 NORMAL 改为空(全部)。
   按负责人要求,默认不预设商品状态过滤。

2. build.bat 结尾用 explorer "%CD%\build\bin" 打开产物目录,
   在某些环境下 %CD% 未展开,会在仓库里建出一个名为 %SystemDrive%
   的垃圾目录(已实际出现并删除)。改为 start "" explorer "%~dp0build\bin",
   并加 if exist 保护。

   同时修复该文件在多次编辑中被引入的字节级损坏:反斜杠被重复转义成
   \,且部分行尾退化为 LF。已统一还原为单反斜杠 + CRLF,
   实测前三项检查可正常执行。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 22:44:19 +08:00
QiuSWandClaude Opus 5 c8ce79f886 fix: 修正历史脏值并改进店铺刷新入口 (#10 #11)
负责人反馈「下拉选择缺少视频筛不出数据」,查库发现 5305 条商品的
video_diagnosis 全是 unknown,product_diagnoses 表 0 条。

根因是我中途取消 Codex 造成的:第一版按三态实现时,负责人的
wails dev 正开着,Codex 一存文件就自动重编译重启,把带
DEFAULT 'unknown' 的迁移真正执行进了数据库。随后我回滚了源码,
但数据库已升到 schema_version=10,那几条迁移不会再跑,
于是库里留下当前代码不认识的 unknown。

三处修改:

1. 追加自愈迁移,把非 missing/ok 的历史值统一收敛为 ok。
   已在负责人真实数据库上验证:5305 条 unknown 全部变为 ok,
   版本升到 11。下次「下载数据」会写入真实诊断值。

2. 去掉「刷新店铺」按钮,改为展开店铺下拉时按需刷新。
   带 5 分钟间隔,避免每次点下拉都要等一次货憨憨登录;
   自动刷新失败不弹窗,下拉框继续用缓存,旁边的「更新于 …」
   会显示旧时间,看得出数据不是最新的。启动时的刷新失败仍然弹窗,
   那是需要立刻知道的。

3. 修掉三个绕过 Wails 生成绑定的 window.go.main.App 调用。
   Codex 这样写是因为 frontend/wailsjs 在禁改清单里,做法自洽,
   但绕过类型包装且会被后来者照抄。已重新生成绑定并改为正常 import。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 21:13:12 +08:00
QiuSWandClaude Opus 5 393c942762 feat: 解析商品质量诊断并支持按「缺少视频」筛选 (#11)
由 Codex (gpt-5.6-sol) 实施,Claude 审核。

货憨憨 getPage 响应里带有商品质量诊断,其中「缺少视频」直接标出了
哪些商品才需要去淘宝找视频,能把工作量从全店商品缩小到真正需要处理的那部分。
此前这个字段被完全丢弃。

字段名是 diagnosisInfo(不是 diagnoses),结构比表面看到的多一层:
{itemId, qualityLevel, diagnoses:[{field, diagnosisResults:[{type, solution}]}]}

判定规则(负责人 2026-09-02 决定,见工单评论):
- missing:诊断明确报了「缺少视频」
- ok:其余全部情况,含 diagnosisInfo 为 null
只有明确报缺少视频才算缺少,其它一律当作有视频。

500 条样本里 293 条 diagnosisInfo 为 null,按此规则归入 ok。
我曾建议保留 unknown 三态以区分「尚未诊断」,负责人已知悉并选择两态,
措辞为「初定」;顾虑与重新评估条件记录在工单 #11 评论中。

- internal/store:products 加 video_diagnosis、quality_level 两列;
  新增 product_diagnoses 表保存全部 6 种诊断类型(缺少尺寸表、缺少标准变体、
  缺少品牌信息、所需属性过少、缺少视频、合格级属性数量不足)。
  只存「缺少视频」的话,另外 5 种将来要用就得重新全量拉一遍,而采集成本为零。
  迁移全部在 migrations 末尾追加,未改动任何已有条目。
- internal/huohanhan:ProductRecord.DiagnosisInfo 用指针以区分 null;
  转换时按上述规则算出 video_diagnosis。
- 前端:工具栏加诊断筛选(全部 / 缺少视频 / 有视频);
  「视频」列改为显示诊断结果。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 21:02:58 +08:00
QiuSWandClaude Opus 5 d34846e26e feat: 店铺列表缓存到 SQLite 并在启动时后台刷新 (#10)
由 Codex (gpt-5.6-sol) 实施,Claude 审核。

以前每次打开商品列表都要现调 erp/shop/all,而这条链路要先完成
货憨憨登录(CID/CST → 验证码 → OCR → 登录 → 验证 token),
任何一环失败下拉框就是空的,连店铺名都看不到。

internal/store/shop.go(新建)
- shops 表在 migrations 末尾追加,未改动任何已有迁移条目
- ReplaceShops 是全量替换:同一事务内先 DELETE 再批量插入。
  只做 upsert 会让货憨憨那边已删除的店铺永远留在下拉框里,
  选中后拉不到任何数据
- 缓存字段是白名单,不含 accessToken、refreshToken、createUser

app.go
- ListShops 改为只读缓存;新增 GetCachedShops、GetShopsUpdatedAt、
  RefreshShops
- startup 里用协程后台刷新,不阻塞窗口显示;且仅在账号密码都已配置时
  才刷新——同事第一次安装还没填账号就启动时,刷新必然失败,
  一开机弹「登录失败」体验很差
- 成功发 shops:refreshed 事件,失败发 shops:refresh-failed 并带上
  失败原因、缓存时间和缓存条数

前端
- 启动先读缓存立即填充下拉框,再由后台刷新更新
- 刷新失败时弹窗同时说明失败原因和缓存时间,只说「更新失败」
  使用者无法判断该不该继续用
- 下拉框旁常驻显示缓存更新时间,并提供手动刷新

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 18:00:11 +08:00
QiuSWandClaude Opus 5 f568cd1061 feat: 商品表格补上操作列(详情 / 目录)
原型里每行末尾有「详情 / 目录」两个操作,实现时漏掉了整个操作列。

- 详情:按店铺地区拼 Shopee 商品页地址并用系统浏览器打开。
  地区来自 erp/shop/all 的 region 字段,在前端建 平台店铺ID -> 地区
  的映射表;认不出地区时置灰,不拼一个打不开的地址。
  各站点域名集中在 shopeeDomains 里,不要在别处硬编码。
- 目录:打开本地视频所在文件夹。视频尚未下载,先置灰并提示
  「下载视频后可用」,等 R4(#5 MVP2)实现下载后解开禁用即可,
  届时不用再动表格结构。

app.go 新增 OpenInBrowser,只允许 http/https,避免变成可以
启动任意程序的入口。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 17:48:51 +08:00
QiuSWandClaude Opus 5 d5e32f4d2b fix: 下载数据点击后无反馈,且大店铺会被分页上限截断 (#8)
负责人反馈「点击下载数据没反应」。用真实登录态做只读诊断后确认
后端正常:ListShops 返回 19 个店铺,getPage 返回 total=4304。
问题有两个,都在前端反馈和分页参数上。

1. 前端没有任何运行状态
   downloadProductData 是个纯 await,期间按钮不禁用、不转圈、不提示。
   商品多的店铺要跑几十秒到几分钟,看起来就像点了没用。
   改为:未选店铺时先给出提示不发请求;开始时提示「进度见运行日志」;
   按钮显示 loading 并互斥禁用;失败用 error 而不是 warning。
   下载视频、上传数据两个按钮同样处理。

2. 每页 20 条导致大店铺超出 200 页上限被截断
   实测同一店铺不同 size 的表现:
     size=20   216 页  506ms/页   约 108 秒
     size=200   22 页  836ms/页   约 18 秒
     size=500    9 页  1.48s/页   约 13 秒
   4304 个商品按 20 条一页是 216 页,超过 maximumProductPages=200,
   会在 4000 条处截断并只记一条警告。
   改为每页 200 条:耗时降到约六分之一,且上限对应 4 万商品,
   远超实际店铺规模。选 200 而非 500 是响应体积与速度的平衡。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 17:43:25 +08:00
QiuSWandClaude Opus 5 406fb80e46 fix: 补回缺失的 DownloadProductData 导入 (#8)
点击「下载数据」报 ReferenceError: DownloadProductData is not defined。

这是审核阶段我自己引入的回归:Codex 原先绕过 Wails 生成的绑定,
直接调 window.go.main.App.DownloadProductData,同时把该函数从 import
列表里删掉了。我把调用改回正规写法时忘了把 import 加回去。

顺带核对了三个前端文件的全部绑定调用,确认没有其它「调用了但没导入」
的情况;移除 ProductListView 中未使用的 CountProducts 导入。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 17:38:04 +08:00
QiuSWandClaude Opus 5 51977e4dc1 fix: 开发模式下配置和数据库写错位置 (#8)
负责人在设置页保存了货憨憨账号,但项目根目录的 config.yaml 仍是空的,
以为保存没生效。实际是写到了 build\bin\config.yaml。

原因:DefaultPath 用 os.Executable() 定位。打包后双击 exe 时这是对的,
但 wails dev 跑的是 build\bin\cmsp-dev.exe,配置和数据库就落在那里,
开发的人在项目根目录怎么找都找不到。

改为按「exe 目录已有 → 工作目录已有 → 新建在 exe 目录」的顺序查找,
配置文件和数据库共用同一套规则(config.ResolveDataPath)。
这样打包后仍在 exe 旁边,开发模式则命中项目根目录那一份。

补两个测试:已存在的文件优先命中;目录不算文件。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 17:20:44 +08:00
QiuSWandClaude Opus 5 56467d3ca4 chore: 忽略临时截图,保留 build/appicon.png
验收证据请贴到 Gitea 工单,不要放进仓库。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 17:16:41 +08:00
QiuSWandClaude Opus 5 5524fe958c feat: 店铺列表与商品数据下载 (#8)
由 Codex (gpt-5.6-sol) 实施,Claude 审核。

实现以真实抓包为准,纠正了 Python 参考实现与接口文档的三处错误:

1. erp/shop/all 请求体是 userId=(空值),不是文档写的 type=1
2. product/shop/getPage 用 form 编码,不是 JSON
3. 两个接口的响应都没有 {type, message, code, bean} 包裹:
   shop/all 返回裸数组,getPage 直接返回分页对象

internal/huohanhan/shop.go
- ListShops 解析裸数组,只保留 platform=="0" 的 Shopee 店铺,
  店铺名去首尾空格后按名称排序
- Shop 结构体只声明界面需要的 8 个字段。货憨憨返回的
  accessToken、refreshToken 是 Shopee OAuth 凭据,createUser 是手机号,
  一律不解析、不记录、不存储、不返回前端;有测试用反射与 JSON
  序列化双重断言

internal/huohanhan/product.go
- GetProductPage 发送完整表单,自定义 UnmarshalJSON 兼容
  total/size/current/pages 既可能是数字也可能是字符串
- DownloadAllProducts 按页拉全量,带 200 页上限保护防止 pages
  异常导致死循环,达上限记警告而非静默截断

app.go
- ListShops、DownloadProductData 换成真实实现
- 未选店铺时返回「请先选择店铺」。该账号有 19 个店铺,
  单店铺 1256 个商品 63 页,默认拉全部会非常慢

前端
- 店铺加载失败不再静默吞掉,改为可见提示
- 「下载数据」传当前店铺 ID,完成后刷新列表

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 17:16:41 +08:00
QiuSWandClaude Opus 5 d8e90789a2 fix: 界面无法正常显示的两个问题 (#4)
由负责人实机运行后发现,均为骨架工单遗留缺陷。

1. Naive UI 组件从未注册
   main.js 少了 app.use(naive),模板里的 n-button / n-input /
   n-select / n-data-table / n-modal 全被 Vue 当成未知元素:
   按钮渲染成纯文本、输入框和表格不显示、日志弹窗不受 v-model:show
   控制直接常驻页面。侧边栏因为是手写 HTML+CSS 所以正常,
   这正是定位问题的线索。
   修复后打包产物由 258 KB 增至 1.46 MB,可反证此前确实没打进去。

2. 窗口比屏幕高,标题栏被顶到屏幕外
   Width/Height 是逻辑像素,Windows 按系统缩放放大。1440x900
   在 125% 缩放下变成 1800x1125 物理像素,超过工作区高度 1030,
   窗口居中后标题栏与最大化/关闭按钮移出屏幕。
   改为 WindowStartState: options.Maximised,还原尺寸降到 1280x760,
   最小尺寸降到 1024x600,并在代码中写明这个坑。

顺带给工具栏加 flex-wrap,窄窗口下筛选控件换行而不是挤在一起。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 16:55:12 +08:00
QiuSWandClaude Opus 5 8f62508dc4 docs: 记录三段交付节奏与 R5 排序 (#2)
MVP1 商品同步 → MVP2 本地拿到视频 → MVP3 回传货憨憨。
R5 排最后是硬约束:唯一缺接口契约、唯一写真实数据、
且货憨憨图片空间仅剩约 26 GB。
Q1、Q2 的解决时机定为 MVP2 交付后使用者手动上传时抓包。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 16:45:02 +08:00
QiuSWandClaude Opus 5 080a77ab5d chore: 增加 run_dev.bat 与 build.bat 启动打包脚本
面向初级维护者:每个检查失败都写明「怎么修」而不只是报错。
- run_dev.bat:检查 Go/Node/Wails/配置/前端依赖后启动开发模式,
  缺 config.yaml 时自动从 config.example.yaml 复制
- build.bat:打包前先跑单元测试,测试不过拒绝打包(可用 skiptest 强制);
  打包成功后提示不要把 config.yaml 和 cmsp.db 一起发给同事

两个脚本必须是 GBK 编码 + CRLF 行尾:cmd 用系统 ANSI 代码页解析 .bat,
存成 UTF-8 会让中文注释把命令行拆坏(实测报「不是内部命令」);
UTF-8 带 BOM 则会让 @echo off 失效。.gitattributes 用 *.bat -text
让 Git 原样存取,已验证磁盘字节与 Git 存储字节一致。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 16:45:02 +08:00
QiuSWandClaude Opus 5 d257752441 feat: 货憨憨登录与认证复用 (#7)
由 Codex (gpt-5.6-sol) 实施,Claude 审核。

internal/huohanhan/auth.go
- 六步登录:取 CID/CST 作 Basic 认证 → getCltConf 取 clientId →
  下载验证码 → OCR 识别 → 提交登录 → app-version/info 在线验证
- OCR 响应递归查找 4~8 位验证码,优先 text/result/data/content/captcha/code
- 错误分支:invalid_verify_code 换图重试(上限可配,默认 3);
  invalid_credentials 与 disabled_credentials 直接报中文错误不重试
- 认证复用改用 SQLite kv 表 + sync.Mutex,不照搬 Python 版的 Redis
- GetValidAuth 按「内存 → SQLite 在线验证 → 重新登录」顺序取认证

internal/huohanhan/client.go
- 统一请求方法,自动带 Authorization 与 cookies
- 认证失败时清状态、重登、最多重试一次;超时、500、普通业务错误不重试

app.go
- 新增 TestHuohanhanLogin,供设置页「测试连接」调用

测试全部使用 httptest 假服务,不需要真实账号即可运行。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 16:45:02 +08:00
QiuSWandClaude Opus 5 9415c20c9e chore: 删除淘宝流程设计规范文档 (#4)
由项目负责人于 2026-09-02 决定删除。其中的稳定结论已并入 Wiki
「业务规则与术语」的「淘宝登录」与「淘宝以图搜」两节,三处引用
已在上一提交改为指向该页。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 16:00:08 +08:00
QiuSWandClaude Opus 5 5c5c6825b0 docs: 记录原型确认结果并移除已删除设计规范的引用 (#4)
- 原型确认记录填入线上链接、版本 v4、确认日期与覆盖范围
- 新增「已确认的界面决定」:侧栏形式与宽度、登录状态唯一显示位置、
  两行工具栏、四个搜索条件到 getPage 参数的映射、店铺来源、
  运行日志独立窗口、路径选择框类型、表格列
- R6 状态改为「原型已确认,未实现」
- 负责人于 2026-09-02 删除了 Wails专属Chrome登录与淘宝以图搜流程.md,
  三处 Wiki 引用改指向业务规则与术语页,并留一句说明避免后人再引用
- 架构页「当前事实」更新为已有 Go 骨架
- README 补充目录说明与当前状态

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 15:59:54 +08:00
QiuSWandClaude Opus 5 2cefa67001 feat: 初始化 Wails v2 + Vue3 骨架与 SQLite 存储 (#4)
建立可运行、可构建的 Go 骨架,供后续 R1~R5 在其上叠加。

后端:
- main.go / app.go:Wails v2 窗口与 14 个前端可调方法。Wails 专有 API
  只出现在这两个文件,internal/ 对 Wails 零依赖,便于将来迁移 v3
- internal/config:config.yaml 的读写、校验、脱敏。保存时按固定模板
  渲染以保留注释,不用 yaml.Marshal
- internal/store:SQLite(modernc.org/sqlite,无需 CGO)。建表迁移、
  商品仓储、KV。重复同步不会覆盖本地下载与上传状态
- internal/logx:9 条脱敏规则,覆盖淘宝 token、Cookie、Authorization、
  密码和图片 base64;并发安全的内存日志缓冲

前端(Vue 3 + Naive UI,按已确认原型 v4):
- Sidebar:176px 可折叠,仅商品列表与参数设置两项,底部是登录状态的
  唯一显示位置
- ProductListView:两行工具栏(四项筛选 + 重置搜索 / 四个操作按钮)、
  商品表格、分页
- SettingsView:四张卡片,路径用文件与目录选择框
- LogWindow:运行日志子窗口,按级别过滤、自动滚动、导出

配置改用 YAML:分 huohanhan / taobao / download 三段。taobao 段刻意
没有账号密码字段,并有单元测试挡住后续加入。

构建相关:
- .gitignore 只忽略 build/bin/,不忽略 build/。Wails 把 build/ 当源码
  目录(图标、manifest),忽略掉会让别人克隆后构建失败
- .gitignore 忽略 config.yaml、payloads/ 和 *.har(含真实手机号与凭据)

未实现的下载数据、下载视频、上传数据三个方法返回明确的「还没实现」,
不静默失败。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 15:59:54 +08:00
QiuSWandClaude Opus 5 c5c47f98f9 docs: 同步语言规范与 PowerShell 约束的 Wiki 镜像 (#1)
- Development-Workflow:整页取上游新版,新增「语言与术语」
- Local-Development-and-Verification:新增「PowerShell 语法与外部命令」
  子节,本项目原有内容保留
- Project-Profile:运行环境补充 PowerShell 7 pwsh;DevHarness 基线提交
  更新为 40b99387adc184274c43fda5f7efb3746642e27a

check --strict 通过,55 项单元测试全部通过。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 15:26:23 +08:00
QiuSWandClaude Opus 5 0e5a17fe88 chore: 升级 DevHarness 基线到 40b9938 (#1)
同步上游两个提交,均为通用 Agent 工作规则:

- 90938be #31 语言与术语:默认中文协作,标识符、命令、路径、API 名、
  日志与错误原文保持原样不翻译
- 40b9938 #28 Windows PowerShell 命令约束:禁止套用 Bash heredoc 与
  转义规则,正则引号处理,here-string 传多行 Python,rg 用 -g/--glob

AGENTS.md 以上游新版重建,逐字保留本项目第 10 节「项目专用规则」。
dev_scripts/ 与 tests/ 本项目未定制,整体替换为上游版本。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 15:26:23 +08:00
QiuSWandClaude Opus 5 0ac1345005 docs: 对齐轻量模式固定措辞并同步需求总览镜像
Harness 自测要求需求总览包含「局部样式或布局」与「可直接实施」的固定
措辞。已在 Wiki 侧修正原型门禁条目并回读同步镜像。

修正后 53 项 Harness 自测全部通过,check --strict 通过。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 11:58:18 +08:00
QiuSWandClaude Opus 5 ea7371a25f docs: 同步 chengma/cmsp Wiki 核心页面镜像
从线上 Wiki 回读 15 个核心页面并写入镜像头(页面名、地址、revision、
同步时间)。`python dev_scripts/harness.py sync --verify` 通过,
新项目 Wiki 初始化门禁完成。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 11:57:44 +08:00
QiuSWandClaude Opus 5 93f4c94522 chore: 接入 DevHarness 并建立项目文档基线
从 D:/OPC/dev_harness (c13f6adb76c2ffbcae080dabf1df869a241e8f63) 接入
DevHarness 工作流,治理模式选择轻量。

- 复制 Harness 骨架:AGENTS.md、CLAUDE.md、工单模板、dev_scripts、tests
- 编写 cmsp 专属文档:Home、项目档案、架构与代码地图、业务规则与术语、
  本地开发与验证、常见修改、故障排查、需求总览
- AGENTS.md 填入项目专用红线:凭据边界、淘宝登录不可自动化、
  登录失效全局停止门、货憨憨写操作显式确认
- wiki-docs.json 指向 chengma/cmsp,登记 15 个核心页面映射

本提交为 DevHarness 允许的一次性引导提交,只包含仓库骨架与文档,
不含产品功能代码。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LbdtsD3ohhSMy3KPoCgARq
2026-09-02 11:57:18 +08:00