from datetime import datetime from sqlalchemy import JSON, Boolean, DateTime, Integer, String, Text, true from sqlalchemy.orm import Mapped, mapped_column from app.core.database import Base from app.core.utils.time import utc_now from app.modules.feishu.constants import ( FEISHU_INBOUND_MAX_ATTEMPTS, FeishuInboundStatus, ) class FeishuEventReceipt(Base): __tablename__ = "feishu_event_receipts" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) event_key: Mapped[str] = mapped_column(String(256), unique=True, index=True) source: Mapped[str] = mapped_column(String(64), index=True) event_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True) message_id: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True) event_type: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True) payload: Mapped[dict | None] = mapped_column(JSON, nullable=True) auto_reply: Mapped[bool] = mapped_column( Boolean, default=True, server_default=true(), ) status: Mapped[str] = mapped_column( String(32), default=FeishuInboundStatus.PENDING, server_default=FeishuInboundStatus.PENDING, index=True, ) attempt_count: Mapped[int] = mapped_column(Integer, default=0, server_default="0") max_attempts: Mapped[int] = mapped_column( Integer, default=FEISHU_INBOUND_MAX_ATTEMPTS, server_default=str(FEISHU_INBOUND_MAX_ATTEMPTS), ) last_error: Mapped[str | None] = mapped_column(Text, nullable=True) next_attempt_at: Mapped[datetime | None] = mapped_column( DateTime, nullable=True, index=True, ) locked_until: Mapped[datetime | None] = mapped_column( DateTime, nullable=True, index=True, ) locked_by: Mapped[str | None] = mapped_column( String(128), nullable=True, index=True, ) received_at: Mapped[datetime] = mapped_column(DateTime, default=utc_now, index=True) processed_at: Mapped[datetime | None] = mapped_column( DateTime, nullable=True, index=True, ) reply_payload: Mapped[dict | None] = mapped_column(JSON, nullable=True) reply_status: Mapped[str | None] = mapped_column( String(32), nullable=True, index=True, ) reply_attempt_count: Mapped[int] = mapped_column( Integer, default=0, server_default="0", ) reply_last_error: Mapped[str | None] = mapped_column(Text, nullable=True) reply_next_attempt_at: Mapped[datetime | None] = mapped_column( DateTime, nullable=True, index=True, ) reply_locked_until: Mapped[datetime | None] = mapped_column( DateTime, nullable=True, index=True, ) reply_locked_by: Mapped[str | None] = mapped_column( String(128), nullable=True, index=True, ) reply_sent_at: Mapped[datetime | None] = mapped_column( DateTime, nullable=True, index=True, ) class FeishuAppTicket(Base): __tablename__ = "feishu_app_tickets" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) app_id: Mapped[str] = mapped_column(String(128), unique=True, index=True) app_ticket: Mapped[str] = mapped_column(Text) received_at: Mapped[datetime] = mapped_column(DateTime, default=utc_now, index=True) updated_at: Mapped[datetime] = mapped_column( DateTime, default=utc_now, onupdate=utc_now, )