Files
company-ai-platform/app/modules/reports/services/enterprise.py
JiuContinent bf309ecdf7 ```
refactor(core,ai): 调整模块导入路径并移除废弃文件

- 修复 scheduler.py 中的导入路径错误,将 reports.service
  改为 reports.services
- 移除废弃的 app/core/background/task_queue.py 文件
- 移除废弃的 app/modules/ai_agent/adapters.py 文件
- 修复 ai_memory/service.py 中的导入路径错误,将
  events.service 改为 events.services
```
2026-07-09 18:50:52 +08:00

195 lines
8.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from datetime import date
from typing import Any
from app.core.constants import ActorValue
from app.modules.audit.constants import AuditAction, AuditSource, AuditTargetType
from app.modules.audit.schemas import AuditLogCreate
from app.modules.audit.service import AuditService
from app.modules.business.models import (
PerformanceMetric,
)
from app.modules.events.constants import (
EventAggregateType,
EventPayloadKey,
EventSource,
EventType,
)
from app.modules.events.services import EventService
from app.modules.reports.constants import (
EnterpriseAnalyticsKey,
LifecycleResponseKey,
LifecycleSection,
MetricKey,
ReportStatus,
ReportText,
ReportTitle,
)
from app.modules.reports.services.common import _json_safe, _money, _next_code, _rate
class ReportEnterpriseAnalyticsMixin:
def enterprise_analytics(
self,
project_code: str | None = None,
owner: str | None = None,
period_start: date | None = None,
period_end: date | None = None,
actor: str = ActorValue.API,
) -> dict[str, Any]:
"""Build V3 read-only finance, procurement, performance, and operations analytics."""
code = _next_code("ANALYTICS")
lifecycle = self.project_lifecycle_report(
project_code=project_code,
owner=owner,
period_start=period_start,
period_end=period_end,
include_ai=False,
actor=actor,
)
metrics = lifecycle[LifecycleResponseKey.METRICS]
projects = metrics[LifecycleSection.PROJECTS]
procurements = metrics[LifecycleSection.PROCUREMENTS]
expenses = metrics[LifecycleSection.EXPENSES]
funds = metrics[LifecycleSection.FUNDS]
tasks = metrics[LifecycleSection.TASKS]
risks = metrics[LifecycleSection.RISKS]
health = metrics[LifecycleSection.HEALTH]
finance = {
MetricKey.BUDGET_TOTAL: projects[MetricKey.BUDGET_TOTAL],
MetricKey.ACTUAL_TOTAL: projects[MetricKey.ACTUAL_TOTAL],
MetricKey.BUDGET_USAGE_RATE: projects[MetricKey.BUDGET_USAGE_RATE],
MetricKey.CURRENT_BALANCE_TOTAL: funds[MetricKey.CURRENT_BALANCE_TOTAL],
MetricKey.NET_POSITION: funds[MetricKey.NET_POSITION],
MetricKey.RISK_ACCOUNTS: funds[MetricKey.RISK_ACCOUNTS],
MetricKey.PAYMENT_EXPOSURE: (
procurements[MetricKey.ACTUAL_TOTAL] + expenses[MetricKey.AMOUNT_TOTAL]
),
}
procurement = {
MetricKey.TOTAL: procurements[MetricKey.TOTAL],
MetricKey.PENDING_APPROVAL: procurements[MetricKey.PENDING_APPROVAL],
MetricKey.PENDING_DELIVERY: procurements[MetricKey.PENDING_DELIVERY],
MetricKey.UNPAID: procurements[MetricKey.UNPAID],
MetricKey.EXPECTED_TOTAL: procurements[MetricKey.EXPECTED_TOTAL],
MetricKey.ACTUAL_TOTAL: procurements[MetricKey.ACTUAL_TOTAL],
MetricKey.DELIVERY_RISK: procurements[MetricKey.PENDING_DELIVERY],
}
performance = self._enterprise_performance_stats()
operations = {
MetricKey.READINESS_SCORE: health[MetricKey.SCORE],
MetricKey.LEVEL: health[MetricKey.LEVEL],
MetricKey.COMPLETION_RATE: tasks[MetricKey.COMPLETION_RATE],
MetricKey.OVERDUE_TASKS: risks[MetricKey.OVERDUE_TASKS],
MetricKey.DELAYED_PROJECTS: risks[MetricKey.DELAYED_PROJECTS],
MetricKey.OVER_BUDGET_PROJECTS: risks[MetricKey.OVER_BUDGET_PROJECTS],
MetricKey.OPEN_EVENTS: risks[MetricKey.OPEN_EVENTS],
MetricKey.HIGH_EVENTS: risks[MetricKey.HIGH_EVENTS],
}
recommendations = lifecycle[LifecycleResponseKey.RECOMMENDATIONS]
lines = self._enterprise_analytics_lines(
lifecycle[LifecycleResponseKey.FILTERS],
finance,
procurement,
performance,
operations,
recommendations,
)
report = _json_safe(
{
EnterpriseAnalyticsKey.CODE: code,
EnterpriseAnalyticsKey.TITLE: ReportTitle.ENTERPRISE_ANALYTICS,
EnterpriseAnalyticsKey.FILTERS: lifecycle[LifecycleResponseKey.FILTERS],
EnterpriseAnalyticsKey.FINANCE: finance,
EnterpriseAnalyticsKey.PROCUREMENT: procurement,
EnterpriseAnalyticsKey.PERFORMANCE: performance,
EnterpriseAnalyticsKey.OPERATIONS: operations,
EnterpriseAnalyticsKey.RECOMMENDATIONS: recommendations,
EnterpriseAnalyticsKey.LINES: lines,
EnterpriseAnalyticsKey.CONTENT: "\n".join(lines),
}
)
AuditService(self.db).log(
AuditLogCreate(
actor=actor,
source=AuditSource.REPORTS,
action=AuditAction.ENTERPRISE_ANALYTICS,
target_type=AuditTargetType.ENTERPRISE_ANALYTICS,
target_id=code,
response_payload=report,
)
)
EventService(self.db).emit(
event_type=EventType.ENTERPRISE_ANALYTICS_GENERATED,
source=EventSource.ANALYTICS,
aggregate_type=EventAggregateType.ENTERPRISE_ANALYTICS,
aggregate_id=code,
actor=actor,
payload={
EventPayloadKey.CODE: code,
EventPayloadKey.STATUS: ReportStatus.GENERATED,
},
idempotency_key=f"enterprise-analytics:{code}",
dispatch=True,
)
return report
def _enterprise_performance_stats(self) -> dict[str, Any]:
total = self._count(PerformanceMetric)
confirmed = self._count(PerformanceMetric, PerformanceMetric.confirmed_score.is_not(None))
return {
MetricKey.TOTAL: total,
MetricKey.CONFIRMED: confirmed,
MetricKey.CONFIRMED_RATE: _rate(confirmed, total),
MetricKey.AVERAGE_AUTO_SCORE: self._avg(PerformanceMetric.auto_score),
MetricKey.AVERAGE_CONFIRMED_SCORE: self._avg(PerformanceMetric.confirmed_score),
MetricKey.WEIGHT_TOTAL: self._sum(PerformanceMetric.weight),
MetricKey.BY_STATUS: self._group_counts(PerformanceMetric, PerformanceMetric.status),
}
def _enterprise_analytics_lines(
self,
filters: dict[str, Any],
finance: dict[str, Any],
procurement: dict[str, Any],
performance: dict[str, Any],
operations: 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
)
lines = [
f"- 范围:{scope}",
(
f"- 财务:预算 {_money(finance[MetricKey.BUDGET_TOTAL])}"
f"实际 {_money(finance[MetricKey.ACTUAL_TOTAL])}"
f"净头寸 {_money(finance[MetricKey.NET_POSITION])}"
f"支付暴露 {_money(finance[MetricKey.PAYMENT_EXPOSURE])}"
),
(
f"- 采购:总数 {procurement[MetricKey.TOTAL]}"
f"待审批 {procurement[MetricKey.PENDING_APPROVAL]}"
f"待交付 {procurement[MetricKey.PENDING_DELIVERY]}"
f"未付款 {procurement[MetricKey.UNPAID]}"
),
(
f"- 绩效:指标 {performance[MetricKey.TOTAL]}"
f"已确认 {performance[MetricKey.CONFIRMED]}"
f"确认率 {performance[MetricKey.CONFIRMED_RATE]}%"
f"平均自动分 {performance[MetricKey.AVERAGE_AUTO_SCORE]}"
),
(
f"- 运营:准备度 {operations[MetricKey.READINESS_SCORE]}"
f"任务完成率 {operations[MetricKey.COMPLETION_RATE]}%"
f"逾期任务 {operations[MetricKey.OVERDUE_TASKS]}"
f"打开风险 {operations[MetricKey.OPEN_EVENTS]}"
),
ReportText.ACTION_HEADER,
]
lines.extend(f" - {item}" for item in recommendations)
return lines