Files
cursor/projects/Users-ray-Documents-project-www-slot-backend/agent-transcripts/b919f3a2-082e-468e-ac59-85445eecdbcc/subagents/864ff1d5-4fe9-4681-a6b2-db0f9d7a1198.jsonl
2026-05-21 18:16:26 +08:00

23 lines
36 KiB
JSON
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.

{"role":"user","message":{"content":[{"type":"text","text":"<user_query>\n在 /Users/ray/Documents/project/www/slot/backend/slot_admin 这个 Webman PHP 管理后台后端工程里,需要梳理\"活动配置\"的存储与编辑接口。\n\n请回答以下问题并标出具体的文件路径与代码片段包含行号\n\n1. 活动相关的 controller / logic / model / validate / dto 文件路径分别在哪?(按 controller、logic、validate、dto、model、service 分类列出)\n2. 活动表数据库表名的字段结构是什么样特别关注活动类型字段名type / activity_type 等),是否有专门存放活动配置 JSON 的字段(如 config / extra / settings 等)。请提供 db/migration 或 model 文件中字段定义的位置。\n3. 创建 / 编辑活动的接口路由是什么请提供接口的入参字段定义validate 类里)。\n4. 现有的活动类型枚举type 列表 + 含义)在后端是怎么定义的?类型值有哪些?类型 11首充前免费余额定格与分档释放当前是否有任何配置 / 占位?\n5. 不同活动类型在后端是否会做差异化处理(例如不同类型用不同的 validate / dto / config 校验)?如果是,请给出示例代码。\n6. 项目使用的是 ThinkORM 还是其他 ORM多语言字段label / description一般怎么存\n7. 项目中是否已有\"按 activity_type 分发到不同 handler/strategy\"的现成模式?\n\n直接读 backend/slot_admin/app 下的源码,不要修改任何东西。返回时请用 markdown 列表形式给出关键文件路径、关键代码片段(用代码块包裹,附上文件路径和行号),以及你的总结结论。\n</user_query>"}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"梳理 slot_admin 中活动配置的存储与编辑接口,先从 app 目录检索相关 controller、logic、model 等文件。\n\n[REDACTED]"},{"type":"tool_use","name":"updateCurrentStep","input":{"current_step":"Searching activity config files"}},{"type":"tool_use","name":"Grep","input":{"pattern":"activit","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app","glob":"**/*","output_mode":"files_with_matches","-i":true}},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"**/*Activit*","target_directory":"/Users/ray/Documents/project/www/slot/backend/slot_admin"}},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type|ActivityType","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin","output_mode":"files_with_matches"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"Activity","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin","output_mode":"files_with_matches"}},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"**/*activity*","target_directory":"/Users/ray/Documents/project/www/slot/backend/slot_admin"}},{"type":"tool_use","name":"Grep","input":{"pattern":"活动","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app","output_mode":"files_with_matches"}},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"**/route*.php","target_directory":"/Users/ray/Documents/project/www/slot/backend/slot_admin"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/controller/ActivityController.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/validate/ActivityValidate.php"}},{"type":"tool_use","name":"Grep","input":{"pattern":"ActivityModel|activity_","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin","output_mode":"content","head_limit":80}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/config/route.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"ActivityController|ActivityService","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin","output_mode":"content"}},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"**/*Activity*","target_directory":"/Users/ray/Documents/project/www/slot/slot_lib"}},{"type":"tool_use","name":"Grep","input":{"pattern":"ActivityService|ActivityModel|activity","path":"/Users/ray/Documents/project/www/slot/slot_lib","glob":"**/*","output_mode":"files_with_matches","head_limit":40}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_lib/src/services/ActivityService.php"}},{"type":"tool_use","name":"Grep","input":{"pattern":"class Activity|ActivityType|activity\\.|table.*activity","path":"/Users/ray/Documents/project/www/slot","glob":"**/*.{php,sql}","output_mode":"files_with_matches","head_limit":60}},{"type":"tool_use","name":"Grep","input":{"pattern":"game/activity|Activity","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/plugin","output_mode":"content","head_limit":50}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"activity|Activity","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app","output_mode":"content","head_limit":120}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/ActivityController.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/model/common/ActivityConfigModel.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/entity/activity/ActivityConfigEntity.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"ActivityController|activity","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/plugin/saimulti","output_mode":"content","head_limit":80}},{"type":"tool_use","name":"Grep","input":{"pattern":"ACTIVITY_TYPE|type.*11|free_credits","path":"/Users/ray/Documents/project/www/slot","glob":"**/*.php","output_mode":"content","head_limit":100}},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"**/*recharge_gift*","target_directory":"/Users/ray/Documents/project/www/slot"}},{"type":"tool_use","name":"Grep","input":{"pattern":"s_recharge_gift_config","path":"/Users/ray/Documents/project/www/slot","output_mode":"files_with_matches"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_lib/src/common/const/Consts.php","limit":80}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/model/RechargeGiftConfigModel.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/controller/RechargeGiftConfigController.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/model/common/RechargeGiftConfigModel.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/logic/RechargeGiftConfigLogic.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/validate/RechargeGiftConfigValidate.php"}},{"type":"tool_use","name":"Grep","input":{"pattern":"RechargeGiftConfig|Activity","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin","glob":"**/*.sql"}},{"type":"tool_use","name":"Grep","input":{"pattern":"RechargeGiftConfig|ActivityController","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin","output_mode":"content","head_limit":60}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"game/activity|RechargeGiftConfig|Activity/","path":"/Users/ray/Documents/project/www/slot","output_mode":"content","head_limit":80}},{"type":"tool_use","name":"Grep","input":{"pattern":"s_recharge_gift_config","path":"/Users/ray/Documents/project/www/slot/slot_console/db/install.sql","output_mode":"content","-A":80}},{"type":"tool_use","name":"Grep","input":{"pattern":"TYPE_FREE_CREDITS|type.*11|FREE_CREDITS","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin","output_mode":"content"}},{"type":"tool_use","name":"Grep","input":{"pattern":"Handler|Strategy|activity_type","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app","output_mode":"content","head_limit":60}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"create table.*s_recharge_gift_config","path":"/Users/ray/Documents/project/www/slot","glob":"**/*.sql","output_mode":"content","-i":true}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue","limit":120}},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"**/service/activity/**","target_directory":"/Users/ray/Documents/project/www/slot"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/plugin/saimulti/basic/AdminController.php","limit":80}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type|type.*11|free_credits","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue","output_mode":"content","head_limit":80}},{"type":"tool_use","name":"Grep","input":{"pattern":"s_recharge_gift_config","path":"/Users/ray/Documents/project/www/slot","glob":"**/*.sql","output_mode":"content","head_limit":30}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue","offset":100,"limit":150}},{"type":"tool_use","name":"Grep","input":{"pattern":"innerapi/activity","path":"/Users/ray/Documents/project/www/slot/slot_console","output_mode":"content"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type","path":"/Users/ray/Documents/project/www/slot","glob":"**/*.{sql,php,json}","output_mode":"content","head_limit":50}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/model/BaseSourceModel.php","limit":60}},{"type":"tool_use","name":"Grep","input":{"pattern":"ext_config|goods","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/model","output_mode":"content"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/service/activity/BankruptcyService.php","limit":50}},{"type":"tool_use","name":"Grep","input":{"pattern":"free_credits|TYPE_FREE|type.*11","path":"/Users/ray/Documents/project/www/slot/docs","output_mode":"content","head_limit":40}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"ext_config|release_tier|free_credits","path":"/Users/ray/Documents/project/www/slot/slot_console/app/api/logic/FreeCreditsLogic.php","output_mode":"content","head_limit":60}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/api/logic/FreeCreditsLogic.php","offset":740,"limit":80}},{"type":"tool_use","name":"Grep","input":{"pattern":"\\$json|Json::|type.*json","path":"/Users/ray/Documents/project/www/slot/slot_console/app/model/common","output_mode":"content"}},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"**/config/route.php","target_directory":"/Users/ray/Documents/project/www/slot/slot_console"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/entity/activity/RedPacketActivityEntity.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"ext_config|type.*11|管理后台|slot_admin","path":"/Users/ray/Documents/project/www/slot/docs/requirements/首充前免费余额定格与分档释放需求文档.md","output_mode":"content","head_limit":50}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/api/logic/FreeCreditsLogic.php","limit":80}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/config/route.php"}},{"type":"tool_use","name":"Grep","input":{"pattern":"goods|ext_config|title|sub_title","path":"/Users/ray/Documents/project/www/slot/docs","glob":"**/*","output_mode":"content","head_limit":30}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/controller/PopController.php","limit":40}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"innerapi|ActivityController","path":"/Users/ray/Documents/project/www/slot/slot_console","glob":"**/*.php","output_mode":"content","head_limit":40}},{"type":"tool_use","name":"Grep","input":{"pattern":"recharge_gift_config","path":"/Users/ray/Documents/project/www/slot","output_mode":"content","head_limit":40}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/composer.json","limit":40}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/controller/ActivityNewUserController.php"}},{"type":"tool_use","name":"Grep","input":{"pattern":"SignInConfig|PopService","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app","output_mode":"files_with_matches"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"function save|function update|function destroy","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/plugin/saimulti/basic","output_mode":"content"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/tests/Support/FreeCreditsConfigStub.php"}},{"type":"tool_use","name":"Grep","input":{"pattern":"release_tier|win_threshold|package_amount","path":"/Users/ray/Documents/project/www/slot","glob":"**/*.php","output_mode":"content","head_limit":40}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/api/game/activity.js"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"activity_type","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin","glob":"**/*.sql","output_mode":"content"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/entity/activity/ActivityRuleEntity.php","limit":40}},{"type":"tool_use","name":"Grep","input":{"pattern":"switch.*type|case Consts::ACTIVITY","path":"/Users/ray/Documents/project/www/slot/backend/slot_admin","output_mode":"content"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_lib/src/dto/request/ActivityJoinRqDTO.php"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue","offset":240,"limit":120}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/model/common/ActivityConfigModel.php"}},{"type":"tool_use","name":"Grep","input":{"pattern":"Handler|Strategy|Factory","path":"/Users/ray/Documents/project/www/slot/slot_console/app","glob":"**/activity/**","output_mode":"content"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue","offset":355,"limit":60}},{"type":"tool_use","name":"Grep","input":{"pattern":"model_id|goods_type|ext_config","path":"/Users/ray/Documents/project/www/slot/slot_console/app/entity/activity/ActivityConfigEntity.php","output_mode":"content","head_limit":20}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/backend/slot_admin/app/model/RechargeGiftRuleModel.php","limit":40}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"updateCurrentStep","input":{"current_step":"Compiling activity config report","final_summary":"slot_admin 里「活动配置」主路径是 `ActivityController` → `slot_lib` 的 `ActivityService`CRUD 实际落在 slot_console 的 `s_recharge_gift_config` 表;类型字段为 `type`JSON 配置在 `goods` 与 `ext_config`。类型 11Free Credits仅在 slot_console 运行时实现,管理后台与 `Consts` 尚无占位。另有 legacy 的 `RechargeGiftConfigController` 直连库处理首充/红包。","completed_subtitle":"Mapped activity config architecture"}}]}}
{"role":"assistant","message":{"content":[{"type":"text","text":"## 总览\n\n`slot_admin` 里与「活动配置」相关的能力分 **两条线**\n\n1. **统一活动配置(主路径)**`ActivityController` → `slotLib\\services\\ActivityService` → **slot_console** `innerapi/activity/*`,读写表 **`s_recharge_gift_config`**(连接 `s_common` / `mysql`)。\n2. **Legacy 首充 / 红包**`RechargeGiftConfigController` + `RechargeGiftConfigLogic` **本地直连**同一张表,并写 **`s_recharge_gift_rule`**。\n\n类型 **11首充前免费余额定格与分档释放** 在 **slot_console** 有运行时逻辑,但在 **`slot_admin/app` 内无 Controller/Validate/前端表单占位**。\n\n---\n\n## 1. 文件路径(按分层,`slot_admin/app` 为主)\n\n### Controller\n\n| 路径 | 说明 |\n|------|------|\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/controller/ActivityController.php` | 统一活动 CRUD代理到 console |\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/controller/RechargeGiftConfigController.php` | Legacy首充(type=1)、红包(type=5) |\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/controller/ActivityNewUserController.php` | 红包拉新参与用户列表 |\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/controller/RechargeGiftPlayerController.php` | 充值活动玩家记录 |\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/controller/PopController.php` | 弹窗配置(注释写「充值活动」,走 `PopService` |\n\n### Logic\n\n| 路径 | 说明 |\n|------|------|\n| **无** `ActivityLogic` | `ActivityController` 不经过本地 Logic |\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/logic/RechargeGiftConfigLogic.php` | Legacy 首充/红包保存 |\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/logic/ActivityNewUserLogic.php` | 拉新用户 |\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/logic/RechargeGiftPlayerLogic.php` | 玩家活动(含红包 `extend_data` 解析) |\n\n### Validate\n\n| 路径 | 说明 |\n|------|------|\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/validate/ActivityValidate.php` | 统一活动(很薄,且有字段名不一致问题) |\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/validate/RechargeGiftConfigValidate.php` | Legacy 首充/红包 |\n\n### DTO\n\n| 路径 | 说明 |\n|------|------|\n| **slot_admin/app 内无活动配置 DTO** | `ActivityController` 直接用 `input()` |\n| `/Users/ray/Documents/project/www/slot/slot_lib/src/dto/request/ActivityJoinRqDTO.php` | 仅 `join` 用,非后台 CRUD |\n\n### Model\n\n| 路径 | 表 |\n|------|-----|\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/model/RechargeGiftConfigModel.php` | `s_recharge_gift_config` |\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/model/RechargeGiftRuleModel.php` | `s_recharge_gift_rule` |\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/model/ActivityNewUserModel.php` | `activity_new_user` |\n\n### Service不在 app 目录,但被 Controller 使用)\n\n| 路径 | 说明 |\n|------|------|\n| `/Users/ray/Documents/project/www/slot/slot_lib/src/services/ActivityService.php` | HTTP 代理:`innerapi/activity/{getPageList,add,delete,update}` |\n\n### Entity非 ORM Model用于结构化解构\n\n| 路径 | 说明 |\n|------|------|\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/entity/activity/RedPacketActivityEntity.php` | 红包玩家 `extend_data` |\n| `/Users/ray/Documents/project/www/slot/backend/slot_admin/app/entity/activity/RedPacketActivityDetailEntity.php` | 同上扩展 |\n\n**实际落库/业务编排(不在 slot_admin/app但与 Activity 强相关)**\n\n- `/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/ActivityController.php`\n- `/Users/ray/Documents/project/www/slot/slot_console/app/entity/activity/ActivityConfigEntity.php`\n- `/Users/ray/Documents/project/www/slot/slot_console/app/model/common/ActivityConfigModel.php`(与 `RechargeGiftConfigModel` 同表)\n- `/Users/ray/Documents/project/www/slot/slot_console/app/api/logic/FreeCreditsLogic.php`type=11\n\n---\n\n## 2. 活动表结构与 JSON 配置字段\n\n### 表名\n\n**`s_recharge_gift_config`**(库:`s_common`slot_admin Model 用 `protected $connection = 's_common'`\n\n仓库内 **没有** `CREATE TABLE s_recharge_gift_config` 的 migration字段以 Model/Entity 的 `@property` 为准。最完整的一份在 slot_console\n\n```10:37:/Users/ray/Documents/project/www/slot/slot_console/app/model/common/RechargeGiftConfigModel.php\n/**\n * s_recharge_gift_config 充值活动主配置表\n * @property integer $id (主键)\n * @property string $show_start_time 活动展示开始时间\n * @property string $title 活动标题\n * @property string $show_end_time 活动展示结束时间\n * @property string $start_time 返利生效开始时间\n * @property string $end_time 返利结束时间\n * @property integer $red_dot_type 红点类型 1每次登录 2每日\n * @property integer $status 2关闭 1开启\n * @property integer $red_dot 打开红点 1:开启 2:关闭\n * @property integer $type 活动类型1首冲 2二充 3 三充4普通5:红包拉新 6:每日优惠 7:注册 8:下载 9:兑换码 10:破产保护\n * ...\n * @property array|null $ext_config json格式\n * @property string $source\n */\n```\n\n`ActivityConfigEntity` 还声明了 JSON 类字段:\n\n```33:52:/Users/ray/Documents/project/www/slot/slot_console/app/entity/activity/ActivityConfigEntity.php\n * @property integer $type 活动类型1首冲 2二充 3 三充4普通\n * ...\n * @property string $title 标题\n * @property string $sub_title 副标题\n * @property string $help 规则说明\n * ...\n * @property Json $goods 赠送项\n * @property Json $ext_config 赠送项\n```\n\n### 关键字段\n\n| 字段 | 含义 |\n|------|------|\n| **`type`** | 活动类型(不是 `activity_type``activity_type` 在订单表) |\n| **`goods`** | JSON档位赠送`id/amount/bonus` 等) |\n| **`ext_config`** | JSON按类型扩展配置每日优惠、兑换码、破产保护、Free Credits 等) |\n| `title` / `sub_title` / `help` | 文案(普通字符串列,非多语言 JSON |\n| `model_id` / `source` | 游戏模型与渠道 |\n| `show_start_time` / `show_end_time` | 展示时间(后台表单 `show_time` 数组写入) |\n\n### type=11 关联表(玩家状态,非活动主表)\n\n`/Users/ray/Documents/project/www/slot/slot_console/db/install.sql` 定义 `free_credits_player`、`free_credits_package``activity_id` 指向 `s_recharge_gift_config.id`。\n\n---\n\n## 3. 创建 / 编辑接口路由与入参\n\n### Webman 约定路由(无显式 route 注册)\n\n前端 API`/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/api/game/activity.js`\n\n| 操作 | 方法 | 路径 |\n|------|------|------|\n| 列表 | GET | `/game/activity/index` |\n| 创建 | POST | `/game/activity/save` |\n| 编辑配置 | POST | `/game/activity/update?id={id}`(带 `updateData=1` |\n| 改状态 | POST | `/game/activity/update`(无 `updateData` |\n| 删除 | DELETE | `/game/activity/destroy` |\n\nController 实现:\n\n```51:102:/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/controller/ActivityController.php\n public function index(Request $request): Response\n {\n $where = $request->more([\n ['model_id', ''],\n ['source', ''],\n ...\n ]);\n $data = $this->service->getPageList([...]);\n return $this->success($data);\n }\n\n public function save(Request $request): Response\n {\n $this->checkData('save', input());\n $result = $this->service->add(input());\n ...\n }\n\n public function update(Request $request, $id): Response\n {\n $this->checkData('update', input());\n $result = $this->service->update(input());\n ...\n }\n```\n\nConsole 侧根据 `updateData` 分支:\n\n```35:42:/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/ActivityController.php\n public function update(Request $request): \\support\\Response\n {\n if(!empty(input('updateData'))) {\n return $this->success($this->getActivityConfigEntity($request)->updateActivityConfig(input()));\n }\n return $this->success($this->getActivityConfigEntity($request)->updateStatus(input()));\n }\n```\n\n### `ActivityValidate`slot_admin实际校验很弱\n\n```19:62:/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/validate/ActivityValidate.php\n protected $rule = [\n 'id' => 'require',\n 'model_id' => 'require',\n 'type' => 'require',\n 'show_start_time' => 'require',\n 'show_end_time' => 'require',\n 'status' => 'require',\n ];\n protected $scene = [\n 'save' => [\n 'model_id',\n 'type',\n 'show_time', // rule 里是 show_start_time/show_end_time不一致\n 'modelId',\n 'status',\n ],\n 'update' => [\n 'id',\n 'model_id',\n 'modelId',\n 'status',\n ],\n ];\n```\n\n**真实写入字段**由 console `ActivityConfigEntity::updateConfig()` 决定(前端 `edit.vue` 提交):\n\n- 通用:`model_id`/`modelId`, `source`, `type`, `show_time[]`, `status`, `sort_num`, `goods_type`, `title`, `sub_title`, `help`, `goods[]`, `ext_config`, `reg_end_h` 等\n- 创建走 `addActivityConfig` → `updateConfig(new ActivityConfigEntity(), $params)`\n\n```527:570:/Users/ray/Documents/project/www/slot/slot_console/app/entity/activity/ActivityConfigEntity.php\n private function updateConfig(ActivityConfigEntity $activityConfig, $params)\n {\n $extConfig = $params['ext_config'] ?? [];\n ...\n $activityConfig->type = $params['type'];\n $activityConfig->show_start_time = $params['show_time'][0];\n $activityConfig->show_end_time = $params['show_time'][1];\n ...\n $activityConfig->title = $params['title'] ?? '';\n $activityConfig->sub_title = $params['sub_title'] ?? '';\n $activityConfig->help = $params['help'] ?? '';\n $activityConfig->ext_config = $extConfig;\n if (!empty($params['goods'])) {\n $activityConfig->goods = array_map(...);\n }\n $activityConfig->model_id = $params['modelId'] ?? '';\n return $activityConfig->save();\n }\n```\n\n### Legacy 路由(本地直连 DB\n\n| 路径 | 说明 |\n|------|------|\n| GET `/game/RechargeGiftConfig/index` | type=1 列表 |\n| POST `/game/RechargeGiftConfig/saveFirstRecharge` | 首充创建 |\n| GET `/game/RechargeGiftConfig/redPacket` | type=5 列表 |\n| POST `/game/RechargeGiftConfig/saveRedPacket` | 红包创建 |\n| POST `/game/RechargeGiftConfig/updateRedPacket` | 红包更新 |\n\n`saveFirstRecharge` 场景字段见 `RechargeGiftConfigValidate` 的 `saveFirstRecharge` / `saveRedPacket` scene。\n\n---\n\n## 4. 活动类型枚举与 type=11\n\n### `slot_lib` 常量19**无 10/11**\n\n```15:25:/Users/ray/Documents/project/www/slot/slot_lib/src/common/const/Consts.php\n const ACTIVITY_TYPE_FIRST_RECHARGE = 1; //首次充值\n const ACTIVITY_TYPE_SECOND_RECHARGE = 2;\n const ACTIVITY_TYPE_THIRD_RECHARGE = 3;\n const ACTIVITY_TYPE_COMMON = 4;\n const ACTIVITY_TYPE_RED_PACKET = 5; //红包拉新\n const ACTIVITY_TYPE_DISCOUNT = 6; //每日优惠\n const ACTIVITY_TYPE_REG = 7; //注册赠送\n const ACTIVITY_TYPE_DOWNLOAD = 8; //下载赠送\n const ACTIVITY_TYPE_EXCHANGE_CODE = 9; //兑换码赠送\n```\n\n### slot_console `RechargeGiftConfigModel`(含 10、11\n\n```64:71:/Users/ray/Documents/project/www/slot/slot_console/app/model/common/RechargeGiftConfigModel.php\n const TYPE_FIRST_RECHARGE = 1;\n const TYPE_RED_PACKET = 5;\n const TYPE_BRANKRUPTCY = 10; // 破产保护\n /** 首充前免费余额定格与分档释放;配置存 ext_config玩家状态见 free_credits_* 表 */\n const TYPE_FREE_CREDITS = 11;\n```\n\n### 管理端字典\n\n前端用 `dict=\"activity_type\"``/Users/ray/Documents/project/www/slot/backend/slot_admin_vue/src/views/game/activity/edit.vue`),数据来自 **系统字典表**`sm_system_dict_data`**不在 slot_admin 源码 SQL 中**。\n\n### type=11 在 slot_admin 的状态\n\n| 层级 | 状态 |\n|------|------|\n| `slot_admin/app` | **无** type=11 的 Validate、Logic、Controller 分支、Model 常量 |\n| 管理前端 `edit.vue` | 仅有 type **6/9/10** 专用表单项,**无 type=11 表单** |\n| `slot_console` | `FreeCreditsLogic` + `getFreeCreditsActiveInfo()` 读 `type=11` 的 `ext_config` |\n| 配置键(运行时,非后台表单) | `win_threshold`, `recharge_unlock_amount`, `first_cash_amount`, `package_amount` 及 `*_qf` 后缀 |\n\n```26:36:/Users/ray/Documents/project/www/slot/slot_console/app/api/logic/FreeCreditsLogic.php\n /** 首页 Withdraw 解锁默认门槛... ext_config.win_threshold */\n /** 累计充值解锁第一档... ext_config.recharge_unlock_amount */\n /** 第一档免打码提现... ext_config.first_cash_amount */\n /** 后续档位默认拆分... ext_config.package_amount */\n```\n\n**结论**type=11 可在 DB 手工插 `s_recharge_gift_config``type=11` + `ext_config` JSON并由 C 端消费,但 **管理后台尚无专用配置/编辑占位**。\n\n---\n\n## 5. 是否按类型差异化处理?\n\n### slot_admin\n\n- **统一活动**:几乎无类型分支,全交给 console。\n- **Legacy**`RechargeGiftConfigLogic` 按 `TYPE_FIRST_RECHARGE` / `TYPE_RED_PACKET` 写不同字段和 `s_recharge_gift_rule`。\n\n```88:115:/Users/ray/Documents/project/www/slot/backend/slot_admin/app/game/logic/RechargeGiftConfigLogic.php\n if ($item['type'] == RechargeGiftConfigModel::TYPE_FIRST_RECHARGE) {\n $info = RechargeGiftRuleModel::getOne($item['id']);\n $item['reward_coin'] = showNumberFormat($info->gift_number);\n }\n...\n if ($item['type'] == RechargeGiftConfigModel::TYPE_RED_PACKET) {\n ...\n $item['rule']['gift_min_amount_qf'] = ...\n }\n```\n\n- **无** 按 `activity_type` 的 Validate/DTO 分文件;**无** 独立 handler 注册表。\n\n### slot_console真正按 type 分支)\n\n`ActivityConfigEntity` 中大量 `switch ($activityType)` / `if ($type == ...)`,例如组装礼包、每日优惠、兑换码:\n\n```339:397:/Users/ray/Documents/project/www/slot/slot_console/app/entity/activity/ActivityConfigEntity.php\n switch ($activityType) {\n case Consts::ACTIVITY_TYPE_FIRST_RECHARGE:\n ...\n break;\n case Consts::ACTIVITY_TYPE_COMMON:\n ...\n break;\n case Consts::ACTIVITY_TYPE_DISCOUNT:\n $activityInfo['ext_gift_coin'] = $activity['ext_config']['gift_amount'] ?? 2000;\n ...\n break;\n case Consts::ACTIVITY_TYPE_EXCHANGE_CODE;\n $exchangeList = $activity['ext_config']['exchangeList'] ?? [];\n ...\n break;\n }\n```\n\ntype=10 破产保护走独立 Service`/Users/ray/Documents/project/www/slot/slot_console/app/service/activity/BankruptcyService.php`(读 `ext_config.times/amount/balance`)。\n\ntype=11 走独立 Logic`FreeCreditsLogic`**不是** `ActivityConfigEntity` 的 switch 分支)。\n\n---\n\n## 6. ORM 与多语言字段\n\n### ORM\n\n- 项目使用 **`webman/think-orm`**ThinkORM见 `slot_admin/composer.json` 的 `\"webman/think-orm\": \"^2.1\"`。\n- Model 基类:`think\\Model`;活动配置在 console 侧还用 **`think\\Entity`**`ActivityConfigEntity`)。\n\n### 多语言\n\n- 活动文案字段为 **`title`、`sub_title`、`help` 单列字符串**,按 `source` / `model_id` 区分渠道,**不是** `label_en` / `description_json` 这类 i18n 结构。\n- 列表展示类型名依赖字典 **`activity_type`**(后台字典配置),不是代码内枚举表。\n\n---\n\n## 7. 是否存在「按 activity_type 分发 handler/strategy」模式\n\n### slot_admin/app\n\n**没有** 统一的 `ActivityHandler` / `Strategy` 工厂。\n\n### slot_console\n\n有 **按活动拆分的 Service 类**,但是 **点对点调用**,不是注册表分发:\n\n- `app/service/activity/BankruptcyService.php`type=10\n- `app/service/activity/RedPacketRegisterService.php`type=5\n- `app/service/activity/SignActivityService.php`\n- `FreeCreditsLogic`type=11\n\n主配置 CRUD 集中在 **`ActivityConfigEntity` 的 switch-case**C 端列表还会 `whereNotIn('type', [5, 10])` 过滤部分类型。\n\n**没有** 形如 `ActivityHandlerRegistry::get($type)->handle()` 的现成模式。\n\n---\n\n## 总结结论\n\n1. **主活动配置**`slot_admin` 只有薄 Controller + Validate**Model/Logic 在 slot_console**;通过 `ActivityService` 调 `innerapi/activity`。\n2. **存储**:单表 **`s_recharge_gift_config`**,类型字段 **`type`**JSON 为 **`goods` + `ext_config`**;文案在 **`title/sub_title/help`**。\n3. **路由**`/game/activity/{index,save,update,destroy}`Legacy 首充/红包走 `/game/RechargeGiftConfig/*`。\n4. **类型枚举**`Consts` 仅 1910/11 在 `RechargeGiftConfigModel`**type=11 无管理端占位**,仅有 console 运行时 + `free_credits_*` 玩家表。\n5. **差异化**console 侧 `switch(type)` + 少数独立 Service/Logicadmin 侧几乎无类型分支。\n6. **ORM**ThinkORM**多语言非 JSON 字段设计**。\n7. **Handler 模式**:无统一 strategy仅有分散 Service + Entity 内 switch。\n\n若你要在管理后台落地 type=11需要新增字典项、`edit.vue` 表单项、`ActivityValidate` 规则,以及 console `updateConfig` 对 Free Credits `ext_config` 的校验/格式化(可参考 type=10 的 `ext_config.amount/balance` 处理方式)。\n\n[REDACTED]"}]}}