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, }