validate([ 'page' => ['nullable', 'integer', 'min:1'], 'page_size' => ['nullable', 'integer', 'min:1', 'max:100'], ]); $paginator = WeeklyBrief::query() ->orderByDesc('week_start') ->paginate((int) ($data['page_size'] ?? 20)) ->withQueryString(); $paginator->getCollection()->transform(fn (WeeklyBrief $b) => $this->serializeList($b)); return $this->paginated($paginator); } public function show(int $weeklyBrief): JsonResponse { $model = WeeklyBrief::query()->findOrFail($weeklyBrief); return $this->ok($this->serializeDetail($model)); } public function generate(Request $request): JsonResponse { $data = $request->validate([ 'week_start' => ['nullable', 'date'], 'week_end' => ['nullable', 'date', 'after_or_equal:week_start'], 'week_offset' => ['nullable', 'integer', 'min:0', 'max:52'], ]); try { if (! empty($data['week_start']) && ! empty($data['week_end'])) { $weekStart = Carbon::parse($data['week_start'])->timezone('Asia/Shanghai')->startOfDay(); $weekEnd = Carbon::parse($data['week_end'])->timezone('Asia/Shanghai')->endOfDay(); } elseif (isset($data['week_offset'])) { [$weekStart, $weekEnd] = $this->service->resolveWeekByOffset((int) $data['week_offset']); } else { [$weekStart, $weekEnd] = $this->service->resolvePreviousWeek(); } $brief = $this->service->generate( $weekStart, $weekEnd, $request->user()?->id, replace: true, ); } catch (Throwable $e) { return $this->fail('简报生成失败:'.$e->getMessage(), 500); } return $this->ok($this->serializeDetail($brief), '简报已生成'); } public function weekOptions(): JsonResponse { return $this->ok(['items' => $this->service->weekOptions()]); } public function download(int $weeklyBrief): StreamedResponse|JsonResponse { $model = WeeklyBrief::query()->findOrFail($weeklyBrief); if (! $model->docx_path || ! Storage::disk('local')->exists($model->docx_path)) { return $this->fail('Word 简报文件不存在,请重新生成', 404); } $filename = sprintf( '高校科技成果周报_%s_%s.docx', $model->week_start?->format('Ymd'), $model->week_end?->format('Ymd'), ); return Storage::disk('local')->download($model->docx_path, $filename); } /** * @return array */ protected function serializeList(WeeklyBrief $brief): array { return [ 'id' => $brief->id, 'week_start' => $brief->week_start?->toDateString(), 'week_end' => $brief->week_end?->toDateString(), 'title' => $brief->title, 'stats' => $brief->stats_json, 'generated_at' => $brief->generated_at?->toIso8601String(), 'has_docx' => (bool) ($brief->docx_path && Storage::disk('local')->exists($brief->docx_path)), ]; } /** * @return array */ protected function serializeDetail(WeeklyBrief $brief): array { return [ ...$this->serializeList($brief), 'docx_path' => $brief->docx_path, ]; } }