feat: grant transition plan to existing users

This commit is contained in:
QiuSW
2026-07-22 16:56:13 +08:00
parent 09d13ff64a
commit 82c338cbe7
10 changed files with 374 additions and 4 deletions
+1 -1
View File
@@ -111,7 +111,7 @@
| T-630 | 账号订阅授权与默认多设备模式 | T-628, T-629 | **已完成。** 将蝦皮圈产品专属授权从“设备会话 + 设备凭证”调整为“API Key 对应账号 + 软件订阅权益”。同一账号默认允许多台电脑使用,不设置 `LicenseSeat` 设备数量硬限制;`ClientDevice` / `DeviceSession` 仅保留为可选观测和风控数据。新增 `GET /api/v1/cmshopee/subscription/status`,返回账号对应产品的订阅状态、套餐和到期时间;新增 `subscription_required` / `subscription_expired` 错误码。授权服务只查询用户 `SoftwareEntitlement`,不再读取 `DeviceCredential`。`CMSHOPEE_SUBSCRIPTION_ENFORCEMENT=false` 时只写账号订阅影子日志并保持兼容,开启后产品专属生成入口才拒绝无订阅用户;通用 `/api/v1/generate/*`、充值、点数和历史迁移接口不变。保留旧设备/卡密数据只读,不删除历史记录,不再签发新设备凭证;过期设备会话也不会阻断账号订阅授权。已补账号无订阅/有效订阅状态、强制拦截和多设备上下文回归测试;`check`、编译检查和目标 API 测试通过。 | DONE |
| T-631 | 隐藏旧设备授权模型的 admin 菜单入口 | T-630 | 已完成:从 django-admin 应用索引隐藏 `LicenseSeat`、`LegacyMigrationGrant`、`MigrationRequest`、`DeviceCredential` 四个旧方案模型;保留数据库表、历史数据和只读模型。当前订阅、订单、授权事件、设备和会话观测模型继续可见,不改变 API、授权和账务行为。定向 admin 测试、`check`、迁移一致性和编译检查通过。详见 [`tasks/T-631.md`](tasks/T-631.md)。 | DONE |
| T-632 | 会员订阅三阶段启用与简化运营 | T-630, T-631 | **已完成。** 新增 `CMSHOPEE_SUBSCRIPTION_MODE=open|shadow|enforce`,将真实权益状态与最终访问结果分离;`open` / `shadow` 不写虚假权益,`enforce` 才拦截产品专属 submit。状态接口补齐桌面端账号、套餐、顶层有效期、会员中心、通知及观测字段,并保留 T-630 旧字段。admin 首页仅保留“会员套餐”和“用户会员”,其他模型只隐藏不删除。5 条无数据库单元测试、`check`、迁移一致性和编译通过;数据库集成测试因旧远程 MySQL 拒绝连接未取得结果。详见 [`tasks/T-632.md`](tasks/T-632.md)。 | DONE |
| T-633 | 存量用户批量授予过渡测试套餐 | T-632, T-626 | 新增默认只预演的批量授权命令,通过明确 `plan_id`、非空原因、`--execute` 与预期人数双重确认,为执行时已有的启用非后台账号授予指定套餐。已有同产品有效/宽限期权益用户必须跳过;实际授予复用 `grant_software_entitlement()`,在单事务内生成权益快照、席位和授权事件,任一失败整批回滚。不得修改订阅模式、API Key、点数、充值、软件订单或生成记录。详见 [`tasks/T-633.md`](tasks/T-633.md)。 | DOING |
| T-633 | 存量用户批量授予过渡测试套餐 | T-632, T-626 | **已完成。** 新增默认只预演的 `grant_existing_users_plan`,通过明确 `plan_id`、非空原因、`--execute` 与预期人数双重确认,为启用的非后台账号批量授予套餐;已有同产品有效/宽限期权益会跳过。执行在单事务内复用 `grant_software_entitlement()` 生成权益快照、席位和授权事件,任一失败整批回滚。线上已备份 MySQL 后为 30 个存量用户授予“测试”套餐,反向预演待授予为 0;后台账号、API Key、点数、充值、软件订单和生成记录未由命令修改。详见 [`tasks/T-633.md`](tasks/T-633.md)。 | DONE |
## 里程碑
+4 -2
View File
@@ -75,7 +75,7 @@
任务状态以 [`06-tasks.md`](06-tasks.md) 为准,历史执行记录见 [`../progress.md`](../progress.md)。
- 已完成:T-001 初始化 Django + DRF 项目骨架;T-002 建立 apps 目录、自定义 User 与配置;T-003 接通 django-admin 与最小测试;T-004 Phase 0 骨架审核修补;T-101 Provider 适配器层 + 移植 cmbot 调用;T-102 AiModel + ModelAlias 模型 + 别名解析;T-103 配置变更审计;T-104 跑通一次录制标题生成;T-105 Phase 1 AI 层审核修补;T-201 User / UserWallet / ApiKey / PointsLedger / CallRecord 模型;T-202 PricingRule / ExchangeRate 模型 + 计费计算;T-203 并发安全扣点 / 退点;T-204 Phase 2 计费核心审核加固;T-301 API Key 鉴权;T-302 生成标题 / 图片接口;T-303 余额查询接口;T-304 充值回调;T-305 扫码充值下单 + 轮询;T-306 Phase 3 对外 API 安全加固;T-501 注册 / 登录(allauth);T-502 API Key 自助管理页;T-503 个人中心 / 记录页;T-504 充值页(扫码 + 轮询到账);T-505 Phase 4 用户端审核优化;T-401 运营后台完善;T-402 完整验收 MVP;T-403 部署 / 运行文档;T-601 可用别名发现;T-602 django-admin 中文化(第 1-3 层);T-603 django-admin 中文化(第 4 层·字段级);T-604 中文敏感词本地过滤;T-605 免邮箱验证策略落地;T-606 公开首页 + 客户端下载入口;T-607 桌面端最新版本检查接口;T-608 新用户注册赠送试用点数(当前 10 点);T-609 桌面端版本检查接口增加强制更新标记;T-610 首页导入模板下载入口;T-611 用户端品牌名统一为虾皮圈;T-612 生图同步接口止血(上游硬截止 + 长请求池校准);T-613 抽生成核心 service(计费+审核+上游共享 core);T-614 生图异步任务化接口(提交+轮询,新增不动旧接口);T-615 旧同步生图接口用量遥测 + 弃用口径;T-616 生图失败自动重试 2 次;T-617 桌面端版本检查接口增加文件大小字段;T-618 客户端发布版本后台必填文件校验元数据。
- 已完成补充:T-619 多张图片理解并返回文字;T-620 图生图支持单图 / 多图主图与参考图;T-622 图片生成任务后台图片缩略预览;T-623 图片生成任务单图 / 多图筛选;T-624 蝦皮圈设备登记与会话观测;T-625 蝦皮圈设备使用关联与迁移观测;T-626 软件套餐、权益与设备席位基础模型及凭证续期/撤销清理;T-627 存量用户迁移权益、网页确认与设备凭证;T-628 蝦皮圈专属授权入口与影子校验;T-629 软件套餐购买、续订订单与权益入账;T-630 账号订阅授权与默认多设备模式;T-631 隐藏旧设备授权模型的 admin 菜单入口;T-632 会员订阅三阶段启用、状态接口补全与 admin 简化。
- 已完成补充:T-619 多张图片理解并返回文字;T-620 图生图支持单图 / 多图主图与参考图;T-622 图片生成任务后台图片缩略预览;T-623 图片生成任务单图 / 多图筛选;T-624 蝦皮圈设备登记与会话观测;T-625 蝦皮圈设备使用关联与迁移观测;T-626 软件套餐、权益与设备席位基础模型及凭证续期/撤销清理;T-627 存量用户迁移权益、网页确认与设备凭证;T-628 蝦皮圈专属授权入口与影子校验;T-629 软件套餐购买、续订订单与权益入账;T-630 账号订阅授权与默认多设备模式;T-631 隐藏旧设备授权模型的 admin 菜单入口;T-632 会员订阅三阶段启用、状态接口补全与 admin 简化;T-633 存量用户批量授予过渡测试套餐。
- 正在进行:无。
- T-621 注册赠点运营后台配置继续留在 Backlog。真实支付回调到账闭环、客户端发布、生产多图理解模型配置和线上旧同步接口用量观察仍可继续拆任务。
- 当前 blocker:支付商户真实密钥/证书与生产 SDK 依赖仍待提供;微信回调到账闭环仍需真实支付验收;真实 AI 标题生成已在线上跑通,图片生成慢 / 504 / 客户端超时风险已拆为 T-612~T-616 并完成工程侧处理。
@@ -151,13 +151,15 @@ T-619 已落地同步多图理解:调用方提交有序 `images` 列表,服
T-632 已落地会员订阅三阶段模式:`CMSHOPEE_SUBSCRIPTION_MODE=open` 为当前开发测试默认值,无需给所有用户逐个绑定长期权益;`shadow` 继续放行并记录真实权益覆盖,`enforce` 才按真实权益拦截产品专属 submit。订阅状态接口已补齐桌面端账号、套餐、顶层有效期、会员中心与通知字段;admin 软件授权首页只显示“会员套餐”和“用户会员”,隐藏模型及历史数据未删除。
T-633 已新增默认只预演的 `grant_existing_users_plan`:按套餐 ID、非空原因和预期授予人数安全批量授予启用的非后台账号,跳过同产品已有有效/宽限期权益,并在单事务内复用授权服务生成快照、席位和事件。线上当前保持 `shadow`,已给 30 个存量普通用户授予“测试”套餐;后台账号未授予,重复预演待授予为 0。
## 开始编码前检查
1. 读仓库级 `AGENTS.md` / `CLAUDE.md`。
2. 读 `docs/00-ai-start-here.md`。
3. 读 `docs/05-coding-rules.md`(尤其第 8 节资金安全)。
4. 在 `docs/06-tasks.md` 领取第一个 `TODO` 且依赖均 `DONE` 的任务;当前没有可直接领取的任务。
5. 当前开发测试环境应保持 `CMSHOPEE_SUBSCRIPTION_MODE=open`;切到 `shadow` / `enforce` 前须补跑数据库集成测试,并用真实微信商户完成充值和订阅两条回调闭环验收。
5. 代码默认开发测试模式为 `open`;线上当前为 `shadow` 且存量普通用户已有“测试”套餐。切到 `enforce` 前仍须完成桌面端回归,并用真实微信商户完成充值和订阅两条回调闭环验收。
## 维护规则
+29
View File
@@ -516,3 +516,32 @@ python3.12 manage.py smoke_ai_generation image
- 真实支付 SDK 与商户配置齐全后,`PAYMENT_CALLBACK_MODE=sdk`。
- 真实图片生成耗时已记录并用于设置超时链路;若未记录,发布说明必须标注风险。
- `manage.py check`、迁移、静态文件、关键测试和 smoke 均有记录。
## 十一、存量用户批量授予过渡套餐
仅在运营已创建并确认目标套餐后使用。先用套餐数据库 ID 预演,默认不会写数据:
```bash
python3.12 manage.py grant_existing_users_plan \
--plan-id 2 \
--reason "存量用户测试套餐过渡"
```
确认输出中的 `grant_count`、套餐 ID 和产品代码,并完成数据库备份后,再把预演人数原样传给执行命令:
```bash
python3.12 manage.py grant_existing_users_plan \
--plan-id 2 \
--reason "存量用户测试套餐过渡" \
--execute \
--expected-grant-count 30
```
安全规则:
- 默认只处理启用的非后台、非超级用户账号。
- 同产品已有有效或宽限期权益的用户会跳过,不用测试套餐覆盖正式会员。
- `--expected-grant-count` 与执行时重新计算的人数不一致时整批拒绝。
- 实际授予在一个事务中执行,任一失败整批回滚,并为每个新权益写带原因的授权事件。
- 执行后再次运行预演,正常应得到 `grant_count=0`;再核对权益、授权事件、后台账号和订阅状态接口。
- 示例中的套餐 ID 和人数只表示一次部署记录,每次操作都必须重新从 admin 和预演结果确认,不能照抄。
+22 -1
View File
@@ -58,4 +58,25 @@
## 状态
DOING。
DONE。
## 实施结果
- 新增 `grant_plan_to_existing_users()` 批量服务:执行时锁定套餐和启用的非后台用户,按同产品仍有效或处于宽限期的权益跳过已有会员,并在单事务中复用 `grant_software_entitlement()`。
- 新增 `grant_existing_users_plan` management command。默认只预演;实际执行必须同时提供 `--execute` 和与当前范围一致的 `--expected-grant-count`。
- 命令按套餐 ID 精确选择启用套餐,统一要求非空原因;输出只包含套餐、产品和数量统计。
- 新增 5 条专项测试,覆盖只读预演、普通用户范围、正式权益保护、重复执行、人数不匹配拒绝和异常整批回滚。
## 验证结果
- `manage.py check`:通过,0 issues。
- `manage.py makemigrations --check --dry-run`:No changes detected;本任务无迁移。
- `manage.py test apps.licensing.tests.ExistingUserPlanGrantCommandTests`:5 tests OK。
- SQLite 完整 `apps.licensing` 回归共 35 条,其中 33 条通过;2 条既有多连接并发测试因 SQLite 表级锁返回 `database table is locked`,与本任务断言无关,未误记为全量通过。
- `compileall apps/licensing` 与 `git diff --check`:通过。
- 生产 MySQL 8.4 执行前备份:`/root/cmhub-t633-backup-20260722_164956/cmhub-before-t633.sql.gz`,SHA256 为 `ae53d3edd329a3ed16e55958f2a93453f420b5026bbb3294edb6b4e91ad17162`,本地副本校验一致。
- 线上预演:`eligible=30`、`skipped_existing=0`、`grant_count=30`;按预期人数执行成功。
- 执行后:测试套餐权益 30 条、不同用户 30 个、`granted` 事件 30 条、席位 3030 条;后台账号权益 0,缺少有效权益的目标用户 0,重复有效权益用户 0。
- 反向预演:`eligible=30`、`skipped_existing=30`、`grant_count=0`,证明重复执行不会重复授予。
- 真实 API Key 抽样返回 `access_source=entitlement`、`entitlement_status=active`、套餐“测试”,点数余额保持 148。
- 执行期间线上有 4 个生图任务运行,聚合点数同期减少 2 点并新增一条关联调用的 `consume -2` 流水;这是并发生成业务变化,不是授权命令写账。