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>
This commit is contained in:
egg
2025-12-01 17:42:52 +08:00
commit c8966477b9
135 changed files with 23269 additions and 0 deletions

View File

@@ -0,0 +1,126 @@
"""SQLAlchemy models for chat room management
Tables:
- incident_rooms: Stores room metadata and configuration
- room_members: User-room associations with roles
- room_templates: Predefined templates for common incident types
"""
from sqlalchemy import Column, Integer, String, Text, DateTime, Enum, ForeignKey, UniqueConstraint, Index
from sqlalchemy.orm import relationship
from datetime import datetime
import enum
import uuid
from app.core.database import Base
class IncidentType(str, enum.Enum):
"""Types of production incidents"""
EQUIPMENT_FAILURE = "equipment_failure"
MATERIAL_SHORTAGE = "material_shortage"
QUALITY_ISSUE = "quality_issue"
OTHER = "other"
class SeverityLevel(str, enum.Enum):
"""Incident severity levels"""
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"
class RoomStatus(str, enum.Enum):
"""Room lifecycle status"""
ACTIVE = "active"
RESOLVED = "resolved"
ARCHIVED = "archived"
class MemberRole(str, enum.Enum):
"""Room member roles"""
OWNER = "owner"
EDITOR = "editor"
VIEWER = "viewer"
class IncidentRoom(Base):
"""Incident room model for production incidents"""
__tablename__ = "incident_rooms"
room_id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
title = Column(String(255), nullable=False)
incident_type = Column(Enum(IncidentType), nullable=False)
severity = Column(Enum(SeverityLevel), nullable=False)
status = Column(Enum(RoomStatus), default=RoomStatus.ACTIVE, nullable=False)
location = Column(String(255))
description = Column(Text)
resolution_notes = Column(Text)
# User tracking
created_by = Column(String(255), nullable=False) # User email/ID who created the room
# Timestamps
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
resolved_at = Column(DateTime)
archived_at = Column(DateTime)
last_activity_at = Column(DateTime, default=datetime.utcnow, nullable=False)
last_updated_at = Column(DateTime, default=datetime.utcnow, nullable=False)
# Ownership transfer tracking
ownership_transferred_at = Column(DateTime)
ownership_transferred_by = Column(String(255))
# Denormalized count for performance
member_count = Column(Integer, default=0, nullable=False)
# Relationships
members = relationship("RoomMember", back_populates="room", cascade="all, delete-orphan")
files = relationship("RoomFile", back_populates="room", cascade="all, delete-orphan")
# Indexes for common queries
__table_args__ = (
Index("ix_incident_rooms_status_created", "status", "created_at"),
Index("ix_incident_rooms_created_by", "created_by"),
)
class RoomMember(Base):
"""Room membership model"""
__tablename__ = "room_members"
id = Column(Integer, primary_key=True, autoincrement=True)
room_id = Column(String(36), ForeignKey("incident_rooms.room_id", ondelete="CASCADE"), nullable=False)
user_id = Column(String(255), nullable=False) # User email/ID
role = Column(Enum(MemberRole), nullable=False)
# Tracking
added_by = Column(String(255), nullable=False) # Who added this member
added_at = Column(DateTime, default=datetime.utcnow, nullable=False)
removed_at = Column(DateTime) # Soft delete timestamp
# Relationships
room = relationship("IncidentRoom", back_populates="members")
# Constraints and indexes
__table_args__ = (
# Ensure unique active membership (where removed_at IS NULL)
UniqueConstraint("room_id", "user_id", "removed_at", name="uq_room_member_active"),
Index("ix_room_members_room_user", "room_id", "user_id"),
Index("ix_room_members_user", "user_id"),
)
class RoomTemplate(Base):
"""Predefined templates for common incident types"""
__tablename__ = "room_templates"
template_id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(100), unique=True, nullable=False)
description = Column(Text)
incident_type = Column(Enum(IncidentType), nullable=False)
default_severity = Column(Enum(SeverityLevel), nullable=False)
default_members = Column(Text) # JSON array of user roles
metadata_fields = Column(Text) # JSON schema for additional fields