- 新增 settlement-operations 合并列表 API,收付/调账接口改为标准分页 - actionable_only 流水在内存过滤后正确分页 - 已结账单流水不再展示补差/冲正快捷动作 - 坏账核销支持 idempotency_key 并写入 result_bill_id - 补充操作记录、流水与坏账幂等相关测试
133 lines
4.5 KiB
PHP
133 lines
4.5 KiB
PHP
<?php
|
||
|
||
namespace App\Http\Controllers\Api\V1\E2E;
|
||
|
||
use App\Lottery\ErrorCode;
|
||
use App\Lottery\DrawStatus;
|
||
use App\Support\ApiResponse;
|
||
use Illuminate\Http\Request;
|
||
use Illuminate\Http\JsonResponse;
|
||
use Illuminate\Support\Facades\DB;
|
||
use App\Http\Controllers\Controller;
|
||
use Illuminate\Support\Facades\Artisan;
|
||
|
||
/**
|
||
* E2E 辅助:期号时间/状态快进。
|
||
*
|
||
* 真实生产链路下"等 12 分钟后封盘"是脆的——CI 跑 e2e 不可能等。
|
||
* 这里允许 e2e 直接 SQL 改 close_time / draw_time,模拟:
|
||
* - close-now: close_time 改到 1 秒前、status=closed
|
||
* - reopen: status=open、重置冷却期
|
||
* - inspect: 返回 draw 完整时间/状态快照
|
||
*
|
||
* 严格仅在 LOTTERY_E2E=true 时由 E2EServiceProvider 注册。
|
||
*/
|
||
final class E2EDrawController extends Controller
|
||
{
|
||
public function closeNow(Request $request, string $drawNo): JsonResponse
|
||
{
|
||
$draw = DB::table('draws')->where('draw_no', $drawNo)->first();
|
||
if (! $draw) {
|
||
return ApiResponse::error('draw not found', ErrorCode::NotFound->value, null, 404);
|
||
}
|
||
|
||
DB::table('draws')
|
||
->where('id', $draw->id)
|
||
->update([
|
||
'status' => DrawStatus::Closed->value,
|
||
'close_time' => now()->subSeconds(5),
|
||
'draw_time' => now()->subSeconds(3),
|
||
]);
|
||
|
||
return $this->inspectById((int) $draw->id);
|
||
}
|
||
|
||
/** 将冷静期结束时间改到过去,便于 tick 推进 cooldown → settling。 */
|
||
public function finishCooldown(Request $request, string $drawNo): JsonResponse
|
||
{
|
||
$draw = DB::table('draws')->where('draw_no', $drawNo)->first();
|
||
if (! $draw) {
|
||
return ApiResponse::error('draw not found', ErrorCode::NotFound->value, null, 404);
|
||
}
|
||
|
||
DB::table('draws')
|
||
->where('id', $draw->id)
|
||
->update([
|
||
'cooling_end_time' => now()->subSeconds(5),
|
||
'draw_time' => now()->subMinutes(2),
|
||
'close_time' => now()->subMinutes(3),
|
||
]);
|
||
|
||
return $this->inspectById((int) $draw->id);
|
||
}
|
||
|
||
public function reopen(Request $request, string $drawNo): JsonResponse
|
||
{
|
||
$draw = DB::table('draws')->where('draw_no', $drawNo)->first();
|
||
if (! $draw) {
|
||
return ApiResponse::error('draw not found', ErrorCode::NotFound->value, null, 404);
|
||
}
|
||
|
||
DB::table('draws')
|
||
->where('id', $draw->id)
|
||
->update([
|
||
'status' => DrawStatus::Open->value,
|
||
'start_time' => now()->subMinutes(5),
|
||
'close_time' => now()->addMinutes(20),
|
||
'draw_time' => now()->addMinutes(21),
|
||
'cooling_end_time' => null,
|
||
]);
|
||
|
||
return $this->inspectById((int) $draw->id);
|
||
}
|
||
|
||
public function inspect(Request $request, string $drawNo): JsonResponse
|
||
{
|
||
$draw = DB::table('draws')->where('draw_no', $drawNo)->first();
|
||
if (! $draw) {
|
||
return ApiResponse::error('draw not found', ErrorCode::NotFound->value, null, 404);
|
||
}
|
||
|
||
return $this->inspectById((int) $draw->id);
|
||
}
|
||
|
||
/** 同步跑一次 lottery:draw-tick(不开调度)。返回该 tick 推进的 draw_no 列表。 */
|
||
public function tick(Request $request): JsonResponse
|
||
{
|
||
try {
|
||
$exit = Artisan::call('lottery:draw-tick');
|
||
} catch (\Throwable $e) {
|
||
return ApiResponse::error(
|
||
'draw-tick failed: '.$e->getMessage(),
|
||
ErrorCode::InternalError->value,
|
||
['trace' => array_slice(explode("\n", $e->getTraceAsString()), 0, 5)],
|
||
500,
|
||
);
|
||
}
|
||
|
||
return ApiResponse::success([
|
||
'exit_code' => $exit,
|
||
'output' => Artisan::output(),
|
||
]);
|
||
}
|
||
|
||
private function inspectById(int $id): JsonResponse
|
||
{
|
||
$d = DB::table('draws')->where('id', $id)->first();
|
||
|
||
return ApiResponse::success([
|
||
'id' => (int) $d->id,
|
||
'draw_no' => $d->draw_no,
|
||
'status' => (string) $d->status,
|
||
'business_date' => $d->business_date,
|
||
'start_time' => $d->start_time,
|
||
'close_time' => $d->close_time,
|
||
'draw_time' => $d->draw_time,
|
||
'cooling_end_time' => $d->cooling_end_time,
|
||
'current_result_version' => (int) $d->current_result_version,
|
||
'settle_version' => (int) $d->settle_version,
|
||
'is_reopened' => (bool) $d->is_reopened,
|
||
]);
|
||
}
|
||
}
|