feat: 增强票据与钱包服务的幂等性及错误处理能力

在 TicketItemShowController 与 TicketItemsIndexController 的响应中新增订单状态与失败原因字段。
更新 WalletLogsController:待对账列表支持按币种筛选。
在 TicketPlacementService 中引入幂等性校验,支持处理已退款订单的重复请求。
优化钱包相关操作的错误码与错误提示信息,提升问题定位与用户理解。
增强测试用例,验证票据下单流程中的新幂等性行为。
This commit is contained in:
2026-05-26 15:24:54 +08:00
parent c8c90e3e94
commit 36e50383ba
12 changed files with 154 additions and 23 deletions

View File

@@ -142,6 +142,7 @@ final class TicketItemShowController extends Controller
return ApiResponse::success([
'ticket_no' => $item->ticket_no,
'order_no' => $item->order?->order_no,
'order_status' => $item->order?->status,
'draw_no' => $draw?->draw_no,
'currency_code' => $item->order?->currency_code,
'play_code' => $item->play_code,
@@ -154,6 +155,8 @@ final class TicketItemShowController extends Controller
'rebate_rate_snapshot' => (string) $item->rebate_rate_snapshot,
'actual_deduct_amount' => (int) $item->actual_deduct_amount,
'status' => $item->status,
'fail_reason_code' => $item->fail_reason_code,
'fail_reason_text' => $item->fail_reason_text,
'win_amount' => (int) $item->win_amount,
'jackpot_win_amount' => (int) $item->jackpot_win_amount,
'settled_at' => $item->settled_at?->toIso8601String(),

View File

@@ -43,7 +43,7 @@ final class TicketItemsIndexController extends Controller
->where('ticket_items.player_id', $player->id)
->with([
'draw:id,draw_no,business_date',
'order:id,order_no,currency_code,created_at',
'order:id,order_no,currency_code,status,created_at',
])
->orderByDesc('ticket_items.id');
@@ -85,6 +85,7 @@ final class TicketItemsIndexController extends Controller
return [
'ticket_no' => $row->ticket_no,
'order_no' => $row->order?->order_no,
'order_status' => $row->order?->status,
'draw_no' => $row->draw?->draw_no,
'currency_code' => $row->order?->currency_code,
'play_code' => $row->play_code,

View File

@@ -39,7 +39,9 @@ final class WalletLogsController extends Controller
$perPage = $this->perPage($request, 'size', 20, 100);
$page = $this->page($request);
$pendingPayload = $this->pendingReconcilePayload((int) $player->id);
$currencyCode = strtoupper(trim((string) $request->query('currency', '')));
$pendingPayload = $this->pendingReconcilePayload((int) $player->id, $currencyCode);
$bizFilter = $this->resolveBizTypeFilter((string) $request->query('type', ''));
@@ -58,6 +60,10 @@ final class WalletLogsController extends Controller
->with('wallet')
->orderByDesc('id');
if ($currencyCode !== '') {
$query->whereHas('wallet', fn ($q) => $q->where('currency_code', $currencyCode));
}
if ($bizFilter !== null) {
$query->whereIn('biz_type', $bizFilter);
}
@@ -78,10 +84,11 @@ final class WalletLogsController extends Controller
/**
* @return list<array<string, mixed>>
*/
private function pendingReconcilePayload(int $playerId): array
private function pendingReconcilePayload(int $playerId, string $currencyCode = ''): array
{
return TransferOrder::query()
->where('player_id', $playerId)
->when($currencyCode !== '', fn ($q) => $q->where('currency_code', $currencyCode))
->where('status', 'pending_reconcile')
->orderByDesc('id')
->limit(50)