CleanExpiredActivitiesCommand.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. <?php
  2. namespace App\Module\Activity\Commands;
  3. use App\Module\Activity\Enums\ACTIVITY_STATUS;
  4. use App\Module\Activity\Enums\REWARD_STATUS;
  5. use App\Module\Activity\Models\ActivityConfig;
  6. use App\Module\Activity\Models\ActivityParticipation;
  7. use Carbon\Carbon;
  8. use Illuminate\Console\Command;
  9. use Illuminate\Support\Facades\DB;
  10. use Illuminate\Support\Facades\Log;
  11. /**
  12. * 清理过期活动命令
  13. */
  14. class CleanExpiredActivitiesCommand extends Command
  15. {
  16. /**
  17. * 命令名称
  18. *
  19. * @var string
  20. */
  21. protected $signature = 'activity:clean-expired {--days=30 : 清理多少天前结束的活动} {--dry-run : 仅显示将要清理的内容,不实际执行}';
  22. /**
  23. * 命令描述
  24. *
  25. * @var string
  26. */
  27. protected $description = '清理过期活动和未领取的奖励';
  28. /**
  29. * 执行命令
  30. *
  31. * @return int
  32. */
  33. public function handle()
  34. {
  35. $days = (int)$this->option('days');
  36. $dryRun = $this->option('dry-run');
  37. if ($days <= 0) {
  38. $this->error('天数必须大于0');
  39. return 1;
  40. }
  41. $cutoffDate = Carbon::now()->subDays($days);
  42. $this->info("开始清理 {$cutoffDate} 之前结束的活动...");
  43. if ($dryRun) {
  44. $this->warn('当前为演示模式,不会实际执行清理操作');
  45. }
  46. // 查找过期活动
  47. $expiredActivities = ActivityConfig::where('end_time', '<', $cutoffDate)
  48. ->whereIn('status', [ACTIVITY_STATUS::ENDED, ACTIVITY_STATUS::CLOSED])
  49. ->get();
  50. $this->info("找到 " . count($expiredActivities) . " 个过期活动");
  51. if ($expiredActivities->isEmpty()) {
  52. $this->info('没有需要清理的过期活动');
  53. return 0;
  54. }
  55. // 显示将要清理的活动
  56. $this->table(
  57. ['ID', '名称', '结束时间', '状态'],
  58. $expiredActivities->map(function ($activity) {
  59. return [
  60. $activity->id,
  61. $activity->name,
  62. $activity->end_time,
  63. ACTIVITY_STATUS::getName($activity->status)
  64. ];
  65. })
  66. );
  67. if (!$dryRun && !$this->confirm('确定要清理这些活动吗?此操作不可撤销')) {
  68. $this->info('操作已取消');
  69. return 0;
  70. }
  71. // 清理过期活动
  72. $expiredActivityIds = $expiredActivities->pluck('id')->toArray();
  73. $expiredRewardsCount = 0;
  74. if (!$dryRun) {
  75. DB::beginTransaction();
  76. try {
  77. // 标记未领取的奖励为已过期
  78. $expiredRewardsCount = ActivityParticipation::whereIn('activity_id', $expiredActivityIds)
  79. ->where('reward_status', REWARD_STATUS::NOT_CLAIMED)
  80. ->update(['reward_status' => REWARD_STATUS::EXPIRED]);
  81. // 更新活动状态为已关闭
  82. ActivityConfig::whereIn('id', $expiredActivityIds)
  83. ->where('status', ACTIVITY_STATUS::ENDED)
  84. ->update(['status' => ACTIVITY_STATUS::CLOSED]);
  85. DB::commit();
  86. $this->info("成功清理 " . count($expiredActivityIds) . " 个过期活动");
  87. $this->info("标记 {$expiredRewardsCount} 个未领取的奖励为已过期");
  88. // 记录日志
  89. Log::info("清理过期活动完成", [
  90. 'activity_count' => count($expiredActivityIds),
  91. 'expired_rewards_count' => $expiredRewardsCount,
  92. 'cutoff_date' => $cutoffDate
  93. ]);
  94. } catch (\Exception $e) {
  95. DB::rollBack();
  96. $this->error("清理过期活动失败: " . $e->getMessage());
  97. // 记录日志
  98. Log::error("清理过期活动失败", [
  99. 'error' => $e->getMessage(),
  100. 'cutoff_date' => $cutoffDate
  101. ]);
  102. return 1;
  103. }
  104. } else {
  105. // 演示模式,显示将要执行的操作
  106. $expiredRewardsCount = ActivityParticipation::whereIn('activity_id', $expiredActivityIds)
  107. ->where('reward_status', REWARD_STATUS::NOT_CLAIMED)
  108. ->count();
  109. $this->info("将会清理 " . count($expiredActivityIds) . " 个过期活动");
  110. $this->info("将会标记 {$expiredRewardsCount} 个未领取的奖励为已过期");
  111. }
  112. return 0;
  113. }
  114. }