1.优化后台页面样式
2.优化统一订单中红利的状态和失败原因 3.移除项目中冗余代码和字段
This commit is contained in:
@@ -2,14 +2,17 @@
|
||||
|
||||
说明:本文档严格依据当前代码 `app/api/controller/v1/Playx.php`、`app/api/controller/v1/Auth.php`(临时登录)、`config/playx.php` 与定时任务 `app/process/PlayxJobs.php` 整理。
|
||||
|
||||
三类接口分别为:
|
||||
- `积分商城 -> PlayX`(PlayX 调用商城)
|
||||
- `PlayX -> 积分商城`(商城调用 PlayX)
|
||||
- `积分商城 -> H5`(H5 调用商城)
|
||||
按调用方向分为三类(避免与历史章节标题混淆):
|
||||
|
||||
| 方向 | 含义 | 本文位置 |
|
||||
|------|------|----------|
|
||||
| **PlayX → 积分商城** | PlayX(或上游批处理)**主动 HTTP 调用商城**开放接口 | **§1**(如 Daily Push) |
|
||||
| **积分商城 → PlayX** | 商城 Worker / 后台 **主动 HTTP 调用 PlayX / Cash Market** 提供的接口 | **不展开于本文**;交付 PlayX 的说明见 **`docs/PlayX-接口待完善清单.md`** |
|
||||
| **积分商城 → H5** | H5 / 内嵌页 **调用商城** 的会员与积分业务接口 | **§3** |
|
||||
|
||||
---
|
||||
|
||||
## 1. 积分商城 -> PlayX(PlayX 调用商城)
|
||||
## 1. PlayX → 积分商城(外部系统调用商城开放接口)
|
||||
|
||||
### 1.1 Daily Push API
|
||||
* 方法:`POST`
|
||||
@@ -36,8 +39,8 @@
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `request_id` | string | 是 | 外部推送请求号(原样返回) |
|
||||
| `date` | string(YYYY-MM-DD) | 是 | 业务日期(入库到 `mall_playx_daily_push.date`) |
|
||||
| `user_id` | string | 是 | PlayX 用户 ID(用于幂等;入库 `mall_playx_daily_push.user_id` 等;服务端会映射/创建 `mall_user` 与 `mall_playx_user_asset`) |
|
||||
| `date` | string(YYYY-MM-DD) | 是 | 业务日期(入库到 `mall_daily_push.date`) |
|
||||
| `user_id` | string | 是 | PlayX 用户 ID(用于幂等;入库 `mall_daily_push.user_id` 等;服务端会按 `user_id`/`username` **确保存在** `mall_user_asset` 资产行) |
|
||||
| `username` | string | 否 | 展示冗余(同步到商城用户侧逻辑时使用) |
|
||||
| `yesterday_win_loss_net` | number | 否 | 昨日净输赢(仅当 `< 0` 时计算新增保障金) |
|
||||
| `yesterday_total_deposit` | number | 否 | 昨日总充值(用于计算今日可领取上限) |
|
||||
@@ -178,186 +181,40 @@ curl -X POST 'http://localhost:1818/api/v1/mall/dailyPush' \
|
||||
|
||||
---
|
||||
|
||||
## 2. PlayX -> 积分商城(商城调用 PlayX)
|
||||
## 2. 积分商城 → PlayX(贵方需提供的 HTTP 接口)
|
||||
|
||||
> 下面这些接口由 PlayX 提供。商城侧仅按“请求参数 + 期望返回判定条件”发起调用与处理结果。
|
||||
> **说明**:H5 调商城的 **`/api/v1/mall/verifyToken`** 在配置 **`playx.verify_token_local_only=true`**(默认)时**不会请求**本节接口,而是在商城内校验 `muser` token;远程对接 PlayX 时见 **3.3** 与下文 **2.1**。
|
||||
商城在验 Token、红利发放、交易轮询、Angpow 导入等场景会 **主动请求 PlayX / Cash Market**。
|
||||
**完整 URL、请求/响应字段、成功判定、与 Angpush 双路径关系、联调待办** 已单独整理,便于 **直接转发给 PlayX 平台**:
|
||||
|
||||
### 2.1 Token Verification API(PlayX 侧实现,远程验证时使用)
|
||||
* 方法:`POST`
|
||||
* URL:`${playx.api.base_url}${playx.api.token_verify_url}`
|
||||
* 默认:`/api/v1/auth/verify-token`
|
||||
- **`docs/PlayX-接口待完善清单.md`**
|
||||
|
||||
#### 请求 Body(商城侧发送)
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `request_id` | string | 是 | 形如 `mall_{uniqid}` |
|
||||
| `token` | string | 是 | 前端传入的 PlayX token |
|
||||
|
||||
#### 返回(期望)
|
||||
商城侧校验:
|
||||
* HTTP 状态码必须为 `200`
|
||||
* 且响应体中必须包含 `user_id`
|
||||
|
||||
期望字段(示例):
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `user_id` | string | 必选 |
|
||||
| `username` | string | 可选 |
|
||||
| `token_expire_at` | string | 可选(能被 `strtotime` 解析) |
|
||||
|
||||
示例(成功):
|
||||
```json
|
||||
{
|
||||
"user_id": "U123",
|
||||
"username": "demo_user",
|
||||
"token_expire_at": "2026-04-01T12:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
示例(失败):
|
||||
```json
|
||||
{
|
||||
"message": "invalid token"
|
||||
}
|
||||
```
|
||||
本文 **§1** 仅描述「谁调用商城」;**§3** 描述「H5 调用商城」。
|
||||
|
||||
---
|
||||
|
||||
### 2.2 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 | 是 | `MallPlayxOrder.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` |
|
||||
|
||||
#### 返回(期望)
|
||||
商城侧判定:
|
||||
* HTTP 状态码 `200`
|
||||
* 且 `data.status === "accepted"`
|
||||
|
||||
成功时读取:
|
||||
* `data.playx_transaction_id`
|
||||
|
||||
失败时读取:
|
||||
* `data.message` 写入订单 `fail_reason`
|
||||
|
||||
示例(accepted):
|
||||
```json
|
||||
{
|
||||
"status": "accepted",
|
||||
"playx_transaction_id": "PX_TX_001"
|
||||
}
|
||||
```
|
||||
|
||||
示例(rejected):
|
||||
```json
|
||||
{
|
||||
"status": "rejected",
|
||||
"message": "insufficient balance"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.3 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 | 是 | `MallPlayxOrder.external_transaction_id` |
|
||||
| `user_id` | string | 是 | PlayX 用户 ID |
|
||||
| `amount` | number | 是 | `MallPlayxOrder.amount` |
|
||||
| `multiplier` | int | 是 | `MallPlayxOrder.multiplier` |
|
||||
|
||||
#### 返回(期望)
|
||||
与 Bonus Grant 一致:
|
||||
* `data.status === "accepted"` -> 读取 `playx_transaction_id`
|
||||
* 否则 -> 读取 `message` 写入 `fail_reason`
|
||||
|
||||
示例(accepted):
|
||||
```json
|
||||
{
|
||||
"status": "accepted",
|
||||
"playx_transaction_id": "PX_TX_002"
|
||||
}
|
||||
```
|
||||
|
||||
示例(rejected):
|
||||
```json
|
||||
{
|
||||
"status": "rejected",
|
||||
"message": "insufficient balance"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2.4 Transaction Status Query API(交易终态查询)
|
||||
* 方法:`GET`
|
||||
* URL:`${playx.api.base_url}${playx.api.transaction_status_url}`
|
||||
* 默认:`/api/v1/transaction/status`
|
||||
|
||||
#### Query 参数
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `externalTransactionId` | string | 是 | 订单幂等键 `external_transaction_id` |
|
||||
|
||||
#### 返回(期望)
|
||||
定时任务读取 `data.status`:
|
||||
* `COMPLETED`:商城将订单 `status` 更新为 `COMPLETED`
|
||||
* `FAILED` 或 `REJECTED`:商城将订单 `status=REJECTED`、`grant_status=FAILED_FINAL`,并退回积分
|
||||
* 失败信息取 `data.message` 写入订单 `fail_reason`
|
||||
|
||||
示例(completed):
|
||||
```json
|
||||
{ "status": "COMPLETED" }
|
||||
```
|
||||
|
||||
示例(failed):
|
||||
```json
|
||||
{ "status": "FAILED", "message": "grant rejected by PlayX" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 积分商城 -> H5(服务端提供给 H5 的接口)
|
||||
## 3. 积分商城 → H5(服务端提供给 H5 的接口)
|
||||
|
||||
### 3.0 数据模型说明(与代码一致)
|
||||
|
||||
* **商城用户**:表 `mall_user`(主键 `id`)。
|
||||
* **PlayX 资产扩展**:表 `mall_playx_user_asset`,与 `mall_user` **一对一**(`mall_user_id` 唯一,`playx_user_id` 唯一)。
|
||||
* **对外业务 ID**:接口里返回或订单里使用的 `user_id` 字符串多为 **PlayX 侧用户 ID**(`playx_user_id`);H5 临时登录场景若尚无真实 PlayX ID,会生成形如 **`mall_{mall_user.id}`** 的占位 ID(见 `temLogin`)。
|
||||
* **服务端内部**:`Playx` 控制器内部用 **`mall_user.id`**(整型)解析资产;`session_id` / `token` / `user_id` 会映射到该 `mall_user`。
|
||||
* **积分商城用户资产主表**:`mall_user_asset`(账号、积分、`playx_user_id` 等;H5 临时登录 `temLogin` 直接创建/复用该表行,**不依赖**独立会员 `user` 表)。
|
||||
* **会话缓存**:`mall_session`(字段含 `session_id`、`user_id`(此处存 **PlayX 侧用户标识字符串**,与 `mall_user_asset.playx_user_id` 一致)、`expire_time` 等)。
|
||||
* **统一订单**:`mall_order`(红利/实物/提现订单;`user_id` 字段为 **`playx_user_id` 字符串**)。
|
||||
* **对外业务 ID**:订单与推送中的 `user_id` 多为 **PlayX 用户 ID**(`playx_user_id`)。临时登录场景下,资产表会生成占位 ID,形如 **`mall_{mall_user_asset.id}`**(见 `MallUserAsset::ensureForUsername`)。
|
||||
|
||||
### 3.1 鉴权解析规则(`resolveMallUserIdFromRequest`)
|
||||
### 3.1 鉴权解析规则(`resolvePlayxAssetIdFromRequest`)
|
||||
|
||||
以下接口在服务端最终都会解析出 **商城用户 `mall_user.id`**,再按该用户查询 `mall_playx_user_asset` 等。
|
||||
以下接口在服务端最终都会解析出 **`mall_user_asset.id`(整型,资产表主键)**,再按该 ID 加载资产与关联数据。
|
||||
|
||||
优先级(由高到低):
|
||||
|
||||
1. **`session_id`**(`post` 优先,`get` 兼容)
|
||||
* 在 `mall_playx_session` 中存在且未过期:用会话里的 `user_id`(即 `playx_user_id`)在 `mall_playx_user_asset` 反查 `mall_user_id`。
|
||||
* 在 `mall_session` 中存在且未过期:用会话里的 `user_id`(`playx_user_id` 字符串)在 `mall_user_asset` 按 `playx_user_id` 查找,得到资产主键。
|
||||
* 若会话无效:兼容把 `session_id` 参数误当作 **商城 token** 再试一次(UUID 形态 token)。
|
||||
2. **`token`**(`post` / `get` 或请求头 **`ba-token`** / **`token`**)
|
||||
* 校验 `token` 表:类型为会员 `user` 或商城临时 **`muser`**(`mall_user` 登录),未过期则 `user_id` 字段即为 **`mall_user.id`**。
|
||||
* 校验 `token` 表:类型为会员 `user` 或商城临时 **`muser`**。未过期时,`user_id` 字段为 **`mall_user_asset.id`**(`muser`)或会员体系约定 ID(`user`)。
|
||||
3. **`user_id`**(`post` / `get` 兼容)
|
||||
* **纯数字**:视为 **`mall_user.id`**。
|
||||
* **非纯数字**:视为 **`playx_user_id`**,在 `mall_playx_user_asset` 查找对应 `mall_user_id`。
|
||||
* **纯数字**:视为 **`mall_user_asset.id`**。
|
||||
* **非纯数字**:视为 **`playx_user_id`**,在 `mall_user_asset` 按该字段查找主键。
|
||||
|
||||
> 注意:请求参数的取值方式是 `post()` 优先,`get()` 兼容(即同字段既可传 post 也可传 get)。
|
||||
|
||||
@@ -373,19 +230,18 @@ curl -X POST 'http://localhost:1818/api/v1/mall/dailyPush' \
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `username` | string | 是 | 商城用户名(唯一);不存在则自动创建 `mall_user` |
|
||||
| `username` | string | 是 | 登录名(唯一);不存在则自动创建 `mall_user_asset` 行 |
|
||||
|
||||
#### 行为说明
|
||||
|
||||
* 若 `mall_user` 不存在:创建用户(随机占位手机号、随机密码等,与后台「商城用户」一致)。
|
||||
* **无论是否新用户**:保证存在 **`mall_playx_user_asset`** 一条记录(`MallPlayxUserAsset::ensureForMallUser`),`playx_user_id` 默认 **`mall_{mall_user.id}`**(与 PlayX 真实 ID 冲突概率低)。
|
||||
* 签发 **商城 token**(类型 **`muser`**,非会员表 `user`),并签发 `muser-refresh` 刷新令牌。
|
||||
* 若用户名不存在:`MallUserAsset::ensureForUsername` 创建资产行(随机密码等),并将 `playx_user_id` 更新为 **`mall_{id}`** 形式(与真实 PlayX ID 区分)。
|
||||
* 签发 **商城 token**(类型 **`muser`**,`token` 表内 `user_id` = **`mall_user_asset.id`**),并签发 `muser-refresh` 刷新令牌。
|
||||
|
||||
#### 返回(成功 data.userInfo)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | int | `mall_user.id` |
|
||||
| `id` | int | **`mall_user_asset.id`** |
|
||||
| `username` | string | 用户名 |
|
||||
| `nickname` | string | 同 `username` |
|
||||
| `playx_user_id` | string | 资产表中的 `playx_user_id`(如 `mall_12`) |
|
||||
@@ -413,10 +269,10 @@ curl -G 'http://localhost:1818/api/v1/temLogin' --data-urlencode 'username=demo_
|
||||
* 配置项:`config/playx.php` → **`verify_token_local_only`**(环境变量 **`PLAYX_VERIFY_TOKEN_LOCAL_ONLY`**,未设置时默认为 **`1` / 开启本地验证)。
|
||||
* **`verify_token_local_only = true`(默认)**
|
||||
* **不请求** PlayX HTTP。
|
||||
* 仅接受商城临时登录 token(类型 **`muser`**),校验 `token` 表后,根据 `mall_user` 与 `mall_playx_user_asset` 写入 `mall_playx_session`。
|
||||
* 返回的 `data.user_id` 为 **`playx_user_id`**(无资产记录时回退为 `mall_user.id` 字符串,一般 temLogin 后已有资产)。
|
||||
* 仅接受商城临时登录 token(类型 **`muser`**),校验 `token` 表后写入 **`mall_session`**。
|
||||
* 返回的 `data.user_id` 为 **`playx_user_id`**(与资产表一致)。
|
||||
* **`verify_token_local_only = false`**(生产对接 PlayX)
|
||||
* 需配置 **`playx.api.base_url`**,由商城向 PlayX 发起 `POST` 校验(见下文「远程模式」)。
|
||||
* 需配置 **`playx.api.base_url`**,由商城向 PlayX 发起 `POST` 校验(请求/响应约定见 **`docs/PlayX-接口待完善清单.md`** 第一部分 §1)。
|
||||
* 若未配置 `base_url`,返回 `PlayX API not configured`。
|
||||
|
||||
#### 请求参数
|
||||
@@ -429,7 +285,7 @@ curl -G 'http://localhost:1818/api/v1/temLogin' --data-urlencode 'username=demo_
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `session_id` | string | 写入 `mall_playx_session` |
|
||||
| `session_id` | string | 写入 `mall_session` |
|
||||
| `user_id` | string | PlayX 用户 ID(即 `playx_user_id`,会话内与订单/推送一致) |
|
||||
| `username` | string | 用户名 |
|
||||
| `token_expire_at` | string | ISO 字符串(服务端 `date('c', expireAt)`) |
|
||||
@@ -484,37 +340,6 @@ Body:`id` 必填,其余字段按需传入更新。
|
||||
|
||||
Body:`id` 必填。若删除默认地址,服务端会自动挑选一条剩余地址设为默认(如存在)。
|
||||
|
||||
#### 远程模式(`verify_token_local_only=false` + 已配置 `base_url`)
|
||||
|
||||
商城侧请求 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`
|
||||
|
||||
响应(成功示例):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 1,
|
||||
"msg": "",
|
||||
"data": {
|
||||
"session_id": "7b1c....",
|
||||
"user_id": "U123",
|
||||
"username": "demo_user",
|
||||
"token_expire_at": "2026-04-01T12:00:00+00:00"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.4 用户资产(Assets)
|
||||
@@ -527,7 +352,7 @@ Body:`id` 必填。若删除默认地址,服务端会自动挑选一条剩
|
||||
|
||||
* `session_id`
|
||||
* `token`(或请求头 `ba-token` / `token`)
|
||||
* `user_id`(纯数字为 `mall_user.id`,否则为 `playx_user_id`)
|
||||
* `user_id`(纯数字为 **`mall_user_asset.id`**,否则为 **`playx_user_id`**)
|
||||
|
||||
#### 返回(成功 data)
|
||||
若未找到资产:返回 0。
|
||||
@@ -784,7 +609,7 @@ curl -X POST 'http://localhost:1818/api/v1/mall/withdrawApply' \
|
||||
#### 返回(成功 data)
|
||||
|
||||
* `list`:订单列表(最多 100 条),并包含关联的 `mallItem`(关系对象)
|
||||
* 列表项中的 `user_id` 为 **PlayX 侧 `playx_user_id`**(字符串),与 `mall_playx_order.user_id` 一致
|
||||
* 列表项中的 `user_id` 为 **PlayX 侧 `playx_user_id`**(字符串),与 `mall_order.user_id` 一致
|
||||
|
||||
#### 示例
|
||||
请求:
|
||||
|
||||
@@ -526,7 +526,7 @@ curl -G 'http://localhost:1818/api/v1/mall/orders' --data-urlencode 'session_id=
|
||||
- 发货:录入物流公司、单号 → `SHIPPED`
|
||||
- 驳回:录入驳回原因 → `REJECTED`,自动退回积分
|
||||
- **红利/提现订单**:
|
||||
- 展示 `external_transaction_id`、`playx_transaction_id`、推送playx
|
||||
- 展示 `external_transaction_id`、推送 playx 状态
|
||||
- 手动重试:仅对 `FAILED_RETRYABLE` 状态,记录 `retry_request_id`、操作者、原因
|
||||
|
||||
### 2.3 用户资产与人工调账
|
||||
@@ -613,8 +613,7 @@ curl -G 'http://localhost:1818/api/v1/mall/orders' --data-urlencode 'session_id=
|
||||
| points_cost | int | 消耗积分 |
|
||||
| amount | decimal(15,2) | 现金面值(红利/提现) |
|
||||
| multiplier | int | 流水倍数 |
|
||||
| external_transaction_id | varchar(64) | 订单号 |
|
||||
| playx_transaction_id | varchar(64) | PlayX 流水号 |
|
||||
| external_transaction_id | varchar(64) | 订单号(商城侧幂等/对账主键;Angpow 流程不单独落库 PlayX 内部流水号) |
|
||||
| grant_status | enum | NOT_SENT / SENT_PENDING / ACCEPTED / FAILED_RETRYABLE / FAILED_FINAL |
|
||||
| fail_reason | text | 失败原因 |
|
||||
| retry_count | int | 重试次数 |
|
||||
|
||||
Reference in New Issue
Block a user