--- todos: - id: "constants" content: "TrialRewardConstants:ACTIVITY_CODE + ext 键;移除 center CONFIG_KEY_*" status: pending - id: "model" content: "ActivityModel::findActiveByCode(activity_code, source, now)" status: pending - id: "resolver" content: "新增 TrialRewardActivityResolver,从 activity.ext 解析 threshold/cap" status: pending - id: "logic" content: "TrialFirstDepositLogic / queryRecord 改用 resolver,去掉 ShareConfig trialReward" status: pending - id: "center-cleanup" content: "删除 slot-center share_config activity.trialReward 块" status: pending - id: "seed-sql" content: "新增 activity 表 patch(trial_reward + ext JSON)" status: pending - id: "test-e2e" content: "补 resolver 单测 + 联调验证(不依赖 center 业务配置)" status: pending isProject: false --- # 试玩活动配置:center 迁出,按 activity_code 解析 ## 结论(对你问题的回应) **我同意你的方向**,且比当前联调实现更贴合项目既有模式: - **center 不应承载** `deposit_threshold`、`trial_withdrawal_cap`、`activity_id` 等业务参数;它只适合 `walletApiHost` 等基础设施。 - **活动服决定业务规则**:阈值、上限、开关、时间窗应来自 [`activity`](slot-activity/app/model/ActivityModel.php) 表(运营在 slot-admin 维护),与 [`DepositBonusLogic`](slot-activity/app/innerapi/logic/DepositBonusLogic.php) / [`SignInLogic`](slot-activity/app/api/logic/SignInLogic.php) 读 `ext` 的方式一致。 - **按 `activity_code` 定位**:代码里固定 `trial_reward`(你已选 PHP 常量),各环境 DB 里 `activity_id` 可以不同,**发版/切环境不依赖 center 改数字 ID**。 当前临时方案的问题: ```74:82:slot-center/config/share_config.php 'activity' => [ 'trialReward' => [ 'activity_id' => 9001, 'activity_code' => 'trial_reward', 'deposit_threshold' => 50, 'trial_withdrawal_cap' => 500, ], ], ``` [`TrialFirstDepositLogic`](slot-activity/app/innerapi/logic/TrialFirstDepositLogic.php) 通过 `ShareConfigService` 读上述键,与「活动服自治」冲突。 --- ## 目标架构 ```mermaid sequenceDiagram participant Pay as slot_pay participant Act as slot_activity participant DB as activity_table participant Wal as slot_wallet Pay->>Act: first_deposit(uid, source, order_no, ...) Act->>DB: findActiveByCode(trial_reward, source, now) DB-->>Act: activity_id + ext(deposit_threshold, cap, ...) Act->>Wal: clearTrialBonusForPool Act->>Act: persist trial_reward_record(uid, activity_id) ``` | 配置项 | 现位置 | 目标位置 | |---|---|---| | activity 身份 | center `trialReward.activity_id` | `activity.activity_code = trial_reward` → 得到 `activity_id` | | deposit_threshold | center | `activity.ext.deposit_threshold`(美元,入库 *1000) | | trial_withdrawal_cap | center | `activity.ext.trial_withdrawal_cap` | | activity_code 常量 | center | [`TrialRewardConstants::ACTIVITY_CODE`](slot-activity/app/constants/TrialRewardConstants.php) | center 保留:`walletApiHost`([`TrialWalletGatewayService`](slot-activity/app/service/wallet/TrialWalletGatewayService.php) 仍需要)。 --- ## 实现步骤 ### 1. 定义活动编码与 ext 键(slot-activity) 改 [`TrialRewardConstants.php`](slot-activity/app/constants/TrialRewardConstants.php): - 新增 `ACTIVITY_CODE = 'trial_reward'`(PHP 常量,跨环境稳定) - 新增 ext 字段键名常量:`EXT_DEPOSIT_THRESHOLD`、`EXT_TRIAL_WITHDRAWAL_CAP` 等 - **删除** 指向 center 的 `CONFIG_KEY_ACTIVITY_ID / CODE / DEPOSIT_THRESHOLD / WITHDRAWAL_CAP` - 保留 `DEFAULT_DEPOSIT_THRESHOLD`、`DEFAULT_WITHDRAWAL_CAP` 作为 **ext 缺省兜底**(与 SignIn 读 ext 缺字段时给默认值一致) ### 2. ActivityModel 增加按 code 查询 在 [`ActivityModel.php`](slot-activity/app/model/ActivityModel.php) 新增(对齐现有 `findMatchedSignInBySource` / `findActiveByIdForDeposit` 风格): ```php public static function findActiveByCode(string $activityCode, string $source, string $now): ?self ``` 条件:`activity_code` 精确匹配 + `status=1` + 时间窗 + `source_list` 命中(`all` 或 `$source`)。 可选后续:在 `slot-foundation` 增加 `ActivityType::TRIAL_WITHDRAWAL`,admin 筛选用;**本期不强制**,code 已足够唯一标识。 ### 3. 抽取活动配置解析(避免 Logic 堆 ext 解析) 新增小类,例如 [`TrialRewardActivityResolver.php`](slot-activity/app/service/trial/TrialRewardActivityResolver.php): - 输入:`source`(来自 [`TrialFirstDepositDto`](slot-activity/app/innerapi/dto/TrialFirstDepositDto.php)) - 输出:`activity_id`、`activity_code`(快照用)、`deposit_threshold`(*1000)、`trial_withdrawal_cap`(*1000) - 活动不存在/未启用/未命中渠道 → 抛业务异常或返回明确 reason(如 `activity_not_found`),**禁止静默用 center 默认值** 解析规则: - `activity_id` / `activity_code` 来自 DB 行 - 阈值从 `ext` 读,缺省用 `TrialRewardConstants::DEFAULT_*`,再 `MoneyTool::bigUnitToSmallUnit` ### 4. 改造 TrialFirstDepositLogic 改 [`TrialFirstDepositLogic.php`](slot-activity/app/innerapi/logic/TrialFirstDepositLogic.php): - `handleFirstDeposit()`:先 `resolveActivity($dto->source)`,再用返回的 `activity_id` 查/建 `trial_reward_record` - `persistPool()`:`activity_code`、阈值、cap 均来自 resolver,**移除所有 `ShareConfigService::get(trialReward.*)`** - `queryRecord()`:同样按 `ACTIVITY_CODE` resolve 后再 `findByUidActivity` - `resolveThresholdAmount()` / `resolveWithdrawalCap()`:改为接收已解析的 Activity 或 config DTO,不再读 center 失败语义建议: | reason | 含义 | |---|---| | `activity_not_found` | 无生效的 trial_reward 活动 | | `wallet_clear_failed` | wallet 清零失败(保持现有) | | `duplicate` / `success` | 不变 | ### 5. 移除 center 试玩业务配置 改 [`slot-center/config/share_config.php`](slot-center/config/share_config.php): - **删除整个 `activity.trialReward` 块**(不是只删 threshold) - 重启 center(本地 dev 已习惯 `php webman restart -d`) ### 6. 联调/上线数据:activity 表种子 新增 SQL 补丁,例如 [`slot-activity/db/patch_trial_reward_activity.sql`](slot-activity/db/patch_trial_reward_activity.sql)(插入/更新 `activity` 行,`activity_code=trial_reward`): ```json { "deposit_threshold": 50, "trial_withdrawal_cap": 500, "trial_bonus_amount": 20 } ``` 字段建议:`status=1`、`new_user_only=1`、`first_deposit_only=0`(首充事件另管 Pool)、`source_list=["all"]`、合理 `start_time/end_time`。`activity_id` 自增即可,**各环境可以不同**。 ### 7. 测试调整 - 单测 [`TrialFirstDepositStatusTest`](slot-activity/tests/Unit/Logic/TrialFirstDepositStatusTest.php):纯状态机不变 - 新增 resolver 单测:mock `ActivityModel` 或测 ext 解析私有方法(ext 有/无字段、美元→*1000) - 联调:在 **slotMysql.s_activity** 执行 activity 种子后,重跑首充 happy-path(不再依赖 center trialReward) --- ## 不变更范围(刻意不做) - **slot-pay**:仍只调 `/innerapi/trial-reward/first-deposit`,不传 `activity_id`(符合「pay 只报事件,activity 解析活动」) - **slot-wallet / slot-sdk**:无改动 - **slot-admin 后台表单**:本期可先用 SQL 种子 + ext JSON;完整 07 子需求的可视化配置可后续单独做 --- ## 风险与注意 1. **必须先有 activity 行**:删掉 center 配置后,若 DB 无 `trial_reward` 活动,首充会 `activity_not_found`(比误用 9001 更安全)。 2. **历史联调数据**:若 `trial_reward_record` 里是 `activity_id=9001`,新环境 activity 自增 ID 不同;新用户无影响,老测试 uid 需清数据或对齐 activity_id。 3. **ShareConfigService 仍保留**:仅用于 `walletApiHost` 等,与试玩业务解耦。