Files
company-ai-platform/app/modules/dashboard/service.py
JiuContinent 0cda45238a ```
feat: 添加AI记忆模块和事件调度系统

- 新增AI记忆模块,支持本地记忆召回和自动写入功能
- 实现事件调度系统,支持批量处理待定事件和重试机制
- 集成心跳监控机制,跟踪API、调度器和工作节点状态
- 扩展仪表板数据统计,包含AI记忆条目和心跳概要
- 添加企业运营分析报告功能,提供财务、采购等多维度分析
- 更新配置设置,增加事件调度和AI记忆相关参数
- 优化任务队列,添加事件分发任务类型
- 扩展审计日志,记录AI记忆操作和事件调度行为
- 实现领域事件模型,支持事件持久化和状态管理
- 添加观察性服务,监控系统组件健康状况
```
2026-07-09 17:26:19 +08:00

105 lines
4.7 KiB
Python

from typing import Any
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.modules.ai_memory.constants import AIMemoryStatus
from app.modules.ai_memory.models import AIMemoryEntry
from app.modules.audit.models import AuditLog
from app.modules.business.constants import DONE_STATUSES, PROJECT_CLOSED_STATUSES, StatusValue
from app.modules.business.models import (
LegacySyncRun,
Project,
ReportPushRun,
RiskEvent,
WorkReport,
WorkTask,
)
from app.modules.business.service import serialize_model
from app.modules.events.constants import EventStatus
from app.modules.events.models import DomainEvent
from app.modules.observability.constants import ObservabilityMetricKey
from app.modules.observability.service import ObservabilityService
from app.modules.reports.constants import ReportPushStatus
from app.modules.risk.service import RiskService
from app.modules.workflows.constants import WorkflowStatus
from app.modules.workflows.models import WorkflowInstance
class DashboardService:
"""Build lightweight operational dashboard data for V2/V3."""
def __init__(self, db: Session):
self.db = db
self.risks = RiskService(db)
def summary(self) -> dict[str, Any]:
active_projects = self._count(Project, Project.status.notin_(PROJECT_CLOSED_STATUSES))
open_tasks = self._count(WorkTask, WorkTask.status.notin_(DONE_STATUSES))
open_risk_events = self._count(RiskEvent, RiskEvent.status == StatusValue.OPEN)
unassigned_open_risks = self._count(
RiskEvent,
RiskEvent.status == StatusValue.OPEN,
RiskEvent.assigned_to.is_(None),
)
failed_push_runs = self._count(ReportPushRun, ReportPushRun.status == ReportPushStatus.FAILED)
pending_events = self._count(DomainEvent, DomainEvent.status == EventStatus.PENDING)
failed_events = self._count(DomainEvent, DomainEvent.status == EventStatus.FAILED)
running_workflows = self._count(
WorkflowInstance,
WorkflowInstance.status == WorkflowStatus.RUNNING,
)
failed_workflows = self._count(
WorkflowInstance,
WorkflowInstance.status == WorkflowStatus.FAILED,
)
active_ai_memory = self._count(AIMemoryEntry, AIMemoryEntry.status == AIMemoryStatus.ACTIVE)
heartbeat_summary = ObservabilityService(self.db).heartbeat_summary()
latest_reports = self.db.execute(
select(WorkReport).order_by(WorkReport.id.desc()).limit(5)
).scalars()
latest_push_runs = self.db.execute(
select(ReportPushRun).order_by(ReportPushRun.id.desc()).limit(10)
).scalars()
latest_sync_runs = self.db.execute(
select(LegacySyncRun).order_by(LegacySyncRun.id.desc()).limit(10)
).scalars()
latest_audit_logs = self.db.execute(
select(AuditLog).order_by(AuditLog.id.desc()).limit(10)
).scalars()
risk_summary = self.risks.summary()
return {
"metrics": {
"active_projects": active_projects,
"open_tasks": open_tasks,
"open_risk_events": open_risk_events,
"unassigned_open_risks": unassigned_open_risks,
"failed_push_runs": failed_push_runs,
"pending_events": pending_events,
"failed_events": failed_events,
"running_workflows": running_workflows,
"failed_workflows": failed_workflows,
"active_ai_memory": active_ai_memory,
"stale_heartbeats": heartbeat_summary[ObservabilityMetricKey.STALE],
"risk_level": risk_summary["risk_level"],
"risk_score": float(risk_summary["risk_score"]),
},
"risk_counts": {
"overdue_tasks": len(risk_summary["overdue_tasks"]),
"delayed_projects": len(risk_summary["delayed_projects"]),
"over_budget_projects": len(risk_summary["over_budget_projects"]),
"fund_risks": len(risk_summary["fund_risks"]),
"supplier_risks": len(risk_summary["supplier_risks"]),
},
"latest_reports": [serialize_model(item) for item in latest_reports],
"latest_push_runs": [serialize_model(item) for item in latest_push_runs],
"latest_sync_runs": [serialize_model(item) for item in latest_sync_runs],
"latest_audit_logs": [serialize_model(item) for item in latest_audit_logs],
}
def _count(self, model: type, *conditions: Any) -> int:
stmt = select(func.count()).select_from(model)
for condition in conditions:
stmt = stmt.where(condition)
return int(self.db.execute(stmt).scalar() or 0)