Files
yunke/yunke-server/app/analytics/health.py
T

58 lines
1.8 KiB
Python

"""E0-lite 数据健康——「数据截至」角标 + 实体新鲜度。
完整 E0(调度日志解析/成功率账本)二批;v1 用表新鲜度判定:
msg_daily_stats 当日有行 = 4h 消息链路活着。
"""
from __future__ import annotations
import datetime as dt
from typing import Any
import sqlalchemy as sa
from sqlalchemy.ext.asyncio import AsyncSession
from app.db.models import (
ChatGroup,
Customer,
Media,
Message,
MomentsPost,
MsgDailyStats,
PhoneCall,
)
async def data_as_of(session: AsyncSession) -> int | None:
"""数据截至(ms-epoch)= MAX(msg_daily_stats.last_ts)。
漏迁移导致表缺失时按 500 响亮报错是有意的——本模块是诊断端点,
要暴露部署问题而非掩盖(0ade58a 的空表静默守卫是业务端点的容错,
别好心搬到这里)。
"""
val = (
await session.execute(sa.select(sa.func.max(MsgDailyStats.last_ts)))
).scalar_one_or_none()
return int(val) if val is not None else None
async def entity_freshness(session: AsyncSession) -> list[dict[str, Any]]:
"""各实体最后更新时间(UPDATED_AT 列 MAX;message 用 fetched_at)。
同 data_as_of:表缺失按 500 响亮报错是有意的(诊断端点不容错)。
"""
async def _max(col) -> dt.datetime | None:
return (await session.execute(sa.select(sa.func.max(col)))).scalar_one_or_none()
out = []
for name, col in (
("customer", Customer.updated_at),
("chat_group", ChatGroup.updated_at),
("phone_call", PhoneCall.updated_at),
("media", Media.updated_at),
("moments_post", MomentsPost.updated_at),
("message", Message.fetched_at),
):
out.append({"table": name, "last_seen": await _max(col)})
return out