"""多渠道网关 ORM 模型 - 渠道用户映射、会话映射、消息记录""" from typing import Any from sqlalchemy import ( BigInteger, Column, DateTime, Index, Integer, String, UniqueConstraint, ) from sqlalchemy.dialects.postgresql import JSONB from yuxi.storage.postgres.models_business import Base from yuxi.utils.datetime_utils import format_utc_datetime, utc_now_naive class ChannelUserMapping(Base): """渠道用户映射表 - 渠道用户与内部用户的绑定关系""" __tablename__ = "channels_user_mappings" id = Column(BigInteger, primary_key=True, autoincrement=True) channel_id = Column(String(32), nullable=False) channel_user_id = Column(String(128), nullable=False) internal_user_id = Column(String(64), nullable=False) created_at = Column(DateTime, nullable=False, default=utc_now_naive) updated_at = Column(DateTime, nullable=False, default=utc_now_naive, onupdate=utc_now_naive) __table_args__ = (UniqueConstraint("channel_id", "channel_user_id", name="uq_channel_user"),) def to_dict(self) -> dict[str, Any]: return { "id": self.id, "channel_id": self.channel_id, "channel_user_id": self.channel_user_id, "internal_user_id": self.internal_user_id, "created_at": format_utc_datetime(self.created_at), "updated_at": format_utc_datetime(self.updated_at), } class ChannelThreadMapping(Base): """渠道会话映射表 - 渠道会话与 LangGraph thread_id 的绑定关系""" __tablename__ = "channels_thread_mappings" id = Column(BigInteger, primary_key=True, autoincrement=True) channel_id = Column(String(32), nullable=False) channel_chat_id = Column(String(128), nullable=False) internal_user_id = Column(String(64), nullable=False) thread_id = Column(String(64), nullable=False) agent_id = Column(String(64), nullable=True) last_active_at = Column(DateTime, nullable=False, default=utc_now_naive) created_at = Column(DateTime, nullable=False, default=utc_now_naive) updated_at = Column(DateTime, nullable=False, default=utc_now_naive, onupdate=utc_now_naive) __table_args__ = ( UniqueConstraint( "channel_id", "channel_chat_id", "internal_user_id", name="uq_channel_thread", ), ) def to_dict(self) -> dict[str, Any]: return { "id": self.id, "channel_id": self.channel_id, "channel_chat_id": self.channel_chat_id, "internal_user_id": self.internal_user_id, "thread_id": self.thread_id, "agent_id": self.agent_id, "last_active_at": format_utc_datetime(self.last_active_at), "created_at": format_utc_datetime(self.created_at), "updated_at": format_utc_datetime(self.updated_at), } class ChannelMsgRecord(Base): """消息记录表 - 渠道消息的完整处理生命周期""" __tablename__ = "channels_msg_records" id = Column(BigInteger, primary_key=True, autoincrement=True) channel_id = Column(String(32), nullable=False) channel_type = Column(String(32), nullable=False) message_id = Column(String(128), nullable=False) chat_id = Column(String(128), nullable=False) chat_type = Column(String(32), nullable=False, default="direct") content_type = Column(String(32), nullable=False, default="text") sender_user_id = Column(String(128), nullable=False) content_preview = Column(String(500), nullable=False) reply_to_message_id = Column(String(128), nullable=True) agent_config_id = Column(Integer, nullable=True) status = Column(String(32), nullable=False, default="processing") error_message = Column(String(500), nullable=True) response_time_ms = Column(Integer, nullable=True) reply_message_id = Column(String(128), nullable=True) reply_content_preview = Column(String(500), nullable=True) created_at = Column(DateTime, nullable=False, default=utc_now_naive) replied_at = Column(DateTime, nullable=True) extra_metadata = Column("metadata", JSONB, nullable=True) __table_args__ = ( Index("idx_msg_records_channel_status", "channel_id", "status"), Index("idx_msg_records_created_at", "created_at"), Index( "idx_msg_records_stuck", "channel_id", "created_at", postgresql_where=(status == "processing"), ), Index("idx_msg_records_chat", "channel_id", "chat_id"), ) def to_dict(self) -> dict[str, Any]: return { "id": self.id, "channel_id": self.channel_id, "channel_type": self.channel_type, "message_id": self.message_id, "chat_id": self.chat_id, "chat_type": self.chat_type, "content_type": self.content_type, "sender_user_id": self.sender_user_id, "content_preview": self.content_preview, "reply_to_message_id": self.reply_to_message_id, "agent_config_id": self.agent_config_id, "status": self.status, "error_message": self.error_message, "response_time_ms": self.response_time_ms, "reply_message_id": self.reply_message_id, "reply_content_preview": self.reply_content_preview, "created_at": format_utc_datetime(self.created_at), "replied_at": format_utc_datetime(self.replied_at), "metadata": self.extra_metadata or {}, } class ChannelPolicyConfig(Base): """渠道策略配置表""" __tablename__ = "channels_policy_configs" id = Column(BigInteger, primary_key=True, autoincrement=True) channel_id = Column(String(32), nullable=False, unique=True) group_chat_mode = Column(String(16), nullable=False, default="all") whitelist_ids = Column(JSONB, nullable=False, default=list) welcome_message = Column(String(500), nullable=True) work_hours_start = Column(String(5), nullable=True, default="09:00") work_hours_end = Column(String(5), nullable=True, default="18:00") timezone_offset = Column(Integer, nullable=False, default=8) off_hours_reply = Column(String(500), nullable=True) created_at = Column(DateTime, nullable=False, default=utc_now_naive) updated_at = Column(DateTime, nullable=False, default=utc_now_naive, onupdate=utc_now_naive) def to_dict(self) -> dict[str, Any]: return { "id": self.id, "channel_id": self.channel_id, "group_chat_mode": self.group_chat_mode, "whitelist_ids": self.whitelist_ids or [], "welcome_message": self.welcome_message, "work_hours_start": self.work_hours_start, "work_hours_end": self.work_hours_end, "timezone_offset": self.timezone_offset, "off_hours_reply": self.off_hours_reply, "created_at": format_utc_datetime(self.created_at), "updated_at": format_utc_datetime(self.updated_at), } class ChannelRoutingRule(Base): """渠道 Agent 路由规则表""" __tablename__ = "channels_routing_rules" id = Column(BigInteger, primary_key=True, autoincrement=True) channel_id = Column(String(32), nullable=False) command = Column(String(64), nullable=False) agent_config_id = Column(Integer, nullable=False) priority = Column(Integer, nullable=False, default=0) created_at = Column(DateTime, nullable=False, default=utc_now_naive) updated_at = Column(DateTime, nullable=False, default=utc_now_naive, onupdate=utc_now_naive) __table_args__ = ( Index("idx_routing_rules_channel", "channel_id"), UniqueConstraint("channel_id", "command", name="uq_channel_command"), ) def to_dict(self) -> dict[str, Any]: return { "id": self.id, "channel_id": self.channel_id, "command": self.command, "agent_config_id": self.agent_config_id, "priority": self.priority, "created_at": format_utc_datetime(self.created_at), "updated_at": format_utc_datetime(self.updated_at), }