Files
cursor/plans/Reward Pool V1-2a311123.plan.md
ray zhou 2dd9f17da9 ok
2026-06-29 14:51:55 +08:00

115 lines
8.5 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.

<!-- 2a311123-9045-4246-b265-7e37ecfbf924 -->
---
todos:
- id: "ddl"
content: "编写 reward_pool / reward_pool_item 建表 DDLAUTO_INCREMENT 主键、status 1/2、可选审计字段"
status: pending
- id: "constants"
content: "新建 RewardPoolConstants / RewardPoolRewardType钱包类复用 foundation 数值,新增 COUPON=3"
status: pending
- id: "models"
content: "新建 RewardPoolModel、RewardPoolItemModel完整类 PHPDoc + @property + 查询方法)"
status: pending
- id: "draw-shared"
content: "下沉加权随机算法为可复用方法,供奖池与 TrialPrizeDrawService 共用"
status: pending
- id: "entity"
content: "新建 RewardPoolDrawResultEntity 及列表/汇总 Entity禁止 Logic 返回 array"
status: pending
- id: "draw-logic"
content: "实现 app/innerapi/logic/RewardPoolDrawLogic::drawByPoolCode只读、无副作用"
status: pending
- id: "admin-crud"
content: "slot-activity app/adminRewardPool(Item)Controller/Logic/Validatesaithink 范式)+ admin 路由"
status: pending
- id: "validate"
content: "奖池/奖项校验reward_type 约束 + 优惠券模板存在性 + 启用校验 + pool_code 不可改"
status: pending
- id: "sdk"
content: "ActivityService 增 admin/reward-pool(-item)/* SDK 方法adminXxx 同套路)"
status: pending
- id: "admin"
content: "saas6.x/server operation RewardPoolController + RewardPoolGatewayService 代理(#[Permission]"
status: pending
- id: "admin-vue"
content: "admin-vue 增奖池管理页面与菜单(奖池列表/编辑/启停 + 奖项配置页)"
status: pending
- id: "verify"
content: "运行 slot-backend-completion-report 门禁并粘贴检测结果"
status: pending
isProject: false
---
# 通用奖池 V1 实施方案
落点服务:`slot-activity`(核心 + 数据)、`slot-sdk`(跨服务客户端)、`saas6.x/server`(运营后台代理)+ `saas6.x/admin-vue`(后台前端)。沿用现有「活动管理」后台范式(`operation` 控制器 + `*GatewayService` 封装 slotsdk
> 注意:后台代码不在 `slot-admin`,而在独立仓库 `~/Documents/project/www/tenant/saas6.x`(用户明确指定)。
> 子需求文档SSOT 拆分):[docs/requirements/generic_reward_pool/README.md](docs/requirements/generic_reward_pool/README.md)01 表结构 / 02 抽奖 / 03 后台)。
## 关键决策(已确认 + 需注意的派生项)
- 奖励类型按《奖池.md V1》原口径`reward_type` = `1 可提现释放 / 2 Bonus / 3 充值优惠券`,券用 `coupon_template_id`(不对齐 foundation 数值、不引入 FS
- 新建 `app/constants/RewardPoolRewardType``WITHDRAWABLE_RELEASE=1` / `BONUS=2` / `COUPON=3`)。
- 业务方按抽奖结果发奖时,由**业务侧**显式把 `reward_type` 映射到钱包/券接口(如 `slot-foundation\RewardGrantRewardType` 或 user_coupon奖池本身不做发放。
- 表名冲突风险:现有 `docs/requirements/reward_pool/`(大转盘超集设计)也用 `reward_pool` / `reward_pool_item`。实施前需决策本 V1 是否改用独立表名或与之合并(见子需求 README「重要前提」
- 抽奖算法复用:把 [`TrialPrizeDrawService::pickWeightedRandom()`](slot-activity/app/service/trial/TrialPrizeDrawService.php) 的加权累加逻辑下沉为可复用方法(`random_int(1,total)` + `sort ASC,id ASC` 累加),奖池抽奖与转盘共用,不再写第二份。
- 运营后台(`saas6.x/server`)经 `slot_sdk` 调 slot-activity `innerapi`/`admin`,参照 `saas6.x/server/app/controller/operation/ActivityController.php` + `app/service/activity/ActivityGatewayService.php``proxyActivity()` + `*GatewayService` 范式,不直连 activity 库。
## 一、数据库reward_pool / reward_pool_item
按文档 §6 建表,修订:
- 主键改 `BIGINT UNSIGNED NOT NULL AUTO_INCREMENT`(与现有 Model "自增主键" 一致)。
- `status` 统一为 `1启用 2停用`(文档已是;与 prize_pool 的 enabled 1/0 不强行统一,但 Model 注释写清)。
- `reward_pool_item.pool_code` 为快照;约定 `pool_code` 创建后不可改(编辑接口禁止改 code保证 `(pool_id,item_code)` 快照一致。
- 可选增 `created_by`/`updated_by`(后台操作审计)。
## 二、slot-activity 核心(数据 + 抽奖)
- Model`app/model/RewardPoolModel.php``app/model/RewardPoolItemModel.php`
- 完整类 PHPDoc + `@property`(对齐 DDL COMMENT状态常量带中文注释。
- 查询方法:`findEnabledByPoolCode()``listEnabledItemsByPoolId()``sumEnabledWeight()``existsItemCode()` 等(命名表达意图,禁止 getData/getList
- 常量:`app/constants/RewardPoolConstants.php`status、reward_type 复用 foundation + COUPON
- 抽奖业务逻辑(供本服务/业务内部调用,第一期不强制开 HTTP`app/innerapi/logic/RewardPoolDrawLogic.php::drawByPoolCode(string $poolCode): RewardPoolDrawResultEntity`,编排文档 §8.1 十步:查池→校验启用→取启用且 `weight>0` 奖项→`total_weight`→加权随机→返回快照。只读、无副作用、无幂等幂等由业务方负责PHPDoc 写明)。
- Entity`app/entity/rewardpool/RewardPoolDrawResultEntity.php`抽中快照extends `BaseEntity`),列表用 `RewardPoolListEntity` / `RewardPoolItemListEntity` 包装(禁止 Logic 返回 array / `Xxx[]`)。
## 三、slot-activity 后台app/admin非 innerapi
沿用 `app/admin` saithink 范式(参照 `app/admin/controller/ActivityController.php` + `app/admin/logic/ActivityLogic.php` + `app/admin/validate/ActivityValidate.php`,基类 `app/admin/base/AdminController``$this->success()` 返回):
- Controller`app/admin/controller/RewardPoolController.php`(奖池 index/read/save/changeStatus`app/admin/controller/RewardPoolItemController.php`(奖项 index/save/changeStatus/destroy/sort
- Logic`app/admin/logic/RewardPoolLogic.php``app/admin/logic/RewardPoolItemLogic.php`search/getList + 保存/启停/排序;多表写入走 activity 库事务)。
- Validate`app/admin/validate/RewardPoolValidate.php``RewardPoolItemValidate.php`,覆盖文档 §11.1~§11.4reward_type∈{1,2,3};非 COUPON 时 `reward_amount>0 && coupon_template_id=0`COUPON 时 `reward_amount=0 && coupon_template_id>0` 且校验模板存在;启用奖池需至少 1 个启用奖项且 `weight` 合计 > 0`pool_code` 创建后不可改)。
- 路由:按现有 admin 路由约定接入 `admin/reward-pool/*``admin/reward-pool-item/*`(与 `admin/activity/*` 同套路)。
## 四、slot-sdk
- [slot-sdk/src/service/activity/ActivityService.php](slot-sdk/src/service/activity/ActivityService.php) 增方法POST/GET 到 `admin/reward-pool/*``admin/reward-pool-item/*`(与现有 `adminActivityIndex/adminSave/adminUpdate/adminChangeStatus/adminDestroy` 命名同套路):`rewardPoolIndex/Read/Save/ChangeStatus``rewardPoolItemIndex/Save/ChangeStatus/Destroy/Sort`
## 五、saas6.x 运营后台
后端 `saas6.x/server`
- `app/controller/operation/RewardPoolController.php`extends `OperationController`,方法加 `#[Permission('奖池...','saimulti:operation:rewardPool:xxx')]``proxyActivity()` 统一封装)。
- `app/service/activity/RewardPoolGatewayService.php`(封装 slotsdk `ActivityClient`host 取 `ShareConfigService::get('activityApiHost')`,与 `ActivityGatewayService` 同构)。
- 入参轻校验可放控制器或 `app/validate/`,复杂判断仍在 slot-activity 侧。
前端 `saas6.x/admin-vue`
- 运营管理 → 奖池管理页(列表/新增/编辑/启停/进入奖项配置)+ 奖项配置页(新增/编辑/启停/排序)。
- 新增对应 `api/` 请求模块与菜单/权限项(与活动页同套路)。
## 六、收尾自检(强制)
改完 `app/**/*.php` 后按 `agent-completion-gate` 执行 `~/.cursor/skills/slot-backend-completion-report/scripts/report.sh`(含 verify 门禁 + docker `php -l`),最终回复粘贴"检测结果"章节。
## 调用关系
```mermaid
flowchart LR
Vue[saas6.x admin-vue] --> Server[saas6.x server operation]
Server -->|slot_sdk ActivityClient| AdminApi[slot-activity app/admin reward-pool]
AdminApi --> AdminLogic[admin RewardPoolLogic / ItemLogic]
AdminLogic --> Model[(reward_pool / reward_pool_item)]
Biz[业务活动 Logic] -->|drawByPoolCode| Draw[RewardPoolDrawLogic]
Draw --> Model
Biz -->|按 reward_type 发放| Wallet[slot-wallet / user_coupon]
```