from typing import Any from app.modules.reports.constants import ( LifecycleResponseKey, LifecycleSection, MetricKey, ReportText, ) from app.modules.reports.services.common import _json_safe, _money class ReportLifecycleRenderingMixin: def _lifecycle_lines( self, filters: dict[str, Any], metrics: dict[str, Any], recommendations: list[str], ) -> list[str]: scope = ( "、".join(f"{key}={value}" for key, value in filters.items() if value) or ReportText.DEFAULT_SCOPE ) projects = metrics[LifecycleSection.PROJECTS] tasks = metrics[LifecycleSection.TASKS] procurements = metrics[LifecycleSection.PROCUREMENTS] expenses = metrics[LifecycleSection.EXPENSES] funds = metrics[LifecycleSection.FUNDS] suppliers = metrics[LifecycleSection.SUPPLIERS] attendance = metrics[LifecycleSection.ATTENDANCE] risks = metrics[LifecycleSection.RISKS] health = metrics[LifecycleSection.HEALTH] lines = [ f"- 范围:{scope}", f"- 生命周期健康分:{health[MetricKey.SCORE]}({health[MetricKey.LEVEL]})", ( f"- 项目:总数 {projects[MetricKey.TOTAL]}," f"活跃 {projects[MetricKey.ACTIVE]}," f"平均进度 {projects[MetricKey.AVERAGE_PROGRESS_PERCENT]}%" ), ( f"- 成本:预算 {_money(projects[MetricKey.BUDGET_TOTAL])}," f"实际 {_money(projects[MetricKey.ACTUAL_TOTAL])}," f"预算使用率 {projects[MetricKey.BUDGET_USAGE_RATE]}%" ), ( f"- 任务:总数 {tasks[MetricKey.TOTAL]}," f"完成 {tasks[MetricKey.COMPLETED]}," f"完成率 {tasks[MetricKey.COMPLETION_RATE]}%," f"逾期 {tasks[MetricKey.OVERDUE]}" ), ( f"- 采购/费用:待批采购 {procurements[MetricKey.PENDING_APPROVAL]}," f"待批费用 {expenses[MetricKey.PENDING_APPROVAL]}," f"未付款采购 {procurements[MetricKey.UNPAID]}" ), ( f"- 资金:余额 {_money(funds[MetricKey.CURRENT_BALANCE_TOTAL])}," f"净头寸 {_money(funds[MetricKey.NET_POSITION])}," f"风险账户 {funds[MetricKey.RISK_ACCOUNTS]}" ), ( f"- 风险:等级 {risks[MetricKey.RISK_LEVEL]}," f"风险分 {risks[MetricKey.RISK_SCORE]}," f"延期项目 {risks[MetricKey.DELAYED_PROJECTS]}," f"超预算项目 {risks[MetricKey.OVER_BUDGET_PROJECTS]}," f"打开事件 {risks[MetricKey.OPEN_EVENTS]}" ), ( f"- 供应商/考勤:风险供应商 {suppliers[MetricKey.RISKY]}," f"异常打卡 {attendance[MetricKey.ABNORMAL]}," f"异常率 {attendance[MetricKey.ABNORMAL_RATE]}%" ), ReportText.ACTION_HEADER, ] lines.extend(f" - {item}" for item in recommendations) return lines def _lifecycle_ai_analysis(self, report: dict[str, Any], actor: str) -> dict[str, Any]: try: from app.modules.ai_agent.constants import AIResponseKey from app.modules.ai_agent.skills import AISkillId from app.modules.ai_agent.service import AIService report_snapshot = _json_safe(report) result = AIService(self.db).run_skill( AISkillId.PROJECT_LIFECYCLE_ANALYSIS, context={LifecycleResponseKey.PROJECT_LIFECYCLE_REPORT: report_snapshot}, actor=actor, ) except Exception as exc: return { AIResponseKey.OK: False, AIResponseKey.ERROR: str(exc), AIResponseKey.TYPE: type(exc).__name__, } return _json_safe({AIResponseKey.OK: True, **result})