import { notFound } from "next/navigation"; import { ReviewReport } from "@/components/review/ReviewReport"; import { fetchDraft, fetchOutline, fetchProject, fetchReviews, } from "@/lib/api/server"; import type { ProjectResponse } from "@/lib/api/types"; import { latestReview } from "@/lib/review/history"; import type { ChapterEntry } from "@/lib/workbench/chapter"; interface PageProps { params: Promise<{ id: string }>; searchParams: Promise<{ chapter?: string }>; } const DEFAULT_CHAPTER_NO = 1; // 审稿报告页(UX §6.4)。Server Component 取项目 + 审稿留痕历史(新→旧); // ReviewReport(Client)承载 SSE 重审 / 裁决 / 验收交互。 export default async function ReviewPage({ params, searchParams }: PageProps) { const { id } = await params; const { chapter } = await searchParams; const chapterNo = parsePositiveInt(chapter) ?? DEFAULT_CHAPTER_NO; // 仅当项目确实取不到时才判 404;审稿/草稿/大纲等次级拉取的瞬时错误不应把整页变成「找不到」。 let project: ProjectResponse; try { project = await fetchProject(id); } catch { notFound(); } // 审稿留痕(GET .../reviews,失败→空历史)。 let initialReview; try { const history = await fetchReviews(id, chapterNo); initialReview = latestReview(history.reviews); } catch { initialReview = undefined; } // 终稿初值取已存草稿(GET .../draft,404→null→"");否则 final_text 为空 → accept 422。 let initialDraft = ""; try { const draft = await fetchDraft(id, chapterNo); initialDraft = draft?.content ?? ""; } catch { initialDraft = ""; } // 大纲章节作章节导航目录(fetchOutline 已对空/错误降级为 []);首个节拍当短标题。 const chapters: ChapterEntry[] = (await fetchOutline(id)).map((c) => ({ no: c.no, title: c.beats?.[0], })); return ( // key=章号:客户端切章(?chapter=N 变化)时强制重挂载,用新章审稿/草稿刷新状态, // 避免 ReviewReport 内 useState 基线沿用上一章。 ); } function parsePositiveInt(raw: string | undefined): number | null { if (!raw) return null; const n = Number.parseInt(raw, 10); return Number.isInteger(n) && n > 0 ? n : null; }