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

8.5 KiB
Raw Blame History


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.md01 表结构 / 02 抽奖 / 03 后台)。

关键决策(已确认 + 需注意的派生项)

  • 奖励类型按《奖池.md V1》原口径reward_type = 1 可提现释放 / 2 Bonus / 3 充值优惠券,券用 coupon_template_id(不对齐 foundation 数值、不引入 FS
    • 新建 app/constants/RewardPoolRewardTypeWITHDRAWABLE_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() 的加权累加逻辑下沉为可复用方法(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.phpproxyActivity() + *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 核心(数据 + 抽奖)

  • Modelapp/model/RewardPoolModel.phpapp/model/RewardPoolItemModel.php
    • 完整类 PHPDoc + @property(对齐 DDL COMMENT状态常量带中文注释。
    • 查询方法:findEnabledByPoolCode()listEnabledItemsByPoolId()sumEnabledWeight()existsItemCode() 等(命名表达意图,禁止 getData/getList
  • 常量:app/constants/RewardPoolConstants.phpstatus、reward_type 复用 foundation + COUPON
  • 抽奖业务逻辑(供本服务/业务内部调用,第一期不强制开 HTTPapp/innerapi/logic/RewardPoolDrawLogic.php::drawByPoolCode(string $poolCode): RewardPoolDrawResultEntity,编排文档 §8.1 十步:查池→校验启用→取启用且 weight>0 奖项→total_weight→加权随机→返回快照。只读、无副作用、无幂等幂等由业务方负责PHPDoc 写明)。
  • Entityapp/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() 返回):

  • Controllerapp/admin/controller/RewardPoolController.php(奖池 index/read/save/changeStatusapp/admin/controller/RewardPoolItemController.php(奖项 index/save/changeStatus/destroy/sort
  • Logicapp/admin/logic/RewardPoolLogic.phpapp/admin/logic/RewardPoolItemLogic.phpsearch/getList + 保存/启停/排序;多表写入走 activity 库事务)。
  • Validateapp/admin/validate/RewardPoolValidate.phpRewardPoolItemValidate.php,覆盖文档 §11.1~§11.4reward_type∈{1,2,3};非 COUPON 时 reward_amount>0 && coupon_template_id=0COUPON 时 reward_amount=0 && coupon_template_id>0 且校验模板存在;启用奖池需至少 1 个启用奖项且 weight 合计 > 0pool_code 创建后不可改)。
  • 路由:按现有 admin 路由约定接入 admin/reward-pool/*admin/reward-pool-item/*(与 admin/activity/* 同套路)。

四、slot-sdk

  • slot-sdk/src/service/activity/ActivityService.php 增方法POST/GET 到 admin/reward-pool/*admin/reward-pool-item/*(与现有 adminActivityIndex/adminSave/adminUpdate/adminChangeStatus/adminDestroy 命名同套路):rewardPoolIndex/Read/Save/ChangeStatusrewardPoolItemIndex/Save/ChangeStatus/Destroy/Sort

五、saas6.x 运营后台

后端 saas6.x/server

  • app/controller/operation/RewardPoolController.phpextends OperationController,方法加 #[Permission('奖池...','saimulti:operation:rewardPool:xxx')]proxyActivity() 统一封装)。
  • app/service/activity/RewardPoolGatewayService.php(封装 slotsdk ActivityClienthost 取 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),最终回复粘贴"检测结果"章节。

调用关系

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]