Files
hunjing/backend/app/repositories/runtime.py
T

127 lines
4.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 sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.models.runtime import Event, Run
async def get_run(session: AsyncSession, run_id: str) -> Run | None:
return await session.get(Run, run_id)
async def list_runs(
session: AsyncSession,
*,
character_id: str | None = None,
status: str | None = None,
page: int = 1,
page_size: int = 20,
) -> tuple[list[Run], int]:
"""按创建时间倒序分页返回 Run;可选按人物/状态过滤,返回 (条目, 总数)。"""
conds = []
if character_id is not None:
conds.append(Run.character_id == character_id)
if status is not None:
conds.append(Run.status == status)
count_stmt = select(func.count()).select_from(Run)
list_stmt = select(Run)
if conds:
count_stmt = count_stmt.where(*conds)
list_stmt = list_stmt.where(*conds)
total = int((await session.execute(count_stmt)).scalar_one())
result = await session.execute(
list_stmt.order_by(Run.created_at.desc()).offset((page - 1) * page_size).limit(page_size)
)
return list(result.scalars().all()), total
async def count_events(session: AsyncSession, run_id: str) -> int:
result = await session.execute(
select(func.count()).select_from(Event).where(Event.run_id == run_id)
)
return int(result.scalar_one())
async def next_event_sequence(session: AsyncSession, run_id: str) -> int:
result = await session.execute(select(func.max(Event.sequence)).where(Event.run_id == run_id))
current = result.scalar_one_or_none()
return int(current or 0) + 1
async def get_event(session: AsyncSession, event_id: str) -> Event | None:
return await session.get(Event, event_id)
async def has_active_runs(
session: AsyncSession,
character_id: str,
) -> bool:
"""检查指定角色是否有活跃的进行中 run(大周天任务中断防护用)。"""
result = await session.execute(
select(func.count()).select_from(Run).where(
Run.character_id == character_id,
Run.status == "running",
)
)
return int(result.scalar_one()) > 0
async def list_events_by_run(
session: AsyncSession,
run_id: str,
*,
visibility: str | None = None,
limit: int = 200,
offset: int = 0,
) -> tuple[list[Event], int]:
"""按 run_id 查询事件,按 sequence 升序;可选 visibility 过滤。返回 (条目, 总数)。"""
conditions = [Event.run_id == run_id]
if visibility is not None:
conditions.append(Event.visibility == visibility)
total = int(
(
await session.execute(select(func.count()).select_from(Event).where(*conditions))
).scalar_one()
)
result = await session.execute(
select(Event).where(*conditions).order_by(Event.sequence.asc()).offset(offset).limit(limit)
)
return list(result.scalars().all()), total
async def list_public_events_after(
session: AsyncSession, run_id: str, *, after_sequence: int = 0, limit: int = 100
) -> list[Event]:
result = await session.execute(
select(Event)
.where(
Event.run_id == run_id,
Event.visibility == "public",
Event.sequence > after_sequence,
)
.order_by(Event.sequence.asc())
.limit(limit)
)
return list(result.scalars().all())
async def list_events_by_trace(session: AsyncSession, trace_id: str) -> list[Event]:
"""链路追踪:按 trace_id 查询运行事件,按 created_at 升序(与审计/幂等归并排序)。"""
result = await session.execute(
select(Event).where(Event.trace_id == trace_id).order_by(Event.created_at.asc())
)
return list(result.scalars().all())
async def summarize_by_trace(session: AsyncSession):
"""GROUP BY trace_id:(trace_id, 计数, 最近时间),仅 trace_id 非空。供链路追踪列表聚合。"""
result = await session.execute(
select(Event.trace_id, func.count(), func.max(Event.created_at))
.where(Event.trace_id.is_not(None))
.group_by(Event.trace_id)
)
return [(row[0], int(row[1]), row[2]) for row in result.all()]