Files
cmshoppe/docs/tasks/T-662a.md
T

95 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: T-662a
title: 商品状态领域模型、数据库迁移与采集检测
status: DONE
phase: 7
deps: []
created: 2026-07-18
---
# T-662a 商品状态领域模型、数据库迁移与采集检测
## 问题 / 背景
编辑页仍可打开的「未上架」「审核中」商品当前会被当作普通商品采集,后续可能继续消耗 AI 点数或进入更新流程。真实证据见:
- `docs/html/未上架商品提示.html`
- `docs/html/审核中商品提示.html`
两份样本均使用 Shopee EDS alert 组件;原「审核中」样本文件名尾部空格已移除,后续文档和测试统一使用无空格路径。
## 状态契约
- 新增共享领域模块 `app/product_status.py`,作为①②③唯一状态事实来源,避免各 Tab 重复定义枚举、显示文字和异常判断。
- SQLite 保存稳定代码,不保存中文枚举:
- `normal`:未发现已知异常横幅;界面显示「正常」,**不宣称一定在售**;
- `unlisted`:未上架;
- `reviewing`:审核中;
- `unknown`:历史 NULL、DOM 检测失败或出现未识别状态横幅。
- `NULL` 和非法历史值统一按 `unknown` 读取;只有 `normal` 表示本次检测未发现已知异常,可进入默认的采集、付费生成和更新候选;`unlisted/reviewing/unknown` 均不得被“只处理状态正常商品”策略放行。
- 文本归一化:trim、连续空白折叠、繁简白名单兼容;禁止依赖 `data-v-*`。
- 提供共享纯逻辑 helper,统一完成状态代码归一化、中文显示和任务分组,①②③不得各自复制状态判断。
## DOM 检测规则
- 编辑页就绪后一次读取 `.eds-alert.eds-alert--warning` 下的 `.eds-alert-title` 与 `.eds-alert-desc`,返回结构化文本列表,不保存整段 HTML。
- 白名单:
- `審核中` / `审核中` → `reviewing`;
- `您的商品未上架` → `unlisted`。
- 查询成功且没有 warning 横幅 → `normal`。
- 有 warning 横幅但没有白名单命中 → `unknown`,不得保留旧的 `normal` 或误判正常。
- DOM 查询抛错或返回结构非法 → `unknown`;错误写脱敏诊断日志。是否继续采集标题/封面由 T-662b 的本轮采集策略决定,状态探测本身不直接制造技术失败。
- 多横幅时跳过无关警告,取 DOM 顺序中第一个白名单命中项;没有命中则为 `unknown`。
- `product_status_note` 只保存归一化后的 title/desc 摘要,最多 2000 字符;`product_status_at` 记录本次检测时间。
- 现有 `PRODUCT_UNAVAILABLE_TEXT_MARKERS` 和商品失效 toast 逻辑保持不变。
## 数据模型与采集接入
- `tasks` additive 新增:
- `product_status TEXT NULL`;
- `product_status_note TEXT NULL`;
- `product_status_at TEXT NULL`。
- `db.init_db()` 使用幂等原位迁移;`Task` dataclass 暴露三字段。
- 新增独立、幂等的商品状态写入 API,使标题/封面被采集策略略过时仍能保存状态、note 和检测时间;扩展 `db.set_collected()` 时使用新增 keyword-only 参数,保持现有位置参数调用兼容。
- 编辑器提供可复用的商品状态探测步骤,并保证在读取标题、下载封面之前完成;完整采集成功时,标题、封面和本次状态快照在同一事务写入。
- 每次重新采集都必须重新打开页面检测状态,不得依据数据库旧快照提前略过,否则商品恢复正常后无法刷新状态。
- 状态探测返回 `unknown` 时保存当前快照;是否继续读取标题和封面由 T-662b 传入的本轮策略决定。
## 验收标准
- [ ] 两份真实样本分别识别为 `unlisted/reviewing`;title 尾部空格与繁简不影响结果。
- [ ] 无 warning 为 `normal`;未知 warning、DOM 异常和非法返回均为 `unknown`。
- [ ] 多横幅共存按约定选择,note 规范化且受长度限制。
- [ ] 旧库迁移幂等,历史 NULL 显示语义为「未知」。
- [ ] 只写商品状态时不伪造“采集成功”,也不清空既有标题和封面;完整采集可原子写入内容和状态。
- [ ] 重新采集始终重新检测并覆盖状态、note 和时间,不根据旧快照预过滤。
- [ ] 不改变商品失效判断、Chrome/CDP 页面打开关闭和 Shopee 更新逻辑。
## 测试与文档
- `tests/test_product_status.py`:状态归一化、中文显示、正常与非正常分组。
- `tests/test_editor_login.py`:样本结构、无横幅、未知横幅、多横幅、异常降级、探测顺序与 collect 回归。
- `tests/test_db.py`:迁移幂等、NULL 语义、独立状态写入、keyword-only 写入、既有内容保留与重新采集覆盖。
- 更新 `docs/04-architecture.md` 第七节和 `docs/api.md`;登记样本路径、选择器纪律和状态代码。
## 验证
```bash
py -3.10 -m unittest tests.test_editor_login tests.test_db
py -3.10 -m unittest discover -s tests
py -3.10 -m ruff check app tests main.py
py -3.10 -m compileall app main.py
git diff --check
```
## 非目标
- 不识别无样本的售罄、违规、禁卖等状态;未知横幅先归 `unknown`,不得猜测为正常。
- 不新增 GUI,不过滤任务,不回写 Excel,不做定时刷新。
## 执行记录
- 2026-07-18:新增 `app/product_status.py`,统一状态代码、中文显示、EDS 警示分类与任务分组;`tasks` 原位迁移新增商品状态快照字段,并提供独立状态写入与 `set_collected()` 的 keyword-only 状态快照参数。
- 2026-07-18:`editor.collect()` 在读取标题、封面前探测 EDS warning 状态,`CollectWorker` 完整采集成功时同步写入状态快照;状态读取异常降级为 `unknown` 并写脱敏诊断。
- 验证:`py -3.10 -m unittest tests.test_product_status tests.test_editor_login tests.test_db`、`py -3.10 -m unittest discover -s tests`(600 项)、`py -3.10 -m ruff check app tests main.py`、`py -3.10 -m compileall app main.py`、`git diff --check` 均通过。