Files
cmhub/docs/tasks/T-636.md
T

120 lines
7.3 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.
# T-636 客户端订阅策略 django-admin 运营开关
## 背景
T-635 已经通过公开版本检查接口向新版 cmshopee 客户端发布启动订阅策略,
但策略只能通过修改 `.env` 后重启 Web / Generate 服务变更。该方式不适合日常
运营,也无法在 django-admin 中留下明确的变更原因和操作者。
客户端策略和 `CMSHOPEE_SUBSCRIPTION_MODE` 的服务端授权仍是两件事:前者只
决定新版客户端是否检查 / 展示会员门禁,后者才决定产品专属生成接口是否按真实
权益拦截。本任务不得混淆或合并这两套开关。
## 目标
1. 让超级管理员在 django-admin 中用单一枚举切换 `off`、`observe`、`enforce`。
2. 每次变更必须留存原因、操作者、前后值和时间。
3. 公开版本检查接口在下一次请求立即发布后台策略,无需重启服务。
4. 未配置后台策略时完全保留 T-635 `.env` 行为,保证上线不改变当前客户端。
## 实施范围
### 数据模型与策略来源
- 新增全局单例 `ClientSubscriptionPolicy`,仅保存一个固定主键记录;字段至少有
`mode`、`updated_by`、`created_at`、`updated_at`。
- 模式只能是 `off|observe|enforce`,输出继续复用 T-635 的统一映射:
`off -> false/false`、`observe -> true/false`、`enforce -> true/true`。
- 新增不可修改的策略审计记录,保存 `previous_mode`、`mode`、必填 `reason`、
`changed_by`、`changed_at`。审计不保存 API Key、用户权益、点数、订单、prompt
或图片。
- 迁移只创建空表,不写预置策略记录。策略解析顺序固定为:数据库单例存在时优先
使用其 `mode + updated_at`;记录不存在时回退
`CMSHOPEE_CLIENT_SUBSCRIPTION_POLICY` 与
`CMSHOPEE_CLIENT_SUBSCRIPTION_POLICY_UPDATED_AT`。
- 公开接口每次请求读取单例,不使用进程内缓存;因此 admin 保存成功后下一次请求
即生效。数据库不可用仍按既有 Django 请求失败语义处理,不得静默发布错误策略。
### django-admin
- 日常导航只增加“客户端订阅策略”一个入口;审计仅作为该详情页的只读 inline,
不单独出现在导航。
- 仅超级管理员可新增或编辑;不允许删除。首次创建后禁止新增第二条记录。
- 表单除 `mode` 外必须填写“变更原因”。服务层负责事务内写单例与审计,禁止通过
admin 直接 `save()` 绕过审计。
- 展示当前 `CMSHOPEE_SUBSCRIPTION_MODE` 为只读信息,明确它是服务端授权模式;
本页不得修改该环境变量,也不得修改权益、套餐、席位、凭证、订单、钱包或点数。
- 相同模式不应制造伪变更审计;要求运营选择不同模式后再保存。
### API 与兼容
- `GET /api/v1/client/releases/latest?platform=windows` 顶层 `client_policy` 结构、
`policy_version=1`、字段类型和 `Cache-Control: no-store` 保持 T-635 合约。
- 有发布、`release:null` 和无下载地址路径均使用同一策略来源;既有
`force_update`、`size_bytes` 等发布字段不回归。
- 此接口仍匿名只读、无账号上下文,不读取 `SoftwareEntitlement`、套餐、点数、
订单、API Key 或生成模型;新增的仅是全局策略配置读取。
- 不修改 `CMSHOPEE_SUBSCRIPTION_MODE`,不改变通用或专属生成接口、计费预扣 /
确认 / 退款、支付回调、软件订单、异步图片任务或旧客户端行为。
## 部署与回滚
1. 备份生产应用和数据库,发布代码并执行 `migrate`;迁移不会创建策略记录。
2. 首次部署后确认 `.env` 兜底策略仍为 `off`,公开接口保持
`subscription_check_enabled=false`、`subscription_enforcement_enabled=false`。
3. 超级管理员可创建单例并按 `off -> observe -> enforce` 灰度;每次保存立即以新的
`updated_at` 发布,无需重启。
4. 需要紧急取消客户端门禁时,在后台将模式改为 `off`;若后台记录尚未创建,继续
使用 `.env` 兜底。不要把此操作误认为取消服务端授权。
5. 若需恢复到纯 `.env` 来源,必须先在维护窗口以受控数据操作移除单例记录;本任务
的 admin 不提供删除入口,避免误删和审计丢失。
## 验收条件
1. 无后台记录时三种合法 `.env` 配置保持 T-635 映射与启动校验。
2. 后台记录存在时优先于 `.env`;保存 `observe` / `enforce` 后下一次公开请求立即
返回正确双布尔值与稳定、带时区的 `updated_at`。
3. 模型层和后台均不能创建第二条记录,后台不能删除;非超级管理员不能新增或编辑。
4. 每次有效变更都写一条只读审计,包含前后模式、非空原因、操作者和时间;相同模式
被表单拒绝,不写伪审计。
5. 审计不出现在 admin 日常导航,策略页可只读查看历史;服务端授权模式只读展示。
6. 版本检查接口继续匿名、`Cache-Control: no-store`,且发布字段和无发布路径不回归;
不查询或泄露用户、权益、套餐、点数、订单、API Key 或密钥。
7. 同步更新 `docs/api.md`、`docs/env.md`、`docs/deployment.md`、
`docs/04-architecture.md`、`docs/current-state.md`、任务状态、`progress.md` 和
Obsidian 客户端契约。
8. `makemigrations --check --dry-run`、`migrate`、`check`、目标 API / licensing /
admin 测试、`init.ps1` 与 `git diff --check` 通过。
## 状态
DONE。代码、迁移、目标回归与生产部署均已完成。
## 实施结果
- 新增 `ClientSubscriptionPolicy` 与 `ClientSubscriptionPolicyAudit`,迁移
`licensing.0006_client_subscription_policy` 只建空表。策略固定主键为 `1`,并由
数据库 CHECK 约束拒绝第二个主键;没有预置记录,保留既有 `.env` 兜底。
- `get_published_client_subscription_policy()` 每次公开版本检查优先读取后台单例,
用其 `updated_at` 发布原有 `client_policy` 结构;未创建记录时继续调用 T-635
的环境配置校验。没有进程内缓存,后台保存后下一次请求立即生效。
- django-admin 新增“客户端订阅策略”入口:仅超级管理员可新增 / 编辑,不能删除;
表单强制填写原因,服务层在事务内写策略和只读审计。策略详情显示当前服务端订阅
模式,但不允许修改它;审计只作为 inline 展示,不增加导航噪音。
- 未改通用 / 专属生成、服务端订阅授权、用户权益、套餐、点数、支付、软件订单或
异步图片任务。
## 生产部署
- 2026-07-28 已部署到 `185.216.248.75` / `https://cm.833729.com`,应用 revision
为 `2171968`;发布归档 SHA256 为
`f9c386125226813a5f679487832bda7f28474428bfe173f8a2a53b5e84f08a17`。
- 发布前备份位于 `/root/cmhub_deploy_backups/cmhub-before-t636-20260728_154417`;
线上 `licensing.0006_client_subscription_policy` 已成功应用。
- 未创建 `ClientSubscriptionPolicy` 或审计记录,因此继续使用现有 `.env`
`off` 兜底;公网接口返回 HTTP 200、`Cache-Control: no-store`、
`subscription_check_enabled=false` 和 `subscription_enforcement_enabled=false`。
- `cmhub-web`、`cmhub-generate`、Nginx、MySQL 8.4 均为 active;66 个图片 worker
未重启且保持 active。下一步由超级管理员在 admin 创建策略记录后按
`off -> observe -> enforce` 灰度,无需重启服务。