feat: 添加飞书用户模块和订阅功能支持 - 新增feishu_users模块用于处理飞书用户身份验证和权限管理 - 新增subscriptions模块用于处理订阅相关功能 - 新增personalization模块用于个性化服务 - 在alembic迁移配置中注册新的模型模块 - 在API路由器中添加feishu_users和subscriptions路由 - 实现事件调度服务的改进,包括错误处理和状态更新优化 - 添加飞书命令处理的权限检查机制 - 实现飞书应用票据事件处理 - 改进审计日志记录功能 ```
115 lines
4.5 KiB
Python
115 lines
4.5 KiB
Python
from datetime import date
|
|
from typing import Any
|
|
|
|
|
|
from app.core.constants import ActorValue
|
|
from app.core.config import get_settings
|
|
from app.modules.reports.constants import (
|
|
LifecycleFilterKey,
|
|
LifecycleResponseKey,
|
|
LifecycleSection,
|
|
ReportTitle,
|
|
)
|
|
from app.modules.reports.services.common import _json_safe
|
|
|
|
|
|
class ReportLifecycleReportMixin:
|
|
def project_lifecycle_report(
|
|
self,
|
|
project_code: str | None = None,
|
|
owner: str | None = None,
|
|
period_start: date | None = None,
|
|
period_end: date | None = None,
|
|
include_ai: bool = False,
|
|
actor: str = ActorValue.API,
|
|
) -> dict[str, Any]:
|
|
"""Build a full lifecycle report for project progress, delivery, cost, and risk."""
|
|
|
|
filters = self._lifecycle_filters(project_code, owner, period_start, period_end)
|
|
project_conditions = filters[LifecycleSection.PROJECTS]
|
|
task_conditions = filters[LifecycleSection.TASKS]
|
|
procurement_conditions = filters[LifecycleSection.PROCUREMENTS]
|
|
expense_conditions = filters[LifecycleSection.EXPENSES]
|
|
attendance_conditions = filters[LifecycleSection.ATTENDANCE]
|
|
risk_conditions = filters[LifecycleSection.RISKS]
|
|
|
|
project_stats = self._lifecycle_project_stats(project_conditions)
|
|
task_stats = self._lifecycle_task_stats(task_conditions)
|
|
procurement_stats = self._lifecycle_procurement_stats(procurement_conditions)
|
|
expense_stats = self._lifecycle_expense_stats(expense_conditions)
|
|
fund_stats = self._lifecycle_fund_stats()
|
|
supplier_stats = self._lifecycle_supplier_stats()
|
|
attendance_stats = self._lifecycle_attendance_stats(attendance_conditions)
|
|
risk_stats = self._lifecycle_risk_stats(
|
|
project_conditions,
|
|
task_conditions,
|
|
risk_conditions,
|
|
)
|
|
include_global_risk = not any(
|
|
value is not None for value in (project_code, owner, period_start, period_end)
|
|
)
|
|
health = self._lifecycle_health(
|
|
project_stats,
|
|
task_stats,
|
|
risk_stats,
|
|
supplier_stats,
|
|
include_global_risk,
|
|
)
|
|
attention = self._lifecycle_attention(project_conditions, task_conditions)
|
|
recommendations = self._lifecycle_recommendations(
|
|
project_stats,
|
|
task_stats,
|
|
procurement_stats,
|
|
expense_stats,
|
|
fund_stats,
|
|
supplier_stats,
|
|
risk_stats,
|
|
include_global_risk,
|
|
)
|
|
finance = (
|
|
self.project_finance_needs_report(
|
|
project_code=project_code,
|
|
owner=owner,
|
|
as_of=period_end,
|
|
include_ai=False,
|
|
actor=actor,
|
|
)
|
|
if get_settings().finance_needs_enabled
|
|
else None
|
|
)
|
|
|
|
metrics = {
|
|
LifecycleSection.HEALTH: health,
|
|
LifecycleSection.PROJECTS: project_stats,
|
|
LifecycleSection.TASKS: task_stats,
|
|
LifecycleSection.PROCUREMENTS: procurement_stats,
|
|
LifecycleSection.EXPENSES: expense_stats,
|
|
LifecycleSection.FUNDS: fund_stats,
|
|
LifecycleSection.SUPPLIERS: supplier_stats,
|
|
LifecycleSection.ATTENDANCE: attendance_stats,
|
|
LifecycleSection.RISKS: risk_stats,
|
|
LifecycleSection.FINANCE: finance["summary"] if finance else {"status": "未启用"},
|
|
}
|
|
lines = self._lifecycle_lines(
|
|
filters[LifecycleFilterKey.LABELS],
|
|
metrics,
|
|
recommendations,
|
|
)
|
|
if finance:
|
|
lines.extend(finance[LifecycleResponseKey.LINES])
|
|
report = {
|
|
LifecycleResponseKey.TITLE: ReportTitle.PROJECT_LIFECYCLE,
|
|
LifecycleResponseKey.FILTERS: filters[LifecycleFilterKey.LABELS],
|
|
LifecycleResponseKey.METRICS: _json_safe(metrics),
|
|
LifecycleResponseKey.ATTENTION: _json_safe(attention),
|
|
LifecycleResponseKey.RECOMMENDATIONS: recommendations,
|
|
LifecycleResponseKey.LINES: lines,
|
|
LifecycleResponseKey.CONTENT: "\n".join(lines),
|
|
"source_lifecycle": self.source_project_lifecycle_summary(project_code, owner),
|
|
"finance": finance,
|
|
"finance_chart_data": finance["finance_chart_data"] if finance else None,
|
|
}
|
|
if include_ai:
|
|
report[LifecycleResponseKey.AI_ANALYSIS] = self._lifecycle_ai_analysis(report, actor)
|
|
return report
|