120 lines
4.4 KiB
Python
120 lines
4.4 KiB
Python
from __future__ import annotations
|
|
|
|
from .shared import *
|
|
|
|
class TaskBriefGenerateStatusMixin:
|
|
@classmethod
|
|
async def generate_brief(
|
|
cls,
|
|
db: AsyncSession,
|
|
*,
|
|
tenant_id: int,
|
|
operator_user_id: int,
|
|
title: str,
|
|
template_id: Optional[str],
|
|
changed_files: list[str],
|
|
scope_summary: Optional[str],
|
|
) -> dict[str, Any]:
|
|
row, bundle = await cls._get_or_init_bundle(db, tenant_id=tenant_id)
|
|
now_text = now_cn_naive().isoformat()
|
|
templates = bundle.get("templates") if isinstance(bundle.get("templates"), list) else []
|
|
template_obj = None
|
|
if template_id:
|
|
for item in templates:
|
|
if isinstance(item, dict) and str(item.get("id") or "") == str(template_id):
|
|
template_obj = item
|
|
break
|
|
if template_obj is None:
|
|
template_obj = next((item for item in templates if isinstance(item, dict)), None)
|
|
|
|
brief_id = f"brief_{uuid4().hex[:12]}"
|
|
run_id = f"run_{uuid4().hex[:12]}"
|
|
file_lines = [str(path).strip() for path in changed_files if str(path).strip()]
|
|
summary = str(scope_summary or "").strip() or f"共 {len(file_lines)} 个文件发生变更"
|
|
risks = (
|
|
[
|
|
"跨文件改动可能引发契约/类型不一致,请补充联动核对。",
|
|
"合入前需完成 build/lint/test 并记录结果。",
|
|
]
|
|
if file_lines
|
|
else ["未提供变更文件列表,风险评估信息不足,请人工补充。"]
|
|
)
|
|
content = cls._render_template_content(
|
|
template_content=str((template_obj or {}).get("content") or "").strip(),
|
|
generated_at=now_text,
|
|
base_ref=cls._resolve_git_ref("HEAD~1"),
|
|
head_ref=cls._resolve_git_ref("HEAD"),
|
|
changed_files=file_lines,
|
|
scope_summary=summary,
|
|
risks=risks,
|
|
)
|
|
|
|
run = {
|
|
"id": run_id,
|
|
"brief_id": brief_id,
|
|
"title": str(title or "").strip()[:160] or "增量任务包",
|
|
"template_id": str((template_obj or {}).get("id") or "") or None,
|
|
"template_name": str((template_obj or {}).get("name") or "") or None,
|
|
"generated_at": now_text,
|
|
"generated_by": int(operator_user_id),
|
|
"status": "completed",
|
|
"changed_files": file_lines,
|
|
"scope_summary": summary,
|
|
}
|
|
brief = {
|
|
"id": brief_id,
|
|
"title": run["title"],
|
|
"status": "ready",
|
|
"created_at": now_text,
|
|
"updated_at": now_text,
|
|
"latest_run_id": run_id,
|
|
"template_id": run["template_id"],
|
|
"template_name": run["template_name"],
|
|
"summary": summary,
|
|
"content": content,
|
|
"changed_files": file_lines,
|
|
}
|
|
|
|
runs = bundle.get("runs") if isinstance(bundle.get("runs"), list) else []
|
|
briefs = bundle.get("briefs") if isinstance(bundle.get("briefs"), list) else []
|
|
runs.append(run)
|
|
briefs = [item for item in briefs if isinstance(item, dict) and str(item.get("id") or "") != brief_id]
|
|
briefs.append(brief)
|
|
|
|
bundle["runs"] = runs
|
|
bundle["briefs"] = briefs
|
|
row.config = bundle
|
|
await db.flush()
|
|
return {"run": run, "brief": brief}
|
|
|
|
@classmethod
|
|
async def get_contract_sync_status(cls, db: AsyncSession, *, tenant_id: int) -> dict[str, Any]:
|
|
_ = db
|
|
_ = tenant_id
|
|
root = Path(__file__).resolve().parents[3]
|
|
contract_files = [
|
|
root / "docs/architecture/MASTER_CONTRACT.md",
|
|
root / "docs/architecture/API_CONTRACTS.md",
|
|
root / "docs/architecture/DATAFLOW.md",
|
|
]
|
|
statuses = []
|
|
for file_path in contract_files:
|
|
exists = file_path.exists()
|
|
statuses.append(
|
|
{
|
|
"file": str(file_path.relative_to(root)),
|
|
"exists": exists,
|
|
"updated_at": (
|
|
datetime.fromtimestamp(file_path.stat().st_mtime).isoformat()
|
|
if exists
|
|
else None
|
|
),
|
|
}
|
|
)
|
|
all_synced = all(bool(item.get("exists")) for item in statuses)
|
|
return {
|
|
"all_synced": all_synced,
|
|
"files": statuses,
|
|
}
|
|
|