Procházet zdrojové kódy

feat: 批量重生成 PDF API - 按时间区间遍历 completed_at=NULL 卷子

- POST /api/papers/regenerate-batch
- 参数: start_date, end_date (Y-m-d), limit, include_kp_explain
- set_time_limit(0) 避免 30 秒超时

Made-with: Cursor
yemeishu před 1 týdnem
rodič
revize
f8b669fc7e
2 změnil soubory, kde provedl 140 přidání a 0 odebrání
  1. 135 0
      app/Http/Controllers/ExamPdfController.php
  2. 5 0
      routes/api.php

+ 135 - 0
app/Http/Controllers/ExamPdfController.php

@@ -1321,4 +1321,139 @@ class ExamPdfController extends Controller
             ], 500);
         }
     }
+
+    /**
+     * 批量重新生成 PDF:按时间区间遍历 completed_at=NULL 的卷子,使用新的分析重新生成统一 PDF 入库
+     *
+     * 基于 /api/papers/{paper_id}/regenerate 逻辑,遍历指定时间段创建且未完成的试卷。
+     * 时间格式 Y-m-d,start_date 从 00:00:00,end_date 到 23:59:59。
+     *
+     * @return \Illuminate\Http\JsonResponse
+     */
+    public function regeneratePdfBatch(Request $request)
+    {
+        // 批量 PDF 生成耗时较长,解除 30 秒执行限制(Guzzle 等会触发)
+        set_time_limit(0);
+
+        $startDate = $request->input('start_date');
+        $endDate = $request->input('end_date');
+        $limit = (int) $request->input('limit', 100);
+
+        $datePattern = '/^\d{4}-\d{2}-\d{2}$/';
+        if (empty($startDate) || ! preg_match($datePattern, $startDate)) {
+            return response()->json([
+                'success' => false,
+                'message' => '参数 start_date 必填,格式 Y-m-d(如 2026-03-20)',
+            ], 400);
+        }
+        if (empty($endDate) || ! preg_match($datePattern, $endDate)) {
+            return response()->json([
+                'success' => false,
+                'message' => '参数 end_date 必填,格式 Y-m-d(如 2026-03-22)',
+            ], 400);
+        }
+        if ($startDate > $endDate) {
+            return response()->json([
+                'success' => false,
+                'message' => 'start_date 不能大于 end_date',
+            ], 400);
+        }
+
+        $startTime = $startDate.' 00:00:00';
+        $endTime = $endDate.' 23:59:59';
+
+        $includeKpExplain = $request->has('include_kp_explain')
+            ? filter_var($request->input('include_kp_explain'), FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE)
+            : null;
+
+        Log::info('RegeneratePdfBatch: 开始批量重生成', [
+            'start_date' => $startDate,
+            'end_date' => $endDate,
+            'limit' => $limit,
+        ]);
+
+        try {
+            $papers = Paper::with('questions')
+                ->whereBetween('created_at', [$startTime, $endTime])
+                ->whereNull('completed_at')
+                ->orderBy('paper_id')
+                ->limit($limit)
+                ->get();
+
+            if ($papers->isEmpty()) {
+                return response()->json([
+                    'success' => true,
+                    'message' => '未找到符合条件的试卷',
+                    'start_date' => $startDate,
+                    'end_date' => $endDate,
+                    'total' => 0,
+                    'success_count' => 0,
+                    'fail_count' => 0,
+                    'details' => [],
+                ]);
+            }
+
+            $pdfService = app(\App\Services\ExamPdfExportService::class);
+            $successList = [];
+            $failList = [];
+
+            foreach ($papers as $paper) {
+                if ($paper->questions->isEmpty()) {
+                    $failList[] = [
+                        'paper_id' => $paper->paper_id,
+                        'reason' => '无题目',
+                    ];
+                    continue;
+                }
+
+                $useKpExplain = $includeKpExplain ?? ($paper->paper_type !== 0);
+
+                try {
+                    $pdfUrl = $pdfService->generateUnifiedPdf($paper->paper_id, $useKpExplain);
+                    if ($pdfUrl) {
+                        $successList[] = [
+                            'paper_id' => $paper->paper_id,
+                            'pdf_url' => $pdfUrl,
+                        ];
+                        Log::info('RegeneratePdfBatch: 成功', ['paper_id' => $paper->paper_id]);
+                    } else {
+                        $failList[] = [
+                            'paper_id' => $paper->paper_id,
+                            'reason' => 'generateUnifiedPdf 返回空',
+                        ];
+                    }
+                } catch (\Exception $e) {
+                    Log::error('RegeneratePdfBatch: 异常', [
+                        'paper_id' => $paper->paper_id,
+                        'error' => $e->getMessage(),
+                    ]);
+                    $failList[] = [
+                        'paper_id' => $paper->paper_id,
+                        'reason' => $e->getMessage(),
+                    ];
+                }
+            }
+
+            return response()->json([
+                'success' => true,
+                'message' => '批量重生成完成',
+                'start_date' => $startDate,
+                'end_date' => $endDate,
+                'total' => $papers->count(),
+                'success_count' => count($successList),
+                'fail_count' => count($failList),
+                'details' => [
+                    'succeeded' => $successList,
+                    'failed' => $failList,
+                ],
+            ]);
+        } catch (\Exception $e) {
+            Log::error('RegeneratePdfBatch: 批量异常', ['error' => $e->getMessage()]);
+
+            return response()->json([
+                'success' => false,
+                'message' => '批量重生成异常:'.$e->getMessage(),
+            ], 500);
+        }
+    }
 }

+ 5 - 0
routes/api.php

@@ -1172,6 +1172,11 @@ Route::post('/questions/pdf', [QuestionPdfController::class, 'generate'])
 |--------------------------------------------------------------------------
 */
 
+// 批量重新生成 PDF:按日期遍历 completed_at=NULL 的卷子(需在带 paper_id 的路由之前)
+Route::post('/papers/regenerate-batch', [\App\Http\Controllers\ExamPdfController::class, 'regeneratePdfBatch'])
+    ->withoutMiddleware([Authenticate::class, 'auth', 'auth:sanctum', 'auth:api'])
+    ->name('api.papers.regenerate-batch');
+
 // 重新生成统一 PDF(卷子 + 判卷)
 Route::post('/papers/{paper_id}/regenerate', [\App\Http\Controllers\ExamPdfController::class, 'regeneratePdf'])
     ->withoutMiddleware([Authenticate::class, 'auth', 'auth:sanctum', 'auth:api'])