684 lines
20 KiB
Markdown
684 lines
20 KiB
Markdown
# 积分商城 PlayX 对接实施方案
|
||
|
||
> 基于《积分商城-内部对接与流程说明.md》和《PlayX-对接文档(积分商城).md》整理,结合当前项目结构给出具体落地方案。
|
||
|
||
---
|
||
|
||
## 一、接口创建
|
||
|
||
### 1.1 商城需对外提供的接口(PlayX 调用商城)
|
||
#### Daily Push API
|
||
接收 PlayX 每日 T+1 数据推送。
|
||
|
||
* 方法:`POST`
|
||
* 路径:`/api/v1/mall/dailyPush`
|
||
|
||
##### 请求(Header)
|
||
当配置了 `playx.daily_push_secret`(Daily Push 签名校验)时,需要携带:
|
||
* `X-Request-Id`:请求 ID
|
||
* `X-Timestamp`:时间戳
|
||
* `X-Signature`:签名(HMAC_SHA256)
|
||
|
||
签名计算逻辑(服务端):
|
||
* canonical:`{X-Timestamp}\n{X-Request-Id}\nPOST\n/api/v1/mall/dailyPush\n{sha256(json_body)}`
|
||
* expected:`hash_hmac('sha256', canonical, daily_push_secret)`
|
||
|
||
##### 请求(Body)
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `request_id` | string | 是 | 外部推送请求号(原样返回) |
|
||
| `date` | string(YYYY-MM-DD) | 是 | 业务日期(入库到 `mall_playx_daily_push.date`) |
|
||
| `user_id` | string | 是 | PlayX 用户 ID(幂等键组成部分) |
|
||
| `username` | string | 否 | 展示冗余 |
|
||
| `yesterday_win_loss_net` | number | 否 | 昨日净输赢(仅当 `< 0` 时计算新增保障金) |
|
||
| `yesterday_total_deposit` | number | 否 | 昨日总充值(用于计算今日可领取上限) |
|
||
| `lifetime_total_deposit` | number | 否 | 历史总充值 |
|
||
| `lifetime_total_withdraw` | number | 否 | 历史总提现 |
|
||
|
||
##### 幂等
|
||
* 幂等键:`user_id + date`
|
||
* 重复推送时不会重复入账,返回 `data.deduped = true`
|
||
|
||
##### 返回(Response)
|
||
通用返回包结构:`{ code, msg, time, data }`
|
||
|
||
成功返回(data):
|
||
* `data.request_id`:原样返回
|
||
* `data.accepted`:`true`
|
||
* `data.deduped`:是否幂等命中(`false`=首次入库,`true`=重复推送)
|
||
* `data.message`:首次为 `ok`,重复为 `duplicate input`
|
||
|
||
##### 示例
|
||
无签名校验(`PLAYX_DAILY_PUSH_SECRET` 为空):
|
||
```bash
|
||
curl -X POST 'http://localhost:1818/api/v1/mall/dailyPush' \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"request_id":"req_1001",
|
||
"date":"2026-03-18",
|
||
"user_id":"U123",
|
||
"username":"demo_user",
|
||
"yesterday_win_loss_net":-120.5,
|
||
"yesterday_total_deposit":50,
|
||
"lifetime_total_deposit":5000,
|
||
"lifetime_total_withdraw":2000
|
||
}'
|
||
```
|
||
|
||
成功响应(首次):
|
||
```json
|
||
{
|
||
"code": 1,
|
||
"msg": "",
|
||
"data": {
|
||
"request_id": "req_1001",
|
||
"accepted": true,
|
||
"deduped": false,
|
||
"message": "ok"
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 1.2 商城需调用的 PlayX 接口(外部,由 PlayX 提供)
|
||
以下为商城侧调用(由 PlayX 提供)。
|
||
|
||
#### Token Verification API
|
||
* 方法:`POST`
|
||
* URL:`${playx.api.base_url}${playx.api.token_verify_url}`(默认 `/api/v1/auth/verify-token`)
|
||
|
||
##### 请求 Body
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `request_id` | string | 是 | 服务端生成,如 `mall_{uniqid}` |
|
||
| `token` | string | 是 | 前端传入的 PlayX token |
|
||
|
||
##### 返回(期望)
|
||
HTTP 状态码 `200`,响应体需包含:
|
||
* `user_id`(必选)
|
||
* `username`(可选)
|
||
* `token_expire_at`(可选,能被 `strtotime` 解析)
|
||
|
||
##### 示例
|
||
```json
|
||
{
|
||
"request_id": "mall_abc123",
|
||
"token": "PLAYX_TOKEN_XXX"
|
||
}
|
||
```
|
||
|
||
```json
|
||
{
|
||
"user_id": "U123",
|
||
"username": "demo_user",
|
||
"token_expire_at": "2026-04-01T12:00:00Z"
|
||
}
|
||
```
|
||
|
||
#### Bonus Grant API
|
||
* 方法:`POST`
|
||
* URL:`${playx.api.base_url}${playx.api.bonus_grant_url}`(默认 `/api/v1/bonus/grant`)
|
||
|
||
##### 请求 Body
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `request_id` | string | 是 | 如 `mall_bonus_{uniqid}` |
|
||
| `externalTransactionId` | string | 是 | 订单幂等键:`external_transaction_id` |
|
||
| `user_id` | string | 是 | PlayX 用户 ID |
|
||
| `amount` | number | 是 | 订单金额:`MallPlayxOrder.amount` |
|
||
| `rewardName` | string | 是 | `mall_item.title` |
|
||
| `category` | string | 是 | `mall_item.category`,默认 `daily` |
|
||
| `categoryTitle` | string | 是 | `mall_item.category_title` |
|
||
| `multiplier` | int | 是 | `MallPlayxOrder.multiplier` |
|
||
|
||
##### 示例(请求)
|
||
```json
|
||
{
|
||
"request_id": "mall_bonus_abc123",
|
||
"externalTransactionId": "BONUS_ORD2026....",
|
||
"user_id": "U123",
|
||
"amount": 10.0,
|
||
"rewardName": "每日红利",
|
||
"category": "daily",
|
||
"categoryTitle": "每日",
|
||
"multiplier": 1
|
||
}
|
||
```
|
||
|
||
##### 返回(期望)
|
||
商城侧以 `data.status` 判断:
|
||
* `status = "accepted"`:读取 `playx_transaction_id`
|
||
* 否则:读取 `message` 写入 `fail_reason`
|
||
|
||
示例(accepted):
|
||
```json
|
||
{
|
||
"status": "accepted",
|
||
"playx_transaction_id": "PX_TX_001"
|
||
}
|
||
```
|
||
|
||
示例(reject):
|
||
```json
|
||
{
|
||
"status": "rejected",
|
||
"message": "insufficient balance"
|
||
}
|
||
```
|
||
|
||
#### Balance Credit API
|
||
* 方法:`POST`
|
||
* URL:`${playx.api.base_url}${playx.api.balance_credit_url}`(默认 `/api/v1/balance/credit`)
|
||
|
||
##### 请求 Body
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `request_id` | string | 是 | 如 `mall_withdraw_{uniqid}` |
|
||
| `externalTransactionId` | string | 是 | 订单幂等键:`external_transaction_id` |
|
||
| `user_id` | string | 是 | PlayX 用户 ID |
|
||
| `amount` | number | 是 | 订单金额:`MallPlayxOrder.amount` |
|
||
| `multiplier` | int | 是 | `MallPlayxOrder.multiplier` |
|
||
|
||
##### 返回(期望)
|
||
与 Bonus Grant 一致:
|
||
* 成功:`status="accepted"` + `playx_transaction_id`
|
||
* 失败:`message` 写入 `fail_reason`
|
||
|
||
##### 示例
|
||
请求(示意,由商城侧发起,实际 URL 以配置为准):
|
||
```json
|
||
{
|
||
"request_id": "mall_withdraw_abc123",
|
||
"externalTransactionId": "WITHDRAW_ORD2026....",
|
||
"user_id": "U123",
|
||
"amount": 100.0,
|
||
"multiplier": 1
|
||
}
|
||
```
|
||
|
||
响应(accepted):
|
||
```json
|
||
{
|
||
"status": "accepted",
|
||
"playx_transaction_id": "PX_TX_002"
|
||
}
|
||
```
|
||
|
||
响应(rejected):
|
||
```json
|
||
{
|
||
"status": "rejected",
|
||
"message": "insufficient balance"
|
||
}
|
||
```
|
||
|
||
#### Transaction Status Query API(交易终态查询)
|
||
* 方法:`GET`
|
||
* URL:`${playx.api.base_url}${playx.api.transaction_status_url}`(默认 `/api/v1/transaction/status`)
|
||
|
||
##### Query
|
||
* `externalTransactionId`:订单幂等键
|
||
|
||
##### 示例(请求)
|
||
```bash
|
||
curl -G '${playx.api.base_url}/api/v1/transaction/status' \
|
||
--data-urlencode 'externalTransactionId=BONUS_ORD2026....'
|
||
```
|
||
|
||
##### 返回(期望)
|
||
商城读取 `data.status`:
|
||
* `COMPLETED`:设置订单 `status=COMPLETED`
|
||
* `FAILED` 或 `REJECTED`:设置订单 `status=REJECTED`、`grant_status=FAILED_FINAL`,并退回积分;失败信息取 `data.message`
|
||
|
||
示例(completed):
|
||
```json
|
||
{ "status": "COMPLETED" }
|
||
```
|
||
|
||
示例(failed):
|
||
```json
|
||
{ "status": "FAILED", "message": "grant rejected by PlayX" }
|
||
```
|
||
|
||
---
|
||
|
||
### 1.3 商城内部 API(供 H5 前端调用)
|
||
#### Token 验证
|
||
* 方法:`POST`
|
||
* 路径:`/api/v1/mall/verifyToken`
|
||
|
||
请求(Body):
|
||
* `token`(必填,优先读取)
|
||
* 兼容:`session`(当 `token` 为空时当作 token 使用)
|
||
|
||
成功返回(data):
|
||
* `session_id`
|
||
* `user_id`
|
||
* `username`
|
||
* `token_expire_at`(ISO 时间字符串)
|
||
|
||
示例:
|
||
```bash
|
||
curl -X POST 'http://localhost:1818/api/v1/mall/verifyToken' \
|
||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||
--data-urlencode 'token=PLAYX_TOKEN_XXX'
|
||
```
|
||
|
||
#### 用户资产
|
||
* 方法:`GET`
|
||
* 路径:`/api/v1/mall/assets`
|
||
|
||
请求参数(二选一):
|
||
* `session_id`(优先):从 `mall_playx_session` 查 user_id(并校验过期)
|
||
* `user_id`:直接使用(兼容)
|
||
|
||
成功返回(data):
|
||
* `locked_points`
|
||
* `available_points`
|
||
* `today_limit`
|
||
* `today_claimed`
|
||
* `withdrawable_cash`(`available_points * points_to_cash_ratio`,保留 2 位)
|
||
|
||
##### 示例
|
||
```bash
|
||
curl -G 'http://localhost:1818/api/v1/mall/assets' --data-urlencode 'session_id=7b1c....'
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 1,
|
||
"msg": "",
|
||
"data": {
|
||
"locked_points": 100,
|
||
"available_points": 50,
|
||
"today_limit": 200,
|
||
"today_claimed": 80,
|
||
"withdrawable_cash": 5.2
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 领取(Claim)
|
||
* 方法:`POST`
|
||
* 路径:`/api/v1/mall/claim`
|
||
|
||
请求:
|
||
* `claim_request_id`:幂等键(string,必填且唯一)
|
||
* 鉴权:`session_id` 或 `user_id`
|
||
|
||
成功返回(data):与资产接口一致(`locked_points/available_points/today_limit/today_claimed/withdrawable_cash`)
|
||
|
||
##### 示例
|
||
(首次领取成功,可能返回 `msg=Claim success`;若幂等重复,`msg` 可能为空)
|
||
```bash
|
||
curl -X POST 'http://localhost:1818/api/v1/mall/claim' \
|
||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||
--data-urlencode 'claim_request_id=claim_001' \
|
||
--data-urlencode 'session_id=7b1c....'
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 1,
|
||
"msg": "Claim success",
|
||
"data": {
|
||
"locked_points": 60,
|
||
"available_points": 90,
|
||
"today_limit": 200,
|
||
"today_claimed": 120,
|
||
"withdrawable_cash": 9.0
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 商品列表
|
||
* 方法:`GET`
|
||
* 路径:`/api/v1/mall/items`
|
||
|
||
请求(可选):
|
||
* `type=BONUS|PHYSICAL|WITHDRAW`
|
||
|
||
成功返回(data):
|
||
* `list`:`mall_item` 列表(包含 `amount/multiplier/category/category_title` 等字段)
|
||
|
||
##### 示例
|
||
```bash
|
||
curl -G 'http://localhost:1818/api/v1/mall/items' --data-urlencode 'type=BONUS'
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 1,
|
||
"msg": "",
|
||
"data": {
|
||
"list": [
|
||
{
|
||
"id": 123,
|
||
"title": "每日红利",
|
||
"type": 1,
|
||
"score": 100,
|
||
"amount": 10.0,
|
||
"multiplier": 1,
|
||
"category": "daily",
|
||
"category_title": "每日"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 红利兑换(Bonus Redeem)
|
||
* 方法:`POST`
|
||
* 路径:`/api/v1/mall/bonusRedeem`
|
||
|
||
请求:
|
||
* `item_id`:商品 ID(BONUS)
|
||
* 鉴权:`session_id` 或 `user_id`
|
||
|
||
成功返回(data):
|
||
* `order_id`
|
||
* `status`:`PENDING`
|
||
|
||
##### 示例
|
||
```bash
|
||
curl -X POST 'http://localhost:1818/api/v1/mall/bonusRedeem' \
|
||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||
--data-urlencode 'item_id=123' \
|
||
--data-urlencode 'session_id=7b1c....'
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 1,
|
||
"msg": "Redeem submitted, please wait about 10 minutes",
|
||
"data": {
|
||
"order_id": 456,
|
||
"status": "PENDING"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 实物兑换(Physical Redeem)
|
||
* 方法:`POST`
|
||
* 路径:`/api/v1/mall/physicalRedeem`
|
||
|
||
请求:
|
||
* `item_id`:商品 ID(PHYSICAL)
|
||
* `address_id`:`mall_address.id`(当前用户资产下地址;订单写入 `mall_address_id` 与收货快照)
|
||
* 鉴权:`session_id` 或 `user_id`
|
||
|
||
成功返回:
|
||
* `msg`:`Redeem success`
|
||
* `data`:null
|
||
|
||
##### 示例
|
||
```bash
|
||
curl -X POST 'http://localhost:1818/api/v1/mall/physicalRedeem' \
|
||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||
--data-urlencode 'item_id=200' \
|
||
--data-urlencode 'address_id=10' \
|
||
--data-urlencode 'session_id=7b1c....'
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 1,
|
||
"msg": "Redeem success",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
#### 提现申请(Withdraw Apply)
|
||
* 方法:`POST`
|
||
* 路径:`/api/v1/mall/withdrawApply`
|
||
|
||
请求:
|
||
* `item_id`:商品 ID(WITHDRAW)
|
||
* 鉴权:`session_id` 或 `user_id`
|
||
|
||
成功返回(data):
|
||
* `order_id`
|
||
* `status`:`PENDING`
|
||
|
||
##### 示例
|
||
```bash
|
||
curl -X POST 'http://localhost:1818/api/v1/mall/withdrawApply' \
|
||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||
--data-urlencode 'item_id=321' \
|
||
--data-urlencode 'session_id=7b1c....'
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 1,
|
||
"msg": "Withdraw submitted, please wait about 10 minutes",
|
||
"data": {
|
||
"order_id": 789,
|
||
"status": "PENDING"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 订单列表
|
||
* 方法:`GET`
|
||
* 路径:`/api/v1/mall/orders`
|
||
|
||
请求:
|
||
* `session_id` 或 `user_id`
|
||
|
||
成功返回(data):
|
||
* `list`:订单列表(最多 100 条,包含关联的 `mallItem`)
|
||
|
||
##### 示例
|
||
```bash
|
||
curl -G 'http://localhost:1818/api/v1/mall/orders' --data-urlencode 'session_id=7b1c....'
|
||
```
|
||
|
||
```json
|
||
{
|
||
"code": 1,
|
||
"msg": "",
|
||
"data": {
|
||
"list": [
|
||
{
|
||
"id": 456,
|
||
"type": "BONUS",
|
||
"status": "PENDING",
|
||
"mall_item_id": 123,
|
||
"points_cost": 100,
|
||
"amount": 10.0,
|
||
"external_transaction_id": "BONUS_ORD2026....",
|
||
"grant_status": "NOT_SENT",
|
||
"mallItem": { "id": 123, "title": "每日红利", "type": 1 }
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 同步额度
|
||
当前代码未实现并未注册路由:`/api/v1/mall/syncLimit`。
|
||
如需补齐,请在接口设计阶段新增对应实现与 PlayX API 对接。
|
||
|
||
---
|
||
|
||
## 二、后台修改
|
||
|
||
### 2.1 商品管理(mall_item)
|
||
|
||
- **类型**:需与文档一致
|
||
- `1` = BONUS(红利)
|
||
- `2` = PHYSICAL(实物)
|
||
- `3` = WITHDRAW(提现档位)
|
||
- **新增字段**(红利/提现档位):
|
||
- `amount`:现金面值(元)
|
||
- `multiplier`:流水倍数
|
||
- `category`:红利业务类别(如 daily)
|
||
- `category_title`:类别展示名
|
||
- **库存**:实物需 `stock`;红利/提现可不限制或按业务配置
|
||
|
||
### 2.2 订单管理(统一订单表)
|
||
|
||
- **订单类型**:BONUS / PHYSICAL / WITHDRAW
|
||
- **订单状态**:PENDING / COMPLETED / SHIPPED / REJECTED
|
||
- **实物订单**:
|
||
- 发货:录入物流公司、单号 → `SHIPPED`
|
||
- 驳回:录入驳回原因 → `REJECTED`,自动退回积分
|
||
- **红利/提现订单**:
|
||
- 展示 `external_transaction_id`、`playx_transaction_id`、推送playx
|
||
- 手动重试:仅对 `FAILED_RETRYABLE` 状态,记录 `retry_request_id`、操作者、原因
|
||
|
||
### 2.3 用户资产与人工调账
|
||
|
||
- **用户资产**:按 `user_id` 展示 `locked_points`、`available_points`、`today_limit`、`today_claimed`
|
||
- **人工调账**:针对 T+1 推送异常或客诉,支持对 `locked_points`、`available_points` 手动加减,并记录审计日志
|
||
|
||
### 2.4 每日推送数据
|
||
|
||
- 后台可查看 `mall_playx_daily_push` 表数据,按 `user_id`、`date` 查询,便于排查异常
|
||
|
||
---
|
||
|
||
## 三、数据库修改
|
||
|
||
### 3.1 用户资产表(改造 mall_player 或新建)
|
||
|
||
**方案 A:改造 mall_player**
|
||
|
||
- 将 `user_id` 作为主键(PlayX 的 user_id)或与现有 id 并存
|
||
- 新增字段:
|
||
- `locked_points`(待领取积分)
|
||
- `available_points`(可用积分)
|
||
- `today_limit`(今日可领取上限)
|
||
- `today_claimed`(今日已领取)
|
||
- `today_limit_date`(今日上限所属日期,用于每日重置)
|
||
|
||
**方案 B:新建 mall_playx_user_asset**
|
||
|
||
- `user_id`(PlayX 用户 ID,主键或唯一)
|
||
- `username`(冗余展示)
|
||
- `locked_points`、`available_points`、`today_limit`、`today_claimed`、`today_limit_date`
|
||
- `create_time`、`update_time`
|
||
|
||
---
|
||
|
||
### 3.2 每日推送数据表(新建)
|
||
|
||
**表名**:`mall_playx_daily_push`
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| id | int | 主键 |
|
||
| user_id | varchar(64) | 玩家 ID |
|
||
| date | date | 业务日期 |
|
||
| username | varchar(100) | 展示名 |
|
||
| yesterday_win_loss_net | decimal(15,2) | 昨日净输赢 |
|
||
| yesterday_total_deposit | decimal(15,2) | 昨日总充值 |
|
||
| lifetime_total_deposit | decimal(15,2) | 历史总充值 |
|
||
| lifetime_total_withdraw | decimal(15,2) | 历史总提现 |
|
||
| create_time | bigint | 创建时间 |
|
||
|
||
**唯一索引**:`(user_id, date)` 幂等去重
|
||
|
||
---
|
||
|
||
### 3.3 领取记录表(幂等)
|
||
|
||
**表名**:`mall_playx_claim_log`
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| id | int | 主键 |
|
||
| claim_request_id | varchar(64) | 幂等键,唯一 |
|
||
| user_id | varchar(64) | 用户 ID |
|
||
| claimed_amount | int | 领取积分 |
|
||
| create_time | bigint | 创建时间 |
|
||
|
||
**唯一索引**:`claim_request_id`
|
||
|
||
---
|
||
|
||
### 3.4 统一订单表(改造或新建)
|
||
|
||
**表名**:`mall_playx_order`(或统一改造 mall_pints_order / mall_redemption_order)
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|----------------------------------------------------------------------|
|
||
| id | int | 主键 |
|
||
| user_id | varchar(64) | 用户 ID |
|
||
| type | enum | BONUS / PHYSICAL / WITHDRAW |
|
||
| status | enum | PENDING / COMPLETED / SHIPPED / REJECTED |
|
||
| mall_item_id | int | 商品 ID |
|
||
| points_cost | int | 消耗积分 |
|
||
| amount | decimal(15,2) | 现金面值(红利/提现) |
|
||
| multiplier | int | 流水倍数 |
|
||
| external_transaction_id | varchar(64) | 订单号 |
|
||
| playx_transaction_id | varchar(64) | PlayX 流水号 |
|
||
| grant_status | enum | NOT_SENT / SENT_PENDING / ACCEPTED / FAILED_RETRYABLE / FAILED_FINAL |
|
||
| fail_reason | text | 失败原因 |
|
||
| retry_count | int | 重试次数 |
|
||
| reject_reason | varchar(255) | 驳回原因(实物) |
|
||
| shipping_company | varchar(50) | 物流公司 |
|
||
| shipping_no | varchar(64) | 物流单号 |
|
||
| mall_address_id | int unsigned, NULL | 实物兑换所选 `mall_address.id`(快照仍见 `receiver_*`) |
|
||
| receiver_name | varchar(50) | 收货人 |
|
||
| receiver_phone | varchar(20) | 收货电话 |
|
||
| receiver_address | text | 收货地址 |
|
||
| create_time | bigint | 创建时间 |
|
||
| update_time | bigint | 更新时间 |
|
||
|
||
**索引**:`user_id`、`external_transaction_id`、`type`、`status`
|
||
|
||
---
|
||
|
||
### 3.5 商品表(mall_item)扩展
|
||
|
||
新增字段(若不存在):
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| amount | decimal(15,2) | 现金面值(红利/提现档位) |
|
||
| multiplier | int | 流水倍数 |
|
||
| category | varchar(32) | 红利业务类别 |
|
||
| category_title | varchar(64) | 类别展示名 |
|
||
|
||
---
|
||
|
||
## 四、业务规则
|
||
|
||
### 4.1 计算规则(需配置)
|
||
|
||
- **返还比例**:`新增保障金 = ABS(yesterday_win_loss_net) * 返还比例`(仅 `yesterday_win_loss_net < 0` 时)
|
||
- **解锁比例**:`今日可领取上限 = yesterday_total_deposit * 解锁比例`
|
||
- **提现折算**:积分 → 现金(如 10 分 = 1 元),用于前端展示
|
||
|
||
### 4.2 每日重置
|
||
|
||
- `today_claimed` 与 `today_limit` 按业务日重置(`today_limit_date` 变化时)
|
||
|
||
### 4.3 发放重试
|
||
|
||
- 自动重试:1min / 5min / 15min,最多 3 次,使用同一 `externalTransactionId`
|
||
- 仅对 `NOT_SENT`、`FAILED_RETRYABLE` 重试
|
||
- 收到 `accepted` 后不再重试,改轮询交易终态查询 API
|
||
|
||
---
|
||
|
||
## 五、实施顺序建议
|
||
|
||
1. **数据库**:新增迁移(`mall_playx_daily_push`、`mall_playx_claim_log`、`mall_playx_order`),扩展 `mall_item`、`mall_player`(或新建资产表)
|
||
2. **模型**:`MallPlayxDailyPush`、`MallPlayxClaimLog`、`MallPlayxOrder`、扩展 `MallItem`、`MallPlayer`
|
||
3. **接口**:Daily Push API(含签名校验)→ Token 验证 → 资产/领取 → 商品列表 → 红利/实物/提现 → 订单列表
|
||
4. **后台**:商品扩展、订单管理(含发货/驳回/重试)、人工调账、每日推送数据查看
|
||
5. **定时任务**:轮询交易终态、自动重试失败发放
|
||
|
||
---
|
||
|
||
## 六、待确认事项
|
||
|
||
- PlayX 提供的 **Token Verification API**、**Bonus Grant API**、**Balance Credit API**、**交易终态查询 API** 的 URL、鉴权方式、字段最终表
|
||
- `date` 的时区定义(如 UTC+8)
|
||
- 返还比例、解锁比例、提现折算的具体数值
|
||
- 是否启用「同步额度」功能(需 PlayX 提供对应 API)
|