# 19 高级版订阅、玩法案例模板与 Open API（for AI Agents）

> 关联代码：internal/service/{subscription,play_template,openapi}/、internal/middleware/openapi_auth.go、internal/model/{subscription,play_template_case,openapi_key}.go
> 管理端页面：web/src/views/subscription/plans/、web/src/views/play-template-case/；小程序页面：pages/subscription/buy/

====================================================================
1. 功能概述
====================================================================

三个商业化/开放能力：
- 高级版订阅（SubscriptionPlan/MerchantSubscription）：付费订阅解锁特性码（防刷/脱敏/报表/API/多轮/纯净展示），每商户至多一条 active。
- 玩法案例模板（PlayTemplateCase）：后台维护「开奖设置/参与条件/功能开关」的完整抽奖配置快照（ConfigJSON），供发起人一键套用建抽奖。
- Open API：AK/SK + HMAC 签名对外接口（活动/中奖记录查询、活动创建与状态控制），要求订阅含 api_access 特性。

====================================================================
2. 底层逻辑
====================================================================

2.1 订阅
- SubscriptionPlan：名称/价格/月数/特性多选/排序/状态。特性码枚举：anti_fraud（防刷）/ data_masking（脱敏）/ report_export（报表导出）/ api_access（Open API）/ multi_round（多轮）/ pure_display（纯净展示）。
- MerchantSubscription：商户维度；每商户至多一条 active；新订阅的处理逻辑（叠加/替换）以 service 实现为准。
- 购买：小程序 pages/subscription/buy/index 选套餐下单（ProductType=subscription）→ 支付回调开通。
- 特性校验：GET /mp/subscription/has-feature?feature= 查询；Open API 网关用 api_access 校验。

2.2 玩法案例模板
- PlayTemplateCase：玩法（normal/wheel/slot/stick/code/wechat/xhs/annual 八种）+ 标题/描述/配图/排序/状态 + ConfigJSON（本质是 CreateLotteryInput 的 JSON 快照，含开奖设置、参与条件[关注公众号/群成员/审核/企微/区域/口令/问卷/任务/答题/投票/小游戏]、功能开关[鼓励分享/允许推荐/隐藏分享/签到/禁止助力/留言/订阅通知/统计/纯展示]）。
- 后台 CRUD 维护模板库；小程序「发起抽奖」时按玩法选模板一键套用（GET /mp/play-template-cases）。

2.3 Open API
- 密钥：OpenAPIKey 表（AK/SK、active/revoked、过期时间）；后台密钥管理页 CRUD。
- 调用方式：Header X-Access-Key + HMAC 签名（覆盖 method+path+body+timestamp+nonce；时间窗口内有效）；网关同时校验商户订阅含 api_access。
- 能力：GET /open/v1/activities 活动列表、GET/POST /open/v1/activities/:id(/status) 活动查询/创建/状态控制、GET /open/v1/winning-records 中奖记录。
- 错误码：4014-4016 密钥无效/过期/未开通特性；4011-4013 签名类。

====================================================================
3. 后台操作
====================================================================

3.1 订阅套餐 /subscription/plans（views/subscription/plans/index.vue）
- 筛选：关键词、状态。
- 表格列：ID/名称/时长(天)/价格(元)/权限数/状态/操作。
- CRUD 弹窗：名称/价格（元）/月数/特性多选（六码）/排序/状态。
- 行操作：编辑、启用/禁用、删除。

3.2 玩法案例模板 /play-template-cases（views/play-template-case/index.vue）
- 筛选：玩法。
- 表格列：ID/玩法/标题/排序/状态/操作。
- 行操作：编辑、启用/禁用、删除。
- 「新增/编辑」大弹窗：基础区（玩法/标题/描述/配图/排序/状态）+ 折叠面板「开奖设置」「参与条件」「功能开关」——即完整抽奖表单的模板化（defaultConfigForm 与选项常量在 src/api/play-template-case.ts）。

3.3 Open API 密钥（无独立菜单页时经 API/接口操作）
- GET|POST /api/admin/openapi/keys；DELETE /api/admin/openapi/keys/:id（权限 openapi:read/write）。创建返回 SK 仅一次，吊销即时生效。

3.4 后台操作逻辑备注（隐藏约束，UI 不直接体现）
- 懒过期检查：订阅有效期不在后台定时扫描，而是在每次读取时实时判断 expires_at 是否过期——后台看到的订阅状态始终为当前真实状态。
- 套餐删除为软禁用：DELETE 套餐设置 status=inactive，不影响已购买该套餐的商户的现有订阅（已生效的订阅继续有效直到到期）。
- 激活幂等：支付回调开通订阅按 order_id 幂等，重复回调不会创建重复订阅记录。
- 每商户至多一条 active：新订阅购买时的叠加/替换行为以 service 实现为准（可能延长现有订阅或创建新记录替代旧记录），操作前核对代码确认。
- Open API 密钥吊销即时生效：DELETE 密钥后该 AK/SK 的后续请求立即返回 4014，无需等待缓存过期。

====================================================================
4. 小程序端操作
====================================================================

- pages/subscription/buy/index：浏览套餐 → 下单支付 → 开通高级版。
- 玩法模板消费：发起抽奖流程中按玩法套用模板（见 03 创建页）。

====================================================================
5. API 速查
====================================================================

小程序端（/api/mp）：
GET  /api/mp/subscription/plans             套餐列表
POST /api/mp/subscription/order             下单（subscription 订单）
GET  /api/mp/subscription/mine              我的订阅
GET  /api/mp/subscription/has-feature       特性查询
GET  /api/mp/play-template-cases            玩法案例模板

管理端（/api/admin）：
GET|POST|PUT|DELETE /api/admin/subscription-plans(:id)
GET|POST|PUT|DELETE /api/admin/play-template-cases(:id)
GET|POST /api/admin/openapi/keys；DELETE /api/admin/openapi/keys/:id

Open API（/api/open/v1，AK/SK + HMAC）：
GET  /api/open/v1/activities
GET|POST /api/open/v1/activities/:id(/status)
GET  /api/open/v1/winning-records

====================================================================
6. 注意事项
====================================================================

- Open API 密钥 SK 只在创建响应中出现一次；丢失只能吊销重建。
- 未订阅 api_access 的商户调 Open API 返回 4016（未开通特性），不是签名问题，别按 4011 排查。
- 玩法模板 ConfigJSON 是抽奖创建入参快照：抽奖表单字段演进时模板内容不会自动迁移，老模板套用结果以当前 Create 校验为准。
- 每商户至多一条 active 订阅；重复购买的处理行为先核对 service 再答复用户。
- 特性码消费点分散（api_access 在网关、其余在对应业务），新增特性码是横跨三端的开发动作。
