优化页面和模型

This commit is contained in:
2026-03-30 18:33:24 +08:00
parent 2686c54781
commit 1cd5c3142d
33 changed files with 817 additions and 215 deletions

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
namespace app\admin\controller;
use app\common\controller\Backend;
use app\common\model\MallPlayxClaimLog;
use app\common\model\MallPlayxOrder;
use app\common\model\MallPlayxUserAsset;
use app\common\model\MallClaimLog;
use app\common\model\MallOrder;
use app\common\model\MallUserAsset;
use support\think\Db;
use Webman\Http\Request;
use support\Response;
@@ -23,28 +23,28 @@ class Dashboard extends Backend
$todayStart = strtotime(date('Y-m-d', $now) . ' 00:00:00');
$yesterdayStart = $todayStart - 86400;
$newPlayersToday = MallPlayxUserAsset::where('create_time', '>=', $todayStart)
$newPlayersToday = MallUserAsset::where('create_time', '>=', $todayStart)
->where('create_time', '<=', $now)
->count();
$yesterdayPointsClaimed = MallPlayxClaimLog::where('create_time', '>=', $yesterdayStart)
$yesterdayPointsClaimed = MallClaimLog::where('create_time', '>=', $yesterdayStart)
->where('create_time', '<', $todayStart)
->sum('claimed_amount');
$yesterdayRedeemQuery = MallPlayxOrder::where('create_time', '>=', $yesterdayStart)
$yesterdayRedeemQuery = MallOrder::where('create_time', '>=', $yesterdayStart)
->where('create_time', '<', $todayStart);
$yesterdayRedeemCount = (clone $yesterdayRedeemQuery)->count();
$yesterdayRedeemPointsCostSum = (clone $yesterdayRedeemQuery)->sum('points_cost');
$yesterdayRedeemAmountSum = (clone $yesterdayRedeemQuery)->sum('amount');
$yesterdayRedeemCompletedCount = (clone $yesterdayRedeemQuery)
->where('status', MallPlayxOrder::STATUS_COMPLETED)
->where('status', MallOrder::STATUS_COMPLETED)
->count();
$yesterdayRedeemRejectedCount = (clone $yesterdayRedeemQuery)
->where('status', MallPlayxOrder::STATUS_REJECTED)
->where('status', MallOrder::STATUS_REJECTED)
->count();
$yesterdayRedeemByItem = Db::name('mall_playx_order')
$yesterdayRedeemByItem = Db::name('mall_order')
->alias('o')
->leftJoin('mall_item i', 'i.id = o.mall_item_id')
->where('o.create_time', '>=', $yesterdayStart)
@@ -63,11 +63,11 @@ class Dashboard extends Backend
->select()
->toArray();
$pendingPhysicalToShip = MallPlayxOrder::where('type', MallPlayxOrder::TYPE_PHYSICAL)
->where('status', MallPlayxOrder::STATUS_PENDING)
$pendingPhysicalToShip = MallOrder::where('type', MallOrder::TYPE_PHYSICAL)
->where('status', MallOrder::STATUS_PENDING)
->count();
$grantFailedRetryableCount = MallPlayxOrder::whereIn('type', [MallPlayxOrder::TYPE_BONUS, MallPlayxOrder::TYPE_WITHDRAW])
->where('grant_status', MallPlayxOrder::GRANT_FAILED_RETRYABLE)
$grantFailedRetryableCount = MallOrder::whereIn('type', [MallOrder::TYPE_BONUS, MallOrder::TYPE_WITHDRAW])
->where('grant_status', MallOrder::GRANT_FAILED_RETRYABLE)
->count();
return $this->success('', [

View File

@@ -8,13 +8,13 @@ use support\Response;
use Webman\Http\Request;
/**
* PlayX 领取记录(后台列表)
* 领取记录(后台列表)
*/
class PlayxClaimLog extends Backend
class ClaimLog extends Backend
{
/**
* @var object|null
* @phpstan-var \app\common\model\MallPlayxClaimLog|null
* @phpstan-var \app\common\model\MallClaimLog|null
*/
protected ?object $model = null;
@@ -33,7 +33,7 @@ class PlayxClaimLog extends Backend
public function initialize(): void
{
parent::initialize();
$this->model = new \app\common\model\MallPlayxClaimLog();
$this->model = new \app\common\model\MallClaimLog();
}
/**

View File

@@ -8,13 +8,13 @@ use support\Response;
use Webman\Http\Request;
/**
* PlayX 每日推送数据(后台列表)
* 每日推送数据(后台列表)
*/
class PlayxDailyPush extends Backend
class DailyPush extends Backend
{
/**
* @var object|null
* @phpstan-var \app\common\model\MallPlayxDailyPush|null
* @phpstan-var \app\common\model\MallDailyPush|null
*/
protected ?object $model = null;
@@ -37,7 +37,7 @@ class PlayxDailyPush extends Backend
public function initialize(): void
{
parent::initialize();
$this->model = new \app\common\model\MallPlayxDailyPush();
$this->model = new \app\common\model\MallDailyPush();
}
/**

View File

@@ -4,20 +4,20 @@ namespace app\admin\controller\mall;
use Throwable;
use app\common\controller\Backend;
use app\common\model\MallPlayxOrder;
use app\common\model\MallPlayxUserAsset;
use app\common\model\MallOrder;
use app\common\model\MallUserAsset;
use support\think\Db;
use support\Response;
use Webman\Http\Request;
/**
* PlayX 统一订单(后台列表)
* 统一订单(后台列表)
*/
class PlayxOrder extends Backend
class Order extends Backend
{
/**
* @var object|null
* @phpstan-var \app\common\model\MallPlayxOrder|null
* @phpstan-var \app\common\model\MallOrder|null
*/
protected ?object $model = null;
@@ -53,7 +53,7 @@ class PlayxOrder extends Backend
public function initialize(): void
{
parent::initialize();
$this->model = new \app\common\model\MallPlayxOrder();
$this->model = new \app\common\model\MallOrder();
}
/**
@@ -71,7 +71,7 @@ class PlayxOrder extends Backend
return $this->select($request);
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
[$where, $alias, $limit, $order] = $this->queryBuilder();
$res = $this->model
->with(['mallItem' => function ($query) {
$query->field('id,title');
@@ -104,22 +104,22 @@ class PlayxOrder extends Backend
}
$data = $request->post();
$id = intval($data['id'] ?? 0);
$shippingCompany = strval($data['shipping_company'] ?? '');
$shippingNo = strval($data['shipping_no'] ?? '');
$id = $data['id'] ?? 0;
$shippingCompany = $data['shipping_company'] ?? '';
$shippingNo = $data['shipping_no'] ?? '';
if ($id <= 0 || $shippingCompany === '' || $shippingNo === '') {
if (!$id || $shippingCompany === '' || $shippingNo === '') {
return $this->error(__('Missing required fields'));
}
$order = MallPlayxOrder::where('id', $id)->find();
$order = MallOrder::where('id', $id)->find();
if (!$order) {
return $this->error(__('Record not found'));
}
if ($order->type !== MallPlayxOrder::TYPE_PHYSICAL) {
if ($order->type !== MallOrder::TYPE_PHYSICAL) {
return $this->error(__('Order type not PHYSICAL'));
}
if ($order->status !== MallPlayxOrder::STATUS_PENDING) {
if ($order->status !== MallOrder::STATUS_PENDING) {
return $this->error(__('Order status must be PENDING'));
}
@@ -127,7 +127,7 @@ class PlayxOrder extends Backend
try {
$order->shipping_company = $shippingCompany;
$order->shipping_no = $shippingNo;
$order->status = MallPlayxOrder::STATUS_SHIPPED;
$order->status = MallOrder::STATUS_SHIPPED;
$order->save();
Db::commit();
} catch (Throwable $e) {
@@ -153,40 +153,40 @@ class PlayxOrder extends Backend
}
$data = $request->post();
$id = intval($data['id'] ?? 0);
$rejectReason = strval($data['reject_reason'] ?? '');
$id = $data['id'] ?? 0;
$rejectReason = $data['reject_reason'] ?? '';
if ($id <= 0 || $rejectReason === '') {
if (!$id || $rejectReason === '') {
return $this->error(__('Missing required fields'));
}
$order = MallPlayxOrder::where('id', $id)->find();
$order = MallOrder::where('id', $id)->find();
if (!$order) {
return $this->error(__('Record not found'));
}
if ($order->type !== MallPlayxOrder::TYPE_PHYSICAL) {
if ($order->type !== MallOrder::TYPE_PHYSICAL) {
return $this->error(__('Order type not PHYSICAL'));
}
if ($order->status !== MallPlayxOrder::STATUS_PENDING) {
if ($order->status !== MallOrder::STATUS_PENDING) {
return $this->error(__('Order status must be PENDING'));
}
Db::startTrans();
try {
$asset = MallPlayxUserAsset::where('playx_user_id', strval($order->user_id ?? ''))->find();
$asset = MallUserAsset::where('playx_user_id', $order->user_id ?? '')->find();
if (!$asset) {
throw new \RuntimeException('User asset not found');
}
$refund = intval($order->points_cost ?? 0);
$refund = $order->points_cost ?? 0;
if ($refund > 0) {
$asset->available_points += $refund;
$asset->save();
}
$order->status = MallPlayxOrder::STATUS_REJECTED;
$order->status = MallOrder::STATUS_REJECTED;
$order->reject_reason = $rejectReason;
$order->grant_status = MallPlayxOrder::GRANT_FAILED_FINAL;
$order->grant_status = MallOrder::GRANT_FAILED_FINAL;
$order->save();
Db::commit();
@@ -212,26 +212,26 @@ class PlayxOrder extends Backend
return $this->error(__('Parameter error'));
}
$id = intval($request->post('id', 0));
if ($id <= 0) {
$id = $request->post('id', 0);
if (!$id) {
return $this->error(__('Missing required fields'));
}
$order = MallPlayxOrder::where('id', $id)->find();
$order = MallOrder::where('id', $id)->find();
if (!$order) {
return $this->error(__('Record not found'));
}
if (!in_array($order->type, [MallPlayxOrder::TYPE_BONUS, MallPlayxOrder::TYPE_WITHDRAW], true)) {
if (!in_array($order->type, [MallOrder::TYPE_BONUS, MallOrder::TYPE_WITHDRAW], true)) {
return $this->error(__('Only BONUS/WITHDRAW can retry'));
}
if ($order->grant_status !== MallPlayxOrder::GRANT_FAILED_RETRYABLE) {
if ($order->grant_status !== MallOrder::GRANT_FAILED_RETRYABLE) {
return $this->error(__('Only FAILED_RETRYABLE can retry'));
}
if (intval($order->retry_count) >= 3) {
if (($order->retry_count ?? 0) >= 3) {
return $this->error(__('Retry count exceeded'));
}
$order->grant_status = MallPlayxOrder::GRANT_NOT_SENT;
$order->grant_status = MallOrder::GRANT_NOT_SENT;
$order->save();
return $this->success(__('Retry queued'));

View File

@@ -7,13 +7,13 @@ use support\Response;
use Webman\Http\Request;
/**
* PlayX 用户资产(后台列表)
* 用户资产(后台列表)
*/
class PlayxUserAsset extends Backend
class UserAsset extends Backend
{
/**
* @var object|null
* @phpstan-var \app\common\model\MallPlayxUserAsset|null
* @phpstan-var \app\common\model\MallUserAsset|null
*/
protected ?object $model = null;
@@ -42,7 +42,7 @@ class PlayxUserAsset extends Backend
public function initialize(): void
{
parent::initialize();
$this->model = new \app\common\model\MallPlayxUserAsset();
$this->model = new \app\common\model\MallUserAsset();
}
/**
@@ -55,7 +55,7 @@ class PlayxUserAsset extends Backend
return $response;
}
list($where, $alias, $limit, $order) = $this->queryBuilder();
[$where, $alias, $limit, $order] = $this->queryBuilder();
$res = $this->model
->field('id,username')
->alias($alias)
@@ -67,8 +67,8 @@ class PlayxUserAsset extends Backend
foreach ($res->items() as $row) {
$arr = $row->toArray();
$list[] = [
'id' => intval($arr['id'] ?? 0),
'username' => strval($arr['username'] ?? ''),
'id' => $arr['id'] ?? 0,
'username' => $arr['username'] ?? '',
];
}
@@ -79,3 +79,4 @@ class PlayxUserAsset extends Backend
]);
}
}

View File

@@ -10,7 +10,7 @@ use app\common\controller\Api;
use app\common\facade\Token;
use app\common\library\Auth as UserAuth;
use app\common\library\AgentJwt;
use app\common\model\MallPlayxUserAsset;
use app\common\model\MallUserAsset;
use app\admin\model\Admin;
use Webman\Http\Request;
use support\Response;
@@ -100,7 +100,7 @@ class Auth extends Api
/**
* H5 临时登录GET/POST
* 参数username
* 写入或复用 mall_playx_user_asset签发 muser 类型 tokenuser_id 为资产表主键)
* 写入或复用 mall_user_asset签发 muser 类型 tokenuser_id 为资产表主键)
*/
public function temLogin(Request $request): Response
{
@@ -120,7 +120,7 @@ class Auth extends Api
}
try {
$asset = MallPlayxUserAsset::ensureForUsername($username);
$asset = MallUserAsset::ensureForUsername($username);
} catch (Throwable $e) {
return $this->error($e->getMessage());
}

View File

@@ -9,11 +9,11 @@ use app\common\controller\Api;
use app\common\facade\Token;
use app\common\library\Auth as UserAuth;
use app\common\model\MallItem;
use app\common\model\MallPlayxClaimLog;
use app\common\model\MallPlayxDailyPush;
use app\common\model\MallPlayxSession;
use app\common\model\MallPlayxOrder;
use app\common\model\MallPlayxUserAsset;
use app\common\model\MallClaimLog;
use app\common\model\MallDailyPush;
use app\common\model\MallSession;
use app\common\model\MallOrder;
use app\common\model\MallUserAsset;
use app\common\model\MallAddress;
use support\think\Db;
use Webman\Http\Request;
@@ -25,17 +25,17 @@ use support\Response;
class Playx extends Api
{
/**
* 从请求解析 mall_playx_user_asset.idmuser token、session、user_id 均指向资产表主键或 playx_user_id
* 从请求解析 mall_user_asset.idmuser token、session、user_id 均指向资产表主键或 playx_user_id
*/
private function resolvePlayxAssetIdFromRequest(Request $request): ?int
{
$sessionId = strval($request->post('session_id', $request->get('session_id', '')));
if ($sessionId !== '') {
$session = MallPlayxSession::where('session_id', $sessionId)->find();
$session = MallSession::where('session_id', $sessionId)->find();
if ($session) {
$expireTime = intval($session->expire_time ?? 0);
if ($expireTime > time()) {
$asset = MallPlayxUserAsset::where('playx_user_id', strval($session->user_id ?? ''))->find();
$asset = MallUserAsset::where('playx_user_id', strval($session->user_id ?? ''))->find();
if ($asset) {
return intval($asset->getKey());
}
@@ -63,7 +63,7 @@ class Playx extends Api
return intval($userId);
}
$asset = MallPlayxUserAsset::where('playx_user_id', $userId)->find();
$asset = MallUserAsset::where('playx_user_id', $userId)->find();
if ($asset) {
return intval($asset->getKey());
}
@@ -91,7 +91,7 @@ class Playx extends Api
{
for ($i = 0; $i < 8; $i++) {
$candidate = '13' . str_pad(strval(mt_rand(0, 999999999)), 9, '0', STR_PAD_LEFT);
if (!MallPlayxUserAsset::where('phone', $candidate)->find()) {
if (!MallUserAsset::where('phone', $candidate)->find()) {
return $candidate;
}
}
@@ -99,9 +99,9 @@ class Playx extends Api
return null;
}
private function ensureAssetForPlayx(string $playxUserId, string $username): ?MallPlayxUserAsset
private function ensureAssetForPlayx(string $playxUserId, string $username): ?MallUserAsset
{
$asset = MallPlayxUserAsset::where('playx_user_id', $playxUserId)->find();
$asset = MallUserAsset::where('playx_user_id', $playxUserId)->find();
if ($asset) {
return $asset;
}
@@ -110,7 +110,7 @@ class Playx extends Api
if ($effectiveUsername === '') {
$effectiveUsername = 'playx_' . $playxUserId;
}
$byName = MallPlayxUserAsset::where('username', $effectiveUsername)->find();
$byName = MallUserAsset::where('username', $effectiveUsername)->find();
if ($byName) {
$byName->playx_user_id = $playxUserId;
$byName->save();
@@ -125,7 +125,7 @@ class Playx extends Api
$pwd = hash_password(Random::build('alnum', 16));
$now = time();
return MallPlayxUserAsset::create([
return MallUserAsset::create([
'playx_user_id' => $playxUserId,
'username' => $effectiveUsername,
'phone' => $phone,
@@ -141,9 +141,9 @@ class Playx extends Api
]);
}
private function getAssetById(int $assetId): ?MallPlayxUserAsset
private function getAssetById(int $assetId): ?MallUserAsset
{
return MallPlayxUserAsset::where('id', $assetId)->find();
return MallUserAsset::where('id', $assetId)->find();
}
/**
@@ -219,7 +219,7 @@ class Playx extends Api
$lifetimeTotalDeposit = $m['lty_deposit'] ?? 0;
$lifetimeTotalWithdraw = $m['lty_withdrawal'] ?? 0;
$exists = MallPlayxDailyPush::where('user_id', $playxUserId)->where('date', $date)->find();
$exists = MallDailyPush::where('user_id', $playxUserId)->where('date', $date)->find();
if ($exists) {
$results[] = [
'user_id' => $playxUserId,
@@ -232,7 +232,7 @@ class Playx extends Api
Db::startTrans();
try {
MallPlayxDailyPush::create([
MallDailyPush::create([
'user_id' => $playxUserId,
'date' => $date,
'username' => $username,
@@ -304,7 +304,7 @@ class Playx extends Api
return $this->error(__('Missing required fields: request_id, date, user_id'));
}
$exists = MallPlayxDailyPush::where('user_id', $playxUserId)->where('date', $date)->find();
$exists = MallDailyPush::where('user_id', $playxUserId)->where('date', $date)->find();
if ($exists) {
return $this->success('', [
'request_id' => $requestId,
@@ -316,7 +316,7 @@ class Playx extends Api
Db::startTrans();
try {
MallPlayxDailyPush::create([
MallDailyPush::create([
'user_id' => $playxUserId,
'date' => $date,
'username' => $body['username'] ?? '',
@@ -427,7 +427,7 @@ class Playx extends Api
}
$sessionId = bin2hex(random_bytes(16));
MallPlayxSession::create([
MallSession::create([
'session_id' => $sessionId,
'user_id' => $userId,
'username' => $username,
@@ -448,7 +448,7 @@ class Playx extends Api
}
/**
* 本地校验 temLogin 等写入的商城 token类型 muser写入 mall_playx_session
* 本地校验 temLogin 等写入的商城 token类型 muser写入 mall_session
*/
private function verifyTokenLocal(string $token): Response
{
@@ -465,7 +465,7 @@ class Playx extends Api
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
}
$asset = MallPlayxUserAsset::where('id', $assetId)->find();
$asset = MallUserAsset::where('id', $assetId)->find();
if (!$asset) {
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
}
@@ -477,7 +477,7 @@ class Playx extends Api
$expireAt = time() + intval(config('playx.session_expire_seconds', 3600));
$sessionId = bin2hex(random_bytes(16));
MallPlayxSession::create([
MallSession::create([
'session_id' => $sessionId,
'user_id' => $playxUserId,
'username' => strval($asset->username ?? ''),
@@ -556,7 +556,7 @@ class Playx extends Api
}
$playxUserId = strval($asset->playx_user_id);
$exists = MallPlayxClaimLog::where('claim_request_id', $claimRequestId)->find();
$exists = MallClaimLog::where('claim_request_id', $claimRequestId)->find();
if ($exists) {
return $this->success('', $this->formatAsset($asset));
}
@@ -576,7 +576,7 @@ class Playx extends Api
Db::startTrans();
try {
MallPlayxClaimLog::create([
MallClaimLog::create([
'claim_request_id' => $claimRequestId,
'user_id' => $playxUserId,
'claimed_amount' => $canClaim,
@@ -667,7 +667,7 @@ class Playx extends Api
return $this->success('', ['list' => []]);
}
$list = MallPlayxOrder::where('user_id', strval($asset->playx_user_id))
$list = MallOrder::where('user_id', strval($asset->playx_user_id))
->with(['mallItem'])
->order('id', 'desc')
->limit(100)
@@ -867,7 +867,7 @@ class Playx extends Api
return $this->success('', ['deleted' => true]);
}
private function formatAsset(?MallPlayxUserAsset $asset): array
private function formatAsset(?MallUserAsset $asset): array
{
if (!$asset) {
return [
@@ -924,16 +924,16 @@ class Playx extends Api
$asset->save();
$orderNo = 'BONUS_ORD' . date('YmdHis') . mt_rand(1000, 9999);
$order = MallPlayxOrder::create([
$order = MallOrder::create([
'user_id' => $playxUserId,
'type' => MallPlayxOrder::TYPE_BONUS,
'status' => MallPlayxOrder::STATUS_PENDING,
'type' => MallOrder::TYPE_BONUS,
'status' => MallOrder::STATUS_PENDING,
'mall_item_id' => $item->id,
'points_cost' => $item->score,
'amount' => $amount,
'multiplier' => $multiplier,
'external_transaction_id' => $orderNo,
'grant_status' => MallPlayxOrder::GRANT_NOT_SENT,
'grant_status' => MallOrder::GRANT_NOT_SENT,
'create_time' => time(),
'update_time' => time(),
]);
@@ -990,10 +990,10 @@ class Playx extends Api
$asset->available_points -= $item->score;
$asset->save();
MallPlayxOrder::create([
MallOrder::create([
'user_id' => $playxUserId,
'type' => MallPlayxOrder::TYPE_PHYSICAL,
'status' => MallPlayxOrder::STATUS_PENDING,
'type' => MallOrder::TYPE_PHYSICAL,
'status' => MallOrder::STATUS_PENDING,
'mall_item_id' => $item->id,
'points_cost' => $item->score,
'receiver_name' => $receiverName,
@@ -1053,16 +1053,16 @@ class Playx extends Api
$asset->save();
$orderNo = 'WITHDRAW_ORD' . date('YmdHis') . mt_rand(1000, 9999);
$order = MallPlayxOrder::create([
$order = MallOrder::create([
'user_id' => $playxUserId,
'type' => MallPlayxOrder::TYPE_WITHDRAW,
'status' => MallPlayxOrder::STATUS_PENDING,
'type' => MallOrder::TYPE_WITHDRAW,
'status' => MallOrder::STATUS_PENDING,
'mall_item_id' => $item->id,
'points_cost' => $item->score,
'amount' => $amount,
'multiplier' => $multiplier,
'external_transaction_id' => $orderNo,
'grant_status' => MallPlayxOrder::GRANT_NOT_SENT,
'grant_status' => MallOrder::GRANT_NOT_SENT,
'create_time' => time(),
'update_time' => time(),
]);
@@ -1084,7 +1084,7 @@ class Playx extends Api
]);
}
private function callPlayxBonusGrant(MallPlayxOrder $order, MallItem $item, string $userId): void
private function callPlayxBonusGrant(MallOrder $order, MallItem $item, string $userId): void
{
$baseUrl = rtrim(config('playx.api.base_url', ''), '/');
$url = config('playx.api.bonus_grant_url', '/api/v1/bonus/grant');
@@ -1109,21 +1109,21 @@ class Playx extends Api
$data = json_decode(strval($res->getBody()), true);
if ($res->getStatusCode() === 200 && ($data['status'] ?? '') === 'accepted') {
$order->playx_transaction_id = $data['playx_transaction_id'] ?? '';
$order->grant_status = MallPlayxOrder::GRANT_ACCEPTED;
$order->grant_status = MallOrder::GRANT_ACCEPTED;
$order->save();
} else {
$order->grant_status = MallPlayxOrder::GRANT_FAILED_RETRYABLE;
$order->grant_status = MallOrder::GRANT_FAILED_RETRYABLE;
$order->fail_reason = $data['message'] ?? 'unknown';
$order->save();
}
} catch (\Throwable $e) {
$order->grant_status = MallPlayxOrder::GRANT_FAILED_RETRYABLE;
$order->grant_status = MallOrder::GRANT_FAILED_RETRYABLE;
$order->fail_reason = $e->getMessage();
$order->save();
}
}
private function callPlayxBalanceCredit(MallPlayxOrder $order, string $userId): void
private function callPlayxBalanceCredit(MallOrder $order, string $userId): void
{
$baseUrl = rtrim(config('playx.api.base_url', ''), '/');
$url = config('playx.api.balance_credit_url', '/api/v1/balance/credit');
@@ -1145,15 +1145,15 @@ class Playx extends Api
$data = json_decode(strval($res->getBody()), true);
if ($res->getStatusCode() === 200 && ($data['status'] ?? '') === 'accepted') {
$order->playx_transaction_id = $data['playx_transaction_id'] ?? '';
$order->grant_status = MallPlayxOrder::GRANT_ACCEPTED;
$order->grant_status = MallOrder::GRANT_ACCEPTED;
$order->save();
} else {
$order->grant_status = MallPlayxOrder::GRANT_FAILED_RETRYABLE;
$order->grant_status = MallOrder::GRANT_FAILED_RETRYABLE;
$order->fail_reason = $data['message'] ?? 'unknown';
$order->save();
}
} catch (\Throwable $e) {
$order->grant_status = MallPlayxOrder::GRANT_FAILED_RETRYABLE;
$order->grant_status = MallOrder::GRANT_FAILED_RETRYABLE;
$order->fail_reason = $e->getMessage();
$order->save();
}

View File

@@ -47,6 +47,6 @@ class MallAddress extends Model
public function playxUserAsset(): \think\model\relation\BelongsTo
{
return $this->belongsTo(\app\common\model\MallPlayxUserAsset::class, 'playx_user_asset_id', 'id');
return $this->belongsTo(\app\common\model\MallUserAsset::class, 'playx_user_asset_id', 'id');
}
}

View File

@@ -7,14 +7,15 @@ namespace app\common\model;
use support\think\Model;
/**
* PlayX 领取记录(幂等)
* 领取记录(幂等)
*/
class MallPlayxClaimLog extends Model
class MallClaimLog extends Model
{
protected string $name = 'mall_playx_claim_log';
protected string $name = 'mall_claim_log';
protected array $type = [
'claimed_amount' => 'integer',
'create_time' => 'integer',
];
}

View File

@@ -7,11 +7,11 @@ namespace app\common\model;
use support\think\Model;
/**
* PlayX 每日推送数据
* 每日推送数据
*/
class MallPlayxDailyPush extends Model
class MallDailyPush extends Model
{
protected string $name = 'mall_playx_daily_push';
protected string $name = 'mall_daily_push';
protected array $type = [
'yesterday_win_loss_net' => 'float',
@@ -21,3 +21,4 @@ class MallPlayxDailyPush extends Model
'create_time' => 'integer',
];
}

View File

@@ -7,7 +7,7 @@ namespace app\common\model;
use support\think\Model;
/**
* PlayX 统一订单
* 统一订单
*
* @property int $id
* @property string $user_id
@@ -29,9 +29,9 @@ use support\think\Model;
* @property string $receiver_phone
* @property string|null $receiver_address
*/
class MallPlayxOrder extends Model
class MallOrder extends Model
{
protected string $name = 'mall_playx_order';
protected string $name = 'mall_order';
protected bool $autoWriteTimestamp = true;
@@ -51,12 +51,12 @@ class MallPlayxOrder extends Model
public const GRANT_FAILED_FINAL = 'FAILED_FINAL';
protected array $type = [
'create_time' => 'integer',
'update_time' => 'integer',
'points_cost' => 'integer',
'amount' => 'float',
'multiplier' => 'integer',
'retry_count' => 'integer',
'create_time' => 'integer',
'update_time' => 'integer',
'points_cost' => 'integer',
'amount' => 'float',
'multiplier' => 'integer',
'retry_count' => 'integer',
];
public function mallItem(): \think\model\relation\BelongsTo
@@ -64,3 +64,4 @@ class MallPlayxOrder extends Model
return $this->belongsTo(MallItem::class, 'mall_item_id', 'id');
}
}

View File

@@ -21,6 +21,6 @@ class MallPintsOrder extends Model
public function playxUserAsset(): \think\model\relation\BelongsTo
{
return $this->belongsTo(\app\common\model\MallPlayxUserAsset::class, 'playx_user_asset_id', 'id');
return $this->belongsTo(\app\common\model\MallUserAsset::class, 'playx_user_asset_id', 'id');
}
}

View File

@@ -1,26 +0,0 @@
<?php
declare(strict_types=1);
namespace app\common\model;
use support\think\Model;
/**
* PlayX 会话缓存
*/
class MallPlayxSession extends Model
{
protected string $name = 'mall_playx_session';
protected bool $autoWriteTimestamp = true;
protected array $type = [
// 这里需要显式声明 create_time / update_time 为 integer
// 否则 ThinkORM 可能把 bigint 时间戳当成字符串,导致写入时出现 now 字符串问题。
'create_time' => 'integer',
'update_time' => 'integer',
'expire_time' => 'integer',
];
}

View File

@@ -21,7 +21,7 @@ class MallRedemptionOrder extends Model
public function playxUserAsset(): \think\model\relation\BelongsTo
{
return $this->belongsTo(\app\common\model\MallPlayxUserAsset::class, 'playx_user_asset_id', 'id');
return $this->belongsTo(\app\common\model\MallUserAsset::class, 'playx_user_asset_id', 'id');
}
public function mallItem(): \think\model\relation\BelongsTo

View File

@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace app\common\model;
use support\think\Model;
/**
* 会话缓存
*/
class MallSession extends Model
{
protected string $name = 'mall_session';
protected bool $autoWriteTimestamp = true;
protected array $type = [
'create_time' => 'integer',
'update_time' => 'integer',
'expire_time' => 'integer',
];
}

View File

@@ -8,22 +8,22 @@ use ba\Random;
use support\think\Model;
/**
* PlayX 用户资产(积分商城用户主表,含登录账号字段)
* 用户资产(积分商城用户主表,含登录账号字段)
*/
class MallPlayxUserAsset extends Model
class MallUserAsset extends Model
{
protected string $name = 'mall_playx_user_asset';
protected string $name = 'mall_user_asset';
protected bool $autoWriteTimestamp = true;
protected array $type = [
'create_time' => 'integer',
'update_time' => 'integer',
'locked_points' => 'integer',
'available_points' => 'integer',
'today_limit' => 'integer',
'today_claimed' => 'integer',
'admin_id' => 'integer',
'create_time' => 'integer',
'update_time' => 'integer',
'locked_points' => 'integer',
'available_points' => 'integer',
'today_limit' => 'integer',
'today_claimed' => 'integer',
'admin_id' => 'integer',
];
/**
@@ -46,24 +46,24 @@ class MallPlayxUserAsset extends Model
$now = time();
$temporaryPlayxId = 'tmp_' . bin2hex(random_bytes(16));
$created = self::create([
'playx_user_id' => $temporaryPlayxId,
'username' => $username,
'phone' => $phone,
'password' => $pwd,
'admin_id' => 0,
'locked_points' => 0,
'available_points' => 0,
'today_limit' => 0,
'today_claimed' => 0,
'today_limit_date' => null,
'create_time' => $now,
'update_time' => $now,
'playx_user_id' => $temporaryPlayxId,
'username' => $username,
'phone' => $phone,
'password' => $pwd,
'admin_id' => 0,
'locked_points' => 0,
'available_points' => 0,
'today_limit' => 0,
'today_claimed' => 0,
'today_limit_date' => null,
'create_time' => $now,
'update_time' => $now,
]);
if (!$created) {
throw new \RuntimeException('Failed to create mall_playx_user_asset');
throw new \RuntimeException('Failed to create mall_user_asset');
}
$id = intval($created->getKey());
$id = $created->getKey();
$finalPlayxId = 'mall_' . $id;
if (self::where('playx_user_id', $finalPlayxId)->where('id', '<>', $id)->find()) {
$finalPlayxId = 'mall_' . $id . '_' . bin2hex(random_bytes(4));
@@ -77,7 +77,7 @@ class MallPlayxUserAsset extends Model
private static function allocateUniquePhone(): ?string
{
for ($i = 0; $i < 8; $i++) {
$candidate = '13' . str_pad(strval(mt_rand(0, 999999999)), 9, '0', STR_PAD_LEFT);
$candidate = '13' . str_pad(mt_rand(0, 999999999), 9, '0', STR_PAD_LEFT);
if (!self::where('phone', $candidate)->find()) {
return $candidate;
}
@@ -86,3 +86,4 @@ class MallPlayxUserAsset extends Model
return null;
}
}

View File

@@ -3,8 +3,8 @@
namespace app\process;
use app\common\model\MallItem;
use app\common\model\MallPlayxOrder;
use app\common\model\MallPlayxUserAsset;
use app\common\model\MallOrder;
use app\common\model\MallUserAsset;
use GuzzleHttp\Client;
use Workerman\Timer;
use Workerman\Worker;
@@ -47,14 +47,14 @@ class PlayxJobs
$path = strval(config('playx.api.transaction_status_url', '/api/v1/transaction/status'));
$url = rtrim($baseUrl, '/') . $path;
$list = MallPlayxOrder::where('grant_status', MallPlayxOrder::GRANT_ACCEPTED)
->where('status', MallPlayxOrder::STATUS_PENDING)
$list = MallOrder::where('grant_status', MallOrder::GRANT_ACCEPTED)
->where('status', MallOrder::STATUS_PENDING)
->order('id', 'desc')
->limit(50)
->select();
foreach ($list as $order) {
/** @var MallPlayxOrder $order */
/** @var MallOrder $order */
try {
$res = $this->http->get($url, [
'query' => [
@@ -65,16 +65,16 @@ class PlayxJobs
$data = json_decode(strval($res->getBody()), true) ?? [];
$pxStatus = $data['status'] ?? '';
if ($pxStatus === MallPlayxOrder::STATUS_COMPLETED) {
$order->status = MallPlayxOrder::STATUS_COMPLETED;
if ($pxStatus === MallOrder::STATUS_COMPLETED) {
$order->status = MallOrder::STATUS_COMPLETED;
$order->save();
continue;
}
if ($pxStatus === 'FAILED' || $pxStatus === MallPlayxOrder::STATUS_REJECTED) {
if ($pxStatus === 'FAILED' || $pxStatus === MallOrder::STATUS_REJECTED) {
// 仅在从 PENDING 转 REJECTED 时退分,避免重复入账
$order->status = MallPlayxOrder::STATUS_REJECTED;
$order->grant_status = MallPlayxOrder::GRANT_FAILED_FINAL;
$order->status = MallOrder::STATUS_REJECTED;
$order->grant_status = MallOrder::GRANT_FAILED_FINAL;
$order->fail_reason = strval($data['message'] ?? 'PlayX transaction failed');
$order->save();
$this->refundPoints($order);
@@ -104,18 +104,18 @@ class PlayxJobs
$withdrawUrl = rtrim($baseUrl, '/') . $withdrawPath;
$maxRetry = 3;
$list = MallPlayxOrder::whereIn('grant_status', [
MallPlayxOrder::GRANT_NOT_SENT,
MallPlayxOrder::GRANT_FAILED_RETRYABLE,
$list = MallOrder::whereIn('grant_status', [
MallOrder::GRANT_NOT_SENT,
MallOrder::GRANT_FAILED_RETRYABLE,
])
->where('status', MallPlayxOrder::STATUS_PENDING)
->where('status', MallOrder::STATUS_PENDING)
->where('retry_count', '<', $maxRetry)
->order('id', 'desc')
->limit(50)
->select();
foreach ($list as $order) {
/** @var MallPlayxOrder $order */
/** @var MallOrder $order */
$allow = $this->allowRetryByInterval($order);
if (!$allow) {
continue;
@@ -128,21 +128,21 @@ class PlayxJobs
} catch (\Throwable $e) {
$order->fail_reason = $e->getMessage();
if (intval($order->retry_count) >= $maxRetry) {
$order->grant_status = MallPlayxOrder::GRANT_FAILED_FINAL;
$order->status = MallPlayxOrder::STATUS_REJECTED;
$order->grant_status = MallOrder::GRANT_FAILED_FINAL;
$order->status = MallOrder::STATUS_REJECTED;
$order->save();
$this->refundPoints($order);
} else {
$order->grant_status = MallPlayxOrder::GRANT_FAILED_RETRYABLE;
$order->grant_status = MallOrder::GRANT_FAILED_RETRYABLE;
$order->save();
}
}
}
}
private function allowRetryByInterval(MallPlayxOrder $order): bool
private function allowRetryByInterval(MallOrder $order): bool
{
if ($order->grant_status === MallPlayxOrder::GRANT_NOT_SENT) {
if ($order->grant_status === MallOrder::GRANT_NOT_SENT) {
return true;
}
@@ -167,7 +167,7 @@ class PlayxJobs
return false;
}
private function sendGrantByOrder(MallPlayxOrder $order, string $bonusUrl, string $withdrawUrl, int $maxRetry): void
private function sendGrantByOrder(MallOrder $order, string $bonusUrl, string $withdrawUrl, int $maxRetry): void
{
$item = null;
if ($order->mallItem) {
@@ -176,7 +176,7 @@ class PlayxJobs
$item = MallItem::where('id', $order->mall_item_id)->find();
}
if ($order->type === MallPlayxOrder::TYPE_BONUS) {
if ($order->type === MallOrder::TYPE_BONUS) {
$rewardName = $item ? strval($item->title) : '';
$category = $item ? strval($item->category) : 'daily';
$categoryTitle = $item ? strval($item->category_title) : '';
@@ -201,7 +201,7 @@ class PlayxJobs
$data = json_decode(strval($res->getBody()), true) ?? [];
if ($res->getStatusCode() === 200 && ($data['status'] ?? '') === 'accepted') {
$order->grant_status = MallPlayxOrder::GRANT_ACCEPTED;
$order->grant_status = MallOrder::GRANT_ACCEPTED;
$order->playx_transaction_id = strval($data['playx_transaction_id'] ?? '');
$order->save();
return;
@@ -209,19 +209,19 @@ class PlayxJobs
$order->fail_reason = strval($data['message'] ?? 'PlayX bonus grant not accepted');
if (intval($order->retry_count) >= $maxRetry) {
$order->grant_status = MallPlayxOrder::GRANT_FAILED_FINAL;
$order->status = MallPlayxOrder::STATUS_REJECTED;
$order->grant_status = MallOrder::GRANT_FAILED_FINAL;
$order->status = MallOrder::STATUS_REJECTED;
$order->save();
$this->refundPoints($order);
return;
}
$order->grant_status = MallPlayxOrder::GRANT_FAILED_RETRYABLE;
$order->grant_status = MallOrder::GRANT_FAILED_RETRYABLE;
$order->save();
return;
}
if ($order->type === MallPlayxOrder::TYPE_WITHDRAW) {
if ($order->type === MallOrder::TYPE_WITHDRAW) {
$multiplier = intval($order->multiplier ?? 0);
if ($multiplier <= 0) {
$multiplier = 1;
@@ -239,7 +239,7 @@ class PlayxJobs
$data = json_decode(strval($res->getBody()), true) ?? [];
if ($res->getStatusCode() === 200 && ($data['status'] ?? '') === 'accepted') {
$order->grant_status = MallPlayxOrder::GRANT_ACCEPTED;
$order->grant_status = MallOrder::GRANT_ACCEPTED;
$order->playx_transaction_id = strval($data['playx_transaction_id'] ?? '');
$order->save();
return;
@@ -247,14 +247,14 @@ class PlayxJobs
$order->fail_reason = strval($data['message'] ?? 'PlayX balance credit not accepted');
if (intval($order->retry_count) >= $maxRetry) {
$order->grant_status = MallPlayxOrder::GRANT_FAILED_FINAL;
$order->status = MallPlayxOrder::STATUS_REJECTED;
$order->grant_status = MallOrder::GRANT_FAILED_FINAL;
$order->status = MallOrder::STATUS_REJECTED;
$order->save();
$this->refundPoints($order);
return;
}
$order->grant_status = MallPlayxOrder::GRANT_FAILED_RETRYABLE;
$order->grant_status = MallOrder::GRANT_FAILED_RETRYABLE;
$order->save();
return;
}
@@ -262,12 +262,12 @@ class PlayxJobs
// PHYSICAL 目前由后台手工发货/驳回,不参与 PlayX 发放重试
}
private function refundPoints(MallPlayxOrder $order): void
private function refundPoints(MallOrder $order): void
{
if ($order->points_cost <= 0) {
return;
}
$asset = MallPlayxUserAsset::where('playx_user_id', $order->user_id)->find();
$asset = MallUserAsset::where('playx_user_id', $order->user_id)->find();
if (!$asset) {
return;
}