1.优化后台页面样式
2.优化统一订单中红利的状态和失败原因 3.移除项目中冗余代码和字段
This commit is contained in:
@@ -26,7 +26,7 @@ class Order extends Backend
|
|||||||
|
|
||||||
protected array $withJoinTable = ['mallItem'];
|
protected array $withJoinTable = ['mallItem'];
|
||||||
|
|
||||||
protected string|array $quickSearchField = ['user_id', 'external_transaction_id', 'playx_transaction_id'];
|
protected string|array $quickSearchField = ['user_id', 'external_transaction_id'];
|
||||||
|
|
||||||
protected string|array $indexField = [
|
protected string|array $indexField = [
|
||||||
'id',
|
'id',
|
||||||
@@ -38,7 +38,6 @@ class Order extends Backend
|
|||||||
'amount',
|
'amount',
|
||||||
'multiplier',
|
'multiplier',
|
||||||
'external_transaction_id',
|
'external_transaction_id',
|
||||||
'playx_transaction_id',
|
|
||||||
'grant_status',
|
'grant_status',
|
||||||
'fail_reason',
|
'fail_reason',
|
||||||
'reject_reason',
|
'reject_reason',
|
||||||
@@ -301,7 +300,7 @@ class Order extends Backend
|
|||||||
$result = MallBonusGrantPush::push($order);
|
$result = MallBonusGrantPush::push($order);
|
||||||
if ($result['ok']) {
|
if ($result['ok']) {
|
||||||
$order->grant_status = MallOrder::GRANT_ACCEPTED;
|
$order->grant_status = MallOrder::GRANT_ACCEPTED;
|
||||||
$order->playx_transaction_id = $result['playx_transaction_id'];
|
$order->status = MallOrder::STATUS_COMPLETED;
|
||||||
$order->fail_reason = null;
|
$order->fail_reason = null;
|
||||||
$order->update_time = time();
|
$order->update_time = time();
|
||||||
$order->save();
|
$order->save();
|
||||||
|
|||||||
@@ -100,4 +100,11 @@ return [
|
|||||||
'PlayX API not configured' => 'PlayX API not configured',
|
'PlayX API not configured' => 'PlayX API not configured',
|
||||||
'Current grant status cannot be manually pushed' => 'Current grant status cannot be manually pushed',
|
'Current grant status cannot be manually pushed' => 'Current grant status cannot be manually pushed',
|
||||||
'Order status must be PENDING' => 'Order status must be PENDING',
|
'Order status must be PENDING' => 'Order status must be PENDING',
|
||||||
|
'Missing required fields' => 'Missing required fields',
|
||||||
|
'Order type not PHYSICAL' => 'Order type is not physical goods',
|
||||||
|
'Order type not supported' => 'Order type not supported',
|
||||||
|
'Only BONUS can retry' => 'Only bonus orders can retry push',
|
||||||
|
'Shipped successfully' => 'Shipped successfully',
|
||||||
|
'Approved successfully' => 'Approved successfully',
|
||||||
|
'Rejected successfully' => 'Rejected successfully',
|
||||||
];
|
];
|
||||||
@@ -119,4 +119,11 @@ return [
|
|||||||
'PlayX API not configured' => 'PlayX 接口未配置',
|
'PlayX API not configured' => 'PlayX 接口未配置',
|
||||||
'Current grant status cannot be manually pushed' => '当前发放状态不可手动推送',
|
'Current grant status cannot be manually pushed' => '当前发放状态不可手动推送',
|
||||||
'Order status must be PENDING' => '订单状态须为处理中',
|
'Order status must be PENDING' => '订单状态须为处理中',
|
||||||
|
'Missing required fields' => '缺少必填项',
|
||||||
|
'Order type not PHYSICAL' => '订单类型不是实物',
|
||||||
|
'Order type not supported' => '订单类型不支持',
|
||||||
|
'Only BONUS can retry' => '仅红利订单可重试推送',
|
||||||
|
'Shipped successfully' => '发货成功',
|
||||||
|
'Approved successfully' => '审核通过',
|
||||||
|
'Rejected successfully' => '驳回成功',
|
||||||
];
|
];
|
||||||
@@ -1330,7 +1330,6 @@ SQL;
|
|||||||
]);
|
]);
|
||||||
$data = json_decode(strval($res->getBody()), true);
|
$data = json_decode(strval($res->getBody()), true);
|
||||||
if ($res->getStatusCode() === 200 && ($data['status'] ?? '') === 'accepted') {
|
if ($res->getStatusCode() === 200 && ($data['status'] ?? '') === 'accepted') {
|
||||||
$order->playx_transaction_id = $data['playx_transaction_id'] ?? '';
|
|
||||||
$order->grant_status = MallOrder::GRANT_ACCEPTED;
|
$order->grant_status = MallOrder::GRANT_ACCEPTED;
|
||||||
$order->save();
|
$order->save();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use GuzzleHttp\Client;
|
|||||||
final class MallBonusGrantPush
|
final class MallBonusGrantPush
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @return array{ok: bool, message: string, playx_transaction_id: string}
|
* @return array{ok: bool, message: string}
|
||||||
*/
|
*/
|
||||||
public static function push(MallOrder $order): array
|
public static function push(MallOrder $order): array
|
||||||
{
|
{
|
||||||
@@ -24,7 +24,6 @@ final class MallBonusGrantPush
|
|||||||
return [
|
return [
|
||||||
'ok' => false,
|
'ok' => false,
|
||||||
'message' => 'PlayX angpow_import not configured',
|
'message' => 'PlayX angpow_import not configured',
|
||||||
'playx_transaction_id' => '',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +35,6 @@ final class MallBonusGrantPush
|
|||||||
return [
|
return [
|
||||||
'ok' => false,
|
'ok' => false,
|
||||||
'message' => 'PlayX Angpow Import API not configured',
|
'message' => 'PlayX Angpow Import API not configured',
|
||||||
'playx_transaction_id' => '',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +45,6 @@ final class MallBonusGrantPush
|
|||||||
return [
|
return [
|
||||||
'ok' => false,
|
'ok' => false,
|
||||||
'message' => 'User asset not found',
|
'message' => 'User asset not found',
|
||||||
'playx_transaction_id' => '',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +53,6 @@ final class MallBonusGrantPush
|
|||||||
return [
|
return [
|
||||||
'ok' => false,
|
'ok' => false,
|
||||||
'message' => 'Item not found',
|
'message' => 'Item not found',
|
||||||
'playx_transaction_id' => '',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +63,6 @@ final class MallBonusGrantPush
|
|||||||
return [
|
return [
|
||||||
'ok' => false,
|
'ok' => false,
|
||||||
'message' => 'Build signature failed',
|
'message' => 'Build signature failed',
|
||||||
'playx_transaction_id' => '',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,20 +118,17 @@ final class MallBonusGrantPush
|
|||||||
return [
|
return [
|
||||||
'ok' => true,
|
'ok' => true,
|
||||||
'message' => '',
|
'message' => '',
|
||||||
'playx_transaction_id' => '',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'ok' => false,
|
'ok' => false,
|
||||||
'message' => strval($data['message'] ?? 'PlayX angpow import not accepted'),
|
'message' => strval($data['message'] ?? 'PlayX angpow import not accepted'),
|
||||||
'playx_transaction_id' => '',
|
|
||||||
];
|
];
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return [
|
return [
|
||||||
'ok' => false,
|
'ok' => false,
|
||||||
'message' => $e->getMessage(),
|
'message' => $e->getMessage(),
|
||||||
'playx_transaction_id' => '',
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ use support\think\Model;
|
|||||||
* @property float $amount
|
* @property float $amount
|
||||||
* @property int $multiplier
|
* @property int $multiplier
|
||||||
* @property string $external_transaction_id
|
* @property string $external_transaction_id
|
||||||
* @property string $playx_transaction_id
|
|
||||||
* @property string $grant_status
|
* @property string $grant_status
|
||||||
* @property string|null $fail_reason
|
* @property string|null $fail_reason
|
||||||
* @property int $retry_count
|
* @property int $retry_count
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ class AngpowImportJobs
|
|||||||
if ($code === '0' || $code === 0) {
|
if ($code === '0' || $code === 0) {
|
||||||
MallOrder::whereIn('id', $orderIds)->update([
|
MallOrder::whereIn('id', $orderIds)->update([
|
||||||
'grant_status' => MallOrder::GRANT_ACCEPTED,
|
'grant_status' => MallOrder::GRANT_ACCEPTED,
|
||||||
|
'status' => MallOrder::STATUS_COMPLETED,
|
||||||
'fail_reason' => null,
|
'fail_reason' => null,
|
||||||
'update_time' => time(),
|
'update_time' => time(),
|
||||||
]);
|
]);
|
||||||
@@ -265,6 +266,21 @@ class AngpowImportJobs
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单条失败原因压成一行,避免异常信息自带换行导致与 attempt 分段混在一起。
|
||||||
|
*/
|
||||||
|
private function normalizeReasonLine(string $reason): string
|
||||||
|
{
|
||||||
|
$s = trim($reason);
|
||||||
|
if ($s === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
$s = str_replace(["\r\n", "\r", "\n"], ' ', $s);
|
||||||
|
$replaced = preg_replace('/\s+/', ' ', $s);
|
||||||
|
|
||||||
|
return is_string($replaced) && $replaced !== '' ? $replaced : $s;
|
||||||
|
}
|
||||||
|
|
||||||
private function markFailedAttempt(MallOrder $order, string $reason): void
|
private function markFailedAttempt(MallOrder $order, string $reason): void
|
||||||
{
|
{
|
||||||
// retry_count 在“准备发送”阶段已 +1;此处用当前 retry_count 作为 attempt 编号
|
// retry_count 在“准备发送”阶段已 +1;此处用当前 retry_count 作为 attempt 编号
|
||||||
@@ -277,7 +293,7 @@ class AngpowImportJobs
|
|||||||
|
|
||||||
$prev = $order->fail_reason;
|
$prev = $order->fail_reason;
|
||||||
$prefix = 'attempt ' . $attempt . ': ';
|
$prefix = 'attempt ' . $attempt . ': ';
|
||||||
$line = $prefix . $reason;
|
$line = $prefix . $this->normalizeReasonLine($reason);
|
||||||
$newReason = $line;
|
$newReason = $line;
|
||||||
if (is_string($prev) && $prev !== '') {
|
if (is_string($prev) && $prev !== '') {
|
||||||
$newReason = $prev . "\n" . $line;
|
$newReason = $prev . "\n" . $line;
|
||||||
|
|||||||
@@ -171,7 +171,8 @@ class PlayxJobs
|
|||||||
$result = MallBonusGrantPush::push($order);
|
$result = MallBonusGrantPush::push($order);
|
||||||
if ($result['ok']) {
|
if ($result['ok']) {
|
||||||
$order->grant_status = MallOrder::GRANT_ACCEPTED;
|
$order->grant_status = MallOrder::GRANT_ACCEPTED;
|
||||||
$order->playx_transaction_id = $result['playx_transaction_id'];
|
$order->status = MallOrder::STATUS_COMPLETED;
|
||||||
|
$order->update_time = time();
|
||||||
$order->save();
|
$order->save();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -2,14 +2,17 @@
|
|||||||
|
|
||||||
说明:本文档严格依据当前代码 `app/api/controller/v1/Playx.php`、`app/api/controller/v1/Auth.php`(临时登录)、`config/playx.php` 与定时任务 `app/process/PlayxJobs.php` 整理。
|
说明:本文档严格依据当前代码 `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
|
### 1.1 Daily Push API
|
||||||
* 方法:`POST`
|
* 方法:`POST`
|
||||||
@@ -36,8 +39,8 @@
|
|||||||
| 字段 | 类型 | 必填 | 说明 |
|
| 字段 | 类型 | 必填 | 说明 |
|
||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
| `request_id` | string | 是 | 外部推送请求号(原样返回) |
|
| `request_id` | string | 是 | 外部推送请求号(原样返回) |
|
||||||
| `date` | string(YYYY-MM-DD) | 是 | 业务日期(入库到 `mall_playx_daily_push.date`) |
|
| `date` | string(YYYY-MM-DD) | 是 | 业务日期(入库到 `mall_daily_push.date`) |
|
||||||
| `user_id` | string | 是 | PlayX 用户 ID(用于幂等;入库 `mall_playx_daily_push.user_id` 等;服务端会映射/创建 `mall_user` 与 `mall_playx_user_asset`) |
|
| `user_id` | string | 是 | PlayX 用户 ID(用于幂等;入库 `mall_daily_push.user_id` 等;服务端会按 `user_id`/`username` **确保存在** `mall_user_asset` 资产行) |
|
||||||
| `username` | string | 否 | 展示冗余(同步到商城用户侧逻辑时使用) |
|
| `username` | string | 否 | 展示冗余(同步到商城用户侧逻辑时使用) |
|
||||||
| `yesterday_win_loss_net` | number | 否 | 昨日净输赢(仅当 `< 0` 时计算新增保障金) |
|
| `yesterday_win_loss_net` | number | 否 | 昨日净输赢(仅当 `< 0` 时计算新增保障金) |
|
||||||
| `yesterday_total_deposit` | number | 否 | 昨日总充值(用于计算今日可领取上限) |
|
| `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 提供。商城侧仅按“请求参数 + 期望返回判定条件”发起调用与处理结果。
|
商城在验 Token、红利发放、交易轮询、Angpow 导入等场景会 **主动请求 PlayX / Cash Market**。
|
||||||
> **说明**:H5 调商城的 **`/api/v1/mall/verifyToken`** 在配置 **`playx.verify_token_local_only=true`**(默认)时**不会请求**本节接口,而是在商城内校验 `muser` token;远程对接 PlayX 时见 **3.3** 与下文 **2.1**。
|
**完整 URL、请求/响应字段、成功判定、与 Angpush 双路径关系、联调待办** 已单独整理,便于 **直接转发给 PlayX 平台**:
|
||||||
|
|
||||||
### 2.1 Token Verification API(PlayX 侧实现,远程验证时使用)
|
- **`docs/PlayX-接口待完善清单.md`**
|
||||||
* 方法:`POST`
|
|
||||||
* URL:`${playx.api.base_url}${playx.api.token_verify_url}`
|
|
||||||
* 默认:`/api/v1/auth/verify-token`
|
|
||||||
|
|
||||||
#### 请求 Body(商城侧发送)
|
本文 **§1** 仅描述「谁调用商城」;**§3** 描述「H5 调用商城」。
|
||||||
| 字段 | 类型 | 必填 | 说明 |
|
|
||||||
|------|------|------|------|
|
|
||||||
| `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"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 2.2 Bonus Grant API
|
## 3. 积分商城 → H5(服务端提供给 H5 的接口)
|
||||||
* 方法:`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.0 数据模型说明(与代码一致)
|
### 3.0 数据模型说明(与代码一致)
|
||||||
|
|
||||||
* **商城用户**:表 `mall_user`(主键 `id`)。
|
* **积分商城用户资产主表**:`mall_user_asset`(账号、积分、`playx_user_id` 等;H5 临时登录 `temLogin` 直接创建/复用该表行,**不依赖**独立会员 `user` 表)。
|
||||||
* **PlayX 资产扩展**:表 `mall_playx_user_asset`,与 `mall_user` **一对一**(`mall_user_id` 唯一,`playx_user_id` 唯一)。
|
* **会话缓存**:`mall_session`(字段含 `session_id`、`user_id`(此处存 **PlayX 侧用户标识字符串**,与 `mall_user_asset.playx_user_id` 一致)、`expire_time` 等)。
|
||||||
* **对外业务 ID**:接口里返回或订单里使用的 `user_id` 字符串多为 **PlayX 侧用户 ID**(`playx_user_id`);H5 临时登录场景若尚无真实 PlayX ID,会生成形如 **`mall_{mall_user.id}`** 的占位 ID(见 `temLogin`)。
|
* **统一订单**:`mall_order`(红利/实物/提现订单;`user_id` 字段为 **`playx_user_id` 字符串**)。
|
||||||
* **服务端内部**:`Playx` 控制器内部用 **`mall_user.id`**(整型)解析资产;`session_id` / `token` / `user_id` 会映射到该 `mall_user`。
|
* **对外业务 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` 兼容)
|
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)。
|
* 若会话无效:兼容把 `session_id` 参数误当作 **商城 token** 再试一次(UUID 形态 token)。
|
||||||
2. **`token`**(`post` / `get` 或请求头 **`ba-token`** / **`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` 兼容)
|
3. **`user_id`**(`post` / `get` 兼容)
|
||||||
* **纯数字**:视为 **`mall_user.id`**。
|
* **纯数字**:视为 **`mall_user_asset.id`**。
|
||||||
* **非纯数字**:视为 **`playx_user_id`**,在 `mall_playx_user_asset` 查找对应 `mall_user_id`。
|
* **非纯数字**:视为 **`playx_user_id`**,在 `mall_user_asset` 按该字段查找主键。
|
||||||
|
|
||||||
> 注意:请求参数的取值方式是 `post()` 优先,`get()` 兼容(即同字段既可传 post 也可传 get)。
|
> 注意:请求参数的取值方式是 `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` 不存在:创建用户(随机占位手机号、随机密码等,与后台「商城用户」一致)。
|
* 若用户名不存在:`MallUserAsset::ensureForUsername` 创建资产行(随机密码等),并将 `playx_user_id` 更新为 **`mall_{id}`** 形式(与真实 PlayX ID 区分)。
|
||||||
* **无论是否新用户**:保证存在 **`mall_playx_user_asset`** 一条记录(`MallPlayxUserAsset::ensureForMallUser`),`playx_user_id` 默认 **`mall_{mall_user.id}`**(与 PlayX 真实 ID 冲突概率低)。
|
* 签发 **商城 token**(类型 **`muser`**,`token` 表内 `user_id` = **`mall_user_asset.id`**),并签发 `muser-refresh` 刷新令牌。
|
||||||
* 签发 **商城 token**(类型 **`muser`**,非会员表 `user`),并签发 `muser-refresh` 刷新令牌。
|
|
||||||
|
|
||||||
#### 返回(成功 data.userInfo)
|
#### 返回(成功 data.userInfo)
|
||||||
|
|
||||||
| 字段 | 类型 | 说明 |
|
| 字段 | 类型 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `id` | int | `mall_user.id` |
|
| `id` | int | **`mall_user_asset.id`** |
|
||||||
| `username` | string | 用户名 |
|
| `username` | string | 用户名 |
|
||||||
| `nickname` | string | 同 `username` |
|
| `nickname` | string | 同 `username` |
|
||||||
| `playx_user_id` | string | 资产表中的 `playx_user_id`(如 `mall_12`) |
|
| `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` / 开启本地验证)。
|
* 配置项:`config/playx.php` → **`verify_token_local_only`**(环境变量 **`PLAYX_VERIFY_TOKEN_LOCAL_ONLY`**,未设置时默认为 **`1` / 开启本地验证)。
|
||||||
* **`verify_token_local_only = true`(默认)**
|
* **`verify_token_local_only = true`(默认)**
|
||||||
* **不请求** PlayX HTTP。
|
* **不请求** PlayX HTTP。
|
||||||
* 仅接受商城临时登录 token(类型 **`muser`**),校验 `token` 表后,根据 `mall_user` 与 `mall_playx_user_asset` 写入 `mall_playx_session`。
|
* 仅接受商城临时登录 token(类型 **`muser`**),校验 `token` 表后写入 **`mall_session`**。
|
||||||
* 返回的 `data.user_id` 为 **`playx_user_id`**(无资产记录时回退为 `mall_user.id` 字符串,一般 temLogin 后已有资产)。
|
* 返回的 `data.user_id` 为 **`playx_user_id`**(与资产表一致)。
|
||||||
* **`verify_token_local_only = false`**(生产对接 PlayX)
|
* **`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`。
|
* 若未配置 `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`,会话内与订单/推送一致) |
|
| `user_id` | string | PlayX 用户 ID(即 `playx_user_id`,会话内与订单/推送一致) |
|
||||||
| `username` | string | 用户名 |
|
| `username` | string | 用户名 |
|
||||||
| `token_expire_at` | string | ISO 字符串(服务端 `date('c', expireAt)`) |
|
| `token_expire_at` | string | ISO 字符串(服务端 `date('c', expireAt)`) |
|
||||||
@@ -484,37 +340,6 @@ Body:`id` 必填,其余字段按需传入更新。
|
|||||||
|
|
||||||
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)
|
### 3.4 用户资产(Assets)
|
||||||
@@ -527,7 +352,7 @@ Body:`id` 必填。若删除默认地址,服务端会自动挑选一条剩
|
|||||||
|
|
||||||
* `session_id`
|
* `session_id`
|
||||||
* `token`(或请求头 `ba-token` / `token`)
|
* `token`(或请求头 `ba-token` / `token`)
|
||||||
* `user_id`(纯数字为 `mall_user.id`,否则为 `playx_user_id`)
|
* `user_id`(纯数字为 **`mall_user_asset.id`**,否则为 **`playx_user_id`**)
|
||||||
|
|
||||||
#### 返回(成功 data)
|
#### 返回(成功 data)
|
||||||
若未找到资产:返回 0。
|
若未找到资产:返回 0。
|
||||||
@@ -784,7 +609,7 @@ curl -X POST 'http://localhost:1818/api/v1/mall/withdrawApply' \
|
|||||||
#### 返回(成功 data)
|
#### 返回(成功 data)
|
||||||
|
|
||||||
* `list`:订单列表(最多 100 条),并包含关联的 `mallItem`(关系对象)
|
* `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`
|
- 发货:录入物流公司、单号 → `SHIPPED`
|
||||||
- 驳回:录入驳回原因 → `REJECTED`,自动退回积分
|
- 驳回:录入驳回原因 → `REJECTED`,自动退回积分
|
||||||
- **红利/提现订单**:
|
- **红利/提现订单**:
|
||||||
- 展示 `external_transaction_id`、`playx_transaction_id`、推送playx
|
- 展示 `external_transaction_id`、推送 playx 状态
|
||||||
- 手动重试:仅对 `FAILED_RETRYABLE` 状态,记录 `retry_request_id`、操作者、原因
|
- 手动重试:仅对 `FAILED_RETRYABLE` 状态,记录 `retry_request_id`、操作者、原因
|
||||||
|
|
||||||
### 2.3 用户资产与人工调账
|
### 2.3 用户资产与人工调账
|
||||||
@@ -613,8 +613,7 @@ curl -G 'http://localhost:1818/api/v1/mall/orders' --data-urlencode 'session_id=
|
|||||||
| points_cost | int | 消耗积分 |
|
| points_cost | int | 消耗积分 |
|
||||||
| amount | decimal(15,2) | 现金面值(红利/提现) |
|
| amount | decimal(15,2) | 现金面值(红利/提现) |
|
||||||
| multiplier | int | 流水倍数 |
|
| multiplier | int | 流水倍数 |
|
||||||
| external_transaction_id | varchar(64) | 订单号 |
|
| external_transaction_id | varchar(64) | 订单号(商城侧幂等/对账主键;Angpow 流程不单独落库 PlayX 内部流水号) |
|
||||||
| playx_transaction_id | varchar(64) | PlayX 流水号 |
|
|
||||||
| grant_status | enum | NOT_SENT / SENT_PENDING / ACCEPTED / FAILED_RETRYABLE / FAILED_FINAL |
|
| grant_status | enum | NOT_SENT / SENT_PENDING / ACCEPTED / FAILED_RETRYABLE / FAILED_FINAL |
|
||||||
| fail_reason | text | 失败原因 |
|
| fail_reason | text | 失败原因 |
|
||||||
| retry_count | int | 重试次数 |
|
| retry_count | int | 重试次数 |
|
||||||
|
|||||||
75
web/src/components/table/fieldRender/failReason.vue
Normal file
75
web/src/components/table/fieldRender/failReason.vue
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<template>
|
||||||
|
<div class="mall-order-fail-reason-table-cell">
|
||||||
|
<template v-if="fullText !== ''">
|
||||||
|
<el-tooltip placement="top" effect="dark" popper-class="mall-order-reason-tooltip" :enterable="true">
|
||||||
|
<template #content>
|
||||||
|
<div class="mall-order-fail-reason-tooltip-body">{{ fullText }}</div>
|
||||||
|
</template>
|
||||||
|
<span class="mall-order-fail-reason-ellipsis">{{ displayText }}</span>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { TableColumnCtx } from 'element-plus'
|
||||||
|
import { getCellValue } from '/@/components/table/index'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
row: TableRow
|
||||||
|
field: TableColumn
|
||||||
|
column: TableColumnCtx<TableRow>
|
||||||
|
index: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
/** 与后台换行一致;无换行时在 attempt N: 前补行首,便于阅读 */
|
||||||
|
function normalizeFailReasonText(raw: unknown): string {
|
||||||
|
if (raw === null || raw === undefined) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
let t = String(raw)
|
||||||
|
.replace(/\r\n/g, '\n')
|
||||||
|
.replace(/\r/g, '\n')
|
||||||
|
t = t.replace(/([^\n])(attempt\s+\d+:)/gi, '$1\n$2')
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawCell = computed(() => getCellValue(props.row, props.field, props.column, props.index))
|
||||||
|
|
||||||
|
const fullText = computed(() => normalizeFailReasonText(rawCell.value))
|
||||||
|
|
||||||
|
const displayText = computed(() => fullText.value)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.mall-order-fail-reason-table-cell {
|
||||||
|
width: 100%;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mall-order-fail-reason-ellipsis {
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.el-popper.mall-order-reason-tooltip {
|
||||||
|
max-width: min(560px, 90vw);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mall-order-reason-tooltip .mall-order-fail-reason-tooltip-body {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-word;
|
||||||
|
line-height: 1.5;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -19,7 +19,6 @@ export default {
|
|||||||
amount: 'Cash amount',
|
amount: 'Cash amount',
|
||||||
multiplier: 'Turnover multiplier',
|
multiplier: 'Turnover multiplier',
|
||||||
external_transaction_id: 'Order number',
|
external_transaction_id: 'Order number',
|
||||||
playx_transaction_id: 'PlayX transaction ID',
|
|
||||||
grant_status: 'Grant status',
|
grant_status: 'Grant status',
|
||||||
'grant_status NOT_SENT': 'Not sent',
|
'grant_status NOT_SENT': 'Not sent',
|
||||||
'grant_status SENT_PENDING': 'Sent (queued)',
|
'grant_status SENT_PENDING': 'Sent (queued)',
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ export default {
|
|||||||
amount: 'amount',
|
amount: 'amount',
|
||||||
multiplier: 'multiplier',
|
multiplier: 'multiplier',
|
||||||
external_transaction_id: 'external_transaction_id',
|
external_transaction_id: 'external_transaction_id',
|
||||||
playx_transaction_id: 'playx_transaction_id',
|
|
||||||
grant_status: 'grant_status',
|
grant_status: 'grant_status',
|
||||||
'grant_status NOT_SENT': 'NOT_SENT',
|
'grant_status NOT_SENT': 'NOT_SENT',
|
||||||
'grant_status SENT_PENDING': 'SENT_PENDING',
|
'grant_status SENT_PENDING': 'SENT_PENDING',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export default {
|
export default {
|
||||||
id: 'ID',
|
id: 'ID',
|
||||||
claim_request_id: '领取订单号',
|
claim_request_id: '领取订单号',
|
||||||
user_id: '用户ID',
|
user_id: 'Playx-ID',
|
||||||
claimed_amount: '领取积分',
|
claimed_amount: '领取积分',
|
||||||
create_time: '创建时间',
|
create_time: '创建时间',
|
||||||
'quick Search Fields': 'ID',
|
'quick Search Fields': 'ID',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export default {
|
export default {
|
||||||
id: 'ID',
|
id: 'ID',
|
||||||
user_id: '用户ID',
|
user_id: 'Playx-ID',
|
||||||
date: '业务日期',
|
date: '业务日期',
|
||||||
username: '用户名',
|
username: '用户名',
|
||||||
yesterday_win_loss_net: '昨日净输赢',
|
yesterday_win_loss_net: '昨日净输赢',
|
||||||
|
|||||||
@@ -3,23 +3,22 @@ export default {
|
|||||||
manual_retry: '手动重试',
|
manual_retry: '手动重试',
|
||||||
retry_confirm: '确认将该订单加入重试队列?',
|
retry_confirm: '确认将该订单加入重试队列?',
|
||||||
id: 'ID',
|
id: 'ID',
|
||||||
user_id: '用户ID',
|
user_id: 'Playx-ID',
|
||||||
type: '类型',
|
type: '类型',
|
||||||
'type BONUS': '红利(BONUS)',
|
'type BONUS': '红利(BONUS)',
|
||||||
'type PHYSICAL': '实物(PHYSICAL)',
|
'type PHYSICAL': '实物(PHYSICAL)',
|
||||||
'type WITHDRAW': '提现(WITHDRAW)',
|
'type WITHDRAW': '提现(WITHDRAW)',
|
||||||
status: '状态',
|
status: '状态',
|
||||||
'status PENDING': '处理中(PENDING)',
|
'status PENDING': '处理中',
|
||||||
'status COMPLETED': '已完成(COMPLETED)',
|
'status COMPLETED': '已完成',
|
||||||
'status SHIPPED': '已发货(SHIPPED)',
|
'status SHIPPED': '已发货',
|
||||||
'status REJECTED': '已驳回(REJECTED)',
|
'status REJECTED': '已驳回',
|
||||||
mall_item_id: '商品ID',
|
mall_item_id: '商品ID',
|
||||||
mallitem__title: '商品标题',
|
mallitem__title: '商品标题',
|
||||||
points_cost: '消耗积分',
|
points_cost: '消耗积分',
|
||||||
amount: '现金面值',
|
amount: '现金面值',
|
||||||
multiplier: '流水倍数',
|
multiplier: '流水倍数',
|
||||||
external_transaction_id: '订单号',
|
external_transaction_id: '订单号',
|
||||||
playx_transaction_id: 'PlayX流水号',
|
|
||||||
grant_status: '推送playx状态',
|
grant_status: '推送playx状态',
|
||||||
'grant_status NOT_SENT': '未发送',
|
'grant_status NOT_SENT': '未发送',
|
||||||
'grant_status SENT_PENDING': '已发送排队',
|
'grant_status SENT_PENDING': '已发送排队',
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export default {
|
export default {
|
||||||
id: 'ID',
|
id: 'ID',
|
||||||
claim_request_id: '领取订单号',
|
claim_request_id: '领取订单号',
|
||||||
user_id: '用户ID',
|
user_id: 'Playx-ID',
|
||||||
claimed_amount: '领取积分',
|
claimed_amount: '领取积分',
|
||||||
create_time: '创建时间',
|
create_time: '创建时间',
|
||||||
'quick Search Fields': 'ID',
|
'quick Search Fields': 'ID',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export default {
|
export default {
|
||||||
id: 'ID',
|
id: 'ID',
|
||||||
user_id: '用户ID',
|
user_id: 'Playx-ID',
|
||||||
date: '业务日期',
|
date: '业务日期',
|
||||||
username: '用户名',
|
username: '用户名',
|
||||||
yesterday_win_loss_net: '昨日净输赢',
|
yesterday_win_loss_net: '昨日净输赢',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export default {
|
export default {
|
||||||
id: 'ID',
|
id: 'ID',
|
||||||
user_id: '用户ID',
|
user_id: 'Playx-ID',
|
||||||
type: '类型',
|
type: '类型',
|
||||||
'type BONUS': '红利(BONUS)',
|
'type BONUS': '红利(BONUS)',
|
||||||
'type PHYSICAL': '实物(PHYSICAL)',
|
'type PHYSICAL': '实物(PHYSICAL)',
|
||||||
@@ -16,7 +16,6 @@ export default {
|
|||||||
amount: '现金面值',
|
amount: '现金面值',
|
||||||
multiplier: '流水倍数',
|
multiplier: '流水倍数',
|
||||||
external_transaction_id: '订单号',
|
external_transaction_id: '订单号',
|
||||||
playx_transaction_id: 'PlayX流水号',
|
|
||||||
grant_status: '推送playx状态',
|
grant_status: '推送playx状态',
|
||||||
'grant_status NOT_SENT': '未发送',
|
'grant_status NOT_SENT': '未发送',
|
||||||
'grant_status SENT_PENDING': '已发送排队',
|
'grant_status SENT_PENDING': '已发送排队',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export default {
|
|||||||
id: 'ID',
|
id: 'ID',
|
||||||
username: '用户名',
|
username: '用户名',
|
||||||
phone: '手机号',
|
phone: '手机号',
|
||||||
playx_user_id: 'PlayX用户ID',
|
playx_user_id: 'PlayX-ID',
|
||||||
locked_points: '待领取积分',
|
locked_points: '待领取积分',
|
||||||
available_points: '可用积分',
|
available_points: '可用积分',
|
||||||
today_limit: '今日可领取上限',
|
today_limit: '今日可领取上限',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export default {
|
|||||||
id: 'ID',
|
id: 'ID',
|
||||||
username: '用户名',
|
username: '用户名',
|
||||||
phone: '手机号',
|
phone: '手机号',
|
||||||
playx_user_id: 'PlayX用户ID',
|
playx_user_id: 'PlayX-ID',
|
||||||
locked_points: '待领取积分',
|
locked_points: '待领取积分',
|
||||||
available_points: '可用积分',
|
available_points: '可用积分',
|
||||||
today_limit: '今日可领取上限',
|
today_limit: '今日可领取上限',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ export default {
|
|||||||
'User name': '用户名',
|
'User name': '用户名',
|
||||||
'User nickname': '用户昵称',
|
'User nickname': '用户昵称',
|
||||||
balance: '余额',
|
balance: '余额',
|
||||||
'User ID': '用户ID',
|
'User ID': 'Playx-ID',
|
||||||
'Change balance': '变更余额',
|
'Change balance': '变更余额',
|
||||||
'Before change': '变更前',
|
'Before change': '变更前',
|
||||||
'After change': '变更后',
|
'After change': '变更后',
|
||||||
|
|||||||
@@ -12,6 +12,26 @@ import { SYSTEM_ZINDEX } from '/@/stores/constant/common'
|
|||||||
import { useUserInfo } from '/@/stores/userInfo'
|
import { useUserInfo } from '/@/stores/userInfo'
|
||||||
import { isAdminApp } from '/@/utils/common'
|
import { isAdminApp } from '/@/utils/common'
|
||||||
|
|
||||||
|
/** 与后台 LoadLangPack 一致:优先与当前 i18n 语言对齐,再回落到 config */
|
||||||
|
function resolveAdminThinkLang(): string {
|
||||||
|
const cfg = useConfig()
|
||||||
|
try {
|
||||||
|
const loc = i18n?.global?.locale?.value
|
||||||
|
if (loc !== undefined && loc !== null && loc !== '') {
|
||||||
|
const v = String(loc).toLowerCase().replace('_', '-')
|
||||||
|
if (v === 'zh-cn' || v === 'zh') {
|
||||||
|
return 'zh-cn'
|
||||||
|
}
|
||||||
|
if (v === 'en') {
|
||||||
|
return 'en'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// i18n 未就绪时忽略
|
||||||
|
}
|
||||||
|
return cfg.lang.defaultLang
|
||||||
|
}
|
||||||
|
|
||||||
window.requests = []
|
window.requests = []
|
||||||
window.tokenRefreshing = false
|
window.tokenRefreshing = false
|
||||||
const pendingMap = new Map()
|
const pendingMap = new Map()
|
||||||
@@ -50,7 +70,7 @@ function createAxios<Data = any, T = ApiPromise<Data>>(axiosConfig: AxiosRequest
|
|||||||
baseURL: getUrl(),
|
baseURL: getUrl(),
|
||||||
timeout: 1000 * 10,
|
timeout: 1000 * 10,
|
||||||
headers: {
|
headers: {
|
||||||
'think-lang': config.lang.defaultLang,
|
'think-lang': resolveAdminThinkLang(),
|
||||||
},
|
},
|
||||||
responseType: 'json',
|
responseType: 'json',
|
||||||
})
|
})
|
||||||
@@ -93,7 +113,7 @@ function createAxios<Data = any, T = ApiPromise<Data>>(axiosConfig: AxiosRequest
|
|||||||
if (token) (config.headers as anyObj).batoken = token
|
if (token) (config.headers as anyObj).batoken = token
|
||||||
const userToken = options.anotherToken || userInfo.getToken()
|
const userToken = options.anotherToken || userInfo.getToken()
|
||||||
if (userToken) (config.headers as anyObj)['ba-user-token'] = userToken
|
if (userToken) (config.headers as anyObj)['ba-user-token'] = userToken
|
||||||
;(config.headers as anyObj)['think-lang'] = useConfig().lang.defaultLang
|
;(config.headers as anyObj)['think-lang'] = resolveAdminThinkLang()
|
||||||
}
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ defineOptions({
|
|||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const tableRef = useTemplateRef('tableRef')
|
const tableRef = useTemplateRef('tableRef')
|
||||||
|
|
||||||
const optButtons: OptButton[] = defaultOptButtons(['edit', 'delete']).map((btn) =>
|
const optButtons: OptButton[] = defaultOptButtons(['edit', 'delete']).map((btn) =>
|
||||||
btn.name === 'edit'
|
btn.name === 'edit'
|
||||||
? {
|
? {
|
||||||
@@ -80,7 +81,7 @@ const baTable = new baTableClass(
|
|||||||
align: 'center',
|
align: 'center',
|
||||||
effect: 'dark',
|
effect: 'dark',
|
||||||
custom: { PENDING: 'success', COMPLETED: 'primary', SHIPPED: 'info', REJECTED: 'loading' },
|
custom: { PENDING: 'success', COMPLETED: 'primary', SHIPPED: 'info', REJECTED: 'loading' },
|
||||||
minWidth: 160,
|
minWidth: 100,
|
||||||
operator: 'eq',
|
operator: 'eq',
|
||||||
sortable: false,
|
sortable: false,
|
||||||
render: 'tag',
|
render: 'tag',
|
||||||
@@ -91,7 +92,14 @@ const baTable = new baTableClass(
|
|||||||
REJECTED: t('mall.order.status REJECTED'),
|
REJECTED: t('mall.order.status REJECTED'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ label: t('mall.order.mall_item_id'), prop: 'mall_item_id', align: 'center', operator: 'RANGE', sortable: false },
|
{
|
||||||
|
label: t('mall.order.mall_item_id'),
|
||||||
|
prop: 'mall_item_id',
|
||||||
|
align: 'center',
|
||||||
|
show: false,
|
||||||
|
operator: 'RANGE',
|
||||||
|
sortable: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: t('mall.order.mallitem__title'),
|
label: t('mall.order.mallitem__title'),
|
||||||
prop: 'mallItem.title',
|
prop: 'mallItem.title',
|
||||||
@@ -114,14 +122,6 @@ const baTable = new baTableClass(
|
|||||||
sortable: false,
|
sortable: false,
|
||||||
operator: 'LIKE',
|
operator: 'LIKE',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: t('mall.order.playx_transaction_id'),
|
|
||||||
prop: 'playx_transaction_id',
|
|
||||||
align: 'center',
|
|
||||||
operatorPlaceholder: t('Fuzzy query'),
|
|
||||||
sortable: false,
|
|
||||||
operator: 'LIKE',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: t('mall.order.grant_status'),
|
label: t('mall.order.grant_status'),
|
||||||
prop: 'grant_status',
|
prop: 'grant_status',
|
||||||
@@ -151,7 +151,8 @@ const baTable = new baTableClass(
|
|||||||
label: t('mall.order.fail_reason'),
|
label: t('mall.order.fail_reason'),
|
||||||
prop: 'fail_reason',
|
prop: 'fail_reason',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
showOverflowTooltip: true,
|
minWidth: 140,
|
||||||
|
render: 'failReason',
|
||||||
operatorPlaceholder: t('Fuzzy query'),
|
operatorPlaceholder: t('Fuzzy query'),
|
||||||
sortable: false,
|
sortable: false,
|
||||||
operator: 'LIKE',
|
operator: 'LIKE',
|
||||||
@@ -160,7 +161,8 @@ const baTable = new baTableClass(
|
|||||||
label: t('mall.order.reject_reason'),
|
label: t('mall.order.reject_reason'),
|
||||||
prop: 'reject_reason',
|
prop: 'reject_reason',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
showOverflowTooltip: true,
|
minWidth: 140,
|
||||||
|
render: 'failReason',
|
||||||
sortable: false,
|
sortable: false,
|
||||||
operator: 'LIKE',
|
operator: 'LIKE',
|
||||||
operatorPlaceholder: t('Fuzzy query'),
|
operatorPlaceholder: t('Fuzzy query'),
|
||||||
@@ -315,5 +317,3 @@ onMounted(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ const baTable = new baTableClass(
|
|||||||
{ label: t('mall.playxOrder.amount'), prop: 'amount', align: 'center', operator: 'RANGE', sortable: false },
|
{ label: t('mall.playxOrder.amount'), prop: 'amount', align: 'center', operator: 'RANGE', sortable: false },
|
||||||
{ label: t('mall.playxOrder.multiplier'), prop: 'multiplier', align: 'center', operator: 'eq', sortable: false },
|
{ label: t('mall.playxOrder.multiplier'), prop: 'multiplier', align: 'center', operator: 'eq', sortable: false },
|
||||||
{ label: t('mall.playxOrder.external_transaction_id'), prop: 'external_transaction_id', align: 'center', operatorPlaceholder: t('Fuzzy query'), sortable: false, operator: 'LIKE' },
|
{ label: t('mall.playxOrder.external_transaction_id'), prop: 'external_transaction_id', align: 'center', operatorPlaceholder: t('Fuzzy query'), sortable: false, operator: 'LIKE' },
|
||||||
{ label: t('mall.playxOrder.playx_transaction_id'), prop: 'playx_transaction_id', align: 'center', operatorPlaceholder: t('Fuzzy query'), sortable: false, operator: 'LIKE' },
|
|
||||||
{
|
{
|
||||||
label: t('mall.playxOrder.grant_status'),
|
label: t('mall.playxOrder.grant_status'),
|
||||||
prop: 'grant_status',
|
prop: 'grant_status',
|
||||||
|
|||||||
1
web/types/tableRenderer.d.ts
vendored
1
web/types/tableRenderer.d.ts
vendored
@@ -6,6 +6,7 @@ type TableRenderer =
|
|||||||
| 'customTemplate'
|
| 'customTemplate'
|
||||||
| 'date'
|
| 'date'
|
||||||
| 'datetime'
|
| 'datetime'
|
||||||
|
| 'failReason'
|
||||||
| 'icon'
|
| 'icon'
|
||||||
| 'image'
|
| 'image'
|
||||||
| 'images'
|
| 'images'
|
||||||
|
|||||||
Reference in New Issue
Block a user