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

123 lines
4.8 KiB
Python

from __future__ import annotations
from .shared import *
from ...services.sstc.entity_lookup import _ensure_charter, _ensure_project, _ensure_release
from ...services.sstc.governance import _assert_release_transition_allowed, _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}/status")
async def patch_release_status(
project_id: str,
release_node_id: str,
body: ReleaseStatusPatchRequest,
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
)
await _assert_release_transition_allowed(
db,
tenant_id=tenant_id,
project_id=pid,
release_node=release_node,
target_status=body.status,
)
before = {"status": release_node.status}
release_node.status = body.status
content = _as_dict(release_node.content)
content["status_note"] = body.note
content["last_status_changed_at"] = now_cn_aware().isoformat()
release_node.content = content
await log_operation(
db,
tenant_id=tenant_id,
user_id=int(current_user.id),
project_id=pid,
entity_type="release",
entity_id=str(rid),
action="status_change",
before_data=before,
after_data={"status": body.status, "note": body.note},
)
await db.commit()
return success_response({"id": str(release_node.id), "status": release_node.status})
@router.post("/projects/{project_id}/releases/{release_node_id}/verification/generate")
async def generate_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
)
content = _as_dict(release_node.content)
charter_id = int(content.get("charter_id") or 0)
charter = await _ensure_charter(
db,
tenant_id=tenant_id,
project_id=pid,
charter_id=charter_id,
)
charter_content = _as_dict(charter.content)
derived_artifacts = charter_content.get("derived_artifacts")
if not isinstance(derived_artifacts, dict):
derived_artifacts = {}
verification_metrics = derived_artifacts.get("verification_metrics")
if not isinstance(verification_metrics, list):
verification_metrics = []
if not verification_metrics:
fallback_metrics = charter_content.get("success_metrics") or charter_content.get("core_metrics") or []
verification_metrics = [
{"name": str(metric).strip(), "expected": str(metric).strip()}
for metric in fallback_metrics
if str(metric or "").strip()
]
if not verification_metrics:
raise HTTPException(status_code=409, detail="charter verification metrics 缺失,请先完善宪章 KPI")
items = [
{
"id": f"check-{i+1}",
"title": str(metric.get("name") or metric.get("title") or "").strip(),
"expected": str(metric.get("expected") or metric.get("name") or metric.get("title") or "").strip(),
"status": "pending",
"required": True,
"source": "charter_verification_metric",
"verified_by": None,
"verified_at": None,
"note": None,
}
for i, metric in enumerate(verification_metrics)
if isinstance(metric, dict) and str(metric.get("name") or metric.get("title") or "").strip()
]
if not items:
raise HTTPException(status_code=409, detail="charter verification metrics 缺失,请先完善宪章 KPI")
content["verification_items"] = items
content["verification_progress"] = {"done": 0, "total": len(items)}
content["verification_done"] = False
release_node.content = content
await db.commit()
return success_response({"release_id": str(release_node.id), "verification_items": items})