BackfillVideoTokensCommand.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. <?php
  2. namespace App\Console\Commands;
  3. use App\Models\MpGenerateVideoTask;
  4. use App\Services\PointsService;
  5. use Illuminate\Console\Command;
  6. use Illuminate\Support\Facades\DB;
  7. class BackfillVideoTokensCommand extends Command
  8. {
  9. /**
  10. * The name and signature of the console command.
  11. *
  12. * @var string
  13. */
  14. protected $signature = 'points:backfill-video-tokens
  15. {--dry-run : 只统计将回填的记录数,不实际更新}
  16. {--limit= : 最多处理记录数(默认全部)}';
  17. /**
  18. * The console command description.
  19. *
  20. * @var string
  21. */
  22. protected $description = '回填视频积分明细缺失的 token(历史 bug:扣费时读取到未同步的 result_json 导致 tokens_consumed=0)';
  23. /**
  24. * Execute the console command.
  25. *
  26. * @param PointsService $pointsService
  27. * @return int
  28. */
  29. public function handle(PointsService $pointsService)
  30. {
  31. $dryRun = (bool)$this->option('dry-run');
  32. $limit = (int)$this->option('limit');
  33. if ($limit < 0) {
  34. $limit = 0;
  35. }
  36. $query = DB::table('mp_user_points_details as d')
  37. ->leftJoin('mp_generate_video_tasks as t', 't.id', '=', 'd.task_id')
  38. ->where('d.type', 'video')
  39. ->where('d.points_consumed', '>', 0)
  40. ->where('d.tokens_consumed', 0)
  41. ->select('d.id', 'd.task_id', 't.api_type', 't.result_json');
  42. $total = (clone $query)->count();
  43. $this->info("待回填明细总数: {$total}" . ($dryRun ? '(dry-run 模式,不更新)' : ''));
  44. if ($total <= 0) {
  45. return 0;
  46. }
  47. if ($limit > 0) {
  48. $query->limit($limit);
  49. $this->info("本次最多处理: {$limit} 条");
  50. }
  51. $updated = 0;
  52. $skipped = 0;
  53. $notFound = 0;
  54. $rows = $query->get();
  55. $bar = $this->output->createProgressBar($rows->count());
  56. $bar->start();
  57. foreach ($rows as $row) {
  58. $bar->advance();
  59. if (!$row->task_id || $row->result_json === null) {
  60. $skipped++;
  61. continue;
  62. }
  63. $task = new MpGenerateVideoTask();
  64. $task->api_type = (string)$row->api_type;
  65. $task->result_json = $row->result_json;
  66. try {
  67. $tokens = $pointsService->getVideoTokensConsumed($task);
  68. } catch (\Throwable $e) {
  69. dLog('points')->warning('视频 token 回填解析异常', [
  70. 'detail_id' => $row->id,
  71. 'task_id' => $row->task_id,
  72. 'error' => $e->getMessage(),
  73. ]);
  74. $skipped++;
  75. continue;
  76. }
  77. if ($tokens <= 0) {
  78. $skipped++;
  79. continue;
  80. }
  81. if (!$dryRun) {
  82. DB::table('mp_user_points_details')
  83. ->where('id', $row->id)
  84. ->update([
  85. 'tokens_consumed' => $tokens,
  86. 'updated_at' => date('Y-m-d H:i:s'),
  87. ]);
  88. }
  89. $updated++;
  90. }
  91. $bar->finish();
  92. $this->newLine(2);
  93. $this->info("可回填: {$updated} 条" . ($dryRun ? '(dry-run 未写入)' : ',已写入'));
  94. $this->info("跳过(任务缺失/无 token 数据): {$skipped} 条");
  95. if ($updated > 0 && !$dryRun) {
  96. dLog('points')->info('视频积分明细 token 历史回填完成', [
  97. 'updated' => $updated,
  98. 'skipped' => $skipped,
  99. ]);
  100. }
  101. return 0;
  102. }
  103. }