127 lines
4.2 KiB
PHP
127 lines
4.2 KiB
PHP
<?php
|
||
|
||
|
||
|
||
declare(strict_types=1);
|
||
|
||
|
||
|
||
namespace app\common\service;
|
||
|
||
|
||
|
||
use support\think\Db;
|
||
|
||
|
||
|
||
/**
|
||
|
||
* 充值待支付订单超时处理:
|
||
|
||
* - 同用户最多允许 3 笔待支付订单
|
||
|
||
* - 待支付订单创建后超过有效期未支付,自动标记为失败
|
||
|
||
* - 有效秒数由 .env DEPOSIT_PENDING_EXPIRE_SECONDS 配置(默认 60),全渠道统一
|
||
|
||
*/
|
||
|
||
final class DepositOrderExpireService
|
||
|
||
{
|
||
|
||
public const MAX_PENDING_DEPOSIT = 3;
|
||
|
||
|
||
|
||
/**
|
||
|
||
* 待支付充值单有效秒数(与 config app.deposit_pending_expire_seconds / .env 一致)
|
||
|
||
*/
|
||
|
||
public static function pendingExpireSeconds(): int
|
||
|
||
{
|
||
|
||
$cfg = config('app.deposit_pending_expire_seconds', 60);
|
||
|
||
if (is_numeric($cfg)) {
|
||
|
||
$v = intval($cfg);
|
||
|
||
if ($v > 0) {
|
||
|
||
return $v;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
return 60;
|
||
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
|
||
* @param array<string, mixed> $order
|
||
|
||
*/
|
||
|
||
public static function expireSecondsForOrder(array $order): int
|
||
|
||
{
|
||
|
||
return self::pendingExpireSeconds();
|
||
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
|
||
* @param int|null $userId 仅处理某用户;null 表示不过滤用户
|
||
|
||
* @param string|null $orderNo 仅处理某订单;null 表示不过滤订单
|
||
|
||
*
|
||
|
||
* @return int 本次转失败的订单数
|
||
|
||
*/
|
||
|
||
public static function expirePendingOrders(?int $userId = null, ?string $orderNo = null): int
|
||
|
||
{
|
||
|
||
$query = Db::name('deposit_order')->where('status', 0);
|
||
|
||
if ($userId !== null && $userId > 0) {
|
||
|
||
$query->where('user_id', $userId);
|
||
|
||
}
|
||
|
||
if ($orderNo !== null && $orderNo !== '') {
|
||
|
||
$query->where('order_no', $orderNo);
|
||
|
||
}
|
||
|
||
$rows = $query->field(['id', 'remark', 'pay_channel', 'create_time'])->select()->toArray();
|
||
|
||
if ($rows === []) {
|
||
|
||
return 0;
|
||
|
||
}
|
||
|
||
$now = time();
|
||
|
||
$expireSec = self::pendingExpireSeconds();
|
||
|
||
$affectedCount = 0;
|
||
|
||
foreach ($rows as $row) {
|
||
|
||
if (!is_array($row)) {
|
||
|
||
continue;
|
||
|
||
}
|
||
|
||
$id = isset($row['id']) && is_numeric($row['id']) ? intval($row['id']) : 0;
|
||
|
||
if ($id <= 0) {
|
||
|
||
continue;
|
||
|
||
}
|
||
|
||
$createTime = isset($row['create_time']) && is_numeric($row['create_time']) ? intval($row['create_time']) : 0;
|
||
|
||
if ($createTime > 0 && $createTime + $expireSec > $now) {
|
||
|
||
continue;
|
||
|
||
}
|
||
|
||
$oldRemark = isset($row['remark']) && is_string($row['remark']) ? trim($row['remark']) : '';
|
||
|
||
$reason = '[timeout] unpaid over ' . $expireSec . 's';
|
||
|
||
$remark = $oldRemark === '' ? $reason : mb_substr($oldRemark . ' | ' . $reason, 0, 255);
|
||
|
||
$affected = Db::name('deposit_order')
|
||
|
||
->where('id', $id)
|
||
|
||
->where('status', 0)
|
||
|
||
->update([
|
||
|
||
'status' => 2,
|
||
|
||
'remark' => $remark,
|
||
|
||
'update_time' => $now,
|
||
|
||
]);
|
||
|
||
if (is_numeric($affected) && intval($affected) > 0) {
|
||
|
||
$affectedCount++;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
return $affectedCount;
|
||
|
||
}
|
||
|
||
|
||
|
||
public static function pendingCountByUserId(int $userId): int
|
||
|
||
{
|
||
|
||
if ($userId <= 0) {
|
||
|
||
return 0;
|
||
|
||
}
|
||
|
||
|
||
|
||
return Db::name('deposit_order')
|
||
|
||
->where('user_id', $userId)
|
||
|
||
->where('status', 0)
|
||
|
||
->count();
|
||
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
|
||
* 订单是否仍在待支付有效期内
|
||
|
||
*
|
||
|
||
* @param array<string, mixed> $order
|
||
|
||
*/
|
||
|
||
public static function isPendingPaymentValid(array $order): bool
|
||
|
||
{
|
||
|
||
$status = isset($order['status']) && is_numeric($order['status']) ? intval($order['status']) : -1;
|
||
|
||
if ($status !== 0) {
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
$createTime = isset($order['create_time']) && is_numeric($order['create_time']) ? intval($order['create_time']) : 0;
|
||
|
||
if ($createTime <= 0) {
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
|
||
|
||
return $createTime + self::pendingExpireSeconds() > time();
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|