feat(analytics): E0-lite 数据健康端点——数据截至+实体新鲜度
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
"""分析服务层——所有数据分析指标的口径收敛处(spec 2026-09-04 §2)。
|
||||
|
||||
指标函数是纯查询(只依赖 AsyncSession,无 HTTP/请求上下文),E2 报表订阅
|
||||
(二批)直接复用。口径速查表:spec §8;函数 docstring 与之同步。
|
||||
"""
|
||||
@@ -0,0 +1,48 @@
|
||||
"""E0-lite 数据健康——「数据截至」角标 + 实体新鲜度。
|
||||
|
||||
完整 E0(调度日志解析/成功率账本)二批;v1 用表新鲜度判定:
|
||||
msg_daily_stats 当日有行 = 4h 消息链路活着。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
|
||||
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)。"""
|
||||
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]:
|
||||
"""各实体最后更新时间(UPDATED_AT 列 MAX;message 用 fetched_at)。"""
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user