优化接口以及后台页面样式
This commit is contained in:
@@ -46,6 +46,7 @@ class Order extends Backend
|
||||
'receiver_name',
|
||||
'receiver_phone',
|
||||
'receiver_address',
|
||||
'mall_address_id',
|
||||
'create_time',
|
||||
'update_time',
|
||||
];
|
||||
@@ -139,7 +140,52 @@ class Order extends Backend
|
||||
}
|
||||
|
||||
/**
|
||||
* PHYSICAL 驳回:更新状态为 REJECTED,并退回积分到 available_points
|
||||
* 审核通过(非 PHYSICAL):更新状态为 COMPLETED
|
||||
*/
|
||||
public function approve(Request $request): Response
|
||||
{
|
||||
$response = $this->initializeBackend($request);
|
||||
if ($response !== null) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
if ($request->method() !== 'POST') {
|
||||
return $this->error(__('Parameter error'));
|
||||
}
|
||||
|
||||
$id = $request->post('id', 0);
|
||||
if (!$id) {
|
||||
return $this->error(__('Missing required fields'));
|
||||
}
|
||||
|
||||
$order = MallOrder::where('id', $id)->find();
|
||||
if (!$order) {
|
||||
return $this->error(__('Record not found'));
|
||||
}
|
||||
if ($order->status !== MallOrder::STATUS_PENDING) {
|
||||
return $this->error(__('Order status must be PENDING'));
|
||||
}
|
||||
if ($order->type === MallOrder::TYPE_PHYSICAL) {
|
||||
return $this->error(__('Order type not supported'));
|
||||
}
|
||||
|
||||
Db::startTrans();
|
||||
try {
|
||||
$order->status = MallOrder::STATUS_COMPLETED;
|
||||
$order->update_time = time();
|
||||
$order->save();
|
||||
|
||||
Db::commit();
|
||||
} catch (Throwable $e) {
|
||||
Db::rollback();
|
||||
return $this->error($e->getMessage());
|
||||
}
|
||||
|
||||
return $this->success(__('Approved successfully'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核驳回:更新状态为 REJECTED,并退回积分到 available_points(所有类型通用)
|
||||
*/
|
||||
public function reject(Request $request): Response
|
||||
{
|
||||
@@ -164,9 +210,6 @@ class Order extends Backend
|
||||
if (!$order) {
|
||||
return $this->error(__('Record not found'));
|
||||
}
|
||||
if ($order->type !== MallOrder::TYPE_PHYSICAL) {
|
||||
return $this->error(__('Order type not PHYSICAL'));
|
||||
}
|
||||
if ($order->status !== MallOrder::STATUS_PENDING) {
|
||||
return $this->error(__('Order status must be PENDING'));
|
||||
}
|
||||
@@ -187,6 +230,7 @@ class Order extends Backend
|
||||
$order->status = MallOrder::STATUS_REJECTED;
|
||||
$order->reject_reason = $rejectReason;
|
||||
$order->grant_status = MallOrder::GRANT_FAILED_FINAL;
|
||||
$order->update_time = time();
|
||||
$order->save();
|
||||
|
||||
Db::commit();
|
||||
|
||||
@@ -115,6 +115,24 @@ class Auth extends Api
|
||||
}
|
||||
|
||||
$username = trim(strval($request->get('username', $request->post('username', ''))));
|
||||
// 兼容:querystring 中未编码的 '+' 会被解析为空格(application/x-www-form-urlencoded 规则)
|
||||
// 例如:/api/v1/temLogin?username=+607... 期望保留 '+',则从原始 querystring 提取并还原
|
||||
if ($username !== '' && str_contains($username, ' ')) {
|
||||
$qs = $request->queryString();
|
||||
if (is_string($qs) && $qs !== '') {
|
||||
foreach (explode('&', $qs) as $pair) {
|
||||
if ($pair === '' || !str_contains($pair, '=')) {
|
||||
continue;
|
||||
}
|
||||
[$k, $v] = explode('=', $pair, 2);
|
||||
if (rawurldecode($k) === 'username') {
|
||||
// 先把 %xx 解码;注意这里不把 '+' 当空格处理,从而保留 '+'
|
||||
$username = trim(rawurldecode($v));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($username === '') {
|
||||
return $this->error(__('Parameter username can not be empty'));
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ class Playx extends Api
|
||||
|
||||
$token = strval($request->post('token', $request->post('session', $request->get('token', ''))));
|
||||
if ($token === '') {
|
||||
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
|
||||
if (config('playx.verify_token_local_only', false)) {
|
||||
@@ -410,7 +410,7 @@ class Playx extends Api
|
||||
$data = json_decode(strval($res->getBody()), true);
|
||||
if ($code !== 200 || empty($data['user_id'])) {
|
||||
$remoteMsg = $data['message'] ?? '';
|
||||
$msg = is_string($remoteMsg) && $remoteMsg !== '' ? $remoteMsg : __('Invalid token');
|
||||
$msg = is_string($remoteMsg) && $remoteMsg !== '' ? $remoteMsg : __('Token expiration');
|
||||
|
||||
return $this->error($msg, null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
@@ -454,20 +454,20 @@ class Playx extends Api
|
||||
{
|
||||
$tokenData = Token::get($token);
|
||||
if (empty($tokenData) || (isset($tokenData['expire_time']) && intval($tokenData['expire_time']) <= time())) {
|
||||
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
$tokenType = strval($tokenData['type'] ?? '');
|
||||
if ($tokenType !== UserAuth::TOKEN_TYPE_MALL_USER) {
|
||||
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
$assetId = intval($tokenData['user_id'] ?? 0);
|
||||
if ($assetId <= 0) {
|
||||
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
|
||||
$asset = MallUserAsset::where('id', $assetId)->find();
|
||||
if (!$asset) {
|
||||
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
|
||||
$playxUserId = strval($asset->playx_user_id ?? '');
|
||||
@@ -507,7 +507,7 @@ class Playx extends Api
|
||||
|
||||
$assetId = $this->resolvePlayxAssetIdFromRequest($request);
|
||||
if ($assetId === null) {
|
||||
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
|
||||
$asset = $this->getAssetById($assetId);
|
||||
@@ -546,7 +546,10 @@ class Playx extends Api
|
||||
|
||||
$claimRequestId = strval($request->post('claim_request_id', ''));
|
||||
$assetId = $this->resolvePlayxAssetIdFromRequest($request);
|
||||
if ($claimRequestId === '' || $assetId === null) {
|
||||
if ($assetId === null) {
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
if ($claimRequestId === '') {
|
||||
return $this->error(__('claim_request_id and user_id/session_id required'));
|
||||
}
|
||||
|
||||
@@ -660,7 +663,7 @@ class Playx extends Api
|
||||
|
||||
$assetId = $this->resolvePlayxAssetIdFromRequest($request);
|
||||
if ($assetId === null) {
|
||||
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
$asset = $this->getAssetById($assetId);
|
||||
if (!$asset || strval($asset->playx_user_id ?? '') === '') {
|
||||
@@ -689,7 +692,7 @@ class Playx extends Api
|
||||
|
||||
$assetId = $this->resolvePlayxAssetIdFromRequest($request);
|
||||
if ($assetId === null) {
|
||||
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
|
||||
$list = MallAddress::where('playx_user_asset_id', $assetId)
|
||||
@@ -713,16 +716,16 @@ class Playx extends Api
|
||||
|
||||
$assetId = $this->resolvePlayxAssetIdFromRequest($request);
|
||||
if ($assetId === null) {
|
||||
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
|
||||
$phone = trim(strval($request->post('phone', '')));
|
||||
$receiverName = trim(strval($request->post('receiver_name', '')));
|
||||
$region = $request->post('region', '');
|
||||
$detailAddress = trim(strval($request->post('detail_address', '')));
|
||||
$address = trim(strval($request->post('address', '')));
|
||||
$defaultSetting = strval($request->post('default_setting', '0')) === '1' ? 1 : 0;
|
||||
|
||||
if ($phone === '' || $detailAddress === '' || $address === '' || $region === '' || $region === null) {
|
||||
if ($phone === '' || $receiverName === '' || $detailAddress === '' || $region === '' || $region === null) {
|
||||
return $this->error(__('Missing required fields'));
|
||||
}
|
||||
|
||||
@@ -734,10 +737,10 @@ class Playx extends Api
|
||||
|
||||
$created = MallAddress::create([
|
||||
'playx_user_asset_id' => $assetId,
|
||||
'receiver_name' => $receiverName,
|
||||
'phone' => $phone,
|
||||
'region' => $region,
|
||||
'detail_address' => $detailAddress,
|
||||
'address' => $address,
|
||||
'default_setting' => $defaultSetting,
|
||||
'create_time' => time(),
|
||||
'update_time' => time(),
|
||||
@@ -767,7 +770,7 @@ class Playx extends Api
|
||||
|
||||
$assetId = $this->resolvePlayxAssetIdFromRequest($request);
|
||||
if ($assetId === null) {
|
||||
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
|
||||
$id = intval($request->post('id', 0));
|
||||
@@ -784,15 +787,15 @@ class Playx extends Api
|
||||
if ($request->post('phone', null) !== null) {
|
||||
$updates['phone'] = trim(strval($request->post('phone', '')));
|
||||
}
|
||||
if ($request->post('receiver_name', null) !== null) {
|
||||
$updates['receiver_name'] = trim(strval($request->post('receiver_name', '')));
|
||||
}
|
||||
if ($request->post('region', null) !== null) {
|
||||
$updates['region'] = $request->post('region', '');
|
||||
}
|
||||
if ($request->post('detail_address', null) !== null) {
|
||||
$updates['detail_address'] = trim(strval($request->post('detail_address', '')));
|
||||
}
|
||||
if ($request->post('address', null) !== null) {
|
||||
$updates['address'] = trim(strval($request->post('address', '')));
|
||||
}
|
||||
if ($request->post('default_setting', null) !== null) {
|
||||
$updates['default_setting'] = strval($request->post('default_setting', '0')) === '1' ? 1 : 0;
|
||||
}
|
||||
@@ -830,7 +833,7 @@ class Playx extends Api
|
||||
|
||||
$assetId = $this->resolvePlayxAssetIdFromRequest($request);
|
||||
if ($assetId === null) {
|
||||
return $this->error(__('Invalid token'), null, 0, ['statusCode' => 401]);
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
|
||||
$id = intval($request->post('id', 0));
|
||||
@@ -897,7 +900,10 @@ class Playx extends Api
|
||||
|
||||
$itemId = intval($request->post('item_id', 0));
|
||||
$assetId = $this->resolvePlayxAssetIdFromRequest($request);
|
||||
if ($itemId <= 0 || $assetId === null) {
|
||||
if ($assetId === null) {
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
if ($itemId <= 0) {
|
||||
return $this->error(__('item_id and user_id/session_id required'));
|
||||
}
|
||||
|
||||
@@ -963,11 +969,21 @@ class Playx extends Api
|
||||
}
|
||||
|
||||
$itemId = intval($request->post('item_id', 0));
|
||||
$addressId = intval($request->post('address_id', 0));
|
||||
$assetId = $this->resolvePlayxAssetIdFromRequest($request);
|
||||
$receiverName = $request->post('receiver_name', '');
|
||||
$receiverPhone = $request->post('receiver_phone', '');
|
||||
$receiverAddress = $request->post('receiver_address', '');
|
||||
if ($itemId <= 0 || $assetId === null || $receiverName === '' || $receiverPhone === '' || $receiverAddress === '') {
|
||||
if ($itemId <= 0 || $addressId <= 0) {
|
||||
return $this->error(__('Missing required fields'));
|
||||
}
|
||||
if ($assetId === null) {
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
|
||||
$addrRow = MallAddress::where('id', $addressId)->where('playx_user_asset_id', $assetId)->find();
|
||||
if (!$addrRow) {
|
||||
return $this->error(__('Shipping address not found'));
|
||||
}
|
||||
$snapshot = MallAddress::snapshotForPhysicalOrder($addrRow);
|
||||
if ($snapshot['receiver_phone'] === '' || $snapshot['receiver_address'] === '' || $snapshot['receiver_name'] === '') {
|
||||
return $this->error(__('Missing required fields'));
|
||||
}
|
||||
|
||||
@@ -996,9 +1012,10 @@ class Playx extends Api
|
||||
'status' => MallOrder::STATUS_PENDING,
|
||||
'mall_item_id' => $item->id,
|
||||
'points_cost' => $item->score,
|
||||
'receiver_name' => $receiverName,
|
||||
'receiver_phone' => $receiverPhone,
|
||||
'receiver_address' => $receiverAddress,
|
||||
'mall_address_id' => $addressId,
|
||||
'receiver_name' => $snapshot['receiver_name'],
|
||||
'receiver_phone' => $snapshot['receiver_phone'],
|
||||
'receiver_address' => $snapshot['receiver_address'],
|
||||
'create_time' => time(),
|
||||
'update_time' => time(),
|
||||
]);
|
||||
@@ -1026,7 +1043,10 @@ class Playx extends Api
|
||||
|
||||
$itemId = intval($request->post('item_id', 0));
|
||||
$assetId = $this->resolvePlayxAssetIdFromRequest($request);
|
||||
if ($itemId <= 0 || $assetId === null) {
|
||||
if ($assetId === null) {
|
||||
return $this->error(__('Token expiration'), null, 0, ['statusCode' => 401]);
|
||||
}
|
||||
if ($itemId <= 0) {
|
||||
return $this->error(__('item_id and user_id/session_id required'));
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ return [
|
||||
'Temp login is disabled' => 'Temp login is disabled',
|
||||
'Failed to create temp account' => 'Failed to allocate a unique phone number, please retry later',
|
||||
'Parameter username can not be empty' => 'Parameter username can not be empty',
|
||||
'Token expiration' => 'Session expired, please login again.',
|
||||
// Member center account
|
||||
'Data updated successfully~' => 'Data updated successfully~',
|
||||
'Password has been changed~' => 'Password has been changed~',
|
||||
@@ -49,6 +50,7 @@ return [
|
||||
'Insufficient points' => 'Insufficient points',
|
||||
'Redeem submitted, please wait about 10 minutes' => 'Redeem submitted, please wait about 10 minutes',
|
||||
'Missing required fields' => 'Missing required fields',
|
||||
'Shipping address not found' => 'Shipping address not found',
|
||||
'Out of stock' => 'Out of stock',
|
||||
'Redeem success' => 'Redeem successful',
|
||||
'Withdraw submitted, please wait about 10 minutes' => 'Withdrawal submitted, please wait about 10 minutes',
|
||||
|
||||
@@ -82,6 +82,8 @@ return [
|
||||
'Redeem submitted, please wait about 10 minutes' => '兑换已提交,请等待约 10 分钟',
|
||||
'Missing required fields' => '缺少必填字段',
|
||||
'Out of stock' => '库存不足',
|
||||
'Record not found' => '记录不存在',
|
||||
'Shipping address not found' => '收货地址不存在',
|
||||
'Redeem success' => '兑换成功',
|
||||
'Withdraw submitted, please wait about 10 minutes' => '提现申请已提交,请等待约 10 分钟',
|
||||
];
|
||||
@@ -41,7 +41,18 @@ class MallAddress extends Model
|
||||
public function getregionTextAttr($value, $row): string
|
||||
{
|
||||
if ($row['region'] === '' || $row['region'] === null) return '';
|
||||
$cityNames = \support\think\Db::name('area')->whereIn('id', $row['region'])->column('name');
|
||||
$region = $row['region'];
|
||||
$ids = $region;
|
||||
if (!is_array($ids)) {
|
||||
$ids = explode(',', (string) $ids);
|
||||
}
|
||||
$ids = array_values(array_filter(array_map('trim', $ids), static function ($s) {
|
||||
return $s !== '';
|
||||
}));
|
||||
if (empty($ids)) {
|
||||
return '';
|
||||
}
|
||||
$cityNames = \support\think\Db::name('area')->whereIn('id', $ids)->column('name');
|
||||
return $cityNames ? implode(',', $cityNames) : '';
|
||||
}
|
||||
|
||||
@@ -49,4 +60,27 @@ class MallAddress extends Model
|
||||
{
|
||||
return $this->belongsTo(\app\common\model\MallUserAsset::class, 'playx_user_asset_id', 'id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 实物订单收货快照(写入 mall_order.receiver_*,与 mall_address 当前内容一致)
|
||||
*
|
||||
* @return array{receiver_name: string, receiver_phone: string, receiver_address: string}
|
||||
*/
|
||||
public static function snapshotForPhysicalOrder(self $addr): array
|
||||
{
|
||||
$regionText = $addr->region_text ?? '';
|
||||
$parts = array_filter([
|
||||
trim($regionText),
|
||||
trim($addr->detail_address ?? ''),
|
||||
], static function ($s) {
|
||||
return $s !== '';
|
||||
});
|
||||
$receiverAddress = implode(' ', $parts);
|
||||
|
||||
return [
|
||||
'receiver_name' => trim($addr->receiver_name ?? ''),
|
||||
'receiver_phone' => trim($addr->phone ?? ''),
|
||||
'receiver_address' => $receiverAddress,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ use support\think\Model;
|
||||
* @property string $receiver_name
|
||||
* @property string $receiver_phone
|
||||
* @property string|null $receiver_address
|
||||
* @property int|null $mall_address_id
|
||||
*/
|
||||
class MallOrder extends Model
|
||||
{
|
||||
@@ -56,12 +57,18 @@ class MallOrder extends Model
|
||||
'points_cost' => 'integer',
|
||||
'amount' => 'float',
|
||||
'multiplier' => 'integer',
|
||||
'retry_count' => 'integer',
|
||||
'retry_count' => 'integer',
|
||||
'mall_address_id' => 'integer',
|
||||
];
|
||||
|
||||
public function mallItem(): \think\model\relation\BelongsTo
|
||||
{
|
||||
return $this->belongsTo(MallItem::class, 'mall_item_id', 'id');
|
||||
}
|
||||
|
||||
public function mallAddress(): \think\model\relation\BelongsTo
|
||||
{
|
||||
return $this->belongsTo(MallAddress::class, 'mall_address_id', 'id');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,18 +31,21 @@ class MallUserAsset extends Model
|
||||
*/
|
||||
public static function ensureForUsername(string $username): self
|
||||
{
|
||||
// $username = trim($username);
|
||||
$username = trim($username);
|
||||
$existing = self::where('username', $username)->find();
|
||||
if ($existing) {
|
||||
return $existing;
|
||||
}
|
||||
|
||||
//手机号直接=用户名,暂时不做验证
|
||||
// $phone = self::allocateUniquePhone();
|
||||
// 创建用户时:phone 与 username 同值(H5 临时账号)
|
||||
$phone = $username;
|
||||
|
||||
if ($phone === null) {
|
||||
throw new \RuntimeException('Failed to allocate unique phone');
|
||||
$phoneExisting = self::where('phone', $phone)->find();
|
||||
if ($phoneExisting) {
|
||||
// 若历史数据存在“手机号=用户名”的行,直接复用;若不一致则拒绝创建,避免污染
|
||||
if (trim((string) ($phoneExisting->username ?? '')) === $username) {
|
||||
return $phoneExisting;
|
||||
}
|
||||
throw new \RuntimeException('Username is already used by another account');
|
||||
}
|
||||
|
||||
$pwd = hash_password(Random::build('alnum', 16));
|
||||
@@ -77,16 +80,6 @@ class MallUserAsset extends Model
|
||||
return $created;
|
||||
}
|
||||
|
||||
private static function allocateUniquePhone(): ?string
|
||||
{
|
||||
for ($i = 0; $i < 8; $i++) {
|
||||
$candidate = '13' . sprintf('%09d', mt_rand(0, 999999999));
|
||||
if (!self::where('phone', $candidate)->find()) {
|
||||
return $candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
// allocateUniquePhone 已废弃:临时登录场景下 phone=用户名
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user