Files
writer-work-flow/apps/api/ww_api/routers/rules.py
Yaojia Wang 2fe3bedfba fix(qa): 修 QA C1/H1/H2——写章/规则缺项目校验 + 立项向导字段覆盖
C1 (CRITICAL) stream_draft:对不存在 project 流式写章先 fail-fast 404,
  否则非法 id 静默烧一次付费/限流 LLM 调用并返 200。在触网关前查 project_repo.get。
H2 (HIGH) create_rule:给不存在 project 加规则原 FK 违例逃逸成 500 → 改为入库前
  校验项目存在返 404(仿 chain/_require_project)。
H1 (HIGH) ProjectWizard:第3步「立意」与第4步「主角/金手指」原共用 form.premise
  互相覆盖丢数据 → 新增独立 form.protagonist,toCreateRequest 合并两段进 premise
  (M1 projects 表仍只有 premise,不编造 API)。

回归测试:
- test_projects.py:stream_draft 不存在 project → 404 且网关零调用;已有 draft
  用例改 seed 真项目。
- test_rules.py:create_rule 不存在 project → 404 不写库;已有用例 seed 真项目。
- wizard.test.ts:premise+protagonist 合并不互相覆盖(2 例)。
门禁绿:ruff/format clean · mypy 210 · pytest 749 · 前端 tsc/lint/vitest 干净。
2026-06-24 17:17:35 +02:00

64 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""规则端点C3 扩 / PRODUCT_SPEC §7 `POST /projects/:id/rules`;不变量 #3
POST /projects/:id/rules作者显式加规则——审稿发现的问题/亮点随手沉淀为项目规则。
写一行 `rules`level + content喂给 assemble 的四级合并(`merge_rules`)。
加规则是**作者显式动作**(不变量 #3规则入库不经 AI 静默写库)。`level` 合法性由
`RuleCreateRequest` 的 Literal 校验(非法 → FastAPI 422
提交边界:`RuleWriteRepo.create` 只 `flush()`,端点写后 `await session.commit()`
(仿 foreshadow/outline 写侧,见 memory/gotchas
"""
from __future__ import annotations
import uuid
from typing import Annotated
from fastapi import APIRouter, Depends, Request
from sqlalchemy.ext.asyncio import AsyncSession
from ww_core.domain import RuleWriteRepo
from ww_core.domain.project_repo import ProjectRepo
from ww_db import get_session
from ww_shared import AppError, ErrorCode
from ww_api.logging_config import get_logger
from ww_api.schemas.rules import RuleCreateRequest, RuleView
from ww_api.services.credentials import STUB_OWNER_ID
from ww_api.services.project_deps import get_project_repo, get_rule_write_repo
log = get_logger("ww.api.rules")
router = APIRouter(prefix="/projects", tags=["rules"])
RuleWriteRepoDep = Annotated[RuleWriteRepo, Depends(get_rule_write_repo)]
ProjectRepoDep = Annotated[ProjectRepo, Depends(get_project_repo)]
SessionDep = Annotated[AsyncSession, Depends(get_session)]
@router.post("/{project_id}/rules", status_code=201)
async def create_rule(
project_id: uuid.UUID,
body: RuleCreateRequest,
request: Request,
repo: RuleWriteRepoDep,
project_repo: ProjectRepoDep,
session: SessionDep,
) -> RuleView:
"""新增一条规则201。非法 level / 空 content → FastAPI 422项目不存在 → 404。
项目存在性须在 insert 前校验:否则 FK 违例会逃逸成 500QA H2而非干净的 404。
"""
request_id = getattr(request.state, "request_id", None)
if await project_repo.get(STUB_OWNER_ID, project_id) is None:
raise AppError(ErrorCode.NOT_FOUND, f"project not found: {project_id}")
view = await repo.create(project_id, level=body.level, content=body.content)
await session.commit()
log.info(
"rule_created",
project_id=str(project_id),
request_id=request_id,
level=body.level,
)
return RuleView(level=view.level, content=view.content)