"""月报提交及时性追踪服务。 统计企业月报提交延迟天数和数据质量评分。 """ 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