Files
selfrelease fad458b2a7 docs(uiux): UIUX 设计方案大改 + 5 份作业指导书对齐 + 开发任务文档
- UIUX 文档:填充 19 个缺口(多主体画像/健康度/AI+看板/增长域/洞察域/创始人端/OODA/助推/商密)
- UIUX 文档:插入 6 个新章节(十四~十九),旧章节重编号为二十~三十一,更新目录和交叉引用
- 作业指导书 x5:导航改为 6 域分组,新增 Context Bar/工作模式/Insight Rail/决策线程/多工作区等 UI 概念
- 新建 docs/2-task-uiux.md:50 个代码落地开发任务,按 P0-P6 分优先级 + 8 Sprint 规划
- 后端/前端:大量新增模型、路由、组件(来自之前 Phase 开发)
2026-07-19 11:53:38 +08:00

74 lines
2.4 KiB
Python

"""月报提交及时性追踪服务。
统计企业月报提交延迟天数和数据质量评分。
"""
from datetime import datetime, timezone
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.company import Company
from app.models.report import MonthlyReport
async def compute_timeliness(
db: AsyncSession,
tenant_id: str,
company_id: str | None = None,
) -> list[dict]:
"""计算月报提交及时性和数据质量评分。
返回每个企业每月的提交延迟天数和数据质量评分。
延迟天数 = 实际提交日期 - 应提交日期(每月 10 号)。
数据质量评分 = 结构化字段完整度(0-100)。
"""
query = (
select(MonthlyReport, Company)
.join(Company, MonthlyReport.company_id == Company.id)
.where(Company.tenant_id == tenant_id)
.where(MonthlyReport.status != "draft")
)
if company_id:
query = query.where(MonthlyReport.company_id == company_id)
result = await db.execute(query)
rows = result.all()
items: list[dict] = []
for report, company in rows:
if report.submitted_at is None:
continue
# 应提交日期:报告月份的下一个月 10 号
due_year = report.period_year
due_month = report.period_month + 1
if due_month > 12:
due_year += 1
due_month = 1
due_date = datetime(due_year, due_month, 10, tzinfo=timezone.utc)
delay_days = max(0, (report.submitted_at - due_date).days)
# 数据质量评分:结构化字段完整度
structured = report.structured_data or {}
expected_fields = [
"revenue", "cash_balance", "burn_rate", "runway_months",
"headcount", "key_metrics", "highlights", "concerns",
]
filled = sum(1 for f in expected_fields if structured.get(f) is not None)
quality_score = round(filled / len(expected_fields) * 100, 1)
items.append({
"company_id": str(company.id),
"company_name": company.name,
"period_year": report.period_year,
"period_month": report.period_month,
"submitted_at": report.submitted_at.isoformat(),
"delay_days": delay_days,
"quality_score": quality_score,
"status": report.status,
})
items.sort(key=lambda x: (x["company_name"], x["period_year"], x["period_month"]))
return items