Files
cmshoppe/docs/tasks/T-705.md
T
chengma df9da63841
Tests / Python 3.11 / Windows (push) Has been cancelled
feat: 接入远程订阅策略门禁 T-705
2026-07-28 15:42:19 +08:00

117 lines
8.4 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-705
title: cmhub 远程订阅策略开关与分类门禁提示
phase: 8
deps: [T-700, T-701]
status: DONE
created: 2026-07-28
---
# T-705 cmhub 远程订阅策略开关与分类门禁提示
## 问题 / 背景
当前客户端通过本地常量控制启动订阅检测和强制门禁,cmhub 无法在灰度上线、接口维护或异常期间远程切换。每次调整都需要重新修改并打包客户端。现有强制模式已经能限制业务 Tab、拦截新提交,并对缺少 Key 和套餐过期提供窗口,但未开通套餐、套餐撤销、账号禁用、Key 无效和接口异常仍缺少与原因匹配的独立窗口。
不能把远程开关放进订阅状态接口,否则客户端必须先请求该接口才能知道是否应跳过它。客户端门禁也不能成为服务端授权的唯一安全边界,cmhub 产品接口仍必须独立校验 API Key 和套餐。
## 外部契约(已交付)
cmhub 已按 Obsidian《cmhub-cmshopee客户端订阅策略接口契约》完成 T-635。客户端复用现有 HTTPS 启动版本接口:
最低要求是在客户端启动时已经请求的 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 内部只配置 `CMSHOPEE_CLIENT_SUBSCRIPTION_POLICY=off|observe|enforce`,对客户端仍输出上述两个布尔字段。订阅状态成功响应另提供顶层严格布尔字段 `real_entitlement_allowed`;强制模式必须只按该字段放行,不得使用展示用 `status` 或兼容 `allowed` 推断。当前成功状态为 `active/grace/required/expired`,账号禁用使用 HTTP 403;`revoked` 只保留客户端兼容处理,不属于当前服务端承诺状态。
cmhub 保证无论客户端门禁是否开启,付费产品接口仍独立执行服务端授权;客户端开关不是授权凭据。
## 方案
### 1. 远程双开关
- `check=false`:跳过 `SubscriptionCheckWorker`,不显示会员激活/过期/异常窗口,不锁定业务 Tab;生成前订阅预检直接放行,设置页重新检测入口显示“服务端暂未启用会员检测”并禁用。
- `check=true, enforcement=false`:执行真实查询并展示账号、套餐、有效期和观察状态,但不禁用业务功能、不拦截新提交、不弹强制处理窗口。
- `check=true, enforcement=true`:保持现有强制门禁;仅 `real_entitlement_allowed=true` 放行业务功能,该值只能与 `active/grace` 同时出现。
- `check=false, enforcement=true` 属于非法组合,客户端归一化为两者均关闭并写脱敏诊断,不允许在没有检测结果时强制锁定。
### 2. 启动策略获取与兼容
- 优先复用启动版本接口返回的 `client_policy`,不得为了读取开关再调用订阅状态接口,也不增加独立同步阻塞请求。
- 缓存最近一次结构合法的非敏感策略和取得时间到 `data/config/` 独立文件;不包含 API Key、账号、套餐或订阅结果。
- 当前响应有效时覆盖缓存;接口不可达、字段缺失或非法时使用最近一次有效缓存。缓存额外记录本地取得时间。无有效缓存时回退为“检测开启、强制关闭”的观察模式,避免控制面异常锁死正常用户;cmhub 产品接口继续作为最终授权边界。
- 旧 cmhub 响应不含 `client_policy` 时按上述兼容规则处理,不影响启动版本检查和强制升级。
- 只在启动时确定本次运行策略;服务端开关变更下一次启动生效。设置保存和“重新检测会员状态”只重查套餐,不偷偷改变本次策略。
### 3. 分类窗口
- `not_configured`:复用会员 API Key 激活窗口。
- `required`:新增“尚未开通会员套餐”,提供“前往会员中心”“重新检测”“退出程序”。
- `expired`:保留现有“会员套餐已过期”窗口及会员中心入口。
- `revoked`:保留兼容窗口“会员套餐已失效”,不得使用含糊的技术错误;当前 cmhub 不承诺返回该状态。
- `account_disabled`:新增“会员账号当前不可用”,提示前往会员中心或联系处理。
- `key_invalid`:打开可重新填写 API Key 的激活窗口并聚焦输入框,不使用泛化订阅窗口。
- `unavailable`:显示“暂时无法验证会员状态”,提供“重新检测”“打开设置”“退出程序”;不得误导用户未付费或要求重复订阅。
- 同一运行期间同一状态只显示一次;状态恢复为允许后再次进入无效状态才可重新提示。重复检测、保存设置和陈旧 worker 结果不得造成弹窗循环。
- 会员中心地址继续使用 `subscription.safe_manage_url()` 的 HTTPS 安全结果;无有效地址时禁用入口并显示中文原因。
### 4. 门禁边界
- 强制模式查询期间和明确无效状态只保留“设置”Tab;AI生成、AI帮写和商品套图的新提交继续经过主窗口统一预检。
- 不改变已经运行任务的协作取消策略,不删除本地结果、不修改 SQLite 业务数据、不关闭 Chrome。
- cmhub 托管生成接口仍必须服务端校验套餐;自定义网关和客户端本地功能是否受门禁影响沿用当前产品规则,本任务不扩张授权范围。
## 验收要点
- [x] 服务端双开关三种合法组合分别得到关闭、观察、强制三种稳定行为,非法组合不会锁死客户端。
- [x] 明确关闭时启动不请求订阅状态;观察模式查询但不限制;强制模式保持现有门禁。
- [x] 策略响应、缓存、旧响应、接口失败和无缓存回退均有测试,不记录敏感信息。
- [x] `required/expired/revoked/account_disabled/key_invalid/unavailable/not_configured` 使用对应中文窗口或激活流程,不把技术异常说成未订阅。
- [x] 分类窗口按状态转换去重,会员中心 URL 安全校验、重新检测、设置和退出操作可用。
- [x] 服务端仍是授权最终来源;修改客户端开关不能绕过 cmhub 生成接口套餐校验。
- [x] 不修改 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 启动策略接口契约与测试响应交付后解除阻塞。
- 2026-07-28:cmhub T-635 与线上契约已交付,确认 `client_policy`、服务端单枚举配置及 `real_entitlement_allowed` 语义,任务解除阻塞并开始实现。
- 2026-07-28:新增 `app/client_policy.py`,接入版本响应策略解析、非法组合归一化、`data/config/client_policy.json` 非敏感原子缓存和无缓存观察回退;启动流程把本轮策略传入主窗口。
- 2026-07-28:订阅状态改为只按 `real_entitlement_allowed` 放行;完成关闭/观察/强制三模式、设置页关闭态、七类中文处理流程和恢复前按状态去重,不修改 Shopee/CDP 链路。
- 2026-07-28:线上版本接口实测 HTTP 200、`Cache-Control: no-store`、策略 `off`;线上订阅接口脱敏实测返回 `active` 且真实权益允许。
- 2026-07-28:验证通过:策略/版本/订阅/GUI 定向测试 260 项;全量 `unittest discover` 717 项;`ruff check app tests main.py`、`compileall app main.py`、`git diff --check` 全部通过。