文档
This commit is contained in:
@@ -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`)
|
||||
|
||||
|
||||
@@ -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. **提现收款类型**:首版只做银行卡,还是同时支持电子钱包/加密地址?
|
||||
|
||||
Reference in New Issue
Block a user