CI / lint / typecheck / import-linter / tests / secrets (push) Canceled after 0s
- agent: ag_conversation/ag_message 表(迁移0007),上下文组装注入最近报告画像摘要 (report.profile.latest_profile_summary 经 apps 层闭包注入,同层不互调),历史带最近 AGENT_HISTORY_TURNS=10 轮;POST/GET /agent/conversations 与消息历史接口 - agent 修复:ChatRequest.conversation_id 改 str(雪花ID超2^53,JS Number丢精度); _build_context 挪进 reserve commit 后同一 try,异常走 refund 兜底 - report: rp_feedback(维度反馈 upsert,认同率/修正率 SQL 可算,T-P3-21 最小版—— L1 记忆未建,不回写结论)+ rp_reservation(预约留资幂等,T-P3-22,预约率口径 行数/full_report.card_view 去重曝光,门槛≥8%),迁移0008 - 测试:全量 606 passed;ruff/mypy/lint-imports 全绿;E2E 真实 deepseek-v4-flash 验证 画像注入与历史携带
100 lines
3.2 KiB
Python
100 lines
3.2 KiB
Python
"""rp_report / rp_input / rp_feedback / rp_reservation 的数据访问。
|
||
|
||
**只有本模块可以触这四张表**(rp_share 归后续任务)。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from sqlalchemy import select
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from packages.shared.models import RpFeedback, RpInput, RpReport, RpReservation
|
||
|
||
STATUS_SUCCEEDED = "succeeded"
|
||
TIER_BASIC = "basic"
|
||
|
||
|
||
async def get_report(session: AsyncSession, report_id: int) -> RpReport | None:
|
||
return await session.get(RpReport, report_id)
|
||
|
||
|
||
async def find_succeeded_basic(session: AsyncSession, *, user_id: int) -> int | None:
|
||
"""首次终身资格:该用户是否已有 succeeded 的 basic 报告(需求 §2.1)。
|
||
|
||
只取 report_id 不取整行——资格判定不需要报告内容。
|
||
"""
|
||
result = await session.execute(
|
||
select(RpReport.report_id)
|
||
.where(
|
||
RpReport.user_id == user_id,
|
||
RpReport.tier == TIER_BASIC,
|
||
RpReport.status == STATUS_SUCCEEDED,
|
||
)
|
||
.limit(1)
|
||
)
|
||
return result.scalar_one_or_none()
|
||
|
||
|
||
async def list_reports(session: AsyncSession, *, user_id: int, limit: int = 20) -> list[RpReport]:
|
||
result = await session.execute(
|
||
select(RpReport)
|
||
.where(RpReport.user_id == user_id)
|
||
.order_by(RpReport.created_at.desc(), RpReport.report_id.desc())
|
||
.limit(limit)
|
||
)
|
||
return list(result.scalars().all())
|
||
|
||
|
||
async def get_input(session: AsyncSession, report_id: int) -> RpInput | None:
|
||
result = await session.execute(select(RpInput).where(RpInput.report_id == report_id))
|
||
return result.scalar_one_or_none()
|
||
|
||
|
||
# -- rp_feedback(T-P3-21:同维度覆盖更新,upsert 语义) -----------------------
|
||
|
||
|
||
async def get_feedback(
|
||
session: AsyncSession, *, report_id: int, dimension_key: str
|
||
) -> RpFeedback | None:
|
||
result = await session.execute(
|
||
select(RpFeedback).where(
|
||
RpFeedback.report_id == report_id,
|
||
RpFeedback.dimension_key == dimension_key,
|
||
)
|
||
)
|
||
return result.scalar_one_or_none()
|
||
|
||
|
||
async def list_feedback(session: AsyncSession, *, report_id: int) -> list[RpFeedback]:
|
||
result = await session.execute(
|
||
select(RpFeedback)
|
||
.where(RpFeedback.report_id == report_id)
|
||
.order_by(RpFeedback.created_at.asc(), RpFeedback.feedback_id.asc())
|
||
)
|
||
return list(result.scalars().all())
|
||
|
||
|
||
# -- rp_reservation(T-P3-22:重复预约幂等) ------------------------------------
|
||
|
||
|
||
async def get_reservation(
|
||
session: AsyncSession, *, user_id: int, topic: str
|
||
) -> RpReservation | None:
|
||
result = await session.execute(
|
||
select(RpReservation).where(
|
||
RpReservation.user_id == user_id,
|
||
RpReservation.topic == topic,
|
||
)
|
||
)
|
||
return result.scalar_one_or_none()
|
||
|
||
|
||
async def list_reservation_topics(
|
||
session: AsyncSession, *, user_id: int, topic: str | None = None
|
||
) -> list[str]:
|
||
stmt = select(RpReservation.topic).where(RpReservation.user_id == user_id)
|
||
if topic is not None:
|
||
stmt = stmt.where(RpReservation.topic == topic)
|
||
result = await session.execute(stmt.order_by(RpReservation.topic.asc()))
|
||
return [str(row) for row in result.scalars().all()]
|