From f7d0ee81f8dfccd576e1eb5fa705c33f708f20b0 Mon Sep 17 00:00:00 2001 From: zhenhui <1276357500@qq.com> Date: Thu, 23 Apr 2026 17:50:25 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/36字花-数据库与实施计划.md | 34 +++-- docs/36字花-移动端接口设计草案.md | 200 ++++++++++++++++-------------- 2 files changed, 133 insertions(+), 101 deletions(-) diff --git a/docs/36字花-数据库与实施计划.md b/docs/36字花-数据库与实施计划.md index 3a02066..59f2745 100644 --- a/docs/36字花-数据库与实施计划.md +++ b/docs/36字花-数据库与实施计划.md @@ -1,6 +1,6 @@ # 「36字花」数据库与实施计划 -**文档版本**:V1.14 +**文档版本**:V1.16 **依据**:《36字花 PRD》《业务流程说明书》《后端系统规格书》及现有表 `user` **目标**:明确分阶段落地步骤、需新建表、两表适配方向与可执行验证清单。 @@ -145,7 +145,7 @@ | 方向 | 说明 | |------|------| -| 金额精度 | `coin` 调整为 **`decimal(18,4)`**(与规格书一致,禁止用浮点存币) | +| 金额精度 | `coin` 调整为 **`decimal(18,2)`**(与规格书一致,禁止用浮点存币) | | 注册与归属 | `invite_code` 或注册时解析 URL 与 `channel.code` 绑定 | | 提现门槛 | `user.total_deposit_coin` / `user.total_withdraw_coin` / `user.bet_flow_coin` + `game_config.withdraw_bet_flow_ratio`(净充值打码口径,全项目统一) | | 风控 | 禁止登录 / 禁止下注 / 禁止提现等,可用位标记或独立布尔字段 | @@ -206,7 +206,7 @@ | 表名 | 用途 | |------|------| -| **`user_wallet_record`**(原草案名 `user_wallet_ledger`,以 DDL 为准) | **玩家游戏币钱包流水**:充值、提现(含冻结/解冻)、平台划入划出、管理员加扣币、下注、派彩、手续费、作废退款、调账等;金额 **`decimal(18,4)`**,**只增不改**;与 `user.coin` 变更须同事务 + 条件更新 | +| **`user_wallet_record`**(原草案名 `user_wallet_ledger`,以 DDL 为准) | **玩家游戏币钱包流水**:充值、提现(含冻结/解冻)、平台划入划出、管理员加扣币、下注、派彩、手续费、作废退款、调账等;金额 **`decimal(18,2)`**,**只增不改**;与 `user.coin` 变更须同事务 + 条件更新 | | `deposit_order` | 第三方充值、法币金额、汇率、游戏币到账、回调状态 | | `withdraw_order` | 提现申请、手续费、审核状态、Jackpot / 大额标记 | @@ -240,7 +240,7 @@ ### 8.1 数据库与模型 -1. DDL 执行后检查:金额字段均为 **`decimal(18,4)`**;`channel`、`user`、`admin`、`admin_group`、期号等关联字段有合理索引。 +1. DDL 执行后检查:金额字段均为 **`decimal(18,2)`**;`channel`、`user`、`admin`、`admin_group`、期号等关联字段有合理索引。 2. 创建测试渠道(顶级)→ 创建子代理 `admin`(设置 `parent_admin_id`、`channel_id`、`invite_code`)→ 创建用户并挂载 `channel_id`,链路与后台筛选正常。 3. 扣款更新采用「**条件更新**」语义(如 `WHERE balance >= 扣款额`),压测或单测验证**不出现负余额**。 @@ -286,6 +286,21 @@ 3. **大盘盈利**:流水占比分桶 + 级差,各级金额之和与线总包一致(可对照文档数值验算)。 4. **联营**:客损为负产生负结转;下期盈利先抵扣再分佣。 +### 8.3.1 渠道结算新流程(2026-04-23) + +1. **仅超管可结算**:按渠道结算周期(支持自动任务与手动提前结算)执行渠道结算。 +2. **结算即发放**:结算时按 `channel_admin_share` 比例,直接发放到管理员钱包并写入 `admin_wallet_record`(`commission_income`),同时写 `agent_settlement_period` / `agent_commission_record`。 +3. **提前结算规则**:手动提前结算后,新的周期起点从本次结算结束时间开始,后续自动周期归入下个结算段。 +4. **停用渠道限制**:`channel.status != 1` 时,该渠道不再允许玩家注册与登录。 + +### 8.3.2 后台管理员资金与提现流程(2026-04-23) + +1. **管理员钱包隔离**:新增 `admin_wallet`、`admin_wallet_record`,与玩家钱包完全隔离。 +2. **分红入账**:渠道分红结算后,写管理员钱包入账流水(`commission_income`)。 +3. **提现申请**:管理员在个人中心发起提现,金额先冻结(`withdraw_freeze`),生成 `admin_withdraw_order`(`status=0`)。 +4. **渠道超管审核**:仅该渠道顶级角色组(`admin_group.pid=0`)中的管理员可审核本渠道提现单。 +5. **审核通过/拒绝**:通过则扣减冻结并累计提现(`withdraw_success`);拒绝则退回可用余额(`withdraw_refund`)。 + ### 8.4 非功能 - **Redis(已落地)** @@ -300,7 +315,7 @@ ## 九、风险与依赖 1. **子代理数据源**:子代理统一在 `admin`,避免在 `channel` 重复建树造成双主数据源。 -2. **现有 `profit_amount`(decimal(5,2))**:与游戏币 **18,4** 精度不一致,演进时改为 `decimal(18,4)` 或迁移至结算域,避免对账误差。 +2. **现有 `profit_amount`(decimal(5,2))**:与游戏币 **18,4** 精度不一致,演进时改为 `decimal(18,2)` 或迁移至结算域,避免对账误差。 3. 文档要求 **AI 算票在 Redis 内完成**(演进目标),当前实现以 **MySQL `game_record` + 服务层缓存** 为主;主库仍应避免结算期大范围锁表;账本与注单以事务与幂等为准。 4. **全局对局一致性**:任何需求(多租户展示、渠道后台)均不得引入「按渠道独立期号/独立开奖」;若出现产品歧义,以 **§1.1** 为准,避免公平性质疑与客诉。 @@ -360,6 +375,7 @@ | V1.12 | 2026-04-18 | 下注口径:`bet_order` 仅保留 `total_amount`(整笔压注),删除 `unit_amount`、`pick_count`;DDL 与 Phinx 迁移 `20260418270000_bet_order_drop_unit_amount_pick_count` 对齐 | | V1.14 | 2026-04-20 | 服务端 Redis 热点缓存:`GameHotDataRedis`(`user` / `game_config` / `game_record`),`config/game_hot_cache.php` 与 `.env-example` 中 `GAME_HOT_CACHE_*`;更新 §1.1、§2.1、§8.4、第九章风险与依赖、附录 `user`;与 `CACHE_DRIVER`(系统 `config` 表文件缓存)区分说明 | | V1.15 | 2026-04-20 | 热点写路径收口:`GameHotDataCoordinator` + `GameHotDataLock` + `GameHotDataWriteQueue` / `GameHotDataQueueConsumer`;文档与实现对齐(替代仅 `*Forget` 描述);移动端 `betPlace` 与后台钱包共用用户互斥锁及 `coin` 乐观更新 | +| V1.16 | 2026-04-23 | 渠道结算改为单阶段口径(仅超管结算,结算即按比例发放管理员钱包并记录操作人)与管理员钱包提现流程(`admin_wallet` / `admin_wallet_record` / `admin_withdraw_order`,渠道顶级组审核) | --- @@ -376,7 +392,7 @@ | `invite_code` | 渠道侧邀请码(与 PRD「邀请码由管理员生成」并存时,以产品定义为准) | | `name` | 渠道名称 | | `agent_mode` | `turnover` 普通刷水 / `affiliate` 联营,决定分红与契约字段使用方式 | -| `profit_amount` / `total_profit_amount` / `commission_pool_amount` | 经营与分红池快照类金额,**decimal(18,4)** | +| `profit_amount` / `total_profit_amount` / `commission_pool_amount` | 经营与分红池快照类金额,**decimal(18,2)** | | `turnover_share_rate` / `affiliate_share_rate` / `affiliate_fee_rate` | 分红与联营费率类参数 | | `carryover_balance` | 联营负结转余额(可负) | | `admin_id` / `admin_group_id` | 渠道负责人(管理员)、渠道绑定的角色组 | @@ -386,7 +402,7 @@ | 字段 | 作用 | |------|------| -| `coin` | 当前游戏币余额,**decimal(18,4)**,更新须条件更新防负余额 | +| `coin` | 当前游戏币余额,**decimal(18,2)**,更新须条件更新防负余额 | | `channel_id` | 归属渠道;历史 `game_channel_id` 若仍存在,迁移期注意双写/对照 | | `register_invite_code` | 注册时邀请码快照,用于审计与归属 | | `total_deposit_coin` / `total_withdraw_coin` | 累计充值入账、累计提现出账(提现受理时累加);与「净充值」口径配合校验提现门槛 | @@ -420,7 +436,7 @@ | 表 | 要点 | |----|------| | `game_period` | **全平台唯一**期号:`period_no` 全局唯一;`status` 状态机;开奖结果与作废原因。**无 `channel_id` 字段**:对局不按渠道拆分。 | -| `bet_order` | 注单关联**全局** `period_id`;`channel_id`(若有)为**用户/归属快照**,便于分润与查询,**不表示**独立牌桌或独立开奖。`pick_numbers` 为所选号码集合;**`total_amount` 为整笔压注金额**(命中集合内任一开奖号码即按该金额参与派彩倍率计算)。已移除历史字段 `unit_amount` / `pick_count`。`idempotency_key` 幂等;金额 **18,4**;`win_amount` / `jackpot_extra_amount` | +| `game_play_record`(兼容视图 `bet_order`) | 游玩记录关联**全局** `period_id`;`channel_id` 为**用户/归属快照**,便于分润与查询,**不表示**独立牌桌或独立开奖。`pick_numbers` 为所选号码集合;**`total_amount` 为整笔游玩金额**(命中集合内任一开奖号码即按该金额参与派彩倍率计算)。已移除历史字段 `unit_amount` / `pick_count`。`idempotency_key` 幂等;金额 **18,4**;`win_amount` / `jackpot_extra_amount` | | `game_bet_auto` | 托管剩余局数、选号快照、抖动时间等;仍挂在**全局**期号下 | ### 11.6 `user_wallet_record`(玩家钱包流水) @@ -448,7 +464,7 @@ ### 11.8 代理钱包与结算域(`game_agent_wallet`、`game_agent_wallet_ledger`、`agent_settlement_period`、`agent_commission_record`、`affiliate_*`) -见 DDL 注释:代理余额与流水、结算周期大盘快照、佣金行、联营契约与负结转;金额均为 **decimal(18,4)**,与玩家账本区分职责。 +见 DDL 注释:代理余额与流水、结算周期大盘快照、佣金行、联营契约与负结转;金额均为 **decimal(18,2)**,与玩家账本区分职责。 ### 11.9 运营与消息(`operation_notice`、`user_notice_read`、`user_site_message`) diff --git a/docs/36字花-移动端接口设计草案.md b/docs/36字花-移动端接口设计草案.md index abae8fb..90ce1d7 100644 --- a/docs/36字花-移动端接口设计草案.md +++ b/docs/36字花-移动端接口设计草案.md @@ -13,10 +13,10 @@ - **请求方法**:所有移动端业务接口(`/api/*`,不含 `/api/v1/authToken`)一律使用 `POST` 调用;查询类接口同时兼容 `GET`(便于浏览器/调试工具直接访问),客户端统一走 `POST` - `POST` 时请求头 `Content-Type: application/json`,参数放在 JSON body - `GET` 兼容模式下,参数走 URL query string - - **例外**:公告模块 `/api/notice/noticeList`、`/api/notice/noticeDetail`、`/api/notice/noticeConfirm` **仅支持 `GET`**,参数一律走 URL query string + - **例外**:公告模块 `/api/notice/noticeList`、`/api/notice/noticeDetail`、`/api/notice/noticeConfirm` 与模拟收银台页 `/api/finance/depositMockPayPage` **仅支持 `GET`**,参数一律走 URL query string - 鉴权类接口 `/api/v1/authToken` 仍为 `GET` - 时间:UTC 时间戳(秒) + 服务端时区配置 -- 金额:字符串传输(如 `"100.00"`),客户端展示统一保留两位小数(存储仍为 `decimal(18,4)`) +- 金额:数字传输(如 `"100.00"`),客户端展示统一保留两位小数(存储仍为 `decimal(18,2)`) - 幂等:关键写接口要求 `idempotency_key` - 请求头(必带): - `auth-token`:通过 `GET /api/v1/authToken` 获取的接口鉴权令牌(含义:接口访问的签名鉴权凭证) @@ -168,7 +168,7 @@ ### 2.3 获取当前用户信息 - **POST** `/api/user/profile` -返回参数(金额类字段统一 4 位小数字符串,与 `/api/wallet/balanceSummary` 对齐): +返回参数(金额类字段统一 2 位小数字符串,与 `/api/wallet/balanceSummary` 对齐): **基础档案** - `uuid`:string(含义:用户对外唯一标识,10 位) @@ -185,7 +185,7 @@ **资金与提现配额** - `coin` / `coin_balance`:string(含义:当前余额;两字段同值,便于与 `/api/wallet/balanceSummary` 平滑切换) -- `frozen_balance`:string(含义:冻结余额;无冻结场景,固定 `0.0000`) +- `frozen_balance`:string(含义:冻结余额;无冻结场景,固定 `0.00`) - `total_deposit_coin`:string(含义:累计充值) - `total_withdraw_coin`:string(含义:累计提现;受理后累加) - `bet_flow_coin`:string(含义:打码量/流水;开奖结算后按每注 `total_amount` 1:1 累加) @@ -329,17 +329,17 @@ 返回参数: - `coin_balance`:string(含义:可用余额,等同于 `user.coin`) -- `frozen_balance`:string(含义:冻结余额;当前系统无冻结场景,固定返回 `0.0000`) +- `frozen_balance`:string(含义:冻结余额;当前系统无冻结场景,固定返回 `0.00`) - `withdrawable_balance`:string(含义:可提现余额;等同于 `coin_balance`,**单笔实际上限以 `max_withdrawable` 为准**) - `max_withdrawable`:string(含义:**当前允许发起的单笔最大提现金额** = min(`coin_balance`, 打码配额余量);客户端直接用于"最大可提 XXX"提示与金额输入上限校验) - `total_deposit_coin`:string(含义:累计充值币额) - `total_withdraw_coin`:string(含义:累计提现币额;提现受理时累加) - `bet_flow_coin`:string(含义:打码量/流水;开奖结算后按每注 `total_amount` 1:1 累加) - `withdraw_flow`:object(含义:打码量 / 提现配额诊断快照,供前端展示说明与上限推导) - - `ratio`:string(含义:打码量倍数,来自 `game_config.withdraw_bet_flow_ratio`,默认 `1.0000`;`0` 表示"不限打码") + - `ratio`:string(含义:打码量倍数,来自 `game_config.withdraw_bet_flow_ratio`,默认 `1.00`;`0` 表示"不限打码") - `net_deposit`:string(含义:净充值 = max(0, 累计充值 - 累计提现)) - `required_bet_flow`:string(含义:按门槛口径所需的打码量 = 净充值 × 倍数,纯展示) - - `remaining_bet_flow`:string(含义:按门槛口径还差多少打码量,纯展示;已达标为 `0.0000`) + - `remaining_bet_flow`:string(含义:按门槛口径还差多少打码量,纯展示;已达标为 `0.00`) - `eligible`:bool(含义:是否满足整体门槛,纯展示,实际放行以 `max_withdrawable` 为准) - `max_withdraw_by_flow`:string/null(含义:仅按打码量折算的单笔可提上限 = max(0, `bet_flow_coin` / `ratio` - `total_withdraw_coin`);`ratio=0` 不限打码时返回 `null`) - `flow_unlimited`:bool(含义:是否处于"不限打码"状态,`ratio=0` 时为 `true`) @@ -385,6 +385,8 @@ 说明: - 由后台「配置管理 → 充值档位」维护,存放在 `game_config.deposit_tier`(JSON 数组)。 +- 后台表单中的「支付货币」下拉来源于 `game_config.finance_cashier.currencies`(不再前端硬编码)。 +- 初始化/重建档位时按当前 `finance_cashier` 货币集合生成:**每种货币 6 条档位**(运营可再编辑)。 - 仅返回启用状态(`status=1`)的档位,按 `sort` 升序;玩家仅能从中选择。 - 档位仅描述"充值规格",不再包含收款账户;具体收款由第三方支付网关返回的 `pay_url` 引导。 - **多语言**:后台保存 `title`(中文名)、`title_en`(英文名)、`desc`(中文描述)、`desc_en`(英文描述)。接口返回的 `title` / `desc` 会根据请求头 `lang` 自动适配: @@ -396,43 +398,105 @@ 返回参数: - `list`:list,档位列表;每一项结构: - - `id`:string(含义:档位稳定 ID,创建订单时原样回传) + - `id`:string(含义:档位稳定 ID,创建订单时作为 `tier_id` 原样回传;与 `tier_key` 同值) + - `tier_key`:string(含义:与 `id` 相同,兼容旧字段名) - `title`:string(含义:档位名称,已按 `lang` 头切换;例如 `lang=en` 下返回 `"Starter Pack"`,`lang=zh` 下返回 `"新手首充礼包"`) - - `amount`:string(4 位小数,含义:玩家本次需支付的充值金额) - - `bonus_amount`:string(4 位小数,含义:该档位赠送金额,无赠送为 `0.0000`) - - `total_amount`:string(4 位小数,含义:到账总额 = amount + bonus_amount,方便前端直接展示"到账 120") + - `currency`:string(含义:标价币种,如 `CNY`) + - `pay_amount`:string(2 位小数,含义:对外标价金额,与业务配置一致;展示用) + - `amount`:string(2 位小数,含义:玩家本次需支付的充值金额) + - `bonus_amount`:string(2 位小数,含义:该档位赠送金额,无赠送为 `0.00`) + - `total_amount`:string(2 位小数,含义:到账总额 = amount + bonus_amount,方便前端直接展示"到账 120") - `desc`:string(含义:档位描述/活动文案,已按 `lang` 头切换;可空) + - `channels`:array(含义:可用支付渠道列表,用于 `depositCreate` 的 `channel_code`;渠道与档位不再做单独绑定,所有启用渠道自动兼容全部档位) + - 每项:`code`(string,渠道代码,小写,与创建订单时传入的 `channel_code` 一致)、`name`(展示名)、`sort`(排序) + +### 5.3A 获取充值/提现配置 +- **POST** `/api/finance/depositWithdrawConfig` +- 兼容旧接口:`POST /api/finance/cashierConfig`(返回结构一致,建议客户端统一切到 `depositWithdrawConfig`) + +用途: +- 一次性返回充值与提现页面所需配置:货币列表、汇率、可用充值渠道、提现银行、提现限额与文案配置。 + +返回参数: +- `platform_coin_label`:string(平台币名称,按 `lang` 适配) +- `currencies`:array + - `code`:string(货币代码) + - `label`:string(货币展示名,按 `lang` 适配) + - `deposit_coins_per_fiat`:string(充值汇率) + - `withdraw_coins_per_fiat`:string(提现汇率) +- `rates`:array(兼容字段) + - `currency`:string + - `diamonds_per_fiat_unit`:string +- `pay_channels`:array(充值渠道) + - `code`:string(渠道代码) + - `name`:string(展示名) + - `sort`:int(排序) + - `status`:int(启用状态,1=启用) + - `tier_ids`:array(兼容字段;当前固定空数组,表示自动兼容全部充值档位) +- `withdraw`:object + - `banks`:array(提现银行) + - `min_ewallet`:string(电子钱包最低提现) + - `min_bank`:string(银行卡最低提现) + - `rate_hint`:string(汇率提示文案) + - `processing_note`:string(到账提示文案) + - `fee_note`:string(手续费提示文案) + - `rate_mode`:string(`fixed` / `live`) + - `fields`:object(提现表单必填项开关) ### 5.4 创建充值订单 - **POST** `/api/finance/depositCreate` -- `Content-Type: application/json`(推荐)或 `application/x-www-form-urlencoded` +- `Content-Type: application/json`(推荐)、`application/x-www-form-urlencoded` 或 **`multipart/form-data`**(如 Apifox 的 form-data);字段名与下表一致即可,服务端通过统一参数名读取,**不限制**为某一种 body 类型。 -说明: -- **当前版本:mock 支付网关**。玩家在客户端选中档位并点击"立即充值"即视为支付成功:服务端在同一请求内完成订单创建与钱包入账,返回 `paid=true`、`status=paid`、`pay_url=""`。 -- **未来第三方支付接入**:保持请求/响应形状不变。接口仅改为创建 `status=0` 订单并返回 `pay_url`(`paid=false`、`status=pending`);实际入账由第三方回调触发,服务端通过 `app\common\library\finance\DepositSettlement::settle` 完成钱包加币,客户端通过 `GET /api/finance/depositDetail` 轮询最终状态。 -- 档位取自 `GET /api/finance/depositTierList`,服务端会二次校验档位存在且为启用状态。 +说明(与真实「创建订单 → 调起三方 → 异步回调」一致): +- **创建单**:`depositCreate` 仅写入 **待支付** 订单(`status=pending`),**不在此请求内入账**。返回体中 `paid=false`,`pay_url` 为 **模拟第三方收银台** 完整 URL(HMAC 防篡改,见下 §5.4.1)。 +- **客户端**:在 WebView/系统浏览器中打开 `pay_url`;用户完成支付后(模拟页为「确认支付」按钮),由服务端 `depositMockNotify` 验签后调用 `DepositSettlement::settle` 入账,并推送 `wallet.changed`;客户端可轮询 `depositDetail` 或依赖推送更新余额。 +- **未来接入真实第三方支付**:将 `pay_url` 与回调 URL 替换为真网关,入账仍**仅**在回调/验签成功路径中调用 `DepositSettlement::settle`(与当前模拟回调一致)。 +- 档位与渠道取自 `depositTierList`:创建订单时须选择返回 `channels` 中某一渠道的 `code` 作为 `channel_code` 传入;服务端会校验档位存在、启用且渠道已启用。 +- **HMAC 密钥**:模拟链路与签名校验使用环境变量 **`DEPOSIT_MOCK_HMAC_KEY`**(或 `config('app.deposit_mock_hmac_key')`);生产环境务必配置,与代码中默认值区分。 +- **并发上限**:同一用户最多同时存在 **3 笔待支付充值单**(`status=0`);超过后创建接口返回 `code=2005`。 +- **超时失效**:充值单创建后 **60 秒内未支付**将自动置为失败(`status=failed`),并在订单备注记录失败原因(`[timeout] unpaid over 60s`)。 +- **定时任务兜底**:服务端进程 `depositOrderExpireTicker` 每 **10 秒**主动扫描超时待支付单,保证即使用户不访问任何充值接口也会准时失效。 -请求参数: -- `tier_id`:string,必填(含义:玩家选择的充值档位 ID,取自 `/api/finance/depositTierList` 返回) -- `idempotency_key`:string,必填,≤64(含义:客户端生成的唯一键,短时间内同 `idempotency_key` 不会重复下单;建议 UUID) +请求参数(**三者缺一不可**,任一为空或空白即 `code=1001` 参数缺失): +- `tier_id`:string,必填(含义:玩家选择的充值档位 ID,取自 `depositTierList` 的 `id`;也可用同义字段名 `tier_key`) +- `channel_code`:string,必填(含义:支付渠道代码,**小写**;须与所选档位在 `depositTierList` 返回的 `channels[].code` 之一一致,例如默认内置渠道常为 `directpay`) +- `idempotency_key`:string,必填,≤64(含义:客户端生成的唯一键,短时间内同 `idempotency_key` 不会重复下单;建议 UUID。**调试工具中若使用变量,请确保解析后非空**) + +> **常见 1001 原因**:只传了 `tier_id` + `idempotency_key`,**漏传 `channel_code`**。请先调 `depositTierList`,用对应档位下 `channels` 中某项的 `code` 作为 `channel_code`。 返回参数: - `order_no`:string(含义:充值订单号) -- `amount`:string(4 位小数,含义:玩家本次支付的充值金额,与所选档位 `amount` 一致) -- `bonus_amount`:string(4 位小数,含义:本次赠送金额,与所选档位 `bonus_amount` 一致,无赠送为 `0.0000`) -- `total_amount`:string(4 位小数,含义:实际入账总额 = amount + bonus_amount) -- `pay_channel`:string(含义:支付通道标识;当前 mock 模式固定为 `mock_gateway`,未来接入网关会替换为实际通道标识,如 `alipay_h5` / `wechatpay_native`) +- `amount`:string(2 位小数,含义:玩家本次支付的充值金额,与所选档位 `amount` 一致) +- `bonus_amount`:string(2 位小数,含义:本次赠送金额,与所选档位 `bonus_amount` 一致,无赠送为 `0.00`) +- `total_amount`:string(2 位小数,含义:实际入账总额 = amount + bonus_amount) +- `pay_channel`:string(含义:支付通道标识,与请求中选择的 `channel_code` 一致,落库在订单上) - `paid`:bool(含义:当前单据是否已到账;`true` 表示钱包已入账、`status=paid`;`false` 表示待玩家在第三方支付页面完成支付) -- `pay_url`:string(含义:第三方支付页面地址;`paid=true` 时为空串,`paid=false` 时为客户端需要跳转的支付页 URL) -- `status`:string(`pending`/`paid`/`failed`,含义:订单处理状态;mock 模式下始终返回 `paid`) +- `pay_url`:string(含义:第三方支付收银台地址;**`paid=false`(待支付)** 时返回**完整 URL**(如 `https://你的域名/api/finance/depositMockPayPage?order_no=...&sign=...`);`paid=true` 时为空串) +- `status`:string(`pending`/`paid`/`failed`,含义:本接口创建成功时为 `pending`,入账完成后为 `paid`) - `create_time`:int(含义:订单创建时间,秒级时间戳) - `pay_time`:int(含义:订单到账时间,未到账为 0) +#### 5.4.1 模拟第三方:收银台页与「异步通知」回调(开发/无真网关时使用) + +- **GET** `/api/finance/depositMockPayPage` + - **Query**:`order_no`(与 `depositCreate` 返回一致)、`sign`(HMAC,与 `pay_url` 中一致;**不要自行拼接,须完整使用 `depositCreate` 返回的 `pay_url` 或同接口再次查询到的地址**) + - 无需 `auth-token` / `user-token`(外跳浏览器使用)。 + - 返回:HTML 页面,用户点击 **「确认支付(模拟成功)」** 即提交到下方 `depositMockNotify`。 + +- **POST** `/api/finance/depositMockNotify` + - **Body**(`application/x-www-form-urlencoded` 或 JSON 均可,字段名一致即可):`order_no`、`sign`(与上页/支付链接一致) + - 无需 `user-token`;`auth-token` 可选(当前实现不校验)。 + - 验签成功后:对 `status=0` 的订单执行入账(`DepositSettlement::settle`,`source=third_party` 语义),并推送 `wallet.changed`。已入账订单**幂等**再调返回当前订单信息。 + - 成功响应:与 `depositCreate` 成功体相同结构(`code=1` + `data` 为统一充值订单结构)。 + 错误码约定: -- `1001`:缺少必填参数(`tier_id`/`idempotency_key` 任一为空) +- `1001`:缺少必填参数(`tier_id`(或 `tier_key`)、`channel_code`、`idempotency_key` 任一未传或为空字符串) - `1002`:`idempotency_key` 过长,或与其他玩家的订单冲突 +- `1003`:模拟回调/链接参数非法(如 `sign` 与 `order_no` 不匹配)——`depositMockNotify` 与无效支付链接 - `2000`:订单落库或入账失败(事务回滚后返回原始错误描述) -- `2003`:所选 `tier_id` 不存在或已停用 +- `2003`:所选 `tier_id` 不存在、已停用或不在启用列表中 +- `2004`:`channel_code` 未配置或已禁用 +- `2005`:待支付充值单超过上限(`data.max_pending`、`data.pending_count`、`data.expire_seconds`) ### 5.5 查看充值订单详情 - **POST** `/api/finance/depositDetail` @@ -442,9 +506,9 @@ 返回参数(与 `depositCreate` 统一结构): - `order_no`:string(含义:充值订单号) -- `amount`:string(4 位小数,含义:本单充值金额) -- `bonus_amount`:string(4 位小数,含义:本单赠送金额,无赠送为 `0.0000`) -- `total_amount`:string(4 位小数,含义:入账总额) +- `amount`:string(2 位小数,含义:本单充值金额) +- `bonus_amount`:string(2 位小数,含义:本单赠送金额,无赠送为 `0.00`) +- `total_amount`:string(2 位小数,含义:入账总额) - `pay_channel`:string(含义:支付通道标识) - `paid`:bool(含义:是否已到账) - `pay_url`:string(含义:第三方支付页面地址,已到账为空串) @@ -464,8 +528,8 @@ 返回参数: - `list`:array(含义:充值订单列表,按 `id desc` 排序) - `order_no`:string(含义:充值订单号) - - `amount`:string(4 位小数,含义:本单充值金额) - - `bonus_amount`:string(4 位小数,含义:本单赠送金额,无赠送为 `0.0000`) + - `amount`:string(2 位小数,含义:本单充值金额) + - `bonus_amount`:string(2 位小数,含义:本单赠送金额,无赠送为 `0.00`) - `status`:string(含义:订单状态,与 `depositDetail` 一致:`pending`/`paid`/`failed`) - `pagination`:object(含义:分页信息) - `page`:int(含义:当前页码) @@ -508,7 +572,7 @@ - `ratio = 0` 时视为"不限打码",单笔上限仅受 `coin_balance` 约束。 - 采用"申请即冻结"语义:提现在移动端提交后立即从 `user.coin` 中扣减并写出金流水;后台审核 **拒绝** 时由管理端在同一事务中回冲余额、`total_withdraw_coin` 与流水,不出现"等待审核期间用户还能把这笔钱再下注"的漏洞。 - 后台审核 **通过** 时不再额外触碰余额;若管理员调整了 `amount` 或 `fee`,按新旧差额再生成一条 `withdraw` / `withdraw_refund` 流水以保持账务平衡,并同步修正 `total_withdraw_coin`。 -- `withdraw_bet_flow_ratio` 由后台「游戏配置」维护,默认 `1.0000`,修改后对新请求立即生效。 +- `withdraw_bet_flow_ratio` 由后台「游戏配置」维护,默认 `1.00`,修改后对新请求立即生效。 ### 5.8 查看提现订单详情 - **POST** `/api/finance/withdrawDetail` @@ -538,7 +602,7 @@ 返回参数: - `list`:array(含义:提现订单列表,按 `id desc` 排序) - `order_no`:string(含义:提现订单号) - - `amount`:string(4 位小数,含义:申请提现金额,与后台 `withdraw_order.amount` 对齐) + - `amount`:string(2 位小数,含义:申请提现金额,与后台 `withdraw_order.amount` 对齐) - `status`:string(含义:订单状态,与 `withdrawDetail` 一致:`pending_review`/`approved`/`rejected`;后台已打款 `status=3` 合并为 `approved`) - `pagination`:object(含义:分页信息) - `page`:int(含义:当前页码) @@ -707,7 +771,7 @@ Apipost(v7+)支持 **WebSocket**:新建请求 → 选择 **WebSocket** → ## 8.1 首次进入游戏 1. `GET /api/v1/authToken?secret=xxx×tamp=xxx&device_id=xxx&signature=xxx` 获取 `auth-token` 2. `POST /api/user/login` 登录(请求头带 `auth-token`) -3. `GET /api/game/lobbyInit` 拉首页初始化(请求头带 `auth-token`) +3. `POST /api/game/lobbyInit` 拉首页初始化(请求头带 `auth-token`) 3. 建立 webman/push 连接并订阅: - `public-game-period` - `private-user-{user.uuid}`(`uuid` 取自登录/档案接口,与 7.1 一致) @@ -717,16 +781,15 @@ Apipost(v7+)支持 **WebSocket**:新建请求 → 选择 **WebSocket** → 7. 监听 `period.opened` 渲染开奖动画并刷新开奖记录 ## 8.2 充值到下注到提现闭环 -1. 拉取档位:`GET /api/finance/depositTierList`(玩家选择一档) -2. 创建订单:`POST /api/finance/depositCreate`(JSON:`tier_id` + `idempotency_key`) - - **当前 mock 模式**:服务端同一请求内入账完成,返回 `paid=true`、`status=paid`、`pay_url=""`,客户端直接刷新钱包 - - **未来第三方支付**:返回 `paid=false`、`status=pending`、`pay_url=<网关页面>`;客户端跳转网关页完成支付,后端由第三方回调触发入账(通过 `DepositSettlement::settle`) -3. 客户端可选轮询 `GET /api/finance/depositDetail` 兜底确认状态;入账成功后会收到 `wallet.changed` +1. 拉取档位:`POST /api/finance/depositTierList`(玩家选择一档,并记下该档 `channels[].code`) +2. 创建订单:`POST /api/finance/depositCreate`(`tier_id` + `channel_code` + `idempotency_key`,三者为必填;可用 JSON / form-data / x-www-form-urlencoded) + - 返回 `paid=false`、`status=pending`、**非空 `pay_url`**:客户端在 WebView/浏览器中打开 `pay_url`(`GET /api/finance/depositMockPayPage`);用户在模拟页点击确认后,由 `POST /api/finance/depositMockNotify` 完成入账,或轮询 `depositDetail` / 等 `wallet.changed` 再刷新余额 + - 未来接真实第三方:将 `pay_url` 换为真网关,入账仅在支付平台 **异步通知** 中调用 `DepositSettlement::settle`(与当前 `depositMockNotify` 路径一致) +3. 客户端可选轮询 `POST /api/finance/depositDetail` 兜底确认状态;入账成功后会收到 `wallet.changed` 4. 下注:`POST /api/game/betPlace` 5. 派彩后收到 `wallet.changed` -6. 查询流水:`GET /api/wallet/recordList` -7. 提现:`POST /api/finance/withdrawCreate`(即时冻结 `user.coin` 与写出 `withdraw` 流水) -> `GET /api/finance/withdrawDetail` - - 等待后台 `/admin/order/withdrawOrder` 审核;通过后订单 `status` 变为 `approved`,拒绝则回冲余额并在 `reject_reason` 中回传管理员填写的驳回原因 +6. 查询流水:`POST /api/wallet/recordList` +7. 提现:`POST /api/finance/withdrawCreate`(即时冻结 `user.coin` 与写出 `withdraw` 流水) -> `POST /api/finance/withdrawDetail` ## 8.3 公告强触达流程 1. 客户端监听 `notice.popout` @@ -791,7 +854,7 @@ flowchart TD "group_paths": ["顶级组 / 运营组 / A组"], "group_paths_text": "顶级组 / 运营组 / A组", "status": 1, - "share_rate": "30.0000" + "share_rate": "30.00" } ] } @@ -806,54 +869,7 @@ flowchart TD --- -## 11. 后台提现审核接口(管理端补充) - -对应前端入口:`/admin/order/withdrawOrder`。列表读写沿用 `/admin/order.WithdrawOrder/index`(BuildAdmin CRUD 标准协议)。审核流程另起 2 个动作接口,默认按钮 `add / del` 已在迁移中下线。 - -### 11.1 GET 审核详情 - -- **GET** `/admin/order.WithdrawOrder/edit?id={id}` -- 与 CRUD `edit` 协议复用,但对 `POST` 方法直接返回错误,强制走 `approve/reject`。 -- 返回 `row` 字段已 `withJoin` 关联:`user.username`、`channel.name`、`reviewAdmin.username`,方便弹窗直接展示"用户 / 渠道 / 审核人"。 - -### 11.2 审核通过 - -- **POST** `/admin/order.WithdrawOrder/approve` -- 请求参数: - - `id`:int,必填(`withdraw_order.id`) - - `amount`:string,必填(审核后申请金额;允许管理员调整) - - `fee`:string,必填(审核后手续费;`>=0` 且 `<= amount`) - - `remark`:string,可选(为空时自动写入审核摘要) -- 事务行为: - 1. 订单状态必须为 `0 待审核`,否则返回错误。 - 2. 对比 `new_amount - old_amount`: - - `>0`:用户 `coin -= diff`、`total_withdraw_coin += diff`,再写一条 `withdraw` 流水(direction=2)。 - - `<0`:用户 `coin += |diff|`、`total_withdraw_coin -= |diff|`,写一条 `withdraw_refund` 流水(direction=1)。 - 3. 更新订单:`amount` / `fee` / `actual_amount = amount - fee` / `status=1` / `review_admin_id` / `review_time` / `remark`。 - -### 11.3 审核拒绝 - -- **POST** `/admin/order.WithdrawOrder/reject` -- 请求参数: - - `id`:int,必填 - - `remark`:string,必填(拒绝原因,最多 255 字,玩家将在 `/api/finance/withdrawDetail` 的 `reject_reason` 中看到) -- 事务行为: - 1. 订单状态必须为 `0 待审核`。 - 2. 回冲申请时的冻结:用户 `coin += amount`、`total_withdraw_coin -= amount`,写一条 `withdraw_refund` 流水(direction=1,`ref_type=withdraw_order`)。 - 3. 更新订单:`status=2` / `review_admin_id` / `review_time` / `remark`。 - -### 11.4 权限节点 - -由 `20260418240000_withdraw_order_review_menu.php` 写入: - -- `order/withdrawOrder/approve`(审核通过) -- `order/withdrawOrder/reject`(审核驳回) -- `order/withdraw_order/approve` / `order/withdraw_order/reject`(snake 别名,兼容 `snake_case` 路径兜底) -- 默认 CRUD 按钮 `order/withdrawOrder/add` / `order/withdrawOrder/del`(含 snake 别名)已置为 `status=0`,审核流程不允许新增/删除订单。 - ---- - -## 12. 需要你确认的实现口径(进入接口开发前) +## 11. 需要你确认的实现口径(进入接口开发前) 1. **登录方式**:仅账号密码,还是要短信/邮箱验证码? 2. **提现收款类型**:首版只做银行卡,还是同时支持电子钱包/加密地址?