{"role":"user","message":{"content":[{"type":"text","text":"\nExplore the slot_console codebase at /Users/ray/Documents/project/www/slot/slot_console to understand the Free Credits (首充前免费余额定格与分档释放) feature data model and innerapi.\n\nSpecifically, I need:\n\n1. **Data tables**: Find all DB tables/Models related to Free Credits (activity type 11). Common patterns:\n - User Free Credits Pool (user_free_credits / s_free_credits_pool / similar)\n - Tier/Package list (free_credits_tier / package)\n - Activity log\n Show:\n - Full file paths to Model files (e.g., `slot_console/app/.../FreeCreditsPoolModel.php`)\n - Table name & key columns (with type/comment when present)\n - Status enums (e.g., what values 'frozen', 'first_cash_ready', 'completed' map to; tier states locked/ready/claimed/completed)\n - Total amount field name (frozen_amount?), withdrawn amount, claimed amount, etc.\n - Created_at / completed_at fields\n\n2. **FreeCreditsLogic class**: Read this file fully (likely under `slot_console/app/.../logic/FreeCreditsLogic.php` or app/.../activity/). Show class methods and what data they expose.\n\n3. **Existing innerapi for activity stats**: Look in `slot_console/app/innerapi/controller/` (or similar) for any existing activity statistics endpoints (search for 'statistics', 'stats', '统计', 'list users' patterns). Get a sample existing innerapi controller method to understand the pattern.\n\n4. **Activity config model**: Confirm `RechargeGiftConfigModel::TYPE_FREE_CREDITS = 11` and show the ext_config keys actually used at runtime (`*_qf` keys).\n\nReturn: full file paths, table schemas (column names + comments), enum values, and one example existing innerapi controller method to mimic. Be very thorough — this will be used to design backend statistics API.\n"}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"Exploring the slot_console codebase for Free Credits data models, logic, and innerapi patterns.\n\n[REDACTED]"},{"type":"tool_use","name":"updateCurrentStep","input":{"current_step":"Exploring Free Credits models"}},{"type":"tool_use","name":"Grep","input":{"pattern":"free_credits|FreeCredits|TYPE_FREE_CREDITS|activity.*11","path":"/Users/ray/Documents/project/www/slot/slot_console","-i":true,"output_mode":"files_with_matches"}},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"**/*FreeCredits*","target_directory":"/Users/ray/Documents/project/www/slot/slot_console"}},{"type":"tool_use","name":"Grep","input":{"pattern":"TYPE_FREE_CREDITS|_qf","path":"/Users/ray/Documents/project/www/slot/slot_console","output_mode":"content","head_limit":80}},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"**/innerapi/**/*.php","target_directory":"/Users/ray/Documents/project/www/slot/slot_console"}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/db/install.sql"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/model/common/FreeCreditsPlayerModel.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/model/common/FreeCreditsPackageModel.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/model/common/RechargeGiftConfigModel.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/api/logic/FreeCreditsLogic.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/FreeCreditsController.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/ActivityController.php"}},{"type":"tool_use","name":"Grep","input":{"pattern":"statistics|stats|统计|activity.*log|ActivityLog","path":"/Users/ray/Documents/project/www/slot/slot_console","-i":true,"output_mode":"content","head_limit":60}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"FreeCredits|free-credits|free_credits","path":"/Users/ray/Documents/project/www/slot/slot_console/config","output_mode":"content"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/config/free_credits.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/api/controller/FreeCreditsController.php"}},{"type":"tool_use","name":"Grep","input":{"pattern":"win_threshold|first_cash_amount|package_amount|recharge_unlock|subsequent_min|max_unlock","path":"/Users/ray/Documents/project/www/slot/slot_console","output_mode":"content","head_limit":50}},{"type":"tool_use","name":"Grep","input":{"pattern":"free_credits|FreeCredits","path":"/Users/ray/Documents/project/www/slot/slot_console","glob":"**/route*.php","output_mode":"content"}},{"type":"tool_use","name":"Grep","input":{"pattern":"innerapi.*FreeCredits|FreeCredits.*statistics","path":"/Users/ray/Documents/project/www/slot/slot_console","output_mode":"content"}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"**/*route*","target_directory":"/Users/ray/Documents/project/www/slot/slot_console"}},{"type":"tool_use","name":"Grep","input":{"pattern":"FreeCreditsController|freeCredits|free-credits","path":"/Users/ray/Documents/project/www/slot/slot_console","output_mode":"content"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/BaseController.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/doc/首充前免费余额定格与分档释放需求部署说明.md","limit":120}},{"type":"tool_use","name":"Grep","input":{"pattern":"free_credits|freeCredits","path":"/Users/ray/Documents/project/www/slot/slot_wallet","output_mode":"files_with_matches","head_limit":20}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/config/route.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/SignController.php","limit":80}},{"type":"tool_use","name":"Grep","input":{"pattern":"ext_config|TYPE_FREE_CREDITS|type.*11","path":"/Users/ray/Documents/project/www/slot/docs","glob":"**/*免费*","output_mode":"content","head_limit":40}},{"type":"tool_use","name":"Grep","input":{"pattern":"win_threshold_qf|first_cash_amount_qf","path":"/Users/ray/Documents/project/www/slot/docs","output_mode":"content","head_limit":30}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"innerapi|free-credits|FreeCredits","path":"/Users/ray/Documents/project/www/slot/slot_console","glob":"**/*","output_mode":"content","head_limit":80}},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"**/plugin.php","target_directory":"/Users/ray/Documents/project/www/slot/slot_console"}},{"type":"tool_use","name":"Grep","input":{"pattern":"statistics|/innerapi","path":"/Users/ray/Documents/project/www/slot/slot_lib","output_mode":"content","head_limit":40}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Glob","input":{"glob_pattern":"config/*.php","target_directory":"/Users/ray/Documents/project/www/slot/slot_console"}},{"type":"tool_use","name":"Grep","input":{"pattern":"free-credits|free_credits|FreeCredits","path":"/Users/ray/Documents/project/www/slot/docs/requirements","output_mode":"content","head_limit":50}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/command/event/FreeCreditInitEvent.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/admin/DashboardController.php","limit":60}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"ext_config|win_threshold|first_cash|package_amount|recharge_unlock|subsequent_min|max_unlock","path":"/Users/ray/Documents/project/www/slot/docs/requirements/首充前免费余额定格与分档释放需求文档.md","output_mode":"content","head_limit":60}},{"type":"tool_use","name":"Grep","input":{"pattern":"free_credits_player|free_credits_package|s_recharge_gift","path":"/Users/ray/Documents/project/www/slot/docs","output_mode":"content","head_limit":40}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/config/middleware.php"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/config/app.php","limit":80}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/docs/requirements/首充前免费余额定格与分档释放需求文档.md","offset":980,"limit":150}},{"type":"tool_use","name":"Grep","input":{"pattern":"ext_config|千分位|_qf","path":"/Users/ray/Documents/project/www/slot/docs/requirements/首充前免费余额定格与分档释放需求文档.md","output_mode":"content","head_limit":40}},{"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":"free_credits|FreeCredits","path":"/Users/ray/Documents/project/www/slot/slot_center","output_mode":"files_with_matches","head_limit":15}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"TYPE_FREE_CREDITS|free.?credits|11","path":"/Users/ray/Documents/project/www/slot/slot_console/app/entity","output_mode":"content","head_limit":30}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/command/event/RechargeEvent.php","limit":80}},{"type":"tool_use","name":"Grep","input":{"pattern":"free-credits|freeCredits","path":"/Users/ray/Documents/project/www/slot/slot_console","glob":"**/*.md","output_mode":"content"}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/app/napi/controller/LobbyController.php","limit":40}},{"type":"tool_use","name":"Grep","input":{"pattern":"class BaseController","path":"/Users/ray/Documents/project/www/slot/slot_lib","output_mode":"files_with_matches"}},{"type":"tool_use","name":"Read","input":{"path":"/Users/ray/Documents/project/www/slot/slot_console/tests/Unit/FreeCreditsLogicConfigTest.php"}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"free_credits","path":"/Users/ray/Documents/project/www/slot/slot_wallet/app","output_mode":"content","head_limit":40}},{"type":"tool_use","name":"updateCurrentStep","input":{"current_step":"Compiling exploration report","final_summary":"Free Credits in slot_console uses two s_common tables plus s_recharge_gift_config (type 11), with statistics/list innerapi already implemented. No dedicated activity log table—state lives in player/package rows and wallet biz_ids.","completed_subtitle":"Mapped Free Credits data model"}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"[REDACTED]"},{"type":"tool_use","name":"Grep","input":{"pattern":"FreeCredits|freeCredits","path":"/Users/ray/Documents/project/www/slot/slot_wallet","output_mode":"content","head_limit":25}}]}} {"role":"assistant","message":{"content":[{"type":"text","text":"# Free Credits in `slot_console` — Data Model & InnerAPI Reference\n\nExploration of `/Users/ray/Documents/project/www/slot/slot_console` for activity type **11** (首充前免费余额定格与分档释放).\n\n---\n\n## 1. Data tables & models\n\nThere is **no dedicated Free Credits activity-log table** in `slot_console`. State is in two `s_common` tables plus the shared activity config table. Wallet/pay side effects use `slot_wallet` / `slot_pay` (not modeled in console).\n\n### Model file paths\n\n| Model | Path |\n|-------|------|\n| Player pool | `/Users/ray/Documents/project/www/slot/slot_console/app/model/common/FreeCreditsPlayerModel.php` |\n| Tier/package | `/Users/ray/Documents/project/www/slot/slot_console/app/model/common/FreeCreditsPackageModel.php` |\n| Activity config | `/Users/ray/Documents/project/www/slot/slot_console/app/model/common/RechargeGiftConfigModel.php` |\n\nDDL source: `/Users/ray/Documents/project/www/slot/slot_console/db/install.sql`\n\n---\n\n### `s_common.free_credits_player` (player pool)\n\n| Column | Type | Comment |\n|--------|------|---------|\n| `id` | bigint unsigned PK | 主键 |\n| `activity_id` | bigint unsigned | 关联 `s_recharge_gift_config.id` |\n| `uid` | bigint unsigned | 用户ID |\n| `source` | varchar(64) | 渠道 |\n| `home_withdraw_unlocked` | tinyint unsigned | 首页 Withdraw 是否已解锁 0否 1是 |\n| `frozen_amount_qf` | bigint unsigned | **首充时定格金额**,千分位 |\n| `first_cash_amount_qf` | bigint unsigned | **第一档免打码提现金额**,千分位 |\n| `first_recharge_order_id` | varchar(64) | 触发定格的首笔充值订单号 |\n| `first_recharge_time` | datetime nullable | **首笔充值成功时间**(定格时间) |\n| `first_cashout_order_id` | varchar(64) | 第一档独立提现订单号 |\n| `status` | tinyint unsigned | 主状态(见 enums below) |\n| `completed_time` | datetime nullable | **全部完成时间** |\n| `create_time` | datetime | 创建时间 |\n| `update_time` | datetime | 更新时间 |\n\n**Unique:** `(activity_id, uid)` \n**Indexes:** `uid`, `(activity_id, status)`, `first_recharge_order_id`\n\n**No columns** for aggregate withdrawn/claimed totals or cumulative recharge — those are derived from `free_credits_package` sums or wallet stats at read time.\n\n---\n\n### `s_common.free_credits_package` (tiers)\n\n| Column | Type | Comment |\n|--------|------|---------|\n| `id` | bigint unsigned PK | 主键 |\n| `player_id` | bigint unsigned | `free_credits_player.id` |\n| `activity_id` | bigint unsigned | 关联活动配置 |\n| `uid` | bigint unsigned | 用户ID |\n| `package_no` | int unsigned | 档位序号,从1开始 |\n| `package_type` | tinyint unsigned | 1第一档免打码提现 2后续释放档 |\n| `amount_qf` | bigint unsigned | **档位金额**,千分位 |\n| `status` | tinyint unsigned | 档位状态(见 enums) |\n| `withdraw_order_id` | varchar(64) | 第一档提现订单号 |\n| `claim_biz_id` | varchar(64) nullable | 后续档 Claim 幂等业务号 |\n| `unlocked_time` | datetime nullable | 解锁时间 |\n| `claimed_time` | datetime nullable | **领取时间**(release claim) |\n| `completed_time` | datetime nullable | **完成时间** |\n| `create_time` / `update_time` | datetime | 创建/更新 |\n\n**Unique:** `(player_id, package_no)`, `(claim_biz_id)`\n\n**Amount semantics for stats:**\n- **First cashout (withdrawn):** `package_type=1` AND `status=3` (completed) → sum `amount_qf`\n- **Release claimed:** `package_type=2` AND `status=3` → sum `amount_qf`\n- **Pending release:** often computed as `frozen_amount_qf - first_cashout_completed - release_claimed` (see existing innerapi)\n\n---\n\n### `s_recharge_gift_config` (activity config, type 11)\n\n**Model:** `RechargeGiftConfigModel` → table `s_recharge_gift_config` (not in `install.sql`; shared recharge-gift table).\n\n**Confirmed constant:**\n\n```70:71:/Users/ray/Documents/project/www/slot/slot_console/app/model/common/RechargeGiftConfigModel.php\n /** 首充前免费余额定格与分档释放;配置存 ext_config,玩家状态见 free_credits_* 表 */\n const TYPE_FREE_CREDITS = 11;\n```\n\nLookup: `RechargeGiftConfigModel::getFreeCreditsActiveInfo($source)` — `status=1`, `type=11`, time window, source priority.\n\nRelevant config columns: `id`, `type`, `status`, `start_time`, `end_time`, `source`, `ext_config` (JSON), `title`, etc.\n\n---\n\n### Player main status (`FreeCreditsPlayerModel`)\n\n| Value | Constant | Meaning |\n|------:|----------|---------|\n| -1 | `STATUS_NOT_VISIBLE` | C端/API only,非库字段 |\n| 0 | `STATUS_NOT_STARTED` | 活动未开始 |\n| 1 | `STATUS_HOME_UNLOCKED` | 首页 Withdraw 已解锁,未首充定格 |\n| 2 | `STATUS_FROZEN` | 已首充定格 |\n| 3 | `STATUS_DEPOSIT_PENDING` | 等待累计充值解锁第一档 |\n| 4 | `STATUS_FIRST_CASH_READY` | 第一档可提现 |\n| 5 | `STATUS_FIRST_CASH_PROCESSING` | 第一档提现处理中 |\n| 6 | `STATUS_FIRST_CASH_DONE` | 第一档已提现 |\n| 7 | `STATUS_RELEASING` | 后续档位释放中 |\n| 10 | `STATUS_COMPLETED` | 全部档位已完成 |\n\n`home_withdraw_unlocked`: `0` = locked, `1` = unlocked.\n\n---\n\n### Package status (`FreeCreditsPackageModel`)\n\n| Value | Constant | Meaning |\n|------:|----------|---------|\n| 0 | `STATUS_LOCKED` | 未解锁 |\n| 1 | `STATUS_READY` | 可操作 |\n| 2 | `STATUS_PROCESSING` | 处理中 |\n| 3 | `STATUS_COMPLETED` | 已完成 |\n| 4 | `STATUS_FAILED` | 失败可重试 |\n| 5 | `STATUS_REJECTED` | 风控/审核拒绝 |\n\n**Package type:** `1` = `TYPE_FIRST_CASH`, `2` = `TYPE_RELEASE`.\n\n---\n\n### Related non-table artifacts\n\n- **Env:** `FREE_CREDITS_ENROLL_REG_AFTER` — reg-time gate (`config/free_credits.php`, `FreeCreditsLogic::isEligibleByRegTime`)\n- **MQ types:** `FreeCreditsFirstCashoutSuccess/Fail/Rejected` in `MQBusEntity`\n- **Wallet biz (slot_wallet):** `freeCreditsFreeze`, `freeCreditsClaim`; pay `bizType=free_credit_first_cashout`\n- **`user_dot`** in `install.sql` is generic tracking, not Free Credits–specific\n\n---\n\n## 2. `FreeCreditsLogic` — methods & exposed data\n\n**Path:** `/Users/ray/Documents/project/www/slot/slot_console/app/api/logic/FreeCreditsLogic.php`\n\n| Method | Role | Returns / side effects |\n|--------|------|------------------------|\n| `calcFrozenAmount` | Static: balance after recharge − recharge amount | int qf |\n| `calcFirstCashAmount` | Static: min(frozen, configured first cash) | int qf |\n| `splitReleasePackageAmounts` | Static: split remainder into release tiers | `int[]` |\n| `status($uid)` | C端状态 | `{status, home_withdraw_unlocked?, frozen_amount, first_cash_amount, recharge_unlock_amount, packages[]}` or `{status:-1}` |\n| `syncHomeWithdrawUnlocked($uid)` | Unlock home Withdraw when balance ≥ win_threshold | `FreeCreditsPlayerModel|null` |\n| `getFreeCreditsPlayerModel($uid)` | Load player row if activity + record exist | model or null |\n| `handleFreeCreditInit(...)` | First-recharge freeze (MQ `free_credit_init`) | void; creates player + packages |\n| `advanceAfterRecharge($uid, $walletAmount)` | Post-recharge tier unlock | void |\n| `handleRecharge(...)` | **Deprecated** for freeze; only advances non-first | void |\n| `claim($uid, $packageId)` | Release tier claim → wallet | same as `status()` |\n| `markFirstCashoutProcessing` | After first cashout submit | void |\n| `firstCashout($uid, $packageId, $post)` | Pay apply for tier 1 | `{order_id, amount}` (client float) |\n| `handleFirstCashoutResult($orderId, $success, $rejected)` | MQ callback | updates package + player status |\n| `freezeFirstRecharge` (protected) | DB txn + wallet freeze + package creation | `FreeCreditsPlayerModel` |\n| `buildStatus` (protected) | Internal status DTO | array (client amounts formatted) |\n| `resetEnrollRegAfterTimestampCache` | Test helper | void |\n\n**`status()` / `buildStatus()` payload (when visible):**\n\n```php\n[\n 'status' => int, // player.status\n 'home_withdraw_unlocked' => int,\n 'frozen_amount' => float, // from frozen_amount_qf\n 'first_cash_amount' => float, // from first_cash_amount_qf\n 'recharge_unlock_amount' => float, // from ext_config\n 'packages' => [ // per tier\n 'id', 'package_no', 'package_type', 'amount' (float), 'status'\n ],\n]\n```\n\nAll internal DB/wallet math uses **千分位 `_qf`**; client fields use `CommonFn::getNumberFormat`.\n\n---\n\n## 3. InnerAPI — existing stats & list\n\n**Controller:** `/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/FreeCreditsController.php` \n**Extends:** `slotLib\\basic\\BaseController` \n**Middleware:** `innerapi` → `InnerAuthMiddleware` (`config/middleware.php`)\n\nWebman convention routes (no explicit entry in `config/route.php`):\n\n- `POST/GET /innerapi/free-credits/statistics`\n- `POST/GET /innerapi/free-credits/list`\n\n### `statistics()` — already implements §18.1-style aggregates\n\n```21:68:/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/FreeCreditsController.php\n public function statistics(Request $request)\n {\n $activityId = intval($request->input('activity_id', 0));\n $query = FreeCreditsPlayerModel::where([]);\n if ($activityId > 0) {\n $query->where('activity_id', $activityId);\n }\n\n $playerIds = $query->column('id');\n // ... empty → zeros ...\n\n $frozenTotal = FreeCreditsPlayerModel::whereIn('id', $playerIds)->sum('frozen_amount_qf');\n $completedCount = FreeCreditsPlayerModel::whereIn('id', $playerIds)\n ->where('status', FreeCreditsPlayerModel::STATUS_COMPLETED)\n ->count();\n $firstCashoutCount = FreeCreditsPackageModel::whereIn('player_id', $playerIds)\n ->where('package_type', FreeCreditsPackageModel::TYPE_FIRST_CASH)\n ->where('status', FreeCreditsPackageModel::STATUS_COMPLETED)\n ->count();\n // ... sums on package amount_qf ...\n\n return $this->success([\n 'frozen_user_count' => count($playerIds),\n 'first_cashout_user_count' => $firstCashoutCount,\n 'completed_user_count' => $completedCount,\n 'frozen_amount' => $frozenTotal,\n 'first_cashout_amount' => $firstCashoutAmount,\n 'claimed_amount' => $claimedAmount,\n 'pending_amount' => max($frozenTotal - $firstCashoutAmount - $claimedAmount, 0),\n ]);\n }\n```\n\n**Query param:** `activity_id` (optional). \n**All amounts in response are 千分位 sums**, not client floats.\n\n### `list()` — paginated players + nested packages\n\nParams: `page`, `limit` (max 100), `uid`, `activity_id`, `status`. \nReturns `{ total, list[] }` where each player row includes `packages[]` (raw DB columns).\n\n### Other innerapi controllers (pattern reference)\n\n- `/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/ActivityController.php` — generic activity CRUD via entities\n- `/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/SignController.php` — `input()` + `success()` / `errorCode()`\n- `/Users/ray/Documents/project/www/slot/slot_console/app/innerapi/controller/admin/DashboardController.php` — uses `OutputService::ok()` (different style)\n\n**Best mimic target:** `FreeCreditsController::statistics` + `list` — same domain, same models, `slotLib\\basic\\BaseController::success()`.\n\n### C端 API (for contrast)\n\n`/Users/ray/Documents/project/www/slot/slot_console/app/api/controller/FreeCreditsController.php` \nRoutes: `/api/free-credits/status`, `/claim`, `/first-cashout` (JWT `api` middleware).\n\n---\n\n## 4. Activity config — `TYPE_FREE_CREDITS = 11` & `ext_config` keys\n\n### Runtime `ext_config` keys (via `configAmount` / `configValue`)\n\n`configAmount($config, $key, $default)`:\n1. Tries **`{key}_qf`** first (integer 千分位).\n2. Else **`{key}`** passed through `CommonFn::setNumberFormat()` (display unit → qf).\n\n| Logical key | `_qf` key used at runtime | Default (display unit before format) | Used for |\n|-------------|-------------------------|--------------------------------------|----------|\n| `win_threshold` | `win_threshold_qf` | 50 | Home Withdraw unlock |\n| `recharge_unlock_amount` | `recharge_unlock_amount_qf` | 50 | Cumulative recharge to unlock tier 1 |\n| `first_cash_amount` | `first_cash_amount_qf` | 20 | Tier 1 cap / amount |\n| `package_amount` | `package_amount_qf` | 10 | Release tier split size |\n| `subsequent_min_recharge` | `subsequent_min_recharge_qf` | 10 | Min single recharge to unlock next release tier |\n| `max_unlock_per_recharge` | *(no `_qf`)* | 1 | Integer count via `configValue` only |\n\nTests confirm `_qf` precedence, e.g. `win_threshold_qf: 50000` over `win_threshold: 1` in `FreeCreditsLogicConfigTest.php`.\n\n**Not in `ext_config`:** enroll cutoff — env `FREE_CREDITS_ENROLL_REG_AFTER` only.\n\n**Player table mirrors at freeze:** `frozen_amount_qf`, `first_cash_amount_qf` (snapshotted from config + calc at freeze time).\n\n---\n\n## 5. Design notes for extending statistics API\n\nRequirements doc §18 (`/Users/ray/Documents/project/www/slot/docs/requirements/首充前免费余额定格与分档释放需求文档.md`) asks for fields **not yet on player rows**:\n\n| Req field | Suggested source |\n|-----------|------------------|\n| 累计充值金额 | Wallet `r` / `WalletService` with `needStat=1` at query time |\n| 第一档状态 | `free_credits_package` where `package_no=1` |\n| 完成进度 | `count(completed packages) / count(packages)` |\n| 剩余未释放 | `frozen_amount_qf - sum(completed amounts)` or package-level pending |\n| 已领取金额 | sum release `type=2, status=3` |\n| 时间筛选 | `first_recharge_time`, `completed_time`, `create_time` on player |\n\nExisting `statistics()` counts **all players in scope** as `frozen_user_count` (any row in `free_credits_player`), not only `status >= FROZEN` — align product definition when extending.\n\n**Gap vs requirements:** no date/source filters on `statistics()` yet; `list()` lacks cumulative recharge, progress, and remaining amount.\n\n---\n\n## Quick file index\n\n| Purpose | Path |\n|---------|------|\n| Logic | `.../app/api/logic/FreeCreditsLogic.php` |\n| InnerAPI stats/list | `.../app/innerapi/controller/FreeCreditsController.php` |\n| DDL | `.../db/install.sql` |\n| Config model | `.../app/model/common/RechargeGiftConfigModel.php` |\n| Events | `.../app/command/event/FreeCreditInitEvent.php`, `RechargeEvent.php` |\n| Validator | `.../app/api/validator/FreeCreditsValidator.php` |\n\n[REDACTED]"}]}}