""" 回忆录 feature — books / chapters / memoir-state 合并路由 """ from typing import Optional from fastapi import APIRouter, Body, Depends, Query from app.core.deps_types import CurrentUserDep from app.core.logging import get_logger from app.core.openapi import error_responses from app.features.memoir.deps import get_memoir_service from app.features.memoir.schemas import ( BookResponse, ChapterDetailResponse, ChapterListItemResponse, CoverGenerationResponse, ExportPdfRequest, ExportPdfResponse, MemoirStateResponse, NextQuestionContextResponse, SetChapterStoryOrderRequest, StatusMessageResponse, StoryOrderResponse, UpdateBookRequest, ) from app.features.memoir.service import MemoirService router = APIRouter( prefix="/api", tags=["memoir"], responses=error_responses(401, 403, 404), ) logger = get_logger(__name__) # =========================================================================== # Books endpoints # =========================================================================== @router.get("/books/current", response_model=BookResponse) async def get_current_book( current_user: CurrentUserDep, service: MemoirService = Depends(get_memoir_service), ): """获取当前回忆录(需要认证)""" return await service.get_current_book(current_user.id) @router.post("/books/clear-update", response_model=StatusMessageResponse) async def clear_book_update( current_user: CurrentUserDep, service: MemoirService = Depends(get_memoir_service), ): """清除回忆录更新标记""" return await service.clear_book_update(current_user.id) @router.put("/books/{book_id}", response_model=BookResponse) async def update_book( book_id: str, current_user: CurrentUserDep, request: UpdateBookRequest = Body(...), service: MemoirService = Depends(get_memoir_service), ): """更新书籍标题(需要认证,只能更新自己的回忆录)""" return await service.update_book(book_id, current_user.id, request.title) @router.post("/books/export-pdf", response_model=ExportPdfResponse) async def export_pdf( current_user: CurrentUserDep, request: ExportPdfRequest = Body(...), service: MemoirService = Depends(get_memoir_service), ): """导出 PDF(需要认证,只能导出自己的回忆录)""" return await service.export_pdf(current_user.id, request.book_id) # =========================================================================== # Chapters endpoints # =========================================================================== @router.get("/chapters", response_model=list[ChapterListItemResponse]) async def get_chapters( current_user: CurrentUserDep, is_new: Optional[bool] = Query(None, description="仅返回未读章节"), service: MemoirService = Depends(get_memoir_service), ): """ 获取用户所有有效章节(需要认证,仅返回 active 章节)。 """ return await service.get_chapters(current_user.id, is_new=is_new) @router.get("/chapters/{chapter_id}", response_model=ChapterDetailResponse) async def get_chapter( chapter_id: str, current_user: CurrentUserDep, service: MemoirService = Depends(get_memoir_service), ): """获取章节详情(需要认证,只能访问自己的章节)""" return await service.get_chapter(chapter_id, current_user.id) @router.post("/chapters/check-cover-generation", response_model=CoverGenerationResponse) async def check_cover_generation( current_user: CurrentUserDep, service: MemoirService = Depends(get_memoir_service), ): """ 检查可生成封面的章节(story-linked 且正文 asset 插图 > 3), 若有则触发生成任务。已有封面的章节不再检查。 """ return await service.check_and_trigger_cover_generation(current_user.id) @router.delete("/chapters/{chapter_id}", response_model=StatusMessageResponse) async def disable_chapter( chapter_id: str, current_user: CurrentUserDep, service: MemoirService = Depends(get_memoir_service), ): """清除章节(将章节标记为 disabled,需要认证,只能操作自己的章节)""" return await service.disable_chapter(chapter_id, current_user.id) @router.put("/chapters/{chapter_id}/story-order", response_model=StoryOrderResponse) async def set_chapter_story_order( chapter_id: str, current_user: CurrentUserDep, request: SetChapterStoryOrderRequest = Body(...), service: MemoirService = Depends(get_memoir_service), ): """ 设置章节收录的 stories 顺序(覆盖 chapter_story_links),并立即物化 canonical_markdown。 """ return await service.set_chapter_story_order( chapter_id, current_user.id, request.story_ids ) # =========================================================================== # Memoir-state endpoints # =========================================================================== @router.get("/memoir-state", response_model=MemoirStateResponse) async def get_memoir_state( current_user: CurrentUserDep, service: MemoirService = Depends(get_memoir_service), ): """获取当前用户回忆录状态""" return await service.get_memoir_state(current_user.id) @router.get("/memoir-state/next-question", response_model=NextQuestionContextResponse) async def get_next_question_context( current_user: CurrentUserDep, service: MemoirService = Depends(get_memoir_service), ): """获取下一步问题的上下文(当前阶段与空 slot)""" return await service.get_next_question_context(current_user.id) @router.post("/memoir-state/mark-read", response_model=StatusMessageResponse) async def mark_memoir_read( current_user: CurrentUserDep, service: MemoirService = Depends(get_memoir_service), ): """标记回忆录更新已读""" return await service.mark_memoir_read(current_user.id)