option('dry-run'); $limit = (int)$this->option('limit'); if ($limit < 0) { $limit = 0; } $query = DB::table('mp_user_points_details as d') ->leftJoin('mp_generate_video_tasks as t', 't.id', '=', 'd.task_id') ->where('d.type', 'video') ->where('d.points_consumed', '>', 0) ->where('d.tokens_consumed', 0) ->select('d.id', 'd.task_id', 't.api_type', 't.result_json'); $total = (clone $query)->count(); $this->info("待回填明细总数: {$total}" . ($dryRun ? '(dry-run 模式,不更新)' : '')); if ($total <= 0) { return 0; } if ($limit > 0) { $query->limit($limit); $this->info("本次最多处理: {$limit} 条"); } $updated = 0; $skipped = 0; $notFound = 0; $rows = $query->get(); $bar = $this->output->createProgressBar($rows->count()); $bar->start(); foreach ($rows as $row) { $bar->advance(); if (!$row->task_id || $row->result_json === null) { $skipped++; continue; } $task = new MpGenerateVideoTask(); $task->api_type = (string)$row->api_type; $task->result_json = $row->result_json; try { $tokens = $pointsService->getVideoTokensConsumed($task); } catch (\Throwable $e) { dLog('points')->warning('视频 token 回填解析异常', [ 'detail_id' => $row->id, 'task_id' => $row->task_id, 'error' => $e->getMessage(), ]); $skipped++; continue; } if ($tokens <= 0) { $skipped++; continue; } if (!$dryRun) { DB::table('mp_user_points_details') ->where('id', $row->id) ->update([ 'tokens_consumed' => $tokens, 'updated_at' => date('Y-m-d H:i:s'), ]); } $updated++; } $bar->finish(); $this->newLine(2); $this->info("可回填: {$updated} 条" . ($dryRun ? '(dry-run 未写入)' : ',已写入')); $this->info("跳过(任务缺失/无 token 数据): {$skipped} 条"); if ($updated > 0 && !$dryRun) { dLog('points')->info('视频积分明细 token 历史回填完成', [ 'updated' => $updated, 'skipped' => $skipped, ]); } return 0; } }