"""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()]