from typing import Optional from sqlalchemy import Column, String, Text, Float, DateTime, Integer, Boolean, func, ForeignKey, JSON, UniqueConstraint, BigInteger, Numeric, SmallInteger, Date, Index, Time, text from sqlalchemy.orm import DeclarativeBase, mapped_column, Mapped from datetime import date, time, datetime class Base(DeclarativeBase): pass class DeviceConfig(Base): __tablename__ = "device_configs" id = Column(Integer, primary_key=True, autoincrement=True) device_id = Column(String(64), unique=True, index=True, nullable=False) selected_role_key = Column(String(64), nullable=False) preferred_language = Column(String(10), nullable=True) volume = Column(Integer, nullable=True) last_update_time = Column(Float, nullable=False, default=lambda: datetime.now().timestamp()) created_at = Column(DateTime, nullable=False, server_default=func.now()) updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now()) def to_dict(self): """将配置转换为可序列化的字典""" return { "selected_role_key": self.selected_role_key, "preferred_language": self.preferred_language, "volume": self.volume if hasattr(self, 'volume') else None, } class ConversationHistory(Base): __tablename__ = "conversation_histories" id = Column(Integer, primary_key=True, autoincrement=True) device_id = Column(String(64), nullable=False, index=True) role_key = Column(String(64), nullable=False, index=True) last_interaction_time = Column(Float, nullable=False, default=lambda: datetime.now().timestamp()) created_at = Column(DateTime, nullable=False, server_default=func.now()) updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now()) __table_args__ = ( {'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'} ) class ConversationMessage(Base): __tablename__ = "conversation_messages" id = Column(Integer, primary_key=True, autoincrement=True) conversation_id = Column(Integer, ForeignKey("conversation_histories.id", ondelete="CASCADE"), nullable=False, index=True) is_user = Column(Boolean, nullable=False, default=False) # True表示用户消息,False表示助手消息 content = Column(Text, nullable=False) timestamp = Column(Float, nullable=False, default=lambda: datetime.now().timestamp()) created_at = Column(DateTime, nullable=False, server_default=func.now()) updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now()) __table_args__ = ( {'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'} ) class Role(Base): __tablename__ = "roles" id = Column(Integer, primary_key=True, autoincrement=True) role_key = Column(String(64), unique=True, nullable=False, index=True) name = Column(String(128), nullable=False) description = Column(Text, nullable=True) content = Column(Text, nullable=False) default_language = Column(String(10), nullable=True) volcano_model_id = Column(String(64), nullable=True) minimax_voice_id = Column(String(64), nullable=True) url = Column(String(255), nullable=True) homophones = Column(JSON, nullable=True) enabled = Column(Boolean, default=True, nullable=False) created_at = Column(DateTime, nullable=False, server_default=func.now()) updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now()) __table_args__ = ( {'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'} ) class RoleLanguage(Base): __tablename__ = "role_languages" id = Column(Integer, primary_key=True, autoincrement=True) role_id = Column(Integer, ForeignKey("roles.id", ondelete="CASCADE"), nullable=False) language_code = Column(String(10), nullable=False) name = Column(String(128), nullable=True) content = Column(Text, nullable=True) minimax_voice_id = Column(String(64), nullable=True) url = Column(String(255), nullable=True) created_at = Column(DateTime, nullable=False, server_default=func.now()) updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now()) __table_args__ = ( UniqueConstraint('role_id', 'language_code', name='uix_role_language'), {'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'} ) class DeviceAuth(Base): __tablename__ = "device_auth" id = Column(Integer, primary_key=True, autoincrement=True) device_id = Column(String(64), nullable=False, unique=True, index=True) serial_number = Column(String(64), nullable=False) batch_id = Column(String(20), nullable=True, index=True) is_active = Column(Boolean, default=True, nullable=False) created_at = Column(DateTime, nullable=False, server_default=func.now()) updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now()) __table_args__ = ( {'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'} ) class DeviceImeiMapping(Base): __tablename__ = "device_imei_mapping" id = Column(Integer, primary_key=True, autoincrement=True) imei = Column(String(64), nullable=False, unique=True, index=True) device_id = Column(String(64), nullable=False, unique=True, index=True) serial_number = Column(String(64), nullable=False) status = Column(String(32), nullable=False, server_default=text("'pending'")) activated_at = Column(DateTime, nullable=True) created_at = Column(DateTime, nullable=False, server_default=func.now()) updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now()) __table_args__ = ( Index("idx_device_imei_mapping_status", "status"), {'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'} ) class DeviceFirmwareUpdate(Base): __tablename__ = "device_firmware_update" id = Column(Integer, primary_key=True, autoincrement=True) device_id = Column(String(64), nullable=False, unique=True, index=True) serial_number = Column(String(64), nullable=False, default="") mac_address = Column(String(512), nullable=True) firmware_version = Column(String(64), nullable=False) ota_channel = Column(String(32), nullable=True) target_version = Column(String(64), nullable=True) firmware_url = Column(Text, nullable=True) firmware_id = Column(Integer, nullable=True) source = Column(String(32), nullable=True) update_status = Column(String(32), nullable=False, default="success") # 更新状态,如 updating/success/failed created_at = Column(DateTime, nullable=False, server_default=func.now()) updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now()) progress = Column(Float, nullable=True, default=0.0) # 更新进度 __table_args__ = ( {'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'} ) class DeviceOtaChannel(Base): __tablename__ = "device_ota_channels" id = Column(Integer, primary_key=True, autoincrement=True) channel_key = Column(String(32), unique=True, nullable=False, index=True) name = Column(String(64), nullable=False) device_type = Column(Integer, nullable=True, index=True) enabled = Column(Boolean, nullable=False, default=True) created_at = Column(DateTime, nullable=False, server_default=func.now()) updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now()) __table_args__ = ( {'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'} ) class DeviceOtaFirmware(Base): __tablename__ = "device_ota_firmwares" id = Column(Integer, primary_key=True, autoincrement=True) channel_key = Column(String(32), nullable=False, index=True) version = Column(String(64), nullable=False) firmware_url = Column(Text, nullable=False) checksum = Column(String(128), nullable=True) file_size = Column(BigInteger, nullable=True) release_status = Column(String(32), nullable=False, default="draft", index=True) release_channel = Column(String(32), nullable=False, default="stable") is_latest = Column(Boolean, nullable=False, default=False, index=True) notes = Column(Text, nullable=True) created_at = Column(DateTime, nullable=False, server_default=func.now()) published_at = Column(DateTime, nullable=True) updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now()) __table_args__ = ( UniqueConstraint("channel_key", "version", name="uq_device_ota_firmware_channel_version"), Index("idx_device_ota_firmware_latest", "channel_key", "release_status", "is_latest"), {'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'} ) class SystemConfig(Base): __tablename__ = "system_config" id = Column(Integer, primary_key=True, autoincrement=True) config_key = Column(String(128), unique=True, nullable=False, index=True) config_value = Column(Text, nullable=True) created_at = Column(DateTime, nullable=False, server_default=func.now()) updated_at = Column(DateTime, nullable=False, server_default=func.now(), onupdate=func.now()) __table_args__ = ( {'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'} ) class Card(Base): __tablename__ = "cards" card_id = Column(Integer, primary_key=True, autoincrement=True) card_uuid = Column(String(64), unique=True, nullable=False) device_id = Column(String(64), index=True) card_name = Column(String(64)) status = Column(Integer, server_default="0") total_swaps = Column(Integer, server_default="0") created_at = Column(DateTime, server_default=func.now()) updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now()) __table_args__ = ( {'mysql_charset': 'utf8mb4', 'mysql_collate': 'utf8mb4_unicode_ci'} ) class Parent(Base): __tablename__ = "parents" user_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) openid: Mapped[str] = mapped_column(String(64), unique=True, nullable=False) unionid: Mapped[Optional[str]] = mapped_column(String(64)) nickname: Mapped[Optional[str]] = mapped_column(String(64)) avatar_url: Mapped[Optional[str]] = mapped_column(String(255)) avatar_file_key: Mapped[Optional[str]] = mapped_column(String(255)) phone: Mapped[Optional[str]] = mapped_column(String(20)) status: Mapped[int] = mapped_column(Integer, server_default="1") created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) class Child(Base): __tablename__ = "children" child_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) child_name: Mapped[str] = mapped_column(String(32), nullable=False) child_gender: Mapped[int] = mapped_column(Integer, server_default="2") child_birthday: Mapped[Optional[date]] = mapped_column(Date) status: Mapped[int] = mapped_column(Integer, server_default="1") created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) class ParentChildRelation(Base): __tablename__ = "parent_child_relations" __table_args__ = ( UniqueConstraint("user_id", "child_id", name="uq_user_child"), Index("idx_pcr_user_id", "user_id"), Index("idx_pcr_child_id", "child_id"), ) id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) user_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True) child_id: Mapped[int] = mapped_column(BigInteger, nullable=False, index=True) relation_type: Mapped[int] = mapped_column(Integer, server_default="9") is_primary: Mapped[bool] = mapped_column(Boolean, server_default="0") status: Mapped[int] = mapped_column(Integer, server_default="1") created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) class DeviceBinding(Base): __tablename__ = "device_bindings" __table_args__ = ( UniqueConstraint("device_id", name="uq_device_binding_device"), UniqueConstraint("child_id", name="uq_device_binding_child"), Index("idx_device_bindings_owner_user_id", "owner_user_id"), ) id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) device_id: Mapped[str] = mapped_column(String(64), nullable=False, unique=True) owner_user_id: Mapped[int] = mapped_column(BigInteger, nullable=False) child_id: Mapped[Optional[int]] = mapped_column(BigInteger) status: Mapped[int] = mapped_column(Integer, server_default="1") bound_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) unbound_at: Mapped[Optional[datetime]] = mapped_column(DateTime) created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) class DeviceFamilyMember(Base): __tablename__ = "device_family_members" __table_args__ = ( UniqueConstraint("device_id", "user_id", name="uq_device_family_member"), Index("idx_device_family_members_device_status", "device_id", "status"), Index("idx_device_family_members_user_status", "user_id", "status"), ) id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) device_id: Mapped[str] = mapped_column(String(64), ForeignKey("device_auth.device_id"), nullable=False) user_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("parents.user_id"), nullable=False) role: Mapped[int] = mapped_column(Integer, server_default="2") display_name: Mapped[Optional[str]] = mapped_column(String(64)) status: Mapped[int] = mapped_column(Integer, server_default="1") invited_by_user_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("parents.user_id", ondelete="SET NULL")) joined_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) removed_at: Mapped[Optional[datetime]] = mapped_column(DateTime) created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP"), onupdate=datetime.utcnow) class DeviceFamilyInvitation(Base): __tablename__ = "device_family_invitations" __table_args__ = ( UniqueConstraint("invite_token", name="uq_device_family_invite_token"), Index("idx_device_family_invites_device_status", "device_id", "status"), Index("idx_device_family_invites_owner_status", "owner_user_id", "status"), Index("idx_device_family_invites_expires_at", "expires_at"), ) id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) invite_token: Mapped[str] = mapped_column(String(36), nullable=False) device_id: Mapped[str] = mapped_column(String(64), ForeignKey("device_auth.device_id"), nullable=False) owner_user_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("parents.user_id"), nullable=False) status: Mapped[int] = mapped_column(Integer, server_default="1") expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) accepted_by_user_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("parents.user_id", ondelete="SET NULL")) accepted_at: Mapped[Optional[datetime]] = mapped_column(DateTime) created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP"), onupdate=datetime.utcnow) class DeviceBindSession(Base): __tablename__ = "device_bind_sessions" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) bind_token: Mapped[str] = mapped_column(String(36), unique=True, nullable=False) device_id: Mapped[str] = mapped_column(String(64), nullable=False) initiator_user_id: Mapped[int] = mapped_column(BigInteger, nullable=False) target_child_id: Mapped[Optional[int]] = mapped_column(BigInteger) challenge_code_hash: Mapped[Optional[str]] = mapped_column(String(64)) challenge_set_at: Mapped[Optional[datetime]] = mapped_column(DateTime) expires_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) max_attempt_count: Mapped[int] = mapped_column(Integer, server_default="5") attempt_count: Mapped[int] = mapped_column(Integer, server_default="0") status: Mapped[int] = mapped_column(Integer, server_default="1") bind_mode: Mapped[int] = mapped_column(Integer, server_default="1") card_uuid: Mapped[Optional[str]] = mapped_column(String(64)) confirmed_at: Mapped[Optional[datetime]] = mapped_column(DateTime) consumed_at: Mapped[Optional[datetime]] = mapped_column(DateTime) created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) class DeviceBindHistory(Base): __tablename__ = "device_bind_history" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) device_id: Mapped[str] = mapped_column(String(64), nullable=False) child_id: Mapped[Optional[int]] = mapped_column(BigInteger) bound_by_user_id: Mapped[int] = mapped_column(BigInteger, nullable=False) unbound_by_user_id: Mapped[Optional[int]] = mapped_column(BigInteger) bind_source: Mapped[int] = mapped_column(Integer, server_default="1") bound_at: Mapped[datetime] = mapped_column(DateTime, nullable=False) unbound_at: Mapped[Optional[datetime]] = mapped_column(DateTime) unbind_reason: Mapped[Optional[str]] = mapped_column(String(191)) created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) class DeviceSetting(Base): __tablename__ = "device_settings" setting_id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) device_id: Mapped[str] = mapped_column(String(64), unique=True, nullable=False) sleep_mode: Mapped[int] = mapped_column(Integer, server_default="0") manual_sleep_mode: Mapped[int] = mapped_column(Integer, nullable=False, server_default="0") schedule_suppressed_until: Mapped[Optional[datetime]] = mapped_column(DateTime) disable_time_start: Mapped[Optional[time]] = mapped_column(Time) disable_time_end: Mapped[Optional[time]] = mapped_column(Time) timezone: Mapped[str] = mapped_column(String(32), server_default=text("'Asia/Shanghai'")) volume: Mapped[Optional[int]] = mapped_column(Integer) brightness: Mapped[Optional[int]] = mapped_column(Integer) power: Mapped[Optional[int]] = mapped_column(Integer) signal: Mapped[Optional[int]] = mapped_column("signal", Integer) version: Mapped[Optional[str]] = mapped_column("version", String(64)) imei: Mapped[Optional[str]] = mapped_column(String(64)) device_type: Mapped[Optional[int]] = mapped_column(Integer) ota_channel: Mapped[Optional[str]] = mapped_column(String(32)) disable_weekdays: Mapped[Optional[str]] = mapped_column(String(32)) created_at: Mapped[Optional[datetime]] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[Optional[datetime]] = mapped_column( DateTime, server_default=text("CURRENT_TIMESTAMP"), onupdate=datetime.utcnow, ) class DeviceAlarmEvent(Base): __tablename__ = "device_alarm_events" __table_args__ = ( Index("idx_device_alarm_device_created", "device_id", "created_at"), Index("idx_device_alarm_owner_created", "owner_user_id", "created_at"), Index("idx_device_alarm_child_created", "child_id", "created_at"), ) alarm_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) device_id: Mapped[str] = mapped_column(String(64), nullable=False) owner_user_id: Mapped[Optional[int]] = mapped_column(Integer) child_id: Mapped[Optional[int]] = mapped_column(Integer) source_msg_id: Mapped[str] = mapped_column(String(8), server_default=text("'010'")) coord_type: Mapped[Optional[str]] = mapped_column(String(16)) lat: Mapped[Optional[float]] = mapped_column(Numeric(10, 7)) lng: Mapped[Optional[float]] = mapped_column(Numeric(10, 7)) location_updated_at: Mapped[Optional[datetime]] = mapped_column(DateTime) address: Mapped[Optional[str]] = mapped_column(String(255)) address_resolved_at: Mapped[Optional[datetime]] = mapped_column(DateTime) address_resolve_status: Mapped[Optional[int]] = mapped_column(Integer) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) class IMConversation(Base): __tablename__ = "im_conversations" __table_args__ = ( UniqueConstraint("conversation_type", "pair_key", name="uq_conv_type_pair"), Index("idx_im_conv_participant_a", "participant_a_type", "participant_a_id"), Index("idx_im_conv_participant_b", "participant_b_type", "participant_b_id"), Index("idx_im_conv_last_message_at", "last_message_at"), ) id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) conversation_type: Mapped[int] = mapped_column(Integer, nullable=False) participant_a_type: Mapped[int] = mapped_column(Integer, nullable=False) participant_a_id: Mapped[str] = mapped_column(String(64), nullable=False) participant_b_type: Mapped[int] = mapped_column(Integer, nullable=False) participant_b_id: Mapped[str] = mapped_column(String(64), nullable=False) pair_key: Mapped[str] = mapped_column(String(191), nullable=False) status: Mapped[int] = mapped_column(Integer, default=1) last_seq: Mapped[int] = mapped_column(BigInteger, default=0, index=True) message_count: Mapped[int] = mapped_column(BigInteger, default=0) last_message_preview: Mapped[Optional[str]] = mapped_column(String(255)) last_message_at: Mapped[Optional[datetime]] = mapped_column(DateTime) ext_json: Mapped[Optional[dict]] = mapped_column(JSON) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) updated_at: Mapped[datetime] = mapped_column( DateTime, default=datetime.utcnow, onupdate=datetime.utcnow ) class IMMessage(Base): __tablename__ = "im_messages" __table_args__ = ( UniqueConstraint("conversation_id", "seq", name="uq_im_msg_conv_seq"), UniqueConstraint("conversation_id", "client_msg_id", name="uq_im_msg_client"), Index("idx_im_msg_sender", "sender_type", "sender_id", "created_at"), Index("idx_im_msg_receiver", "receiver_type", "receiver_id", "created_at"), ) id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) conversation_id: Mapped[int] = mapped_column(BigInteger, nullable=False) seq: Mapped[int] = mapped_column(BigInteger, nullable=False) sender_type: Mapped[int] = mapped_column(Integer, nullable=False) sender_id: Mapped[str] = mapped_column(String(64), nullable=False) receiver_type: Mapped[int] = mapped_column(Integer, nullable=False) receiver_id: Mapped[str] = mapped_column(String(64), nullable=False) content_type: Mapped[int] = mapped_column(Integer, nullable=False) content_text: Mapped[Optional[str]] = mapped_column(Text) content_json: Mapped[Optional[dict]] = mapped_column(JSON) media_file_key: Mapped[Optional[str]] = mapped_column(String(255)) media_duration_ms: Mapped[Optional[int]] = mapped_column(Integer) media_mime_type: Mapped[Optional[str]] = mapped_column(String(64)) media_size_bytes: Mapped[Optional[int]] = mapped_column(BigInteger) media_transcript_text: Mapped[Optional[str]] = mapped_column(Text) client_msg_id: Mapped[Optional[str]] = mapped_column(String(64)) sender_name_snapshot: Mapped[Optional[str]] = mapped_column(String(64)) sender_avatar_snapshot: Mapped[Optional[str]] = mapped_column(String(255)) receiver_name_snapshot: Mapped[Optional[str]] = mapped_column(String(64)) receiver_avatar_snapshot: Mapped[Optional[str]] = mapped_column(String(255)) ext_json: Mapped[Optional[dict]] = mapped_column(JSON) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) deleted_at: Mapped[Optional[datetime]] = mapped_column(DateTime) class DevicePendingVoiceMessage(Base): __tablename__ = "device_pending_voice_messages" __table_args__ = ( UniqueConstraint("target_device_id", "im_message_id", name="uq_device_pending_voice_target_msg"), Index("idx_device_pending_voice_target_status_created", "target_device_id", "status", "created_at"), Index("idx_device_pending_voice_sender", "sender_device_id"), Index("idx_device_pending_voice_im_message", "im_message_id"), ) id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) target_device_id: Mapped[str] = mapped_column(String(64), nullable=False) sender_device_id: Mapped[Optional[str]] = mapped_column(String(64)) im_message_id: Mapped[int] = mapped_column(BigInteger, nullable=False) media_file_key: Mapped[str] = mapped_column(String(255), nullable=False) audio_url: Mapped[Optional[str]] = mapped_column(String(512)) source: Mapped[str] = mapped_column(String(32), nullable=False, server_default=text("'unknown'")) status: Mapped[str] = mapped_column(String(32), nullable=False, server_default=text("'pending'")) delivery_count: Mapped[int] = mapped_column(Integer, nullable=False, server_default=text("0")) delivered_at: Mapped[Optional[datetime]] = mapped_column(DateTime) listened_at: Mapped[Optional[datetime]] = mapped_column(DateTime) created_at: Mapped[datetime] = mapped_column(DateTime, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[datetime] = mapped_column( DateTime, server_default=text("CURRENT_TIMESTAMP"), onupdate=datetime.utcnow, ) class ChildLocationCurrent(Base): __tablename__ = "child_location_current" child_id: Mapped[int] = mapped_column(BigInteger, primary_key=True) device_id: Mapped[str] = mapped_column(String(64), nullable=False) coord_type: Mapped[str] = mapped_column(String(16), default="gcj02") lat: Mapped[float] = mapped_column(Numeric(10, 7), nullable=False) lng: Mapped[float] = mapped_column(Numeric(10, 7), nullable=False) accuracy_m: Mapped[Optional[int]] = mapped_column(Integer) altitude_m: Mapped[Optional[float]] = mapped_column(Numeric(8, 2)) speed_mps: Mapped[Optional[float]] = mapped_column(Numeric(8, 2)) heading_deg: Mapped[Optional[int]] = mapped_column(SmallInteger) source: Mapped[int] = mapped_column(Integer, nullable=False) battery_pct: Mapped[Optional[int]] = mapped_column(Integer) device_time: Mapped[datetime] = mapped_column(DateTime, nullable=False) server_time: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) updated_at: Mapped[datetime] = mapped_column( DateTime, default=datetime.utcnow, onupdate=datetime.utcnow ) address: Mapped[Optional[str]] = mapped_column(String(255)) address_resolved_at: Mapped[Optional[datetime]] = mapped_column(DateTime) address_resolve_status: Mapped[Optional[int]] = mapped_column(Integer) class ChildLocationHistory(Base): __tablename__ = "child_location_history" __table_args__ = ( Index("idx_child_loc_hist_child", "child_id", "created_at"), ) id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) child_id: Mapped[int] = mapped_column(BigInteger, nullable=False) device_id: Mapped[str] = mapped_column(String(64), nullable=False) coord_type: Mapped[str] = mapped_column(String(16), default="gcj02") lat: Mapped[float] = mapped_column(Numeric(10, 7), nullable=False) lng: Mapped[float] = mapped_column(Numeric(10, 7), nullable=False) accuracy_m: Mapped[Optional[int]] = mapped_column(Integer) altitude_m: Mapped[Optional[float]] = mapped_column(Numeric(8, 2)) speed_mps: Mapped[Optional[float]] = mapped_column(Numeric(8, 2)) heading_deg: Mapped[Optional[int]] = mapped_column(SmallInteger) source: Mapped[int] = mapped_column(Integer, nullable=False) battery_pct: Mapped[Optional[int]] = mapped_column(Integer) device_time: Mapped[datetime] = mapped_column(DateTime, nullable=False) server_time: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow) created_at: Mapped[datetime] = mapped_column(DateTime, default=datetime.utcnow)