数据库备份

This commit is contained in:
2026-04-21 16:00:02 +08:00
parent 7b9187fb62
commit ca0a9e75e0
42 changed files with 1178 additions and 438 deletions

View File

@@ -17,7 +17,7 @@ class Admin extends Backend
{ {
protected ?object $model = null; protected ?object $model = null;
protected array|string $preExcludeFields = ['create_time', 'update_time', 'password', 'salt', 'login_failure', 'last_login_time', 'last_login_ip', 'agent_id', 'agent_api_secret', 'channel_id']; protected array|string $preExcludeFields = ['create_time', 'update_time', 'password', 'salt', 'login_failure', 'last_login_time', 'last_login_ip', 'channel_id'];
protected array|string $quickSearchField = ['username', 'nickname']; protected array|string $quickSearchField = ['username', 'nickname'];
@@ -98,15 +98,6 @@ class Admin extends Backend
$this->model->startTrans(); $this->model->startTrans();
try { try {
$result = $this->model->save($data); $result = $this->model->save($data);
if ($result !== false) {
$agentId = strtolower(md5($this->model->username . $this->model->id));
$agentSecret = strtoupper(md5($this->model->username . $this->model->id));
// 使用原生 SQL避免 ThinkORM 按当前表结构校验字段时因未迁移缺少 agent_api_secret 列而报错
Db::execute(
'UPDATE `admin` SET `agent_id` = ?, `agent_api_secret` = ? WHERE `id` = ?',
[$agentId, $agentSecret, $this->model->id]
);
}
if (!empty($data['group_arr'])) { if (!empty($data['group_arr'])) {
$groupAccess = []; $groupAccess = [];
foreach ($data['group_arr'] as $datum) { foreach ($data['group_arr'] as $datum) {

View File

@@ -13,7 +13,7 @@ class AdminInfo extends Backend
{ {
protected ?object $model = null; protected ?object $model = null;
protected array|string $preExcludeFields = ['username', 'last_login_time', 'password', 'salt', 'status', 'channel_id', 'agent_id', 'agent_api_secret']; protected array|string $preExcludeFields = ['username', 'last_login_time', 'password', 'salt', 'status', 'channel_id'];
protected array $authAllowFields = ['id', 'username', 'nickname', 'avatar', 'email', 'mobile', 'motto', 'last_login_time']; protected array $authAllowFields = ['id', 'username', 'nickname', 'avatar', 'email', 'mobile', 'motto', 'last_login_time'];
protected function initController(Request $request): ?Response protected function initController(Request $request): ?Response

View File

@@ -97,7 +97,7 @@ return [
'Group Name Arr' => 'Group Name Arr', 'Group Name Arr' => 'Group Name Arr',
'Push succeeded' => 'Push succeeded', 'Push succeeded' => 'Push succeeded',
'Manual push failed' => 'Manual push failed', 'Manual push failed' => 'Manual push failed',
'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', 'Missing required fields' => 'Missing required fields',
@@ -107,4 +107,6 @@ return [
'Shipped successfully' => 'Shipped successfully', 'Shipped successfully' => 'Shipped successfully',
'Approved successfully' => 'Approved successfully', 'Approved successfully' => 'Approved successfully',
'Rejected successfully' => 'Rejected successfully', 'Rejected successfully' => 'Rejected successfully',
'Success' => 'success',
'Failed' => 'failed',
]; ];

View File

@@ -116,7 +116,7 @@ return [
'Group Name Arr' => '分组名称数组', 'Group Name Arr' => '分组名称数组',
'Push succeeded' => '推送成功', 'Push succeeded' => '推送成功',
'Manual push failed' => '手动推送失败', 'Manual push failed' => '手动推送失败',
'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' => '缺少必填项', 'Missing required fields' => '缺少必填项',
@@ -126,4 +126,6 @@ return [
'Shipped successfully' => '发货成功', 'Shipped successfully' => '发货成功',
'Approved successfully' => '审核通过', 'Approved successfully' => '审核通过',
'Rejected successfully' => '驳回成功', 'Rejected successfully' => '驳回成功',
'Success' => '成功',
'Failed' => '失败',
]; ];

View File

@@ -21,8 +21,6 @@ use support\think\Db;
* @property string $password 密码密文 * @property string $password 密码密文
* @property string $salt 密码盐 * @property string $salt 密码盐
* @property string $status 状态:enable=启用,disable=禁用 * @property string $status 状态:enable=启用,disable=禁用
* @property string $agent_id 代理 IDAPI 鉴权)
* @property string $agent_api_secret Agent API 密钥
*/ */
class Admin extends Model class Admin extends Model
{ {

View File

@@ -9,9 +9,7 @@ use Throwable;
use app\common\controller\Api; use app\common\controller\Api;
use app\common\facade\Token; use app\common\facade\Token;
use app\common\library\Auth as UserAuth; use app\common\library\Auth as UserAuth;
use app\common\library\AgentJwt;
use app\common\model\MallUserAsset; use app\common\model\MallUserAsset;
use app\admin\model\Admin;
use Webman\Http\Request; use Webman\Http\Request;
use support\Response; use support\Response;
@@ -20,83 +18,12 @@ use support\Response;
*/ */
class Auth extends Api class Auth extends Api
{ {
/**
* Agent Token 类型
*/
public const TOKEN_TYPE = 'agent';
/**
* 时间戳有效范围(秒),防止重放攻击
*/
protected int $timeTolerance = 300;
/** /**
* 临时登录 token 有效期(秒) * 临时登录 token 有效期(秒)
*/ */
protected int $tempTokenExpire = 86400; protected int $tempTokenExpire = 86400;
/**
* 获取鉴权 TokenGET 请求)
* 参数仅从 Query 读取signature、secret、agent_id、time
* 返回authtoken失败返回 code=0 及失败信息
*/
public function authToken(Request $request): Response
{
$response = $this->initializeApi($request);
if ($response !== null) {
return $response;
}
$signature = $request->get('signature', '');
$secret = $request->get('secret', '');
$agentId = $request->get('agent_id', '');
$time = $request->get('time', '');
if ($signature === '' || $secret === '' || $agentId === '' || $time === '') {
return $this->error(__('Parameter signature/secret/agent_id/time can not be empty'));
}
$timestamp = intval($time);
if ($timestamp <= 0) {
return $this->error(__('Invalid timestamp'));
}
$now = time();
if ($timestamp < $now - $this->timeTolerance || $timestamp > $now + $this->timeTolerance) {
return $this->error(__('Timestamp expired'));
}
$admin = Admin::where('agent_id', $agentId)->find();
if (!$admin) {
return $this->error(__('Agent not found'));
}
$apiSecret = strval($admin->agent_api_secret ?? '');
if ($apiSecret === '') {
return $this->error(__('Agent not found'));
}
if ($apiSecret !== $secret) {
return $this->error(__('Invalid agent or secret'));
}
$expectedSignature = strtoupper(md5($agentId . $secret . $time));
if (!hash_equals($expectedSignature, $signature)) {
return $this->error(__('Invalid signature'));
}
$expire = intval(config('buildadmin.agent_auth.token_expire', 86400));
$payload = [
'agent_id' => $agentId,
'admin_id' => $admin->id,
];
$authtoken = AgentJwt::encode($payload, $expire);
return $this->success('', [
'authtoken' => $authtoken,
]);
}
/** /**
* H5 临时登录GET/POST * H5 临时登录GET/POST
* 参数username * 参数username
@@ -109,7 +36,7 @@ class Auth extends Api
return $response; return $response;
} }
$enabled = config('buildadmin.agent_auth.temp_login_enable', false); $enabled = config('buildadmin.temp_login.enable', false);
if (!$enabled) { if (!$enabled) {
return $this->error(__('Temp login is disabled')); return $this->error(__('Temp login is disabled'));
} }
@@ -145,7 +72,7 @@ class Auth extends Api
$token = Random::uuid(); $token = Random::uuid();
$refreshToken = Random::uuid(); $refreshToken = Random::uuid();
$expire = config('buildadmin.agent_auth.temp_login_expire', $this->tempTokenExpire); $expire = config('buildadmin.temp_login.expire', $this->tempTokenExpire);
$assetId = intval($asset->getKey()); $assetId = intval($asset->getKey());
Token::set($token, UserAuth::TOKEN_TYPE_MALL_USER, $assetId, $expire); Token::set($token, UserAuth::TOKEN_TYPE_MALL_USER, $assetId, $expire);
Token::set($refreshToken, UserAuth::TOKEN_TYPE_MALL_USER . '-refresh', $assetId, 2592000); Token::set($refreshToken, UserAuth::TOKEN_TYPE_MALL_USER . '-refresh', $assetId, 2592000);

View File

@@ -43,7 +43,6 @@ return [
'You have no permission' => 'No permission!', 'You have no permission' => 'No permission!',
'Parameter error' => 'Parameter error!', 'Parameter error' => 'Parameter error!',
'Parameter %s can not be empty' => 'Parameter %s cannot be empty', 'Parameter %s can not be empty' => 'Parameter %s cannot be empty',
'Parameter signature/secret/agent_id/time can not be empty' => 'Parameter signature/secret/agent_id/time cannot be empty',
'Invalid timestamp' => 'Invalid timestamp', 'Invalid timestamp' => 'Invalid timestamp',
'Timestamp expired' => 'Timestamp expired', 'Timestamp expired' => 'Timestamp expired',
'Invalid agent or secret' => 'Invalid agent or secret', 'Invalid agent or secret' => 'Invalid agent or secret',

View File

@@ -35,16 +35,15 @@ return [
'Account not exist' => 'Akaun tidak wujud', 'Account not exist' => 'Akaun tidak wujud',
'Account disabled' => 'Akaun dilumpuhkan', 'Account disabled' => 'Akaun dilumpuhkan',
'Token login failed' => 'Log masuk token gagal', 'Token login failed' => 'Log masuk token gagal',
'Please try again after 1 day' => 'Percubaan gagal terlalu kerap, sila cuba semula selepas 1 hari', 'Please try again after 1 day' => 'Percubaan gagal terlalu kerap, sila cuba semula selepas 24 jam',
'Password is incorrect' => 'Kata laluan tidak betul', 'Password is incorrect' => 'Kata laluan tidak betul',
'You are not logged in' => 'Anda belum log masuk', 'You are not logged in' => 'Anda belum log masuk',
'Unknown operation' => 'Operasi tidak diketahui', 'Unknown operation' => 'Operasi tidak diketahui',
'No action available, please contact the administrator~' => 'Tiada tindakan tersedia, sila hubungi pentadbir~', 'No action available, please contact the administrator~' => 'Tiada tindakan tersedia, sila hubungi pentadbir.',
'Please login first' => 'Sila log masuk dahulu!', 'Please login first' => 'Sila log masuk dahulu!',
'You have no permission' => 'Tiada kebenaran!', 'You have no permission' => 'Tiada kebenaran!',
'Parameter error' => 'Ralat parameter!', 'Parameter error' => 'Ralat parameter!',
'Parameter %s can not be empty' => 'Parameter %s tidak boleh kosong', 'Parameter %s can not be empty' => 'Parameter %s tidak boleh kosong',
'Parameter signature/secret/agent_id/time can not be empty' => 'Parameter signature/secret/agent_id/time tidak boleh kosong',
'Invalid timestamp' => 'Cap masa tidak sah', 'Invalid timestamp' => 'Cap masa tidak sah',
'Timestamp expired' => 'Cap masa tamat tempoh', 'Timestamp expired' => 'Cap masa tamat tempoh',
'Invalid agent or secret' => 'Ejen atau rahsia tidak sah', 'Invalid agent or secret' => 'Ejen atau rahsia tidak sah',

View File

@@ -43,7 +43,6 @@ return [
'You have no permission' => '没有权限操作!', 'You have no permission' => '没有权限操作!',
'Parameter error' => '参数错误!', 'Parameter error' => '参数错误!',
'Parameter %s can not be empty' => '参数%s不能为空', 'Parameter %s can not be empty' => '参数%s不能为空',
'Parameter signature/secret/agent_id/time can not be empty' => '参数 signature/secret/agent_id/time 不能为空',
'Invalid timestamp' => '无效的时间戳', 'Invalid timestamp' => '无效的时间戳',
'Timestamp expired' => '时间戳已过期', 'Timestamp expired' => '时间戳已过期',
'Invalid agent or secret' => '代理或密钥无效', 'Invalid agent or secret' => '代理或密钥无效',

View File

@@ -1,67 +0,0 @@
<?php
declare(strict_types=1);
namespace app\common\library;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
/**
* Agent 鉴权 JWT 工具
*/
class AgentJwt
{
public const ALG = 'HS256';
/**
* 生成 JWT authtoken
* @param array $payload agent_id、admin_id 等
* @param int $expire 有效期(秒)
*/
public static function encode(array $payload, int $expire = 86400): string
{
$now = time();
$payload['iat'] = $now;
$payload['exp'] = $now + $expire;
$secret = self::getSecret();
return JWT::encode($payload, $secret, self::ALG);
}
/**
* 解析并验证 JWT返回 payload
* @return array payload失败返回空数组
*/
public static function decode(string $token): array
{
if ($token === '') {
return [];
}
try {
$secret = self::getSecret();
$decoded = JWT::decode($token, new Key($secret, self::ALG));
return (array) $decoded;
} catch (ExpiredException|SignatureInvalidException|\Throwable) {
return [];
}
}
/**
* 验证 JWT 是否有效
*/
public static function verify(string $token): bool
{
return !empty(self::decode($token));
}
private static function getSecret(): string
{
$secret = config('buildadmin.agent_auth.jwt_secret', '');
if ($secret === '') {
$secret = config('buildadmin.token.key', '');
}
return $secret;
}
}

View File

@@ -165,18 +165,6 @@ if (!function_exists('get_auth_token')) {
} }
} }
if (!function_exists('get_agent_jwt_payload')) {
/**
* 解析 Agent JWT authtoken返回 payloadagent_id、admin_id 等)
* @param string $token authtoken
* @return array 成功返回 payload失败返回空数组
*/
function get_agent_jwt_payload(string $token): array
{
return \app\common\library\AgentJwt::decode($token);
}
}
if (!function_exists('get_controller_path')) { if (!function_exists('get_controller_path')) {
/** /**
* 从 Request 或路由获取控制器路径(等价于 ThinkPHP controllerPath * 从 Request 或路由获取控制器路径(等价于 ThinkPHP controllerPath

View File

@@ -76,6 +76,13 @@ abstract class BaseController
*/ */
protected function result(string $msg, mixed $data = null, int $code = 0, array $header = []): Response protected function result(string $msg, mixed $data = null, int $code = 0, array $header = []): Response
{ {
if (trim($msg) === '') {
$msg = $code === 1 ? __('Success') : __('Failed');
if (trim($msg) === '') {
$msg = $code === 1 ? 'success' : 'failed';
}
}
$body = [ $body = [
'code' => $code, 'code' => $code,
'msg' => $msg, 'msg' => $msg,

View File

@@ -81,20 +81,12 @@ return [
'cdn_url' => '', 'cdn_url' => '',
// 内容分发网络URL参数将自动添加 `?`,之后拼接到 cdn_url 的结尾(例如 `imageMogr2/format/heif` // 内容分发网络URL参数将自动添加 `?`,之后拼接到 cdn_url 的结尾(例如 `imageMogr2/format/heif`
'cdn_url_params' => '', 'cdn_url_params' => '',
// 代理鉴权配置(/api/v1/authToken // H5 临时登录配置(/api/v1/temLogin
'agent_auth' => [ 'temp_login' => [
// agent_id => secret 映射 // 是否启用 H5 临时登录接口
'agents' => [ 'enable' => true,
// 'agent_001' => 'your_secret_key',
],
// JWT 签名密钥(留空则使用 token.key建议 AGENT_AUTH_JWT_SECRET 注入
'jwt_secret' => strval(env('AGENT_AUTH_JWT_SECRET', '')),
// 是否启用 H5 临时登录接口 /api/v1/temLogin
'temp_login_enable' => true,
// Token 有效期(秒),默认 24 小时
'token_expire' => 86400,
// 临时登录 token 有效期(秒),默认 1 天 // 临时登录 token 有效期(秒),默认 1 天
'temp_login_expire' => 86400, 'expire' => 86400,
], ],
// 版本号 // 版本号
'version' => 'v2.3.6', 'version' => 'v2.3.6',

View File

@@ -109,7 +109,6 @@ Route::post('/api/account/retrievePassword', [\app\api\controller\Account::class
Route::post('/api/ems/send', [\app\api\controller\Ems::class, 'send']); Route::post('/api/ems/send', [\app\api\controller\Ems::class, 'send']);
// api/v1 鉴权 // api/v1 鉴权
Route::get('/api/v1/authToken', [\app\api\controller\v1\Auth::class, 'authToken']);
Route::get('/api/v1/temLogin', [\app\api\controller\v1\Auth::class, 'temLogin']); Route::get('/api/v1/temLogin', [\app\api\controller\v1\Auth::class, 'temLogin']);
// api/v1 PlayX 积分商城 // api/v1 PlayX 积分商城

File diff suppressed because one or more lines are too long

View File

@@ -10,7 +10,7 @@
### 1.1 流程 AH5 临时登录(推荐) ### 1.1 流程 AH5 临时登录(推荐)
适用场景H5 只需要“用户名级别”的轻量登录,不依赖 PlayX 的 token。 适用场景H5 只需要“用户名级别”的轻量登录,不依赖 playX 的 token。
1. H5 调用 `GET/POST /api/v1/temLogin?username=xxx` 获取 **商城 token**(类型 `muser` 1. H5 调用 `GET/POST /api/v1/temLogin?username=xxx` 获取 **商城 token**(类型 `muser`
2. H5 后续请求统一携带该 token推荐放在 Header`token: <muser_token>`,也可用参数 `token` 2. H5 后续请求统一携带该 token推荐放在 Header`token: <muser_token>`,也可用参数 `token`
@@ -22,9 +22,9 @@
- `GET /api/v1/mall/orders` 查询订单 - `GET /api/v1/mall/orders` 查询订单
- `GET/POST /api/v1/mall/address*` 管理地址addressList/addressAdd/addressEdit/addressDelete - `GET/POST /api/v1/mall/address*` 管理地址addressList/addressAdd/addressEdit/addressDelete
### 1.2 流程 BPlayX token 换取 session兼容 ### 1.2 流程 BplayX token 换取 session兼容
适用场景H5 已经拿到了 PlayX 下发的 token希望换取商城侧 `session_id` 适用场景H5 已经拿到了 playX 下发的 token希望换取商城侧 `session_id`
1. H5 调用 `POST /api/v1/mall/verifyToken`(传 `token``session` 1. H5 调用 `POST /api/v1/mall/verifyToken`(传 `token``session`
2. 服务端返回 `data.session_id` 2. 服务端返回 `data.session_id`
@@ -59,7 +59,7 @@
成功返回 `data.userInfo` 成功返回 `data.userInfo`
- `id`:资产主键(`mall_user_asset.id` - `id`:资产主键(`mall_user_asset.id`
- `username`:用户名 - `username`:用户名
- `playx_user_id`:映射的 PlayX 用户标识(字符串) - `playx_user_id`:映射的 playX 用户标识(字符串)
- `token`**muser token**(后续请求使用) - `token`**muser token**(后续请求使用)
- `refresh_token`:刷新 token当前前端未强依赖可不接 - `refresh_token`:刷新 token当前前端未强依赖可不接
- `expires_in`:秒 - `expires_in`:秒

View File

@@ -1,17 +1,17 @@
## 0. 交付说明(给 PlayX ## 0. 交付说明(给 playX
- **交付物**:本文件(接口清单 + 业务流程 + 联调验收清单)。 - **交付物**:本文件(接口清单 + 业务流程 + 联调验收清单)。
- **建议联调顺序**Token 验证(远程 PlayX 或本地 `verify_token_local_only`)→ 每日推送 → 领取 → 红利发放 → 提现入账 → 实物后台处理。 - **建议联调顺序**Token 验证(远程 playX 或本地 `verify_token_local_only`)→ 每日推送 → 领取 → 红利发放 → 提现入账 → 实物后台处理。
- **约定**:接口 URL、字段最终表、签名细节以 PlayX 提供的最终口径为准;本文档负责把流程、幂等、重试与最小字段集合先对齐。 - **约定**:接口 URL、字段最终表、签名细节以 playX 提供的最终口径为准;本文档负责把流程、幂等、重试与最小字段集合先对齐。
## 1. 文档目的与范围 ## 1. 文档目的与范围
本文档用于 PlayX 与积分商城Points Mall联调对接。范围仅包含 本文档用于 playX 与积分商城Points Mall联调对接。范围仅包含
- 前端:PlayX 以内嵌 Iframe 打开商城 H5使用 postMessage 传递 token/session。 - 前端:playX 以内嵌 Iframe 打开商城 H5使用 postMessage 传递 token/session。
- 后端:商城后端独立部署;与 PlayX 后端通过 REST API 通讯。 - 后端:商城后端独立部署;与 playX 后端通过 REST API 通讯。
- 数据同步:仅 PlayX 每日 Cron 推送T+1玩家数据到商城用于计算“待领取积分/今日可领取上限”。 - 数据同步:仅 playX 每日 Cron 推送T+1玩家数据到商城用于计算“待领取积分/今日可领取上限”。
- 发放方式:商城在红利兑换/提现(回平台余额)下单后,直接调用 PlayX API 发放/入账;PlayX 侧每 10 分钟 Cron 执行 5.9 adjustment/最终入账。 - 发放方式:商城在红利兑换/提现(回平台余额)下单后,直接调用 playX API 发放/入账;playX 侧每 10 分钟 Cron 执行 5.9 adjustment/最终入账。
不在本文档范围内: 不在本文档范围内:
@@ -22,22 +22,22 @@
```mermaid ```mermaid
flowchart LR flowchart LR
PlayXFrontend["PlayXFrontend"] -->|"postMessage(token/session)"| MallFrontend["MallFrontend(Iframe)"] playXFrontend["playXFrontend"] -->|"postMessage(token/session)"| MallFrontend["MallFrontend(Iframe)"]
MallFrontend -->|"API(商城后端)"| MallBackend["MallBackend"] MallFrontend -->|"API(商城后端)"| MallBackend["MallBackend"]
MallBackend -->|"TokenVerificationAPI"| PlayXBackend["PlayXBackend"] MallBackend -->|"TokenVerificationAPI"| playXBackend["playXBackend"]
PlayXBackend -->|"DailyPushAPI(T+1)"| MallBackend playXBackend -->|"DailyPushAPI(T+1)"| MallBackend
MallBackend -->|"BonusGrantAPI/BalanceCreditAPI"| PlayXBackend MallBackend -->|"BonusGrantAPI/BalanceCreditAPI"| playXBackend
``` ```
> 当 **`playx.verify_token_local_only=true`** 时「Token 验证」一步在商城内完成,**不经过** `PlayXBackend` 的 Token Verification API详见 **§4.1**。 > 当 **`playX.verify_token_local_only=true`** 时「Token 验证」一步在商城内完成,**不经过** `playXBackend` 的 Token Verification API详见 **§4.1**。
## 3. 关键业务对象与状态机 ## 3. 关键业务对象与状态机
### 3.1 资产口径(最小集合) ### 3.1 资产口径(最小集合)
- **LockedPoints待领取积分**:由 PlayX 每日推送的“昨日输赢净额”在商城端按规则计算得到,未领取前不可消费。 - **LockedPoints待领取积分**:由 playX 每日推送的“昨日输赢净额”在商城端按规则计算得到,未领取前不可消费。
- **AvailablePoints可用积分**:领取后可用于兑换/提现的积分余额。 - **AvailablePoints可用积分**:领取后可用于兑换/提现的积分余额。
- **TodayLimit今日可领取上限**:由 PlayX 每日推送的“昨日总存款”按规则计算得到。 - **TodayLimit今日可领取上限**:由 playX 每日推送的“昨日总存款”按规则计算得到。
- **TodayClaimed今日已领取**:当日累计领取量(用于进度条与上限控制)。 - **TodayClaimed今日已领取**:当日累计领取量(用于进度条与上限控制)。
### 3.2 订单类型 ### 3.2 订单类型
@@ -57,48 +57,48 @@ flowchart LR
### 4.1 登录鉴权Iframe + token ### 4.1 登录鉴权Iframe + token
> **接口与字段细节**以代码为准,完整说明见同目录《PlayX-接口文档.md》§3 H5、§3.2 `temLogin`、§3.3 `verifyToken`)。 > **接口与字段细节**以代码为准,完整说明见同目录《playX-接口文档.md》§3 H5、§3.2 `temLogin`、§3.3 `verifyToken`)。
#### 4.1.1 身份与数据模型(商城侧) #### 4.1.1 身份与数据模型(商城侧)
- **商城用户**:表 `mall_user`H5 临时登录、后台创建等均落此表)。 - **商城用户**:表 `mall_user`H5 临时登录、后台创建等均落此表)。
- **PlayX 资产扩展**:表 `mall_playx_user_asset`,与 `mall_user` **一对一**`mall_user_id``playx_user_id` 均唯一)。 - **playX 资产扩展**:表 `mall_playx_user_asset`,与 `mall_user` **一对一**`mall_user_id``playx_user_id` 均唯一)。
- **业务侧用户标识**:对外接口中的 `user_id`(字符串)在多数场景下即 **`playx_user_id`**PlayX 玩家 ID - **业务侧用户标识**:对外接口中的 `user_id`(字符串)在多数场景下即 **`playx_user_id`**playX 玩家 ID
- 若用户仅通过商城 **临时登录** 进入、尚无 PlayX 正式 ID商城会生成占位 ID形如 **`mall_{mall_user.id}`**,与每日推送中的真实 `user_id` 区分(避免与纯数字 ID 混淆)。 - 若用户仅通过商城 **临时登录** 进入、尚无 playX 正式 ID商城会生成占位 ID形如 **`mall_{mall_user.id}`**,与每日推送中的真实 `user_id` 区分(避免与纯数字 ID 混淆)。
- **H5 调业务接口时**:服务端内部统一解析为 **`mall_user.id`**,再查资产与订单(解析规则见《PlayX-接口文档》§3.1)。 - **H5 调业务接口时**:服务端内部统一解析为 **`mall_user.id`**,再查资产与订单(解析规则见《playX-接口文档》§3.1)。
#### 4.1.2 模式 A联调 PlayX生产/预发,远程校验 token #### 4.1.2 模式 A联调 playX生产/预发,远程校验 token
1. 用户在 PlayX 内打开积分商城入口iframe 1. 用户在 playX 内打开积分商城入口iframe
2. PlayX 前端通过 postMessage 将 **PlayX 下发的 token**(及必要上下文)传给商城 H5。 2. playX 前端通过 postMessage 将 **playX 下发的 token**(及必要上下文)传给商城 H5。
3. 商城 H5 调用商城后端 **`POST /api/v1/mall/verifyToken`**,由商城向 PlayX 的 **Token Verification API**`playx.api.base_url` + `playx.api.token_verify_url`)发起校验。 3. 商城 H5 调用商城后端 **`POST /api/v1/mall/verifyToken`**,由商城向 playX 的 **Token Verification API**`playX.api.base_url` + `playX.api.token_verify_url`)发起校验。
4. **前提**:配置 **`playx.verify_token_local_only = false`**,且 **`playx.api.base_url`** 已配置为可访问的 PlayX 基地址。 4. **前提**:配置 **`playX.verify_token_local_only = false`**,且 **`playX.api.base_url`** 已配置为可访问的 playX 基地址。
5. PlayX 返回 **`user_id``username`**(及可选会话过期时间等)。 5. playX 返回 **`user_id``username`**(及可选会话过期时间等)。
6. 商城写入 **`mall_playx_session`**`session_id` + 上述 `user_id`/`username` + 过期时间),后续 H5 可用 **`session_id`** 或 **`token`(商城临时 token见模式 B** 调用资产/领取等接口。 6. 商城写入 **`mall_playx_session`**`session_id` + 上述 `user_id`/`username` + 过期时间),后续 H5 可用 **`session_id`** 或 **`token`(商城临时 token见模式 B** 调用资产/领取等接口。
幂等与安全: 幂等与安全:
- H5 **不要**把 PlayX 的 `user_id` 当作唯一可信凭据直传下单;**以 token 换 session** 或由商城签发 token 的流程为准。 - H5 **不要**把 playX 的 `user_id` 当作唯一可信凭据直传下单;**以 token 换 session** 或由商城签发 token 的流程为准。
- PlayX 侧 Token Verification API 的鉴权/签名(若有)按双方约定(可参考《PlayX-接口文档》§2.1)。 - playX 侧 Token Verification API 的鉴权/签名(若有)按双方约定(可参考《playX-接口文档》§2.1)。
#### 4.1.3 模式 B本地 / 无 PlayX 环境(商城自校验,不请求 PlayX #### 4.1.3 模式 B本地 / 无 playX 环境(商城自校验,不请求 playX
用于开发、联调前自测、或 PlayX 接口未就绪时: 用于开发、联调前自测、或 playX 接口未就绪时:
1. 配置 **`playx.verify_token_local_only = true`**(环境变量 **`PLAYX_VERIFY_TOKEN_LOCAL_ONLY`**,默认可为开启,以项目 `config/playx.php` 为准)。 1. 配置 **`playX.verify_token_local_only = true`**(环境变量 **`PLAYX_VERIFY_TOKEN_LOCAL_ONLY`**,默认可为开启,以项目 `config/playx.php` 为准)。
2. 此时 **`/api/v1/mall/verifyToken` 不会访问 PlayX**,仅在商城内校验 **商城临时 token**token 表类型 **`muser`**,由下方 `temLogin` 签发)。 2. 此时 **`/api/v1/mall/verifyToken` 不会访问 playX**,仅在商城内校验 **商城临时 token**token 表类型 **`muser`**,由下方 `temLogin` 签发)。
3. 调用 **`GET/POST /api/v1/temLogin?username=...`**(需 **`buildadmin.agent_auth.temp_login_enable = true`**):不存在则创建 **`mall_user`**,并保证存在 **`mall_playx_user_asset`**(含 `playx_user_id`,默认 **`mall_{id}`**),返回 **`userInfo.token`**、**`playx_user_id`**、**`expires_in`** 等。 3. 调用 **`GET/POST /api/v1/temLogin?username=...`**(需 **`buildadmin.agent_auth.temp_login_enable = true`**):不存在则创建 **`mall_user`**,并保证存在 **`mall_playx_user_asset`**(含 `playx_user_id`,默认 **`mall_{id}`**),返回 **`userInfo.token`**、**`playx_user_id`**、**`expires_in`** 等。
4. 再用该 token 调用 **`verifyToken`** 可得到 **`session_id`**,与模式 A 一样供后续接口使用;或直接带 **`token` / `ba-token`** 调资产等接口(见《PlayX-接口文档》§3.1)。 4. 再用该 token 调用 **`verifyToken`** 可得到 **`session_id`**,与模式 A 一样供后续接口使用;或直接带 **`token` / `ba-token`** 调资产等接口(见《playX-接口文档》§3.1)。
#### 4.1.4 会话续期与前端约定 #### 4.1.4 会话续期与前端约定
- **会话续期**:玩家停留时间较长时,若商城 API 返回 token/session 失效(如 401H5 可通过 postMessage 请 PlayX 父页面 **重新派发 PlayX token**(模式 A模式 B 下可重新 **`temLogin`** 或走 **`/api/common/refreshToken`**`muser-refresh`)换取新 access token。 - **会话续期**:玩家停留时间较长时,若商城 API 返回 token/session 失效(如 401H5 可通过 postMessage 请 playX 父页面 **重新派发 playX token**(模式 A模式 B 下可重新 **`temLogin`** 或走 **`/api/common/refreshToken`**`muser-refresh`)换取新 access token。
- 具体错误码与 Header`ba-token`)以前端与《PlayX-接口文档》为准。 - 具体错误码与 Header`ba-token`)以前端与《playX-接口文档》为准。
### 4.2 每日 T+1 入池(PlayX → 商城) ### 4.2 每日 T+1 入池(playX → 商城)
1. PlayX 在每日固定时间向商城调用 **Daily Push API**,推送昨日玩家数据。(**注:请确认并约定好 `date` 字段对应的具体时区边界,如以 UTC+8 为准**)。 1. playX 在每日固定时间向商城调用 **Daily Push API**,推送昨日玩家数据。(**注:请确认并约定好 `date` 字段对应的具体时区边界,如以 UTC+8 为准**)。
2. 商城按 `user_id + date` 幂等去重入库。由于不支持通过重复推送做数据修正,**若 PlayX 发现个别账单算错了,请联系商城运营在后台进行人工调账处理**,勿重复推送。 2. 商城按 `user_id + date` 幂等去重入库。由于不支持通过重复推送做数据修正,**若 playX 发现个别账单算错了,请联系商城运营在后台进行人工调账处理**,勿重复推送。
3. 商城计算: 3. 商城计算:
- 新增保障金(待领取积分增量) - 新增保障金(待领取积分增量)
- 今日可领取上限 - 今日可领取上限
@@ -118,19 +118,19 @@ flowchart LR
- 领取操作建议使用 `claim_request_id`(由前端生成或后端生成返回)实现幂等,避免重复点击导致重复领取。 - 领取操作建议使用 `claim_request_id`(由前端生成或后端生成返回)实现幂等,避免重复点击导致重复领取。
### 4.4 红利兑换(商城 → PlayX 发放) ### 4.4 红利兑换(商城 → playX 发放)
1. 会员在“红利”商品点击兑换并确认(**为避免客诉,商城前端会提示会员:红利发放预计在此后约 10 分钟内入账,请耐心等待**)。 1. 会员在“红利”商品点击兑换并确认(**为避免客诉,商城前端会提示会员:红利发放预计在此后约 10 分钟内入账,请耐心等待**)。
2. 商城创建 BONUS 订单PENDING并校验/扣减可用积分(原子扣减)。 2. 商城创建 BONUS 订单PENDING并校验/扣减可用积分(原子扣减)。
3. 商城调用 PlayX **Bonus Grant API**,传递红利发放信息(字段见 5.3)。 3. 商城调用 playX **Bonus Grant API**,传递红利发放信息(字段见 5.3)。
4.PlayX API 返回初步排队接收成功HTTP 200 且 `status="accepted"` 4.playX API 返回初步排队接收成功HTTP 200 且 `status="accepted"`
- 商城订单保持 PENDING等待 PlayX 侧 10 分钟 Cron 最终发放/入账)。 - 商城订单保持 PENDING等待 playX 侧 10 分钟 Cron 最终发放/入账)。
- 记录 `playx_transaction_id`(或外部流水号)用于后续追踪。 - 记录 `playx_transaction_id`(或外部流水号)用于后续追踪。
- **商城后端将通过调用 PlayX 的 “交易状态查询 API”见 5.5)来轮询获取最终结果**,最终确认为成功后,商城订单才会流转闭环为 COMPLETED。 - **商城后端将通过调用 playX 的 “交易状态查询 API”见 5.5)来轮询获取最终结果**,最终确认为成功后,商城订单才会流转闭环为 COMPLETED。
5.PlayX API 返回失败: 5.playX API 返回失败:
- 订单保持 PENDING并记录失败原因与下一次可重试时间 - 订单保持 PENDING并记录失败原因与下一次可重试时间
- 支持后台“手动重试”(见 6.3 - 支持后台“手动重试”(见 6.3
- 若经过 N 次重试仍失败或确认 PlayX 侧不可达成:订单转 REJECTED 并退回积分(见 6.2 - 若经过 N 次重试仍失败或确认 playX 侧不可达成:订单转 REJECTED 并退回积分(见 6.2
### 4.5 实物兑换(商城后台人工处理) ### 4.5 实物兑换(商城后台人工处理)
@@ -140,25 +140,25 @@ flowchart LR
- 发货:录入物流公司与单号 → 状态 SHIPPED - 发货:录入物流公司与单号 → 状态 SHIPPED
- 驳回:录入原因 → 状态 REJECTED → 自动退回积分 - 驳回:录入原因 → 状态 REJECTED → 自动退回积分
### 4.6 提现回平台余额(商城 → PlayX 入账) ### 4.6 提现回平台余额(商城 → playX 入账)
1. 会员在“提现到平台余额”商品点击提现并确认(**前端同样需向用户提示约 10 分钟入账预期**)。 1. 会员在“提现到平台余额”商品点击提现并确认(**前端同样需向用户提示约 10 分钟入账预期**)。
2. 商城创建 WITHDRAW 订单PENDING并原子扣减可用积分。 2. 商城创建 WITHDRAW 订单PENDING并原子扣减可用积分。
3. 商城调用 PlayX **Balance Credit API**(或同一发放接口的提现模式),传入入账信息。 3. 商城调用 playX **Balance Credit API**(或同一发放接口的提现模式),传入入账信息。
4.PlayX API 返回初步排队接收成功HTTP 200 且 `status="accepted"` 4.playX API 返回初步排队接收成功HTTP 200 且 `status="accepted"`
- 商城订单保持 PENDING等待 PlayX 侧 10 分钟 Cron 最终入账)。 - 商城订单保持 PENDING等待 playX 侧 10 分钟 Cron 最终入账)。
- 记录 `playx_transaction_id`(或外部流水号)用于后续追踪。 - 记录 `playx_transaction_id`(或外部流水号)用于后续追踪。
- **商城后端通过「交易状态查询 API」见 5.5)轮询获取终态**,确认成功后订单才流转为 COMPLETED。 - **商城后端通过「交易状态查询 API」见 5.5)轮询获取终态**,确认成功后订单才流转为 COMPLETED。
5.PlayX API 返回失败(非 200 或 `status``accepted`):失败处理同 4.4。 5.playX API 返回失败(非 200 或 `status``accepted`):失败处理同 4.4。
## 5. 接口清单(按调用方向) ## 5. 接口清单(按调用方向)
> 说明:以下为接口“结构与字段清单”。具体 URL、Header、签名算法、错误码需 PlayX 提供或双方确认后固化。 > 说明:以下为接口“结构与字段清单”。具体 URL、Header、签名算法、错误码需 playX 提供或双方确认后固化。
### 5.1 PlayX → MallDaily Push API每日推送 ### 5.1 playX → MallDaily Push API每日推送
- **目的**:推送昨日玩家数据,用于 T+1 计算入池与领取上限。 - **目的**:推送昨日玩家数据,用于 T+1 计算入池与领取上限。
- **幂等键**`user_id + date`date 建议为 PlayX 业务日) - **幂等键**`user_id + date`date 建议为 playX 业务日)
- **Method/Path建议**`POST /api/v1/mall/dailyPush` - **Method/Path建议**`POST /api/v1/mall/dailyPush`
请求字段说明(最小集合,来自现有资料): 请求字段说明(最小集合,来自现有资料):
@@ -167,7 +167,7 @@ flowchart LR
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `request_id` | String | 是 | 请求的唯一网关流水号,商城端用于日志追踪和外层防重。 | | `request_id` | String | 是 | 请求的唯一网关流水号,商城端用于日志追踪和外层防重。 |
| `date` | String | 是 | 数据归属的业务日期(如 `"2026-03-18"`),用于限定该批数据的生效周期。 | | `date` | String | 是 | 数据归属的业务日期(如 `"2026-03-18"`),用于限定该批数据的生效周期。 |
| `user_id` | String | 是 | 玩家在 PlayX 的唯一标识 ID在此商城体系中以此作为核心绑定主键。 | | `user_id` | String | 是 | 玩家在 playX 的唯一标识 ID在此商城体系中以此作为核心绑定主键。 |
| `username` | String | 否 | 玩家展示名,仅用于后台日志人工可读性或冗余展示,不作业务主键。 | | `username` | String | 否 | 玩家展示名,仅用于后台日志人工可读性或冗余展示,不作业务主键。 |
| `lifetime_total_deposit` | Decimal | 否 | 玩家历史总充值(如有需要用于玩家 VIP 分层,当前传值保留即可)。 | | `lifetime_total_deposit` | Decimal | 否 | 玩家历史总充值(如有需要用于玩家 VIP 分层,当前传值保留即可)。 |
| `lifetime_total_withdraw` | Decimal | 否 | 玩家历史总提现(储备字段)。 | | `lifetime_total_withdraw` | Decimal | 否 | 玩家历史总提现(储备字段)。 |
@@ -209,7 +209,7 @@ flowchart LR
} }
``` ```
### 5.2 Mall → PlayXToken Verification API ### 5.2 Mall → playXToken Verification API
- **目的**:商城后端校验 token/session获取可信 `user_id``username` - **目的**:商城后端校验 token/session获取可信 `user_id``username`
- **Method/Path示例占位**`POST /api/v1/auth/verify-token` - **Method/Path示例占位**`POST /api/v1/auth/verify-token`
@@ -235,7 +235,7 @@ flowchart LR
| 字段名 | 类型 | 必填 | 说明 | | 字段名 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `request_id` | String | 是 | 透传请求时的 `request_id`。 | | `request_id` | String | 是 | 透传请求时的 `request_id`。 |
| `user_id` | String | 是 | 该凭证解密后对应的、在 PlayX 平台具有唯一性的玩家专属 ID。 | | `user_id` | String | 是 | 该凭证解密后对应的、在 playX 平台具有唯一性的玩家专属 ID。 |
| `username` | String | 否 | 该玩家显示名用于加载商城的界面头部“欢迎xxx”渲染。 | | `username` | String | 否 | 该玩家显示名用于加载商城的界面头部“欢迎xxx”渲染。 |
| `token_expire_at` | String | 否 | Token 的物理过期时间(如 ISO8601用于商城前端预判是否到了需要执行无感续期重置的底线时间。 | | `token_expire_at` | String | 否 | Token 的物理过期时间(如 ISO8601用于商城前端预判是否到了需要执行无感续期重置的底线时间。 |
@@ -250,26 +250,26 @@ flowchart LR
} }
``` ```
### 5.3 Mall → PlayXBonus Grant API红利发放 ### 5.3 Mall → playXBonus Grant API红利发放
来自 PlayX 现有字段清单(待 PlayX 确认最终口径): 来自 playX 现有字段清单(待 playX 确认最终口径):
请求字段说明(待 PlayX 确认最终口径): 请求字段说明(待 playX 确认最终口径):
| 字段名 | 类型 | 必填 | 说明 | | 字段名 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `request_id` | String | 是 | 商城发起的 HTTP 请求流水号(纯用于网关层)。 | | `request_id` | String | 是 | 商城发起的 HTTP 请求流水号(纯用于网关层)。 |
| `externalTransactionId` | String | 是 | **核心防重键:强制要求 PlayX 凭此字段做完全的幂等拦截**。这是商城侧派发红利的唯一本地订单号(如 `"BONUS_ORD001"`)。 | | `externalTransactionId` | String | 是 | **核心防重键:强制要求 playX 凭此字段做完全的幂等拦截**。这是商城侧派发红利的唯一本地订单号(如 `"BONUS_ORD001"`)。 |
| `user_id` | String | 是 | 要派发红利的玩家在 PlayX 的基础 ID这需对齐每日推送。 | | `user_id` | String | 是 | 要派发红利的玩家在 playX 的基础 ID这需对齐每日推送。 |
| `memberLogin` | String | 否 | 玩家登录名(若当前 PlayX 核心接口必须传登录名,则商城会补充;若以 `user_id` 为准,此项可废弃)。 | | `memberLogin` | String | 否 | 玩家登录名(若当前 playX 核心接口必须传登录名,则商城会补充;若以 `user_id` 为准,此项可废弃)。 |
| `amount` | Decimal | 是 | 实际加给玩家游戏余额或红利钱包的具体现金数字。 | | `amount` | Decimal | 是 | 实际加给玩家游戏余额或红利钱包的具体现金数字。 |
| `rewardName` | String | 否 | 商城中对应的该红利商品名称,用于让用户后续在 PlayX 流水里看懂这笔钱从何而来。 | | `rewardName` | String | 否 | 商城中对应的该红利商品名称,用于让用户后续在 playX 流水里看懂这笔钱从何而来。 |
| `description` | String | 否 | 系统行为备注说明(如 `"PointsMall bonus"`)。 | | `description` | String | 否 | 系统行为备注说明(如 `"PointsMall bonus"`)。 |
| `memberInboxMessage` | String | 否 | 是否需借调此时机向玩家发送站内站群信内容提示。 | | `memberInboxMessage` | String | 否 | 是否需借调此时机向玩家发送站内站群信内容提示。 |
| `category` | String | 是 | 标明该红利在游戏侧的所属业务类别的枚举代码(如 `daily`)。 | | `category` | String | 是 | 标明该红利在游戏侧的所属业务类别的枚举代码(如 `daily`)。 |
| `categoryTitle` | String | 否 | 该红利业务类别的中文展示名称。 | | `categoryTitle` | String | 否 | 该红利业务类别的中文展示名称。 |
| `multiplier` (或 `turnover`) | Int | 是 | 款项入账后,玩家需完成的打码流水约束倍数(如 1 倍或 5 倍)。 | | `multiplier` (或 `turnover`) | Int | 是 | 款项入账后,玩家需完成的打码流水约束倍数(如 1 倍或 5 倍)。 |
| `startTime` / `endTime` | String | 否 | 红利生效时间窗口(起止时间,视 PlayX 规则传参)。 | | `startTime` / `endTime` | String | 否 | 红利生效时间窗口(起止时间,视 playX 规则传参)。 |
请求示例: 请求示例:
@@ -296,7 +296,7 @@ flowchart LR
| 字段名 | 类型 | 必填 | 说明 | | 字段名 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `request_id` | String | 是 | 透传请求号。 | | `request_id` | String | 是 | 透传请求号。 |
| `playx_transaction_id` | String | 否 | PlayX 内部初创的接收入列单号或派发流水号,商城会将其归档以备应对极端客诉争议寻找记录用。 | | `playx_transaction_id` | String | 否 | playX 内部初创的接收入列单号或派发流水号,商城会将其归档以备应对极端客诉争议寻找记录用。 |
| `status` | String | 是 | 核心状态枚举。若为 `accepted`,表示请求成功列入 10 分钟 Cron商城中止重试其他值皆触发商城的补偿拦截网。 | | `status` | String | 是 | 核心状态枚举。若为 `accepted`,表示请求成功列入 10 分钟 Cron商城中止重试其他值皆触发商城的补偿拦截网。 |
| `message` | String | 否 | 对入列状态的额外提示信息内容。 | | `message` | String | 否 | 对入列状态的额外提示信息内容。 |
@@ -311,7 +311,7 @@ flowchart LR
} }
``` ```
### 5.4 Mall → PlayXBalance Credit API提现回平台余额 ### 5.4 Mall → playXBalance Credit API提现回平台余额
字段建议与 5.3 保持结构一致,至少包含: 字段建议与 5.3 保持结构一致,至少包含:
@@ -323,7 +323,7 @@ flowchart LR
| `externalTransactionId` | String | 是 | **提现唯一单号,提现接口也需要基于此值做绝对拦截幂等功能**。 | | `externalTransactionId` | String | 是 | **提现唯一单号,提现接口也需要基于此值做绝对拦截幂等功能**。 |
| `user_id` | String | 是 | 申请提现的玩家 ID。 | | `user_id` | String | 是 | 申请提现的玩家 ID。 |
| `memberLogin` | String | 否 | 玩家名(视老接口历史包袱兼容)。 | | `memberLogin` | String | 否 | 玩家名(视老接口历史包袱兼容)。 |
| `amount` | Decimal | 是 | 本次提现要充入 PlayX 主游戏平台真金余额池的具体现金。 | | `amount` | Decimal | 是 | 本次提现要充入 playX 主游戏平台真金余额池的具体现金。 |
| `multiplier` (或 `turnover_rule`) | Int | 是 | 本真金提现入账后的硬性流水锁定要求倍数限制。 | | `multiplier` (或 `turnover_rule`) | Int | 是 | 本真金提现入账后的硬性流水锁定要求倍数限制。 |
| `description` | String | 否 | 日志源记录说明(如 `"PointsMall withdraw"`)。 | | `description` | String | 否 | 日志源记录说明(如 `"PointsMall withdraw"`)。 |
@@ -337,7 +337,7 @@ flowchart LR
"memberLogin": "demo_user_01", "memberLogin": "demo_user_01",
"amount": 100.0, "amount": 100.0,
"multiplier": 1, "multiplier": 1,
"description": "PointsMall withdraw to PlayX balance" "description": "PointsMall withdraw to playX balance"
} }
``` ```
@@ -352,11 +352,11 @@ flowchart LR
} }
``` ```
### 5.5 Mall → PlayXTransaction Status Query API交易终态查询 ### 5.5 Mall → playXTransaction Status Query API交易终态查询
- **目的**:红利/提现申请经 PlayX 接收后(即返回 `accepted` 后)可能处于排队发放下款状态(如 10 分钟 Cron。商城将通过此接口查询最终业务结果用于闭环商城自身的 PENDING 订单。 - **目的**:红利/提现申请经 playX 接收后(即返回 `accepted` 后)可能处于排队发放下款状态(如 10 分钟 Cron。商城将通过此接口查询最终业务结果用于闭环商城自身的 PENDING 订单。
- **Method/Path预留**`GET /api/v1/transaction/status` - **Method/Path预留**`GET /api/v1/transaction/status`
- **传参方式**:使用 **Query String** 传递查询主键(若 PlayX 更倾向 POST可改为 `POST` + JSON body但需在联调前双方定稿一种即可 - **传参方式**:使用 **Query String** 传递查询主键(若 playX 更倾向 POST可改为 `POST` + JSON body但需在联调前双方定稿一种即可
示例: 示例:
@@ -378,21 +378,21 @@ flowchart LR
| --- | --- | --- | --- | | --- | --- | --- | --- |
| `status` | String | 是 | 该笔资产调拨定时任务执行的彻底终态。只有两种预期终点:**`COMPLETED`**(入账成功) 或 **`FAILED`**(发放彻底阻断:如平台风控/未通过规则/账号封禁)。如果返回 `PENDING` 表示该 10 分钟 Cron 仍然没碰这笔单。 | | `status` | String | 是 | 该笔资产调拨定时任务执行的彻底终态。只有两种预期终点:**`COMPLETED`**(入账成功) 或 **`FAILED`**(发放彻底阻断:如平台风控/未通过规则/账号封禁)。如果返回 `PENDING` 表示该 10 分钟 Cron 仍然没碰这笔单。 |
| `amount` | Decimal | 否 | 最后实际结算派发的精准明细金额数。 | | `amount` | Decimal | 否 | 最后实际结算派发的精准明细金额数。 |
| `message` | String | 否 | 若拦截至 `FAILED` 终态,该字段负责说明 PlayX 端驳回的业务层原因,便于商城后端登记审计并自动回退积分。 | | `message` | String | 否 | 若拦截至 `FAILED` 终态,该字段负责说明 playX 端驳回的业务层原因,便于商城后端登记审计并自动回退积分。 |
## 6. 一致性、幂等与退回规则 ## 6. 一致性、幂等与退回规则
### 6.1 幂等原则 ### 6.1 幂等原则
- **每日推送**:以 `user_id + date` 去重,重复推送不得导致重复入池。 - **每日推送**:以 `user_id + date` 去重,重复推送不得导致重复入池。
- **兑换/提现交易**:以 `externalTransactionId` 幂等(商城生成并传给 PlayX - **兑换/提现交易**:以 `externalTransactionId` 幂等(商城生成并传给 playX
- **领取**:以 `claim_request_id` 幂等,避免重复领取。 - **领取**:以 `claim_request_id` 幂等,避免重复领取。
### 6.2 退积分规则(建议统一) ### 6.2 退积分规则(建议统一)
- **红利/提现** - **红利/提现**
- PlayX API 调用失败:订单保持 PENDING进入可重试队列不立即退积分避免“退了但 PlayX 已受理/最终入账”的不一致)。 - playX API 调用失败:订单保持 PENDING进入可重试队列不立即退积分避免“退了但 playX 已受理/最终入账”的不一致)。
- 当订单被判定为“最终失败”(例如超过最大重试次数或 PlayX 返回不可恢复错误)时:订单转 REJECTED退回积分并记录原因。 - 当订单被判定为“最终失败”(例如超过最大重试次数或 playX 返回不可恢复错误)时:订单转 REJECTED退回积分并记录原因。
- **实物** - **实物**
- 驳回必须退回积分,并记录 `reject_reason` - 驳回必须退回积分,并记录 `reject_reason`
@@ -400,12 +400,12 @@ flowchart LR
- 仅允许对 “尚未收到 `status = \"accepted\"` 响应、且可以确认未成功发放/未入账” 的订单发起重试。 - 仅允许对 “尚未收到 `status = \"accepted\"` 响应、且可以确认未成功发放/未入账” 的订单发起重试。
- 每次重试必须生成并记录 `retry_request_id` 与操作者审计日志。 - 每次重试必须生成并记录 `retry_request_id` 与操作者审计日志。
- **强制防重约定****PlayX 必须根据 `externalTransactionId` 提供严格的幂等拦截能力!**由于网络请求存在“Read Timeout读超时”的黑盒场景PlayX 实际已处理但响应由于网络中断未抵达商城,商城将会发起重试保护。如果 PlayX 不去拦截此重发单号,将必然出现给用户发双份钱的高危资损事故。 - **强制防重约定****playX 必须根据 `externalTransactionId` 提供严格的幂等拦截能力!**由于网络请求存在“Read Timeout读超时”的黑盒场景playX 实际已处理但响应由于网络中断未抵达商城,商城将会发起重试保护。如果 playX 不去拦截此重发单号,将必然出现给用户发双份钱的高危资损事故。
接收成功与终态闭环判定(关键约定): 接收成功与终态闭环判定(关键约定):
- **第一步(接收排队)**:本系统调用发放类 API仅当收到 HTTP 200 且 `status = "accepted"` 时,视为 PlayX 已接收入队成功,此时商城**绝不再对发放接口发起新的成功路径请求**。若返回非 200、或响应超时、或未能解析出明确 `accepted`:商城可对**同一** `externalTransactionId` 进行有限次重试;**PlayX 须对该单号严格幂等**——重复请求不得产生第二笔发放,且应返回与首次受理一致或可识别的幂等结果(如再次返回 `accepted` 或明确 `DUPLICATE_REQUEST` 等,由双方约定响应形态)。 - **第一步(接收排队)**:本系统调用发放类 API仅当收到 HTTP 200 且 `status = "accepted"` 时,视为 playX 已接收入队成功,此时商城**绝不再对发放接口发起新的成功路径请求**。若返回非 200、或响应超时、或未能解析出明确 `accepted`:商城可对**同一** `externalTransactionId` 进行有限次重试;**playX 须对该单号严格幂等**——重复请求不得产生第二笔发放,且应返回与首次受理一致或可识别的幂等结果(如再次返回 `accepted` 或明确 `DUPLICATE_REQUEST` 等,由双方约定响应形态)。
- **第二步(确认终态闭环)**:针对已入队返回 accepted 的订单,商城将调用**“交易终态查询 API”5.5**验证 PlayX 后台的最终发放结果实现闭环更新。 - **第二步(确认终态闭环)**:针对已入队返回 accepted 的订单,商城将调用**“交易终态查询 API”5.5**验证 playX 后台的最终发放结果实现闭环更新。
默认重试策略(建议): 默认重试策略(建议):
@@ -415,7 +415,7 @@ flowchart LR
## 7. 安全要求Shared Secret Key ## 7. 安全要求Shared Secret Key
建议所有 PlayX ↔ Mall 的后端调用统一: 建议所有 playX ↔ Mall 的后端调用统一:
- Header - Header
- `X-Request-Id` - `X-Request-Id`
@@ -459,7 +459,7 @@ flowchart LR
### 8.1 幂等:同一 `externalTransactionId` 重复调用Bonus Grant / Balance Credit ### 8.1 幂等:同一 `externalTransactionId` 重复调用Bonus Grant / Balance Credit
PlayX 须保证:**同一** `externalTransactionId` 无论被调用多少次,**资金侧最多只入账一次**。商城在「读超时重试」或联调压测时会重复提交同一单号,响应须符合以下 **两种约定之一**(联调前择一写死,避免双方解析不一致)。 playX 须保证:**同一** `externalTransactionId` 无论被调用多少次,**资金侧最多只入账一次**。商城在「读超时重试」或联调压测时会重复提交同一单号,响应须符合以下 **两种约定之一**(联调前择一写死,避免双方解析不一致)。
**模式 A推荐再次请求仍返回 HTTP 200且与首次受理语义一致** **模式 A推荐再次请求仍返回 HTTP 200且与首次受理语义一致**
@@ -475,7 +475,7 @@ PlayX 须保证:**同一** `externalTransactionId` 无论被调用多少次,
} }
``` ```
**模式 B显式重复错误码HTTP 状态可与 PlayX 规范一致,如 200 或 409联调前约定** **模式 B显式重复错误码HTTP 状态可与 playX 规范一致,如 200 或 409联调前约定**
- `code``DUPLICATE_REQUEST`(或双方统一的幂等冲突码),`retryable``false`,提示商城勿再重试发放接口、改查 5.5 终态。 - `code``DUPLICATE_REQUEST`(或双方统一的幂等冲突码),`retryable``false`,提示商城勿再重试发放接口、改查 5.5 终态。
@@ -515,9 +515,9 @@ PlayX 须保证:**同一** `externalTransactionId` 无论被调用多少次,
### 9.4 红利/提现 ### 9.4 红利/提现
- 发放接口HTTP 200 且 `status="accepted"` 后,订单 PENDING记录 `playx_transaction_id`**不再对发放接口重放**(终态靠 5.5)。 - 发放接口HTTP 200 且 `status="accepted"` 后,订单 PENDING记录 `playx_transaction_id`**不再对发放接口重放**(终态靠 5.5)。
- 发放接口:非 200 / 超时 / 非 `accepted`:失败原因落库,可自动或人工重试;**PlayX 对同一 `externalTransactionId` 须严格幂等**。 - 发放接口:非 200 / 超时 / 非 `accepted`:失败原因落库,可自动或人工重试;**playX 对同一 `externalTransactionId` 须严格幂等**。
- **交易终态查询5.5**:按 `externalTransactionId` 查询,验证返回 `COMPLETED` / `FAILED` / `PENDING`;长时间 `PENDING` 走告警与人工。 - **交易终态查询5.5**:按 `externalTransactionId` 查询,验证返回 `COMPLETED` / `FAILED` / `PENDING`;长时间 `PENDING` 走告警与人工。
- 幂等联调:同一 `externalTransactionId` 连续发送 2 次,PlayX 侧**不得重复入账**,第二次响应须符合双方约定的幂等语义。 - 幂等联调:同一 `externalTransactionId` 连续发送 2 次,playX 侧**不得重复入账**,第二次响应须符合双方约定的幂等语义。
### 9.5 实物 ### 9.5 实物
@@ -525,7 +525,7 @@ PlayX 须保证:**同一** `externalTransactionId` 无论被调用多少次,
- 发货录入物流单号 - 发货录入物流单号
- 驳回退积分并展示原因 - 驳回退积分并展示原因
## 10. 需要 PlayX 提供/确认的信息清单(用于联调收口) ## 10. 需要 playX 提供/确认的信息清单(用于联调收口)
- **Token Verification API**URL、请求/响应字段、错误码、token 有效期/刷新策略、是否支持 session。 - **Token Verification API**URL、请求/响应字段、错误码、token 有效期/刷新策略、是否支持 session。
- **Daily Push API**推送时间点、时区、date 口径(业务日还是自然日)、失败重发策略、字段定义(特别是 `yesterday_win_loss_net` 的扣项范围)。 - **Daily Push API**推送时间点、时区、date 口径(业务日还是自然日)、失败重发策略、字段定义(特别是 `yesterday_win_loss_net` 的扣项范围)。

View File

@@ -1,4 +1,4 @@
# PlayX 接口文档(按调用方向拆分) # playX 接口文档(按调用方向拆分)
说明:本文档严格依据当前代码 `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` 整理。
@@ -6,13 +6,13 @@
| 方向 | 含义 | 本文位置 | | 方向 | 含义 | 本文位置 |
|------|------|----------| |------|------|----------|
| **PlayX → 积分商城** | PlayX或上游批处理**主动 HTTP 调用商城**开放接口 | **§1**(如 Daily Push | | **playX → 积分商城** | playX或上游批处理**主动 HTTP 调用商城**开放接口 | **§1**(如 Daily Push |
| **积分商城 → PlayX** | 商城 Worker / 后台 **主动 HTTP 调用 PlayX / Cash Market** 提供的接口 | **不展开于本文**;交付 PlayX 的说明见 **`docs/PlayX-接口待完善清单.md`** | | **积分商城 → playX** | 商城 Worker / 后台 **主动 HTTP 调用 playX / Cash Market** 提供的接口 | **不展开于本文**;交付 playX 的说明见 **`docs/playX-接口待完善清单.md`** |
| **积分商城 → H5** | H5 / 内嵌页 **调用商城** 的会员与积分业务接口 | **§3** | | **积分商城 → H5** | H5 / 内嵌页 **调用商城** 的会员与积分业务接口 | **§3** |
--- ---
## 1. PlayX → 积分商城(外部系统调用商城开放接口) ## 1. playX → 积分商城(外部系统调用商城开放接口)
### 1.1 Daily Push API ### 1.1 Daily Push API
* 方法:`POST` * 方法:`POST`
@@ -22,7 +22,7 @@
- `lang`: `zh`/`zh-cn` 返回中文(默认);`en` 返回英文 - `lang`: `zh`/`zh-cn` 返回中文(默认);`en` 返回英文
#### Header签名校验HMAC 必填) #### Header签名校验HMAC 必填)
`playx.daily_push_secret` 配置非空时需要携带HMAC `playX.daily_push_secret` 配置非空时需要携带HMAC
- `X-Request-Id`:请求 ID - `X-Request-Id`:请求 ID
- `X-Timestamp`:时间戳 - `X-Timestamp`:时间戳
- `X-Signature`签名HMAC_SHA256 - `X-Signature`签名HMAC_SHA256
@@ -40,7 +40,7 @@
|------|------|------|------| |------|------|------|------|
| `request_id` | string | 是 | 外部推送请求号(原样返回) | | `request_id` | string | 是 | 外部推送请求号(原样返回) |
| `date` | string(YYYY-MM-DD) | 是 | 业务日期(入库到 `mall_daily_push.date` | | `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` 资产行) | | `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 | 否 | 昨日总充值(用于计算今日可领取上限) |
@@ -181,12 +181,12 @@ curl -X POST 'http://localhost:1818/api/v1/mall/dailyPush' \
--- ---
## 2. 积分商城 → PlayX贵方需提供的 HTTP 接口) ## 2. 积分商城 → playX贵方需提供的 HTTP 接口)
商城在验 Token、红利发放、交易轮询、Angpow 导入等场景会 **主动请求 PlayX / Cash Market** 商城在验 Token、红利发放、交易轮询、Angpow 导入等场景会 **主动请求 playX / Cash Market**
**完整 URL、请求/响应字段、成功判定、与 Angpush 双路径关系、联调待办** 已单独整理,便于 **直接转发给 PlayX 平台** **完整 URL、请求/响应字段、成功判定、与 Angpush 双路径关系、联调待办** 已单独整理,便于 **直接转发给 playX 平台**
- **`docs/PlayX-接口待完善清单.md`** - **`docs/playX-接口待完善清单.md`**
本文 **§1** 仅描述「谁调用商城」;**§3** 描述「H5 调用商城」。 本文 **§1** 仅描述「谁调用商城」;**§3** 描述「H5 调用商城」。
@@ -197,9 +197,9 @@ curl -X POST 'http://localhost:1818/api/v1/mall/dailyPush' \
### 3.0 数据模型说明(与代码一致) ### 3.0 数据模型说明(与代码一致)
* **积分商城用户资产主表**`mall_user_asset`(账号、积分、`playx_user_id`H5 临时登录 `temLogin` 直接创建/复用该表行,**不依赖**独立会员 `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_session`(字段含 `session_id``user_id`(此处存 **playX 侧用户标识字符串**,与 `mall_user_asset.playx_user_id` 一致)、`expire_time` 等)。
* **统一订单**`mall_order`(红利/实物/提现订单;`user_id` 字段为 **`playx_user_id` 字符串**)。 * **统一订单**`mall_order`(红利/实物/提现订单;`user_id` 字段为 **`playx_user_id` 字符串**)。
* **对外业务 ID**:订单与推送中的 `user_id` 多为 **PlayX 用户 ID**`playx_user_id`)。临时登录场景下,资产表会生成占位 ID形如 **`mall_{mall_user_asset.id}`**(见 `MallUserAsset::ensureForUsername`)。 * **对外业务 ID**:订单与推送中的 `user_id` 多为 **playX 用户 ID**`playx_user_id`)。临时登录场景下,资产表会生成占位 ID形如 **`mall_{mall_user_asset.id}`**(见 `MallUserAsset::ensureForUsername`)。
### 3.1 鉴权解析规则(`resolvePlayxAssetIdFromRequest` ### 3.1 鉴权解析规则(`resolvePlayxAssetIdFromRequest`
@@ -234,7 +234,7 @@ curl -X POST 'http://localhost:1818/api/v1/mall/dailyPush' \
#### 行为说明 #### 行为说明
* 若用户名不存在:`MallUserAsset::ensureForUsername` 创建资产行(随机密码等),并将 `playx_user_id` 更新为 **`mall_{id}`** 形式(与真实 PlayX ID 区分)。 * 若用户名不存在:`MallUserAsset::ensureForUsername` 创建资产行(随机密码等),并将 `playx_user_id` 更新为 **`mall_{id}`** 形式(与真实 playX ID 区分)。
* 签发 **商城 token**(类型 **`muser`**`token` 表内 `user_id` = **`mall_user_asset.id`**),并签发 `muser-refresh` 刷新令牌。 * 签发 **商城 token**(类型 **`muser`**`token` 表内 `user_id` = **`mall_user_asset.id`**),并签发 `muser-refresh` 刷新令牌。
#### 返回(成功 data.userInfo #### 返回(成功 data.userInfo
@@ -264,16 +264,16 @@ curl -G 'http://localhost:1818/api/v1/temLogin' --data-urlencode 'username=demo_
* 方法:`POST`(推荐 `GET``token` 亦可) * 方法:`POST`(推荐 `GET``token` 亦可)
* 路径:`/api/v1/mall/verifyToken` * 路径:`/api/v1/mall/verifyToken`
#### 配置:本地验证 vs 远程 PlayX #### 配置:本地验证 vs 远程 playX
* 配置项:`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_session`**。 * 仅接受商城临时登录 token类型 **`muser`**),校验 `token` 表后写入 **`mall_session`**。
* 返回的 `data.user_id`**`playx_user_id`**(与资产表一致)。 * 返回的 `data.user_id`**`playx_user_id`**(与资产表一致)。
* **`verify_token_local_only = false`**(生产对接 PlayX * **`verify_token_local_only = false`**(生产对接 playX
* 需配置 **`playx.api.base_url`**,由商城向 PlayX 发起 `POST` 校验(请求/响应约定见 **`docs/PlayX-接口待完善清单.md`** 第一部分 §1 * 需配置 **`playX.api.base_url`**,由商城向 playX 发起 `POST` 校验(请求/响应约定见 **`docs/playX-接口待完善清单.md`** 第一部分 §1
* 若未配置 `base_url`,返回 `PlayX API not configured` * 若未配置 `base_url`,返回 `playX API not configured`
#### 请求参数 #### 请求参数
@@ -286,14 +286,14 @@ curl -G 'http://localhost:1818/api/v1/temLogin' --data-urlencode 'username=demo_
| 字段 | 类型 | 说明 | | 字段 | 类型 | 说明 |
|------|------|------| |------|------|------|
| `session_id` | string | 写入 `mall_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)` |
失败: 失败:
* token 为空HTTP 401msg=`INVALID_TOKEN` * token 为空HTTP 401msg=`INVALID_TOKEN`
* 远程模式且 PlayX 未配置:`msg=PlayX API not configured` * 远程模式且 playX 未配置:`msg=playX API not configured`
#### 示例(本地验证) #### 示例(本地验证)
@@ -609,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_order.user_id` 一致 * 列表项中的 `user_id`**playX 侧 `playx_user_id`**(字符串),与 `mall_order.user_id` 一致
#### 示例 #### 示例
请求: 请求:

View File

@@ -1,6 +1,6 @@
# PlayX 调用积分商城接口说明 # playX 调用积分商城接口说明
本文档描述 **PlayX 平台(或 PlayX 侧脚本/服务)如何调用积分商城已开放的 HTTP 接口**:基础约定、推荐流程、鉴权方式、请求参数与返回结构。 本文档描述 **playX 平台(或 playX 侧脚本/服务)如何调用积分商城已开放的 HTTP 接口**:基础约定、推荐流程、鉴权方式、请求参数与返回结构。
实现依据:`config/route.php``app/api/controller/v1/Playx.php``config/playx.php` 实现依据:`config/route.php``app/api/controller/v1/Playx.php``config/playx.php`
@@ -68,13 +68,13 @@
## 2. 使用流程(推荐) ## 2. 使用流程(推荐)
### 2.1 PlayX 服务端 → 商城:每日数据推送(主流程) ### 2.1 playX 服务端 → 商城:每日数据推送(主流程)
适用于 T+1 等业务数据由 **PlayX 服务端**主动推送到积分商城。 适用于 T+1 等业务数据由 **playX 服务端**主动推送到积分商城。
```mermaid ```mermaid
sequenceDiagram sequenceDiagram
participant PX as PlayX 服务端 participant PX as playX 服务端
participant M as 积分商城 participant M as 积分商城
Note over PX,M: 按约定配置 HMAC 密钥 Note over PX,M: 按约定配置 HMAC 密钥
@@ -88,24 +88,20 @@ sequenceDiagram
### 2.2 用户侧H5 / 内嵌页)→ 商城:会话与业务接口 ### 2.2 用户侧H5 / 内嵌页)→ 商城:会话与业务接口
以下接口多由 **用户在浏览器内**打开积分商城 H5 后调用,通过 **`session_id`**(先调 `verifyToken` 获取)或 **`token`**(商城 `muser` 类 token标识用户**不一定由 PlayX 后端直接调用** 以下接口多由 **用户在浏览器内**打开积分商城 H5 后调用,通过 **`session_id`**(先调 `verifyToken` 获取)或 **`token`**(商城 `muser` 类 token标识用户**不一定由 playX 后端直接调用**
- `POST /api/v1/mall/verifyToken`:用 PlayX token 换商城 `session_id` - `POST /api/v1/mall/verifyToken`:用 playX token 换商城 `session_id`
- `GET /api/v1/mall/assets`:查询资产 - `GET /api/v1/mall/assets`:查询资产
- `POST /api/v1/mall/claim`:领取积分 - `POST /api/v1/mall/claim`:领取积分
- `GET /api/v1/mall/items`:商品列表 - `GET /api/v1/mall/items`:商品列表
- `POST /api/v1/mall/bonusRedeem` / `physicalRedeem` / `withdrawApply`:兑换与提现申请 - `POST /api/v1/mall/bonusRedeem` / `physicalRedeem` / `withdrawApply`:兑换与提现申请
- `GET /api/v1/mall/orders`:订单列表 - `GET /api/v1/mall/orders`:订单列表
PlayX 后端需要代替用户调用上述接口,须同样携带有效的 `session_id``token`,并遵守同一用户身份规则(见 **§4 身份说明**)。 playX 后端需要代替用户调用上述接口,须同样携带有效的 `session_id``token`,并遵守同一用户身份规则(见 **§4 身份说明**)。
### 2.3 代理鉴权(非 PlayX 通用)
`GET /api/v1/authToken` 为 **渠道/代理**签名换 JWT`authtoken`),与 PlayX 用户体系不同一般不在本文「PlayX 平台对接」主流程中展开;需要时由运营向商城索取单独说明。
--- ---
## 3. PlayX 服务端推送Daily Push ## 3. playX 服务端推送Daily Push
### 3.1 概要 ### 3.1 概要
@@ -157,7 +153,7 @@ expected = HMAC_SHA256( canonical , PLAYX_DAILY_PUSH_SECRET )
|------|------|------|------| |------|------|------|------|
| `request_id` | string | 是 | 本次推送请求号;响应中原样返回 | | `request_id` | string | 是 | 本次推送请求号;响应中原样返回 |
| `date` | string | 是 | 业务日期,格式 `YYYY-MM-DD` | | `date` | string | 是 | 业务日期,格式 `YYYY-MM-DD` |
| `user_id` | string | 是 | PlayX 用户 ID幂等键之一 | | `user_id` | string | 是 | playX 用户 ID幂等键之一 |
| `username` | string | 否 | 展示名;用于同步/创建商城侧用户资产展示信息 | | `username` | string | 否 | 展示名;用于同步/创建商城侧用户资产展示信息 |
| `yesterday_win_loss_net` | number | 否 | 昨日净输赢;**小于 0** 时按配置比例计入待领取保障金(`locked_points` | | `yesterday_win_loss_net` | number | 否 | 昨日净输赢;**小于 0** 时按配置比例计入待领取保障金(`locked_points` |
| `yesterday_total_deposit` | number | 否 | 昨日总充值;用于计算当日可领取上限等 | | `yesterday_total_deposit` | number | 否 | 昨日总充值;用于计算当日可领取上限等 |
@@ -168,12 +164,12 @@ expected = HMAC_SHA256( canonical , PLAYX_DAILY_PUSH_SECRET )
| 字段 | 类型 | 必填 | 说明 | | 字段 | 类型 | 必填 | 说明 |
|------|------|------|------| |------|------|------|------|
| `report_date` | string/number | 是 | 报表日期;可以为 Unix 秒时间戳(如 `1700000000`)或 `YYYY-MM-DD` | | `report_date` | string/number | 是 | 报表日期;可以为 Unix 秒时间戳(如 `1700000000`)或 `YYYY-MM-DD` |
| `member` | array | 是 | 成员列表,每个成员包含一名 PlayX 用户数据 | | `member` | array | 是 | 成员列表,每个成员包含一名 playX 用户数据 |
成员元素字段: 成员元素字段:
| 字段 | 类型 | 必填 | 说明 | | 字段 | 类型 | 必填 | 说明 |
|------|------|------|------| |------|------|------|------|
| `member_id` | string | 是 | PlayX 用户 ID幂等键之一 | | `member_id` | string | 是 | playX 用户 ID幂等键之一 |
| `login` | string | 否 | 用户展示名 | | `login` | string | 否 | 用户展示名 |
| `yesterday_total_w` | number | 否 | 昨日净输赢;小于 0 才会累加到 `locked_points` | | `yesterday_total_w` | number | 否 | 昨日净输赢;小于 0 才会累加到 `locked_points` |
| `yesterday_total_deposit` | number | 否 | 昨日总充值;用于计算 `today_limit` | | `yesterday_total_deposit` | number | 否 | 昨日总充值;用于计算 `today_limit` |
@@ -281,20 +277,20 @@ curl -X POST 'https://{商城域名}/api/v1/mall/dailyPush' \
### 5.1 `POST /api/v1/mall/verifyToken` ### 5.1 `POST /api/v1/mall/verifyToken`
用于将 **PlayX token**(或本地联调 token**商城 `session_id`** 用于将 **playX token**(或本地联调 token**商城 `session_id`**
| 参数位置 | 名称 | 说明 | | 参数位置 | 名称 | 说明 |
|----------|------|------| |----------|------|------|
| POST/GET | `token``session` | PlayX 或商城 token | | POST/GET | `token``session` | playX 或商城 token |
**说明:**`playx.verify_token_local_only=true`(默认),商城**仅本地校验** token不请求 PlayX 远程接口;远程模式需配置 `PLAYX_API_BASE_URL` 等。 **说明:**`playX.verify_token_local_only=true`(默认),商城**仅本地校验** token不请求 playX 远程接口;远程模式需配置 `PLAYX_API_BASE_URL` 等。
**成功 `data` 示例:** **成功 `data` 示例:**
| 字段 | 说明 | | 字段 | 说明 |
|------|------| |------|------|
| `session_id` | 后续接口可带此字段 | | `session_id` | 后续接口可带此字段 |
| `user_id` | PlayX 用户 ID 或映射后的标识 | | `user_id` | playX 用户 ID 或映射后的标识 |
| `username` | 用户名 | | `username` | 用户名 |
| `token_expire_at` | ISO8601 过期时间 | | `token_expire_at` | ISO8601 过期时间 |
@@ -436,8 +432,8 @@ Body
| 环境变量 / 配置 | 作用 | | 环境变量 / 配置 | 作用 |
|-----------------|------| |-----------------|------|
| `PLAYX_DAILY_PUSH_SECRET` | 非空则 Daily Push 必须带合法 HMAC 头 | | `PLAYX_DAILY_PUSH_SECRET` | 非空则 Daily Push 必须带合法 HMAC 头 |
| `PLAYX_VERIFY_TOKEN_LOCAL_ONLY` | 为 true 时 verifyToken 不请求 PlayX 远程 | | `PLAYX_VERIFY_TOKEN_LOCAL_ONLY` | 为 true 时 verifyToken 不请求 playX 远程 |
| `PLAYX_API_BASE_URL` | 商城调用 PlayX 接口时使用(与「PlayX 调商城」方向相反) | | `PLAYX_API_BASE_URL` | 商城调用 playX 接口时使用(与「playX 调商城」方向相反) |
--- ---

View File

@@ -1,20 +1,20 @@
# 积分商城 PlayX 对接实施方案 # 积分商城 playX 对接实施方案
> 基于《积分商城-内部对接与流程说明.md》和《PlayX-对接文档(积分商城).md》整理结合当前项目结构给出具体落地方案。 > 基于《积分商城-内部对接与流程说明.md》和《playX-对接文档(积分商城).md》整理结合当前项目结构给出具体落地方案。
--- ---
## 一、接口创建 ## 一、接口创建
### 1.1 商城需对外提供的接口(PlayX 调用商城) ### 1.1 商城需对外提供的接口(playX 调用商城)
#### Daily Push API #### Daily Push API
接收 PlayX 每日 T+1 数据推送。 接收 playX 每日 T+1 数据推送。
* 方法:`POST` * 方法:`POST`
* 路径:`/api/v1/mall/dailyPush` * 路径:`/api/v1/mall/dailyPush`
##### 请求Header ##### 请求Header
当配置了 `playx.daily_push_secret`Daily Push 签名校验)时,需要携带: 当配置了 `playX.daily_push_secret`Daily Push 签名校验)时,需要携带:
* `X-Request-Id`:请求 ID * `X-Request-Id`:请求 ID
* `X-Timestamp`:时间戳 * `X-Timestamp`:时间戳
* `X-Signature`签名HMAC_SHA256 * `X-Signature`签名HMAC_SHA256
@@ -28,7 +28,7 @@
|------|------|------|------| |------|------|------|------|
| `request_id` | string | 是 | 外部推送请求号(原样返回) | | `request_id` | string | 是 | 外部推送请求号(原样返回) |
| `date` | string(YYYY-MM-DD) | 是 | 业务日期(入库到 `mall_playx_daily_push.date` | | `date` | string(YYYY-MM-DD) | 是 | 业务日期(入库到 `mall_playx_daily_push.date` |
| `user_id` | string | 是 | PlayX 用户 ID幂等键组成部分 | | `user_id` | string | 是 | playX 用户 ID幂等键组成部分 |
| `username` | string | 否 | 展示冗余 | | `username` | string | 否 | 展示冗余 |
| `yesterday_win_loss_net` | number | 否 | 昨日净输赢(仅当 `< 0` 时计算新增保障金) | | `yesterday_win_loss_net` | number | 否 | 昨日净输赢(仅当 `< 0` 时计算新增保障金) |
| `yesterday_total_deposit` | number | 否 | 昨日总充值(用于计算今日可领取上限) | | `yesterday_total_deposit` | number | 否 | 昨日总充值(用于计算今日可领取上限) |
@@ -81,18 +81,18 @@ curl -X POST 'http://localhost:1818/api/v1/mall/dailyPush' \
--- ---
### 1.2 商城需调用的 PlayX 接口(外部,由 PlayX 提供) ### 1.2 商城需调用的 playX 接口(外部,由 playX 提供)
以下为商城侧调用(由 PlayX 提供)。 以下为商城侧调用(由 playX 提供)。
#### Token Verification API #### Token Verification API
* 方法:`POST` * 方法:`POST`
* URL`${playx.api.base_url}${playx.api.token_verify_url}`(默认 `/api/v1/auth/verify-token` * URL`${playX.api.base_url}${playX.api.token_verify_url}`(默认 `/api/v1/auth/verify-token`
##### 请求 Body ##### 请求 Body
| 字段 | 类型 | 必填 | 说明 | | 字段 | 类型 | 必填 | 说明 |
|------|------|------|------| |------|------|------|------|
| `request_id` | string | 是 | 服务端生成,如 `mall_{uniqid}` | | `request_id` | string | 是 | 服务端生成,如 `mall_{uniqid}` |
| `token` | string | 是 | 前端传入的 PlayX token | | `token` | string | 是 | 前端传入的 playX token |
##### 返回(期望) ##### 返回(期望)
HTTP 状态码 `200`,响应体需包含: HTTP 状态码 `200`,响应体需包含:
@@ -118,14 +118,14 @@ HTTP 状态码 `200`,响应体需包含:
#### Bonus Grant API #### Bonus Grant API
* 方法:`POST` * 方法:`POST`
* URL`${playx.api.base_url}${playx.api.bonus_grant_url}`(默认 `/api/v1/bonus/grant` * URL`${playX.api.base_url}${playX.api.bonus_grant_url}`(默认 `/api/v1/bonus/grant`
##### 请求 Body ##### 请求 Body
| 字段 | 类型 | 必填 | 说明 | | 字段 | 类型 | 必填 | 说明 |
|------|------|------|------| |------|------|------|------|
| `request_id` | string | 是 | 如 `mall_bonus_{uniqid}` | | `request_id` | string | 是 | 如 `mall_bonus_{uniqid}` |
| `externalTransactionId` | string | 是 | 订单幂等键:`external_transaction_id` | | `externalTransactionId` | string | 是 | 订单幂等键:`external_transaction_id` |
| `user_id` | string | 是 | PlayX 用户 ID | | `user_id` | string | 是 | playX 用户 ID |
| `amount` | number | 是 | 订单金额:`MallPlayxOrder.amount` | | `amount` | number | 是 | 订单金额:`MallPlayxOrder.amount` |
| `rewardName` | string | 是 | `mall_item.title` | | `rewardName` | string | 是 | `mall_item.title` |
| `category` | string | 是 | `mall_item.category`,默认 `daily` | | `category` | string | 是 | `mall_item.category`,默认 `daily` |
@@ -169,14 +169,14 @@ HTTP 状态码 `200`,响应体需包含:
#### Balance Credit API #### Balance Credit API
* 方法:`POST` * 方法:`POST`
* URL`${playx.api.base_url}${playx.api.balance_credit_url}`(默认 `/api/v1/balance/credit` * URL`${playX.api.base_url}${playX.api.balance_credit_url}`(默认 `/api/v1/balance/credit`
##### 请求 Body ##### 请求 Body
| 字段 | 类型 | 必填 | 说明 | | 字段 | 类型 | 必填 | 说明 |
|------|------|------|------| |------|------|------|------|
| `request_id` | string | 是 | 如 `mall_withdraw_{uniqid}` | | `request_id` | string | 是 | 如 `mall_withdraw_{uniqid}` |
| `externalTransactionId` | string | 是 | 订单幂等键:`external_transaction_id` | | `externalTransactionId` | string | 是 | 订单幂等键:`external_transaction_id` |
| `user_id` | string | 是 | PlayX 用户 ID | | `user_id` | string | 是 | playX 用户 ID |
| `amount` | number | 是 | 订单金额:`MallPlayxOrder.amount` | | `amount` | number | 是 | 订单金额:`MallPlayxOrder.amount` |
| `multiplier` | int | 是 | `MallPlayxOrder.multiplier` | | `multiplier` | int | 是 | `MallPlayxOrder.multiplier` |
@@ -215,14 +215,14 @@ HTTP 状态码 `200`,响应体需包含:
#### Transaction Status Query API交易终态查询 #### Transaction Status Query API交易终态查询
* 方法:`GET` * 方法:`GET`
* URL`${playx.api.base_url}${playx.api.transaction_status_url}`(默认 `/api/v1/transaction/status` * URL`${playX.api.base_url}${playX.api.transaction_status_url}`(默认 `/api/v1/transaction/status`
##### Query ##### Query
* `externalTransactionId`:订单幂等键 * `externalTransactionId`:订单幂等键
##### 示例(请求) ##### 示例(请求)
```bash ```bash
curl -G '${playx.api.base_url}/api/v1/transaction/status' \ curl -G '${playX.api.base_url}/api/v1/transaction/status' \
--data-urlencode 'externalTransactionId=BONUS_ORD2026....' --data-urlencode 'externalTransactionId=BONUS_ORD2026....'
``` ```
@@ -238,7 +238,7 @@ curl -G '${playx.api.base_url}/api/v1/transaction/status' \
示例failed 示例failed
```json ```json
{ "status": "FAILED", "message": "grant rejected by PlayX" } { "status": "FAILED", "message": "grant rejected by playX" }
``` ```
--- ---
@@ -499,7 +499,7 @@ curl -G 'http://localhost:1818/api/v1/mall/orders' --data-urlencode 'session_id=
#### 同步额度 #### 同步额度
当前代码未实现并未注册路由:`/api/v1/mall/syncLimit` 当前代码未实现并未注册路由:`/api/v1/mall/syncLimit`
如需补齐,请在接口设计阶段新增对应实现与 PlayX API 对接。 如需补齐,请在接口设计阶段新增对应实现与 playX API 对接。
--- ---
@@ -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 状态 - 展示 `external_transaction_id`、推送 playX 状态
- 手动重试:仅对 `FAILED_RETRYABLE` 状态,记录 `retry_request_id`、操作者、原因 - 手动重试:仅对 `FAILED_RETRYABLE` 状态,记录 `retry_request_id`、操作者、原因
### 2.3 用户资产与人工调账 ### 2.3 用户资产与人工调账
@@ -546,7 +546,7 @@ curl -G 'http://localhost:1818/api/v1/mall/orders' --data-urlencode 'session_id=
**方案 A改造 mall_player** **方案 A改造 mall_player**
-`user_id` 作为主键(PlayX 的 user_id或与现有 id 并存 -`user_id` 作为主键(playX 的 user_id或与现有 id 并存
- 新增字段: - 新增字段:
- `locked_points`(待领取积分) - `locked_points`(待领取积分)
- `available_points`(可用积分) - `available_points`(可用积分)
@@ -556,7 +556,7 @@ curl -G 'http://localhost:1818/api/v1/mall/orders' --data-urlencode 'session_id=
**方案 B新建 mall_playx_user_asset** **方案 B新建 mall_playx_user_asset**
- `user_id`PlayX 用户 ID主键或唯一 - `user_id`playX 用户 ID主键或唯一
- `username`(冗余展示) - `username`(冗余展示)
- `locked_points``available_points``today_limit``today_claimed``today_limit_date` - `locked_points``available_points``today_limit``today_claimed``today_limit_date`
- `create_time``update_time` - `create_time``update_time`
@@ -613,7 +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) | 订单号(商城侧幂等/对账主键Angpow 流程不单独落库 PlayX 内部流水号) | | external_transaction_id | varchar(64) | 订单号(商城侧幂等/对账主键Angpow 流程不单独落库 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 | 重试次数 |
@@ -676,7 +676,7 @@ curl -G 'http://localhost:1818/api/v1/mall/orders' --data-urlencode 'session_id=
## 六、待确认事项 ## 六、待确认事项
- PlayX 提供的 **Token Verification API**、**Bonus Grant API**、**Balance Credit API**、**交易终态查询 API** 的 URL、鉴权方式、字段最终表 - playX 提供的 **Token Verification API**、**Bonus Grant API**、**Balance Credit API**、**交易终态查询 API** 的 URL、鉴权方式、字段最终表
- `date` 的时区定义(如 UTC+8 - `date` 的时区定义(如 UTC+8
- 返还比例、解锁比例、提现折算的具体数值 - 返还比例、解锁比例、提现折算的具体数值
- 是否启用「同步额度」功能(需 PlayX 提供对应 API - 是否启用「同步额度」功能(需 playX 提供对应 API

View File

@@ -2,35 +2,35 @@
本文件为 **内部使用** 的完整说明,用于: 本文件为 **内部使用** 的完整说明,用于:
- 梳理积分商城与 PlayX 之间的 **全量业务流程**(含当前选型与备选方案)。 - 梳理积分商城与 playX 之间的 **全量业务流程**(含当前选型与备选方案)。
- 统一后台实现口径(资产计算、订单状态机、幂等、重试、对账)。 - 统一后台实现口径(资产计算、订单状态机、幂等、重试、对账)。
- 为后续版本扩展(实时 webhook、同步按钮、PlayX 拉取模式)预留空间。 - 为后续版本扩展(实时 webhook、同步按钮、playX 拉取模式)预留空间。
对外给 PlayX 的精简版请参考:`PlayX-对接文档(积分商城).md` 对外给 playX 的精简版请参考:`playX-对接文档(积分商城).md`
## 2. 当前选型概览V1.0 ## 2. 当前选型概览V1.0
- **主键标识**`user_id`(贯穿每日推送、资产、订单、发放接口)。 - **主键标识**`user_id`(贯穿每日推送、资产、订单、发放接口)。
- **集成方式** - **集成方式**
- 前端:商城 H5 以 Iframe 嵌入 PlayXpostMessage 传 token/session。 - 前端:商城 H5 以 Iframe 嵌入 playXpostMessage 传 token/session。
- 后端:商城独立服务,通过 REST API 与 PlayX 通讯。 - 后端:商城独立服务,通过 REST API 与 playX 通讯。
- **数据来源** - **数据来源**
- 资产池:仅使用 **每日 Cron 推送T+1** 的历史输赢与充值数据。 - 资产池:仅使用 **每日 Cron 推送T+1** 的历史输赢与充值数据。
- 不引入实时充值/流水 webhook 作为资产来源。 - 不引入实时充值/流水 webhook 作为资产来源。
- **发放模式** - **发放模式**
- 商城在红利/提现下单后,**直接调用 PlayX 发放接口**Bonus Grant / Balance Credit - 商城在红利/提现下单后,**直接调用 playX 发放接口**Bonus Grant / Balance Credit
- PlayX 侧每 10 分钟 Cron 执行 5.9 adjustment / 最终入账。 - playX 侧每 10 分钟 Cron 执行 5.9 adjustment / 最终入账。
- **幂等责任分工(方案 A与对外文档一致** - **幂等责任分工(方案 A与对外文档一致**
- **PlayX**:对 Bonus Grant / Balance Credit 按 **`externalTransactionId` 严格幂等**——同一单号重复请求不得产生第二笔发放;须返回与首次受理一致或可识别的幂等结果(具体 HTTP 体字段由联调约定)。 - **playX**:对 Bonus Grant / Balance Credit 按 **`externalTransactionId` 严格幂等**——同一单号重复请求不得产生第二笔发放;须返回与首次受理一致或可识别的幂等结果(具体 HTTP 体字段由联调约定)。
- **商城**:为每笔红利/提现生成**全局唯一** `externalTransactionId`;收到 HTTP 200 且 `status="accepted"` 后**不再向发放接口重放**;对网络超时等“未知是否受理”场景,可在有限次内重试**同一** `externalTransactionId`,依赖 PlayX 幂等保证不双发(见第 6 章)。 - **商城**:为每笔红利/提现生成**全局唯一** `externalTransactionId`;收到 HTTP 200 且 `status="accepted"` 后**不再向发放接口重放**;对网络超时等“未知是否受理”场景,可在有限次内重试**同一** `externalTransactionId`,依赖 playX 幂等保证不双发(见第 6 章)。
## 3. 角色、系统与对象 ## 3. 角色、系统与对象
### 3.1 角色 ### 3.1 角色
- **会员**:在 PlayX 内通过 Iframe 使用积分商城。 - **会员**:在 playX 内通过 Iframe 使用积分商城。
- **运营/客服**:使用商城后台管理商品、订单、调账。 - **运营/客服**:使用商城后台管理商品、订单、调账。
- **PlayX 平台**:数据源与权益发放执行方。 - **playX 平台**:数据源与权益发放执行方。
### 3.2 主要对象与前端看板展示映射 ### 3.2 主要对象与前端看板展示映射
@@ -38,13 +38,13 @@
- 前端展示专用虚拟字段由可用积分按固定比例折算10 分 = 1 元),用于给玩家呈现直观价值,非底层独立资产。 - 前端展示专用虚拟字段由可用积分按固定比例折算10 分 = 1 元),用于给玩家呈现直观价值,非底层独立资产。
- **待领取积分 (LockedPoints)** - **待领取积分 (LockedPoints)**
- **定义**:基于昨日玩家亏损转化来的“保障池”,未领取前不可消费。 - **定义**:基于昨日玩家亏损转化来的“保障池”,未领取前不可消费。
- **数据源**PlayX 每日推送的 `Yesterday Player Win loss`(取绝对值 × 返还比例)。 - **数据源**playX 每日推送的 `Yesterday Player Win loss`(取绝对值 × 返还比例)。
- **可用积分 (AvailablePoints)** - **可用积分 (AvailablePoints)**
- **定义**:玩家当前拥有的、可立即抵扣兑换和提现的真实积分资产。 - **定义**:玩家当前拥有的、可立即抵扣兑换和提现的真实积分资产。
- **交互消耗**:所有兑换、提现操作均扣减该字段。 - **交互消耗**:所有兑换、提现操作均扣减该字段。
- **今日可领取上限 (TodayLimit)** - **今日可领取上限 (TodayLimit)**
- **定义**:限制玩家当日最多能“挽回”多少积分。 - **定义**:限制玩家当日最多能“挽回”多少积分。
- **数据源**PlayX 每日推送的 `Yesterday Total Deposit`(昨日总充值 × 解锁比例)。 - **数据源**playX 每日推送的 `Yesterday Total Deposit`(昨日总充值 × 解锁比例)。
- **今日已领取 (TodayClaimed)** - **今日已领取 (TodayClaimed)**
- **定义**:记录玩家当日累计已领取的积分规模,辅助校验上限,每日重置。 - **定义**:记录玩家当日累计已领取的积分规模,辅助校验上限,每日重置。
- **订单 (Order)** - **订单 (Order)**
@@ -55,11 +55,11 @@
### 4.1 登录与会话建立 ### 4.1 登录与会话建立
1. 会员在 PlayX 点击“积分商城”入口。 1. 会员在 playX 点击“积分商城”入口。
2. 父页面加载 Iframe商城前端进入“连接中/鉴权中”态。 2. 父页面加载 Iframe商城前端进入“连接中/鉴权中”态。
3. PlayX 前端通过 postMessage 发送: 3. playX 前端通过 postMessage 发送:
- `token` 或 session 标识。 - `token` 或 session 标识。
4. 商城前端将 token 传给后端,后端调用 PlayX 的 **Token Verification API** 验证身份: 4. 商城前端将 token 传给后端,后端调用 playX 的 **Token Verification API** 验证身份:
- **请求参数** - **请求参数**
- `token`:接收到的用户会话凭证。 - `token`:接收到的用户会话凭证。
- `request_id`:系统发起的鉴权校验流水号。 - `request_id`:系统发起的鉴权校验流水号。
@@ -72,11 +72,11 @@
**安全与会话续期要点** **安全与会话续期要点**
- **安全拦截**:不信任前端传入的 `user_id`,必须通过 Token Verification API 获取可信 `user_id` - **安全拦截**:不信任前端传入的 `user_id`,必须通过 Token Verification API 获取可信 `user_id`
- **Token 续期**:如果用户在商城停留过久导致 Token 过期(接口返回 `401 / INVALID_TOKEN`),商城前端需通过 `postMessage` 通知 PlayX 父页面重新派发新 Token 以实现静默续期;若无法自动续期,则需弹窗引导用户重新进入商城。 - **Token 续期**:如果用户在商城停留过久导致 Token 过期(接口返回 `401 / INVALID_TOKEN`),商城前端需通过 `postMessage` 通知 playX 父页面重新派发新 Token 以实现静默续期;若无法自动续期,则需弹窗引导用户重新进入商城。
### 4.2 每日 T+1 数据推送与资产入池 ### 4.2 每日 T+1 数据推送与资产入池
数据来源:PlayX 每日 Cron。 数据来源:playX 每日 Cron。
- **交互字段说明T+1 核心输入)** - **交互字段说明T+1 核心输入)**
- `date`:归属业务定日(用于限定该批数据的生效周期)。 - `date`:归属业务定日(用于限定该批数据的生效周期)。
@@ -87,7 +87,7 @@
商城处理逻辑: 商城处理逻辑:
1.`user_id + date` 幂等入库,避免重复处理。(**注:需与 PlayX 明确 `date` 的时区定义,如 UTC+8 等**)。 1.`user_id + date` 幂等入库,避免重复处理。(**注:需与 playX 明确 `date` 的时区定义,如 UTC+8 等**)。
2. 根据业务规则计算: 2. 根据业务规则计算:
- `新增保障金 = ABS(yesterday_win_loss_net) * 返还比例`(仅当 yesterday_win_loss_net < 0 时产生)。 - `新增保障金 = ABS(yesterday_win_loss_net) * 返还比例`(仅当 yesterday_win_loss_net < 0 时产生)。
- `今日可领取上限 = yesterday_total_deposit * 解锁比例`。**注意:今日上限每日独立计算,不结转至次日。** - `今日可领取上限 = yesterday_total_deposit * 解锁比例`。**注意:今日上限每日独立计算,不结转至次日。**
@@ -95,7 +95,7 @@
**数据修正机制** **数据修正机制**
- T+1 推送不支持覆盖更新(冲正)。如果 PlayX 上游数据算错导致推送有误,商城在二次推送时会触发去重拦截(`deduped`)。此类异常数据统一由**商城后台人工调账**处理。 - T+1 推送不支持覆盖更新(冲正)。如果 playX 上游数据算错导致推送有误,商城在二次推送时会触发去重拦截(`deduped`)。此类异常数据统一由**商城后台人工调账**处理。
### 4.3 领取流程(从待挽回转化为可用) ### 4.3 领取流程(从待挽回转化为可用)
@@ -137,18 +137,18 @@
- 商品信息 & 消耗积分 - 商品信息 & 消耗积分
5. 原子扣减 `AvailablePoints` 5. 原子扣减 `AvailablePoints`
6. 生成 `externalTransactionId`(例如 `BONUS_ORD{订单号}`)。 6. 生成 `externalTransactionId`(例如 `BONUS_ORD{订单号}`)。
7. 调用 PlayX Bonus Grant API核心透传字段详见外部对接文档 7. 调用 playX Bonus Grant API核心透传字段详见外部对接文档
- `externalTransactionId`:本单派发流水号(**防重**:用于要求下游强制幂等拦截)。 - `externalTransactionId`:本单派发流水号(**防重**:用于要求下游强制幂等拦截)。
- `user_id`:玩家标示。 - `user_id`:玩家标示。
- `amount`:红利发放切实的现金面值。 - `amount`:红利发放切实的现金面值。
- `multiplier`:这笔款项后续提款的流水约束倍数。 - `multiplier`:这笔款项后续提款的流水约束倍数。
- `rewardName`:展示给玩家此笔红利来源名称。 - `rewardName`:展示给玩家此笔红利来源名称。
- `category`:便于平台统计对账的红利业务大类。 - `category`:便于平台统计对账的红利业务大类。
8. PlayX 返回: 8. playX 返回:
- 若 HTTP 200 且 `status = "accepted"` - 若 HTTP 200 且 `status = "accepted"`
- 记录 `playx_transaction_id` - 记录 `playx_transaction_id`
- 订单保持 PENDING等待对方系统最终发放 - 订单保持 PENDING等待对方系统最终发放
- **发起请求动作不再重试**。但商城侧需定时调用 PlayX 提供的 **交易状态查询 API** 轮询确认该订单最终结果,成功则转 `COMPLETED`,失败则转 `REJECTED` 并退分。 - **发起请求动作不再重试**。但商城侧需定时调用 playX 提供的 **交易状态查询 API** 轮询确认该订单最终结果,成功则转 `COMPLETED`,失败则转 `REJECTED` 并退分。
- 否则: - 否则:
- 记录失败原因。 - 记录失败原因。
- 进入“可重试队列”(自动/人工重试,见第 6 章)。 - 进入“可重试队列”(自动/人工重试,见第 6 章)。
@@ -160,12 +160,12 @@
- 校验库存/积分。 - 校验库存/积分。
- 创建 PENDING 订单,扣减 `AvailablePoints` - 创建 PENDING 订单,扣减 `AvailablePoints`
3. 后台处理: 3. 后台处理:
- 发货:录入物流公司与单号,状态 → SHIPPED。**(可选:调用 PlayX Inbox API 给用户发送发货通知站内信)**。 - 发货:录入物流公司与单号,状态 → SHIPPED。**(可选:调用 playX Inbox API 给用户发送发货通知站内信)**。
- 驳回:录入驳回原因,状态 → REJECTED并退回积分。**(可选:调用 PlayX Inbox API 告知用户驳回原因)**。 - 驳回:录入驳回原因,状态 → REJECTED并退回积分。**(可选:调用 playX Inbox API 告知用户驳回原因)**。
### 4.6 提现回平台余额WITHDRAW 操作逻辑) ### 4.6 提现回平台余额WITHDRAW 操作逻辑)
本流程旨在将兑换所得的虚拟积分,按照规定“提现”为充入 PlayX 平台账户的真实金额。 本流程旨在将兑换所得的虚拟积分,按照规定“提现”为充入 playX 平台账户的真实金额。
1. **前端选择与展示** 1. **前端选择与展示**
- 会员在首页或“提现到平台”类别列表中选择特定提现档位(如:提现 100 元,需要 1000 积分1倍流水要求 - 会员在首页或“提现到平台”类别列表中选择特定提现档位(如:提现 100 元,需要 1000 积分1倍流水要求
@@ -178,19 +178,19 @@
- 原子扣减数据库内该会员的 `AvailablePoints` - 原子扣减数据库内该会员的 `AvailablePoints`
- 创建 WITHDRAW 暂挂订单(状态 `PENDING`),记录该单提现对应的 `amount``multiplier` - 创建 WITHDRAW 暂挂订单(状态 `PENDING`),记录该单提现对应的 `amount``multiplier`
- 生成外部交易单号 `externalTransactionId`(如 `WITHDRAW_ORD{订单号}`)。 - 生成外部交易单号 `externalTransactionId`(如 `WITHDRAW_ORD{订单号}`)。
4. **调用 PlayX API 发放(核心参数解析)** 4. **调用 playX API 发放(核心参数解析)**
- 商城发包调用 PlayX 的 Balance Credit API - 商城发包调用 playX 的 Balance Credit API
- `externalTransactionId`:本提现申请的订单号(**提现防重唯一拦截键**)。 - `externalTransactionId`:本提现申请的订单号(**提现防重唯一拦截键**)。
- `user_id`:发起提现的玩家 ID。 - `user_id`:发起提现的玩家 ID。
- `amount`:要充入平台余额池的真金面值。 - `amount`:要充入平台余额池的真金面值。
- `multiplier/turnover_rule`:该笔提现资金入账后锁定的打码流水倍数条件。 - `multiplier/turnover_rule`:该笔提现资金入账后锁定的打码流水倍数条件。
5. **异步等待终态与 UI 回显** 5. **异步等待终态与 UI 回显**
- 收到 API `accepted` 响应后,商城将不间断返回前端“提交成功,预计 10 分钟内处理”。 - 收到 API `accepted` 响应后,商城将不间断返回前端“提交成功,预计 10 分钟内处理”。
- 商城内部保持该订单为 PENDING并进入定时轮询状态监控 PlayX 10 分钟 Cron 执行后的“最终业务发货结果”,闭环完成后才转入 COMPLETED 或对失败按规则退分。 - 商城内部保持该订单为 PENDING并进入定时轮询状态监控 playX 10 分钟 Cron 执行后的“最终业务发货结果”,闭环完成后才转入 COMPLETED 或对失败按规则退分。
## 5. 扩展与备选方案(暂不对外) ## 5. 扩展与备选方案(暂不对外)
本章为 **扩展设计/备选方案**,当前版本不对 PlayX 提出实现要求,只在内部保留。 本章为 **扩展设计/备选方案**,当前版本不对 playX 提出实现要求,只在内部保留。
### 5.1 实时充值 webhook备选 ### 5.1 实时充值 webhook备选
@@ -198,7 +198,7 @@
示例设计: 示例设计:
- PlayX 在充值成功后,调用商城的充值 webhook - playX 在充值成功后,调用商城的充值 webhook
- 字段:`user_id``amount``transaction_id``occurred_at` - 字段:`user_id``amount``transaction_id``occurred_at`
- 商城: - 商城:
-`transaction_id` 幂等入库。 -`transaction_id` 幂等入库。
@@ -210,11 +210,11 @@
### 5.2 外部积分来源 webhook任务/轮盘) ### 5.2 外部积分来源 webhook任务/轮盘)
用途:把 PlayX 任务、幸运轮盘等活动产出的积分汇总到商城。 用途:把 playX 任务、幸运轮盘等活动产出的积分汇总到商城。
示例设计: 示例设计:
- PlayX 调用 webhook - playX 调用 webhook
- 字段:`user_id``points``source``transaction_id` - 字段:`user_id``points``source``transaction_id`
- 商城: - 商城:
-`transaction_id` 幂等增加 `AvailablePoints` 或某个“活动积分池”。 -`transaction_id` 幂等增加 `AvailablePoints` 或某个“活动积分池”。
@@ -235,20 +235,20 @@
**后端业务逻辑选型推荐** **后端业务逻辑选型推荐**
- **方案 A建议商城做拉取请求** - **方案 A建议商城做拉取请求**
- 会员点击同步按钮,商城后端拦截并向 PlayX 系统调用一个**“查询今日实时余额/重算存款 API”**。 - 会员点击同步按钮,商城后端拦截并向 playX 系统调用一个**“查询今日实时余额/重算存款 API”**。
- 获取到最新存款后,累加或覆盖当前的 `TodayLimit` - 获取到最新存款后,累加或覆盖当前的 `TodayLimit`
- **方案 BPlayX 控制权)** - **方案 BplayX 控制权)**
- 点击后,通过 iframe 的 `postMessage` 向父级 PlayX 窗口发送同步指令。 - 点击后,通过 iframe 的 `postMessage` 向父级 playX 窗口发送同步指令。
- PlayX 在自身域内统计今日所有游戏存款流水进行汇总归集(甚至一键转账入主钱包的操作)。 - playX 在自身域内统计今日所有游戏存款流水进行汇总归集(甚至一键转账入主钱包的操作)。
- 处理完成后 PlayX 主动请求积分商城的`更新 webhook`来给 `TodayLimit` 加额,最后前端获取成功事件刷新。 - 处理完成后 playX 主动请求积分商城的`更新 webhook`来给 `TodayLimit` 加额,最后前端获取成功事件刷新。
- **当前定案落地方向** - **当前定案落地方向**
- 根据原型要求,本功能**必须落地**。推荐使用方案 A。商城侧需准备一个接受通知的 APIPlayX 需要支持实时提供玩家当日总存款的只读 API以供点击拉取。对于 V1.0 对外文档里若不打算实现,需与 PlayX 进一步交涉决定是否阉割掉此按钮。 - 根据原型要求,本功能**必须落地**。推荐使用方案 A。商城侧需准备一个接受通知的 APIplayX 需要支持实时提供玩家当日总存款的只读 API以供点击拉取。对于 V1.0 对外文档里若不打算实现,需与 playX 进一步交涉决定是否阉割掉此按钮。
### 5.4 发放模式备选:PlayX 定时拉取 ### 5.4 发放模式备选:playX 定时拉取
备选方案: 备选方案:
-PlayX 每 10 分钟调用商城查询接口,拉取待发放订单列表,然后自行发放。 -playX 每 10 分钟调用商城查询接口,拉取待发放订单列表,然后自行发放。
需要新增: 需要新增:
@@ -257,7 +257,7 @@
当前选型: 当前选型:
- 考虑到复杂度与 PlayX 当前发放系统形态,最终选择 **商城主动调用 PlayX 发放 API** 的模式,拉取模式仅保留在内部文档中作为备选。 - 考虑到复杂度与 playX 当前发放系统形态,最终选择 **商城主动调用 playX 发放 API** 的模式,拉取模式仅保留在内部文档中作为备选。
## 6. 幂等、重试与状态机(内部实现口径) ## 6. 幂等、重试与状态机(内部实现口径)
@@ -270,25 +270,25 @@
### 6.2 发放请求状态机(商城内部) ### 6.2 发放请求状态机(商城内部)
针对每个订单BONUS/WITHDRAW在商城内部维护推送playx状态,例如: 针对每个订单BONUS/WITHDRAW在商城内部维护推送playX状态,例如:
- `NOT_SENT`:未发送给 PlayX。 - `NOT_SENT`:未发送给 playX。
- `SENT_PENDING`:已发送,等待 PlayX 响应。 - `SENT_PENDING`:已发送,等待 playX 响应。
- `ACCEPTED`:收到 HTTP 200 且 `status = "accepted"` - `ACCEPTED`:收到 HTTP 200 且 `status = "accepted"`
- `FAILED_RETRYABLE`:失败且可重试(如超时、上游错误)。 - `FAILED_RETRYABLE`:失败且可重试(如超时、上游错误)。
- `FAILED_FINAL`:最终失败(达到重试上限或 PlayX 返回不可恢复错误)。 - `FAILED_FINAL`:最终失败(达到重试上限或 playX 返回不可恢复错误)。
关键规则: 关键规则:
- 只有在 `NOT_SENT``FAILED_RETRYABLE` 状态下才允许“再次发送”。 - 只有在 `NOT_SENT``FAILED_RETRYABLE` 状态下才允许“再次发送”。
- 一旦进入 `ACCEPTED`,不得再发请求(自动或人工)。 - 一旦进入 `ACCEPTED`,不得再发请求(自动或人工)。
- 订单业务状态PENDING/COMPLETED/REJECTED与推送playx状态之间要有清晰映射: - 订单业务状态PENDING/COMPLETED/REJECTED与推送playX状态之间要有清晰映射:
- `ACCEPTED` + 对账确认成功 → `COMPLETED` - `ACCEPTED` + 对账确认成功 → `COMPLETED`
- `FAILED_FINAL``REJECTED`(并退积分)。 - `FAILED_FINAL``REJECTED`(并退积分)。
### 6.3 重试策略(内部)与防重底线 ### 6.3 重试策略(内部)与防重底线
- **前提**PlayX 已承诺按 **`externalTransactionId` 严格幂等**(方案 A。在此前提下读超时后使用**同一** `externalTransactionId` 的有限次自动重试是安全的。 - **前提**playX 已承诺按 **`externalTransactionId` 严格幂等**(方案 A。在此前提下读超时后使用**同一** `externalTransactionId` 的有限次自动重试是安全的。
- 自动重试: - 自动重试:
- 仅针对网络错误、Read Timeout读超时`PLAYX_UPSTREAM_ERROR` 等,且**尚未收到** HTTP 200 + `status="accepted"` - 仅针对网络错误、Read Timeout读超时`PLAYX_UPSTREAM_ERROR` 等,且**尚未收到** HTTP 200 + `status="accepted"`
- 建议间隔1min / 5min / 15min最多 3 次;**每次重试必须使用原订单的同一** `externalTransactionId`,不得生成新单号冒充新单。 - 建议间隔1min / 5min / 15min最多 3 次;**每次重试必须使用原订单的同一** `externalTransactionId`,不得生成新单号冒充新单。
@@ -303,16 +303,16 @@
- 对账来源: - 对账来源:
- 商城订单表(含 externalTransactionId、playx_transaction_id - 商城订单表(含 externalTransactionId、playx_transaction_id
- PlayX 提供的对账/流水查询(如有)。 - playX 提供的对账/流水查询(如有)。
- 常见问题场景: - 常见问题场景:
- 商城显示 REJECTED 但会员反馈已收到红利:需检查是否在“发放后回滚积分”链路出错。 - 商城显示 REJECTED 但会员反馈已收到红利:需检查是否在“发放后回滚积分”链路出错。
- 商城显示 PENDING 时间过长:需排查 PlayX 侧 10 分钟 Cron 是否正常。 - 商城显示 PENDING 时间过长:需排查 playX 侧 10 分钟 Cron 是否正常。
## 8. 与对外文档的关系 ## 8. 与对外文档的关系
- 本文档:覆盖“能想到的所有对接与流程设计”,供产品、后端、运营、商务内部统一认识。 - 本文档:覆盖“能想到的所有对接与流程设计”,供产品、后端、运营、商务内部统一认识。
- `PlayX-对接文档(积分商城).md` - `playX-对接文档(积分商城).md`
- 仅暴露 PlayX V1.0 必须提供/实现的部分。 - 仅暴露 playX V1.0 必须提供/实现的部分。
- 承诺最小闭环,不在主文中提及实时 webhook、同步按钮、拉取模式。 - 承诺最小闭环,不在主文中提及实时 webhook、同步按钮、拉取模式。
- **同步额度等能力**若产品仍要落地,以本文 **5.3** 与商务/PlayX 结论为准;对外文档不写不代表产品一定不做。 - **同步额度等能力**若产品仍要落地,以本文 **5.3** 与商务/playX 结论为准;对外文档不写不代表产品一定不做。

View File

@@ -7,7 +7,6 @@ export default {
mobile: 'Mobile Number', mobile: 'Mobile Number',
'Last login': 'Last login', 'Last login': 'Last login',
Password: 'Password', Password: 'Password',
agent_id: 'agent',
'Please leave blank if not modified': 'Please leave blank if you do not modify.', 'Please leave blank if not modified': 'Please leave blank if you do not modify.',
'Personal signature': 'Personal Signature', 'Personal signature': 'Personal Signature',
'Administrator login': 'Administrator Login Name', 'Administrator login': 'Administrator Login Name',

View File

@@ -1,6 +1,6 @@
export default { export default {
id: 'id', id: 'id',
playx_user_asset_id: 'PlayX user asset', playx_user_asset_id: 'playX user asset',
playxuserasset__username: 'username', playxuserasset__username: 'username',
receiver_name: 'receiver name', receiver_name: 'receiver name',
phone: 'phone', phone: 'phone',

View File

@@ -1,7 +1,7 @@
export default { export default {
id: 'id', id: 'id',
order: 'order', order: 'order',
playx_user_asset_id: 'PlayX user asset', playx_user_asset_id: 'playX user asset',
playxuserasset__username: 'username', playxuserasset__username: 'username',
type: 'type', type: 'type',
'type 1': 'type 1', 'type 1': 'type 1',

View File

@@ -1,5 +1,5 @@
export default { export default {
title: 'PlayX Integration Center', title: 'playX Integration Center',
desc: 'Manage orders, daily push, claim logs and user assets in one place.', desc: 'Manage orders, daily push, claim logs and user assets in one place.',
orders: 'Orders', orders: 'Orders',
dailyPush: 'Daily Push', dailyPush: 'Daily Push',

View File

@@ -2,7 +2,7 @@ export default {
id: 'id', id: 'id',
username: 'username', username: 'username',
phone: 'phone', phone: 'phone',
playx_user_id: 'playx_user_id', playx_user_id: 'playX_user_id',
locked_points: 'locked_points', locked_points: 'locked_points',
available_points: 'available_points', available_points: 'available_points',
today_limit: 'today_limit', today_limit: 'today_limit',
@@ -10,5 +10,5 @@ export default {
today_limit_date: 'today_limit_date', today_limit_date: 'today_limit_date',
create_time: 'create_time', create_time: 'create_time',
update_time: 'update_time', update_time: 'update_time',
'quick Search Fields': 'id, playx_user_id, username, phone', 'quick Search Fields': 'id, playX_user_id, username, phone',
} }

View File

@@ -1,7 +1,7 @@
export default { export default {
id: 'id', id: 'id',
order: 'order', order: 'order',
playx_user_asset_id: 'PlayX user asset', playx_user_asset_id: 'playX user asset',
playxuserasset__username: 'username', playxuserasset__username: 'username',
status: 'status', status: 'status',
'status 0': 'status 0', 'status 0': 'status 0',

View File

@@ -2,7 +2,7 @@ export default {
id: 'id', id: 'id',
username: 'username', username: 'username',
phone: 'phone', phone: 'phone',
playx_user_id: 'playx_user_id', playx_user_id: 'playX_user_id',
locked_points: 'locked_points', locked_points: 'locked_points',
available_points: 'available_points', available_points: 'available_points',
today_limit: 'today_limit', today_limit: 'today_limit',
@@ -10,6 +10,6 @@ export default {
today_limit_date: 'today_limit_date', today_limit_date: 'today_limit_date',
create_time: 'create_time', create_time: 'create_time',
update_time: 'update_time', update_time: 'update_time',
'quick Search Fields': 'id, playx_user_id, username, phone', 'quick Search Fields': 'id, playX_user_id, username, phone',
} }

View File

@@ -108,11 +108,11 @@ export default {
mall_dailyPush: 'Daily push', mall_dailyPush: 'Daily push',
mall_claimLog: 'Claim log', mall_claimLog: 'Claim log',
mall_item: 'Products', mall_item: 'Products',
mall_playxOrder: 'PlayX orders', mall_playxOrder: 'playX orders',
mall_playxCenter: 'PlayX center', mall_playxCenter: 'playX center',
mall_playxClaimLog: 'PlayX claim log', mall_playxClaimLog: 'playX claim log',
mall_playxDailyPush: 'PlayX daily push', mall_playxDailyPush: 'playX daily push',
mall_playxUserAsset: 'PlayX user assets', mall_playxUserAsset: 'playX user assets',
mall_pintsOrder: 'Points orders', mall_pintsOrder: 'Points orders',
mall_redemptionOrder: 'Redemption orders', mall_redemptionOrder: 'Redemption orders',
}, },

View File

@@ -7,7 +7,6 @@ export default {
mobile: '手机号', mobile: '手机号',
'Last login': '最后登录', 'Last login': '最后登录',
Password: '密码', Password: '密码',
agent_id: '代理',
'Please leave blank if not modified': '不修改请留空', 'Please leave blank if not modified': '不修改请留空',
'Personal signature': '个性签名', 'Personal signature': '个性签名',
'Administrator login': '管理员登录名', 'Administrator login': '管理员登录名',

View File

@@ -1,7 +1,7 @@
export default { export default {
id: 'ID', id: 'ID',
claim_request_id: '领取订单号', claim_request_id: '领取订单号',
user_id: 'Playx-ID', user_id: 'playX-ID',
claimed_amount: '领取积分', claimed_amount: '领取积分',
create_time: '创建时间', create_time: '创建时间',
'quick Search Fields': 'ID', 'quick Search Fields': 'ID',

View File

@@ -1,6 +1,6 @@
export default { export default {
id: 'ID', id: 'ID',
user_id: 'Playx-ID', user_id: 'playX-ID',
date: '业务日期', date: '业务日期',
username: '用户名', username: '用户名',
yesterday_win_loss_net: '昨日净输赢', yesterday_win_loss_net: '昨日净输赢',

View File

@@ -3,7 +3,7 @@ export default {
manual_retry: '手动重试', manual_retry: '手动重试',
retry_confirm: '确认将该订单加入重试队列?', retry_confirm: '确认将该订单加入重试队列?',
id: 'ID', id: 'ID',
user_id: 'Playx-ID', user_id: 'playX-ID',
type: '类型', type: '类型',
'type BONUS': '红利(BONUS)', 'type BONUS': '红利(BONUS)',
'type PHYSICAL': '实物(PHYSICAL)', 'type PHYSICAL': '实物(PHYSICAL)',
@@ -19,7 +19,7 @@ export default {
amount: '现金面值', amount: '现金面值',
multiplier: '流水倍数', multiplier: '流水倍数',
external_transaction_id: '订单号', external_transaction_id: '订单号',
grant_status: '推送playx状态', grant_status: '推送playX状态',
'grant_status NOT_SENT': '未发送', 'grant_status NOT_SENT': '未发送',
'grant_status SENT_PENDING': '已发送排队', 'grant_status SENT_PENDING': '已发送排队',
'grant_status ACCEPTED': '已接收', 'grant_status ACCEPTED': '已接收',

View File

@@ -1,6 +1,6 @@
export default { export default {
title: 'PlayX 对接中心', title: 'playX 对接中心',
desc: '集中管理积分商城与 PlayX 的订单、推送、领取与资产数据。建议优先处理“推送playx状态=失败可重试”的订单。', desc: '集中管理积分商城与 playX 的订单、推送、领取与资产数据。建议优先处理“推送playX状态=失败可重试”的订单。',
orders: '统一订单', orders: '统一订单',
dailyPush: '每日推送', dailyPush: '每日推送',
claimLog: '领取记录', claimLog: '领取记录',

View File

@@ -1,7 +1,7 @@
export default { export default {
id: 'ID', id: 'ID',
claim_request_id: '领取订单号', claim_request_id: '领取订单号',
user_id: 'Playx-ID', user_id: 'playX-ID',
claimed_amount: '领取积分', claimed_amount: '领取积分',
create_time: '创建时间', create_time: '创建时间',
'quick Search Fields': 'ID', 'quick Search Fields': 'ID',

View File

@@ -1,6 +1,6 @@
export default { export default {
id: 'ID', id: 'ID',
user_id: 'Playx-ID', user_id: 'playX-ID',
date: '业务日期', date: '业务日期',
username: '用户名', username: '用户名',
yesterday_win_loss_net: '昨日净输赢', yesterday_win_loss_net: '昨日净输赢',

View File

@@ -1,6 +1,6 @@
export default { export default {
id: 'ID', id: 'ID',
user_id: 'Playx-ID', user_id: 'playX-ID',
type: '类型', type: '类型',
'type BONUS': '红利(BONUS)', 'type BONUS': '红利(BONUS)',
'type PHYSICAL': '实物(PHYSICAL)', 'type PHYSICAL': '实物(PHYSICAL)',
@@ -16,7 +16,7 @@ export default {
amount: '现金面值', amount: '现金面值',
multiplier: '流水倍数', multiplier: '流水倍数',
external_transaction_id: '订单号', external_transaction_id: '订单号',
grant_status: '推送playx状态', grant_status: '推送playX状态',
'grant_status NOT_SENT': '未发送', 'grant_status NOT_SENT': '未发送',
'grant_status SENT_PENDING': '已发送排队', 'grant_status SENT_PENDING': '已发送排队',
'grant_status ACCEPTED': '已接收(accepted)', 'grant_status ACCEPTED': '已接收(accepted)',

View File

@@ -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: '今日可领取上限',
@@ -10,5 +10,5 @@ export default {
today_limit_date: '今日上限日期', today_limit_date: '今日上限日期',
create_time: '创建时间', create_time: '创建时间',
update_time: '修改时间', update_time: '修改时间',
'quick Search Fields': 'ID、PlayX用户ID、用户名、手机号', 'quick Search Fields': 'ID、playX用户ID、用户名、手机号',
} }

View File

@@ -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: '今日可领取上限',
@@ -10,6 +10,6 @@ export default {
today_limit_date: '今日上限日期', today_limit_date: '今日上限日期',
create_time: '创建时间', create_time: '创建时间',
update_time: '修改时间', update_time: '修改时间',
'quick Search Fields': 'ID、PlayX用户ID、用户名、手机号', 'quick Search Fields': 'ID、playX用户ID、用户名、手机号',
} }

View File

@@ -109,11 +109,11 @@ export default {
mall_dailyPush: '每日推送', mall_dailyPush: '每日推送',
mall_claimLog: '领取记录', mall_claimLog: '领取记录',
mall_item: '商品管理', mall_item: '商品管理',
mall_playxOrder: 'PlayX订单', mall_playxOrder: 'playX订单',
mall_playxCenter: 'PlayX中心', mall_playxCenter: 'playX中心',
mall_playxClaimLog: 'PlayX领取记录', mall_playxClaimLog: 'playX领取记录',
mall_playxDailyPush: 'PlayX每日推送', mall_playxDailyPush: 'playX每日推送',
mall_playxUserAsset: 'PlayX用户资产', mall_playxUserAsset: 'playX用户资产',
mall_pintsOrder: '积分订单', mall_pintsOrder: '积分订单',
mall_redemptionOrder: '兑换订单', mall_redemptionOrder: '兑换订单',
}, },

View File

@@ -2,7 +2,7 @@ export default {
'User name': '用户名', 'User name': '用户名',
'User nickname': '用户昵称', 'User nickname': '用户昵称',
balance: '余额', balance: '余额',
'User ID': 'Playx-ID', 'User ID': 'playX-ID',
'Change balance': '变更余额', 'Change balance': '变更余额',
'Before change': '变更前', 'Before change': '变更前',
'After change': '变更后', 'After change': '变更后',

View File

@@ -61,13 +61,6 @@ const baTable = new baTableClass(
operator: 'RANGE', operator: 'RANGE',
width: 160, width: 160,
}, },
{
label: t('auth.admin.agent_id'),
prop: 'agent_id',
align: 'center',
width: '160',
showOverflowTooltip: true,
},
{ label: t('Create time'), prop: 'create_time', align: 'center', render: 'datetime', sortable: 'custom', operator: 'RANGE', width: 160 }, { label: t('Create time'), prop: 'create_time', align: 'center', render: 'datetime', sortable: 'custom', operator: 'RANGE', width: 160 },
{ {
label: t('State'), label: t('State'),