perf(backend): 大纲卷过滤下推 DB WHERE——list_for_project 加 volume 参数

GET /outline?volume=N 原在端点 Python 侧过滤全部章节;现把 volume 条件下推到
OutlineRepo.list_for_project 的 DB WHERE,只查该卷行(少读多余卷 + 语义正确)。
Protocol/SqlOutlineRepo/各 OutlineRepo fake 同步加 keyword-only volume;补 repo 级
下推断言测试。向后兼容:不传 volume 仍返回全部(默认 None)。
This commit is contained in:
Yaojia Wang
2026-07-08 12:34:06 +02:00
parent c22f6b41d1
commit c596a8d342
17 changed files with 1330 additions and 18 deletions

View File

@@ -44,8 +44,11 @@ class FakeOutlineRepo:
async def get(self, project_id: uuid.UUID, chapter_no: int) -> OutlineView | None:
return self.rows.get(chapter_no)
async def list_for_project(self, project_id: uuid.UUID) -> list[OutlineView]:
return [self.rows[k] for k in sorted(self.rows)]
async def list_for_project(
self, project_id: uuid.UUID, *, volume: int | None = None
) -> list[OutlineView]:
rows = [self.rows[k] for k in sorted(self.rows)]
return [v for v in rows if volume is None or v.volume == volume]
@dataclass

View File

@@ -115,7 +115,11 @@ class ProjectSpecView(BaseModel):
class OutlineRepo(Protocol):
async def get(self, project_id: uuid.UUID, chapter_no: int) -> OutlineView | None: ...
async def list_for_project(self, project_id: uuid.UUID) -> list[OutlineView]: ...
async def list_for_project(
self, project_id: uuid.UUID, *, volume: int | None = None
) -> list[OutlineView]:
"""按 chapter_no 升序返回。`volume` 非 None 时**在 DB WHERE 下推**只取该卷。"""
...
class CharacterRepo(Protocol):

View File

@@ -56,12 +56,13 @@ class SqlOutlineRepo:
foreshadow_windows=list(row.foreshadow_windows or []),
)
async def list_for_project(self, project_id: uuid.UUID) -> list[OutlineView]:
rows = (
await self._s.execute(
select(Outline).where(Outline.project_id == project_id).order_by(Outline.chapter_no)
)
).scalars()
async def list_for_project(
self, project_id: uuid.UUID, *, volume: int | None = None
) -> list[OutlineView]:
stmt = select(Outline).where(Outline.project_id == project_id)
if volume is not None:
stmt = stmt.where(Outline.volume == volume)
rows = (await self._s.execute(stmt.order_by(Outline.chapter_no))).scalars()
return [
OutlineView(
volume=r.volume,