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 干净。
This commit is contained in:
Yaojia Wang
2026-06-24 17:17:35 +02:00
parent a4ef250fc9
commit 2fe3bedfba
13 changed files with 688 additions and 27 deletions

View File

@@ -18,17 +18,21 @@ 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.project_deps import get_rule_write_repo
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)]
@@ -38,10 +42,16 @@ async def create_rule(
body: RuleCreateRequest,
request: Request,
repo: RuleWriteRepoDep,
project_repo: ProjectRepoDep,
session: SessionDep,
) -> RuleView:
"""新增一条规则201。非法 level / 空 content → FastAPI 422。"""
"""新增一条规则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(