- 新增 `LotteryDevPruneDrawBacklogCommand` 命令,用于按营业日区间删除积压的抽奖期号,并支持干运行和级联删除相关数据。 - 添加多个迁移文件以同步数据库结构,包括重命名重复的迁移文件、添加用户名字段、迁移抽奖状态到领域字典、合并显示名称字段、扩展审计日志目标类型字段,以及细化后台权限管理。 - 更新 `AdminRbacAndUserSeeder` 以包含角色代码字段,确保一致性与可维护性。
120 lines
3.9 KiB
PHP
120 lines
3.9 KiB
PHP
<?php
|
||
|
||
namespace App\Console\Commands;
|
||
|
||
use App\Models\Draw;
|
||
use Illuminate\Console\Command;
|
||
use App\Services\Draw\DrawTickService;
|
||
use Illuminate\Support\Facades\DB;
|
||
|
||
/**
|
||
* 开发/测试库:按营业日区间删除积压期号(级联清理关联数据)。
|
||
*
|
||
* 仅允许 local / testing 环境执行。
|
||
*/
|
||
final class LotteryDevPruneDrawBacklogCommand extends Command
|
||
{
|
||
protected $signature = 'lottery:dev-prune-draw-backlog
|
||
{--from=2026-05-11 : 营业日起(Y-m-d)}
|
||
{--to=2026-05-22 : 营业日止(Y-m-d)}
|
||
{--dry-run : 只统计,不删除}
|
||
{--force : 跳过确认}
|
||
{--tick : 删除后执行 lottery:draw-tick(积压多时可能占内存)}';
|
||
|
||
protected $description = '【仅 dev/test】删除指定营业日区间的期号积压(cascade)';
|
||
|
||
public function handle(DrawTickService $tickService): int
|
||
{
|
||
if (! app()->environment(['local', 'testing'])) {
|
||
$this->error('Refused: only allowed in local or testing environment.');
|
||
|
||
return self::FAILURE;
|
||
}
|
||
|
||
$from = (string) $this->option('from');
|
||
$to = (string) $this->option('to');
|
||
$dryRun = (bool) $this->option('dry-run');
|
||
|
||
$query = Draw::query()->whereBetween('business_date', [$from, $to]);
|
||
$total = (int) $query->count();
|
||
|
||
if ($total === 0) {
|
||
$this->info("No draws found between {$from} and {$to}.");
|
||
|
||
return self::SUCCESS;
|
||
}
|
||
|
||
$byStatus = (clone $query)
|
||
->selectRaw('status, count(*) as c')
|
||
->groupBy('status')
|
||
->orderByDesc('c')
|
||
->pluck('c', 'status');
|
||
|
||
$drawIds = (clone $query)->pluck('id');
|
||
$ticketOrders = (int) DB::table('ticket_orders')->whereIn('draw_id', $drawIds)->count();
|
||
|
||
$this->table(
|
||
['Metric', 'Value'],
|
||
[
|
||
['Date range', "{$from} .. {$to}"],
|
||
['Draws to delete', (string) $total],
|
||
['Ticket orders (cascade)', (string) $ticketOrders],
|
||
],
|
||
);
|
||
|
||
$this->line('By status:');
|
||
foreach ($byStatus as $status => $count) {
|
||
$this->line(" {$status}: {$count}");
|
||
}
|
||
|
||
if ($dryRun) {
|
||
$this->info('Dry run — no rows deleted.');
|
||
|
||
return self::SUCCESS;
|
||
}
|
||
|
||
if (! $this->option('force') && ! $this->confirm("Delete {$total} draws and related rows?", false)) {
|
||
$this->warn('Aborted.');
|
||
|
||
return self::SUCCESS;
|
||
}
|
||
|
||
$deleted = 0;
|
||
(clone $query)->orderBy('id')->chunkById(200, function ($draws) use (&$deleted): void {
|
||
foreach ($draws as $draw) {
|
||
$draw->delete();
|
||
$deleted++;
|
||
}
|
||
$this->output->write('.');
|
||
});
|
||
|
||
$this->newLine();
|
||
$this->info("Deleted {$deleted} draws.");
|
||
|
||
if ($this->option('tick')) {
|
||
$report = $tickService->tick();
|
||
$this->info(sprintf(
|
||
'Post-tick: status_updates=%d rng=%d planned_created=%d',
|
||
array_sum($report['status_updates'] ?? []),
|
||
$report['rng_rung'],
|
||
$report['planned']['created'] ?? 0,
|
||
));
|
||
} else {
|
||
$this->comment('Skipped tick (pass --tick to run draw-tick after prune).');
|
||
}
|
||
|
||
$head = Draw::query()
|
||
->whereNotIn('status', ['settled', 'cancelled'])
|
||
->orderBy('draw_time')
|
||
->first(['draw_no', 'status', 'draw_time']);
|
||
|
||
if ($head !== null) {
|
||
$this->info("Hall pipeline head is now: {$head->draw_no} ({$head->status}) @ {$head->draw_time}");
|
||
} else {
|
||
$this->info('Hall pipeline head: none (all settled/cancelled).');
|
||
}
|
||
|
||
return self::SUCCESS;
|
||
}
|
||
}
|