Files
Task_Reporter/app/modules/realtime/models.py
egg c8966477b9 feat: Initial commit - Task Reporter incident response system
Complete implementation of the production line incident response system (生產線異常即時反應系統) including:

Backend (FastAPI):
- User authentication with AD integration and session management
- Chat room management (create, list, update, members, roles)
- Real-time messaging via WebSocket (typing indicators, reactions)
- File storage with MinIO (upload, download, image preview)

Frontend (React + Vite):
- Authentication flow with token management
- Room list with filtering, search, and pagination
- Real-time chat interface with WebSocket
- File upload with drag-and-drop and image preview
- Member management and room settings
- Breadcrumb navigation
- 53 unit tests (Vitest)

Specifications:
- authentication: AD auth, sessions, JWT tokens
- chat-room: rooms, members, templates
- realtime-messaging: WebSocket, messages, reactions
- file-storage: MinIO integration, file management
- frontend-core: React SPA structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 17:42:52 +08:00

107 lines
4.1 KiB
Python

"""SQLAlchemy models for realtime messaging
Tables:
- messages: Stores all messages sent in incident rooms
- message_reactions: User reactions to messages (emoji)
- message_edit_history: Audit trail for message edits
"""
from sqlalchemy import Column, Integer, String, Text, DateTime, Enum, ForeignKey, UniqueConstraint, Index, BigInteger, JSON
from sqlalchemy.orm import relationship
from datetime import datetime
import enum
import uuid
from app.core.database import Base
class MessageType(str, enum.Enum):
"""Types of messages in incident rooms"""
TEXT = "text"
IMAGE_REF = "image_ref"
FILE_REF = "file_ref"
SYSTEM = "system"
INCIDENT_DATA = "incident_data"
class Message(Base):
"""Message model for incident room communications"""
__tablename__ = "messages"
message_id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
room_id = Column(String(36), ForeignKey("incident_rooms.room_id", ondelete="CASCADE"), nullable=False)
sender_id = Column(String(255), nullable=False) # User email/ID
content = Column(Text, nullable=False)
message_type = Column(Enum(MessageType), default=MessageType.TEXT, nullable=False)
# Message metadata for structured data, mentions, file references, etc.
message_metadata = Column(JSON)
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
edited_at = Column(DateTime) # Last edit timestamp
deleted_at = Column(DateTime) # Soft delete timestamp
# Sequence number for FIFO ordering within a room
# Note: Autoincrement doesn't work for non-PK in SQLite, will be set in service layer
sequence_number = Column(BigInteger, nullable=False)
# Relationships
reactions = relationship("MessageReaction", back_populates="message", cascade="all, delete-orphan")
edit_history = relationship("MessageEditHistory", back_populates="message", cascade="all, delete-orphan")
# Indexes for common queries
__table_args__ = (
Index("ix_messages_room_created", "room_id", "created_at"),
Index("ix_messages_room_sequence", "room_id", "sequence_number"),
Index("ix_messages_sender", "sender_id"),
# PostgreSQL full-text search index on content (commented for SQLite compatibility)
# Note: Uncomment when using PostgreSQL with pg_trgm extension enabled
# Index("ix_messages_content_search", "content", postgresql_using='gin', postgresql_ops={'content': 'gin_trgm_ops'}),
)
class MessageReaction(Base):
"""Message reaction model for emoji reactions"""
__tablename__ = "message_reactions"
reaction_id = Column(Integer, primary_key=True, autoincrement=True)
message_id = Column(String(36), ForeignKey("messages.message_id", ondelete="CASCADE"), nullable=False)
user_id = Column(String(255), nullable=False) # User email/ID who reacted
emoji = Column(String(10), nullable=False) # Emoji character or code
# Timestamp
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
# Relationships
message = relationship("Message", back_populates="reactions")
# Constraints and indexes
__table_args__ = (
# Ensure unique reaction per user per message
UniqueConstraint("message_id", "user_id", "emoji", name="uq_message_reaction"),
Index("ix_message_reactions_message", "message_id"),
)
class MessageEditHistory(Base):
"""Message edit history model for audit trail"""
__tablename__ = "message_edit_history"
edit_id = Column(Integer, primary_key=True, autoincrement=True)
message_id = Column(String(36), ForeignKey("messages.message_id", ondelete="CASCADE"), nullable=False)
original_content = Column(Text, nullable=False) # Content before edit
edited_by = Column(String(255), nullable=False) # User who made the edit
# Timestamp
edited_at = Column(DateTime, default=datetime.utcnow, nullable=False)
# Relationships
message = relationship("Message", back_populates="edit_history")
# Indexes
__table_args__ = (
Index("ix_message_edit_history_message", "message_id", "edited_at"),
)