Files
cmshoppe/docs/tasks/T-705.md
T

110 lines
6.9 KiB
Markdown
Raw Normal View History

---
id: T-705
title: cmhub 远程订阅策略开关与分类门禁提示
phase: 8
deps: [T-700, T-701]
status: BLOCKED
created: 2026-07-28
---
# T-705 cmhub 远程订阅策略开关与分类门禁提示
## 问题 / 背景
当前客户端通过本地常量控制启动订阅检测和强制门禁,cmhub 无法在灰度上线、接口维护或异常期间远程切换。每次调整都需要重新修改并打包客户端。现有强制模式已经能限制业务 Tab、拦截新提交,并对缺少 Key 和套餐过期提供窗口,但未开通套餐、套餐撤销、账号禁用、Key 无效和接口异常仍缺少与原因匹配的独立窗口。
不能把远程开关放进订阅状态接口,否则客户端必须先请求该接口才能知道是否应跳过它。客户端门禁也不能成为服务端授权的唯一安全边界,cmhub 产品接口仍必须独立校验 API Key 和套餐。
## 外部前置条件
cmhub 先按 Obsidian《cmshopee-客户端订阅策略接口契约》交付并冻结启动策略字段、兼容规则和测试响应;未交付前本任务保持 `BLOCKED`,不得猜测字段或先写临时解析。
最低要求是在客户端启动时已经请求的 HTTPS 版本接口中增加可选 `client_policy`:
```json
{
"client_policy": {
"policy_version": 1,
"subscription_check_enabled": true,
"subscription_enforcement_enabled": true,
"updated_at": "2026-07-28T10:00:00+08:00"
}
}
```
cmhub 必须保证:`subscription_check_enabled=false` 时服务端产品接口的订阅策略与客户端关闭策略一致;无论客户端门禁是否开启,付费授权最终仍由服务端执行。
## 方案
### 1. 远程双开关
- `check=false`:跳过 `SubscriptionCheckWorker`,不显示会员激活/过期/异常窗口,不锁定业务 Tab;生成前订阅预检直接放行,设置页重新检测入口显示“服务端暂未启用会员检测”并禁用。
- `check=true, enforcement=false`:执行真实查询并展示账号、套餐、有效期和观察状态,但不禁用业务功能、不拦截新提交、不弹强制处理窗口。
- `check=true, enforcement=true`:保持现有强制门禁;仅 `active/grace/legacy` 放行业务功能。
- `check=false, enforcement=true` 属于非法组合,客户端归一化为两者均关闭并写脱敏诊断,不允许在没有检测结果时强制锁定。
### 2. 启动策略获取与兼容
- 优先复用启动版本接口返回的 `client_policy`,不得为了读取开关再调用订阅状态接口,也不增加独立同步阻塞请求。
- 缓存最近一次结构合法的非敏感策略和取得时间到 `data/config/` 独立文件;不包含 API Key、账号、套餐或订阅结果。
- 当前响应有效时覆盖缓存;接口不可达、字段缺失或非法时使用最近一次有效缓存。无有效缓存时回退为“检测开启、强制关闭”的观察模式,避免控制面异常锁死正常用户;cmhub 产品接口继续作为最终授权边界。
- 旧 cmhub 响应不含 `client_policy` 时按上述兼容规则处理,不影响启动版本检查和强制升级。
- 只在启动时确定本次运行策略;服务端开关变更下一次启动生效。设置保存和“重新检测会员状态”只重查套餐,不偷偷改变本次策略。
### 3. 分类窗口
- `not_configured`:复用会员 API Key 激活窗口。
- `required`:新增“尚未开通会员套餐”,提供“前往会员中心”“重新检测”“退出程序”。
- `expired`:保留现有“会员套餐已过期”窗口及会员中心入口。
- `revoked`:新增“会员套餐已失效”,不得使用含糊的技术错误。
- `account_disabled`:新增“会员账号当前不可用”,提示前往会员中心或联系处理。
- `key_invalid`:打开可重新填写 API Key 的激活窗口并聚焦输入框,不使用泛化订阅窗口。
- `unavailable`:显示“暂时无法验证会员状态”,提供“重新检测”“打开设置”“退出程序”;不得误导用户未付费或要求重复订阅。
- 同一运行期间同一状态只显示一次;状态恢复为允许后再次进入无效状态才可重新提示。重复检测、保存设置和陈旧 worker 结果不得造成弹窗循环。
- 会员中心地址继续使用 `subscription.safe_manage_url()` 的 HTTPS 安全结果;无有效地址时禁用入口并显示中文原因。
### 4. 门禁边界
- 强制模式查询期间和明确无效状态只保留“设置”Tab;AI生成、AI帮写和商品套图的新提交继续经过主窗口统一预检。
- 不改变已经运行任务的协作取消策略,不删除本地结果、不修改 SQLite 业务数据、不关闭 Chrome。
- cmhub 托管生成接口仍必须服务端校验套餐;自定义网关和客户端本地功能是否受门禁影响沿用当前产品规则,本任务不扩张授权范围。
## 验收要点
- [ ] 服务端双开关三种合法组合分别得到关闭、观察、强制三种稳定行为,非法组合不会锁死客户端。
- [ ] 明确关闭时启动不请求订阅状态;观察模式查询但不限制;强制模式保持现有门禁。
- [ ] 策略响应、缓存、旧响应、接口失败和无缓存回退均有测试,不记录敏感信息。
- [ ] `required/expired/revoked/account_disabled/key_invalid/unavailable/not_configured` 使用对应中文窗口或激活流程,不把技术异常说成未订阅。
- [ ] 分类窗口按状态转换去重,会员中心 URL 安全校验、重新检测、设置和退出操作可用。
- [ ] 服务端仍是授权最终来源;修改客户端开关不能绕过 cmhub 生成接口套餐校验。
- [ ] 不修改 Chrome、CDP、蝦皮采集、封面上传、拖拽或线上更新逻辑。
## 测试与文档
- 扩展 `tests/test_update_check.py`:策略字段解析、非法组合、旧响应和策略透传。
- 新增或扩展订阅策略缓存测试:有效缓存、损坏缓存、接口失败和无缓存观察回退。
- 扩展 `tests/test_gui.py`:关闭/观察/强制模式、七类状态窗口、去重、重检和门禁。
- 扩展 `tests/test_subscription.py`:状态与窗口动作映射所需的纯逻辑契约。
- 更新 `docs/04-architecture.md`、`docs/api.md`、`docs/routes.md` 和打包/发布说明。
## 验证
```bash
py -3.10 -m unittest tests.test_update_check tests.test_subscription tests.test_gui
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
```
## 边界(不改什么)
- 不把客户端开关当成服务端授权凭据。
- 不在订阅状态接口中返回“是否调用本接口”的开关。
- 不新增同步启动阻塞请求,不把网络故障等同于未付费。
- 不提交 API Key、账号、套餐响应、配置、数据库、日志或业务数据。
## 执行记录
- 2026-07-28:完成客户端任务定义;等待 cmhub 启动策略接口契约与测试响应交付后解除阻塞。