PMFlow/backend/app/api/sstc_api/releases_verification.py

104 lines
4.2 KiB
Python

from __future__ import annotations
from .shared import *
from ...services.sstc.entity_lookup import _ensure_project, _ensure_release
from ...services.sstc.governance import _require_manager
from ...services.sstc.signal_core import _as_dict, _parse_node_id, _parse_project_id
router = APIRouter(tags=["sstc"])
@router.patch("/projects/{project_id}/releases/{release_node_id}/verification/items/{item_id}")
async def patch_release_verification_item(
project_id: str,
release_node_id: str,
item_id: str,
body: ReleaseVerificationItemPatchRequest,
permission_user: User = Depends(require_project_member_or_manager),
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
_ = permission_user
_require_manager(current_user)
pid = _parse_project_id(project_id)
rid = _parse_node_id(release_node_id, "release_node_id")
tenant_id = int(current_user.tenant_id)
await _ensure_project(SSTCService(db, tenant_id=tenant_id), pid)
release_node = await _ensure_release(
db, tenant_id=tenant_id, project_id=pid, release_node_id=rid
)
content = _as_dict(release_node.content)
items = content.get("verification_items")
if not isinstance(items, list) or len(items) == 0:
raise HTTPException(status_code=404, detail="验证清单不存在,请先生成")
target = None
for item in items:
if str(item.get("id")) == item_id:
target = item
break
if target is None:
raise HTTPException(status_code=404, detail="验证项不存在")
target["status"] = body.status
if body.status == "done":
target["verified_by"] = str(current_user.id)
target["verified_at"] = now_cn_aware().isoformat()
else:
target["verified_by"] = None
target["verified_at"] = None
target["note"] = body.note
done_count = len([item for item in items if str(item.get("status") or "").lower() == "done"])
content["verification_items"] = items
content["verification_progress"] = {"done": done_count, "total": len(items)}
content["verification_done"] = done_count == len(items) and len(items) > 0
release_node.content = content
await db.commit()
return success_response(
{
"release_id": str(release_node.id),
"item_id": item_id,
"verification_progress": content["verification_progress"],
"verification_done": content["verification_done"],
}
)
@router.post("/projects/{project_id}/releases/{release_node_id}/verification/complete")
async def complete_release_verification(
project_id: str,
release_node_id: str,
permission_user: User = Depends(require_project_member_or_manager),
db: AsyncSession = Depends(get_db),
current_user: User = Depends(get_current_user),
):
_ = permission_user
_require_manager(current_user)
pid = _parse_project_id(project_id)
rid = _parse_node_id(release_node_id, "release_node_id")
tenant_id = int(current_user.tenant_id)
await _ensure_project(SSTCService(db, tenant_id=tenant_id), pid)
release_node = await _ensure_release(
db, tenant_id=tenant_id, project_id=pid, release_node_id=rid
)
status_raw = str(release_node.status or "").lower()
if status_raw not in {"online", "releasing"}:
raise HTTPException(status_code=409, detail="仅上线中的发布计划可完成验证")
content = _as_dict(release_node.content)
items = content.get("verification_items", [])
if not isinstance(items, list) or len(items) == 0:
raise HTTPException(status_code=409, detail="验证清单为空,请先生成并完成验证项")
incomplete = [item for item in items if str(item.get("status") or "").lower() != "done"]
if incomplete:
raise HTTPException(status_code=409, detail="仍有未完成验证项,请先逐项完成")
now_str = now_cn_aware().isoformat()
content["verification_items"] = items
content["verification_done"] = True
content["verification_progress"] = {"done": len(items), "total": len(items)}
content["verified_at"] = now_str
release_node.content = content
release_node.status = "verified"
await db.commit()
return success_response({"id": str(release_node.id), "status": release_node.status})