Files
Task_Reporter/openspec/changes/archive/2025-11-17-add-chat-room-management/module-documentation.md
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

15 KiB
Raw Blame History

Chat Room Module Documentation

模組概述

app/modules/chat_room 模組提供生產線異常事件協作管理功能,允許團隊成員建立事件室、管理成員權限、追蹤事件狀態,並進行即時協作。


模組架構

app/modules/chat_room/
├── __init__.py              # 模組入口,匯出 router
├── models.py                # SQLAlchemy 資料模型
├── schemas.py               # Pydantic 請求/回應模型
├── router.py                # FastAPI 路由定義
├── dependencies.py          # 依賴注入與權限驗證
└── services/
    ├── __init__.py
    ├── room_service.py      # 房間業務邏輯
    ├── membership_service.py # 成員管理業務邏輯
    └── template_service.py   # 範本管理業務邏輯

核心概念

1. 房間生命週期

事件室從建立到封存的完整生命週期:

┌─────────────┐
│   建立房間    │ ← 使用者建立新事件室
└──────┬──────┘
       ↓
┌─────────────┐
│   ACTIVE    │ ← 事件進行中,可新增成員、更新資訊
│   (活躍)     │
└──────┬──────┘
       ↓ (事件處理完成)
┌─────────────┐
│  RESOLVED   │ ← 事件已解決,填寫解決方案
│  (已解決)    │
└──────┬──────┘
       ↓ (確認可封存)
┌─────────────┐
│  ARCHIVED   │ ← 事件已封存,僅供查詢
│  (已封存)    │
└─────────────┘

狀態轉換規則:

  • 只能單向前進ACTIVE → RESOLVED → ARCHIVED
  • 不允許跳過狀態或反向轉換
  • 每次狀態變更都會更新 last_activity_at

關鍵時間戳記:

  • created_at: 房間建立時間
  • resolved_at: 標記為已解決的時間
  • archived_at: 封存時間(軟刪除)
  • last_activity_at: 最後活動時間(用於排序)

2. 權限模型

2.1 角色定義

角色 說明 權限
OWNER 擁有者 - 完全控制權
- 可更新房間資訊
- 可管理所有成員
- 可轉移所有權
- 可刪除房間
EDITOR 編輯者 - 讀寫權限
- 可新增 VIEWER 成員
- 不可變更房間狀態
- 不可管理 OWNER/EDITOR
VIEWER 檢視者 - 僅讀取權限
- 可查看房間資訊與成員
- 無法進行任何修改
ADMIN 系統管理員 - 覆寫所有限制
- 可存取所有房間
- 執行任何操作

2.2 系統管理員

管理員帳號: ymirliu@panjit.com.tw

特殊權限:

  1. 查看系統中所有房間(即使非成員)
  2. 覆寫所有角色限制
  3. 強制執行任何操作
  4. 不受成員資格限制

實作位置:

  • app/modules/chat_room/services/membership_service.py:is_system_admin()
  • 硬編碼檢查信箱是否為 ymirliu@panjit.com.tw

2.3 權限檢查流程

# 在 dependencies.py 中的權限檢查流程

def require_room_permission(permission: str):
    """
    1. 檢查是否為系統管理員 → 通過
    2. 檢查使用者是否為房間成員 → 否則拒絕
    3. 根據角色檢查特定權限 → 通過/拒絕
    """
    async def dependency(
        room_id: str,
        current_user: dict = Depends(get_current_user),
        db: Session = Depends(get_db)
    ):
        user_email = current_user["username"]

        # 管理員覆寫
        if membership_service.is_system_admin(user_email):
            return None

        # 檢查成員資格
        role = membership_service.get_user_role_in_room(db, room_id, user_email)
        if not role:
            raise HTTPException(status_code=403, detail="Not a room member")

        # 檢查權限
        if not has_permission(role, permission):
            raise HTTPException(status_code=403, detail="Insufficient permissions")

        return None

    return dependency

3. 所有權轉移機制

3.1 轉移流程

使用者需求:現有 owner 指派給新 owner

實作步驟:
1. 驗證當前使用者為 OWNER
2. 驗證新 owner 為現有房間成員
3. 執行角色變更:
   - 原 owner → EDITOR
   - 新 owner → OWNER
4. 記錄稽核資訊:
   - ownership_transferred_at: 轉移時間
   - ownership_transferred_by: 執行轉移的使用者

3.2 程式碼範例

# services/membership_service.py

def transfer_ownership(
    self,
    db: Session,
    room_id: str,
    current_owner_id: str,
    new_owner_id: str
) -> bool:
    """轉移房間所有權"""

    # 1. 驗證新 owner 是房間成員
    new_owner_member = db.query(RoomMember).filter(
        RoomMember.room_id == room_id,
        RoomMember.user_id == new_owner_id,
        RoomMember.removed_at.is_(None)
    ).first()

    if not new_owner_member:
        return False

    # 2. 取得原 owner
    current_owner = db.query(RoomMember).filter(
        RoomMember.room_id == room_id,
        RoomMember.user_id == current_owner_id,
        RoomMember.role == MemberRole.OWNER
    ).first()

    if not current_owner:
        return False

    # 3. 執行角色交換
    current_owner.role = MemberRole.EDITOR
    new_owner_member.role = MemberRole.OWNER

    # 4. 記錄稽核資訊
    room = db.query(IncidentRoom).filter(
        IncidentRoom.room_id == room_id
    ).first()

    room.ownership_transferred_at = datetime.utcnow()
    room.ownership_transferred_by = current_owner_id
    room.last_updated_at = datetime.utcnow()

    db.commit()
    return True

3.3 API 使用範例

POST /api/rooms/{room_id}/transfer-ownership
Authorization: Bearer <owner_token>
Content-Type: application/json

{
  "new_owner_id": "engineer@panjit.com.tw"
}

# 回應
{
  "message": "Ownership transferred successfully"
}

# 效果:
# - engineer@panjit.com.tw 成為新 OWNER
# - 原 OWNER 降級為 EDITOR
# - 記錄於 ownership_transferred_at 和 ownership_transferred_by

4. 成員管理

4.1 軟刪除機制

成員採用軟刪除設計,不實際刪除記錄:

# models.py
class RoomMember(Base):
    removed_at = Column(DateTime, nullable=True)  # NULL = 活躍成員

    # 唯一性約束:同一房間中,同一使用者只能有一個活躍成員記錄
    __table_args__ = (
        Index('ix_room_members_active', 'room_id', 'user_id',
              postgresql_where=text('removed_at IS NULL')),
    )

優點:

  • 保留歷史記錄
  • 支援稽核追蹤
  • 可恢復誤刪的成員

4.2 成員計數同步

member_count 欄位自動同步:

def add_member(...):
    # 新增成員
    member = RoomMember(...)
    db.add(member)

    # 更新計數
    room.member_count += 1
    room.last_activity_at = datetime.utcnow()

    db.commit()

def remove_member(...):
    # 軟刪除
    member.removed_at = datetime.utcnow()

    # 更新計數
    room.member_count -= 1
    room.last_activity_at = datetime.utcnow()

    db.commit()

5. 範本系統

5.1 預設範本

系統提供三個預設範本,在應用啟動時自動初始化:

# services/template_service.py

DEFAULT_TEMPLATES = [
    {
        "name": "equipment_failure",
        "description": "設備故障事件需要立即處理",
        "incident_type": IncidentType.EQUIPMENT_FAILURE,
        "default_severity": SeverityLevel.HIGH,
        "default_members": [
            {"user_id": "maintenance_team@panjit.com.tw", "role": "editor"},
            {"user_id": "engineering@panjit.com.tw", "role": "viewer"}
        ]
    },
    {
        "name": "material_shortage",
        "description": "物料短缺影響生產",
        "incident_type": IncidentType.MATERIAL_SHORTAGE,
        "default_severity": SeverityLevel.MEDIUM,
        "default_members": [
            {"user_id": "procurement@panjit.com.tw", "role": "editor"},
            {"user_id": "logistics@panjit.com.tw", "role": "editor"}
        ]
    },
    {
        "name": "quality_issue",
        "description": "品質問題需要調查",
        "incident_type": IncidentType.QUALITY_ISSUE,
        "default_severity": SeverityLevel.HIGH,
        "default_members": [
            {"user_id": "quality_team@panjit.com.tw", "role": "editor"},
            {"user_id": "production_manager@panjit.com.tw", "role": "viewer"}
        ]
    }
]

5.2 使用範本建立房間

# router.py - create_room endpoint

if room_data.template:
    # 查詢範本
    template = template_service.get_template_by_name(db, room_data.template)

    if template:
        # 從範本建立房間(自動設定類型、嚴重度、預設成員)
        room = template_service.create_room_from_template(
            db,
            template.template_id,
            user_email,
            room_data.title,
            room_data.location,
            room_data.description
        )

優勢:

  • 快速建立標準化事件室
  • 自動新增相關人員
  • 確保一致性

整合範例

範例 1: 在其他模組中查詢使用者的房間

from app.modules.chat_room.services.membership_service import membership_service

def get_user_active_rooms(db: Session, user_email: str):
    """取得使用者所有活躍房間"""
    from app.modules.chat_room.models import RoomStatus
    from app.modules.chat_room.schemas import RoomFilterParams
    from app.modules.chat_room.services.room_service import room_service

    filters = RoomFilterParams(
        status=RoomStatus.ACTIVE,
        limit=100,
        offset=0
    )

    rooms, total = room_service.list_user_rooms(
        db,
        user_email,
        filters,
        is_admin=membership_service.is_system_admin(user_email)
    )

    return rooms

範例 2: 檢查使用者是否有特定房間的權限

from app.modules.chat_room.services.membership_service import membership_service
from app.modules.chat_room.models import MemberRole

def can_user_edit_room(db: Session, room_id: str, user_email: str) -> bool:
    """檢查使用者是否可編輯房間"""

    # 管理員直接通過
    if membership_service.is_system_admin(user_email):
        return True

    # 檢查角色
    role = membership_service.get_user_role_in_room(db, room_id, user_email)

    return role in [MemberRole.OWNER, MemberRole.EDITOR]

範例 3: 建立帶有自訂成員的房間

from app.modules.chat_room.services.room_service import room_service
from app.modules.chat_room.services.membership_service import membership_service
from app.modules.chat_room.schemas import CreateRoomRequest
from app.modules.chat_room.models import MemberRole, IncidentType, SeverityLevel

def create_custom_incident_room(
    db: Session,
    creator_email: str,
    title: str,
    additional_members: list[tuple[str, MemberRole]]
):
    """建立自訂成員的事件室"""

    # 1. 建立房間
    room_data = CreateRoomRequest(
        title=title,
        incident_type=IncidentType.OTHER,
        severity=SeverityLevel.MEDIUM,
        location="",
        description=""
    )

    room = room_service.create_room(db, creator_email, room_data)

    # 2. 新增額外成員
    for user_email, role in additional_members:
        membership_service.add_member(
            db,
            room.room_id,
            user_email,
            role,
            added_by=creator_email
        )

    return room

資料庫查詢優化

索引策略

-- 房間查詢優化
CREATE INDEX ix_incident_rooms_status_created ON incident_rooms(status, created_at);
CREATE INDEX ix_incident_rooms_created_by ON incident_rooms(created_by);

-- 成員查詢優化
CREATE INDEX ix_room_members_room_user ON room_members(room_id, user_id);
CREATE INDEX ix_room_members_user ON room_members(user_id);

-- 範本查詢優化
CREATE UNIQUE INDEX ix_room_templates_name ON room_templates(name);

常見查詢模式

# 1. 取得使用者參與的所有房間(已優化)
user_rooms = db.query(IncidentRoom).join(
    RoomMember,
    and_(
        RoomMember.room_id == IncidentRoom.room_id,
        RoomMember.user_id == user_email,
        RoomMember.removed_at.is_(None)
    )
).filter(
    IncidentRoom.archived_at.is_(None)
).order_by(
    IncidentRoom.last_activity_at.desc()
).all()

# 2. 取得房間所有活躍成員(已優化)
active_members = db.query(RoomMember).filter(
    RoomMember.room_id == room_id,
    RoomMember.removed_at.is_(None)
).all()

# 3. 檢查使用者權限(已優化)
member = db.query(RoomMember).filter(
    RoomMember.room_id == room_id,
    RoomMember.user_id == user_email,
    RoomMember.removed_at.is_(None)
).first()

未來擴展

WebSocket 整合準備

模組已為 WebSocket 即時通訊預留設計空間:

# 未來可實作:
# - 房間訊息廣播
# - 成員上線狀態
# - 即時通知
# - 協作編輯

# 建議架構:
# app/modules/chat_room/websocket.py
class RoomWebSocketManager:
    def __init__(self):
        self.active_connections: dict[str, list[WebSocket]] = {}

    async def connect(self, room_id: str, websocket: WebSocket):
        """連接到房間頻道"""
        pass

    async def broadcast(self, room_id: str, message: dict):
        """廣播訊息到房間所有成員"""
        pass

稽核日誌

建議新增完整的稽核日誌表:

# 未來可實作:
class RoomAuditLog(Base):
    __tablename__ = "room_audit_logs"

    log_id = Column(String(36), primary_key=True)
    room_id = Column(String(36), ForeignKey("incident_rooms.room_id"))
    action = Column(String(50))  # created, updated, member_added, etc.
    actor = Column(String(255))  # 執行操作的使用者
    details = Column(JSON)  # 詳細變更內容
    timestamp = Column(DateTime, default=datetime.utcnow)

測試建議

單元測試重點

# tests/test_chat_room/test_membership_service.py

def test_ownership_transfer():
    """測試所有權轉移"""
    # 1. 建立房間與成員
    # 2. 執行轉移
    # 3. 驗證角色變更
    # 4. 驗證稽核記錄

def test_admin_override():
    """測試管理員覆寫"""
    # 1. 使用非成員的管理員帳號
    # 2. 驗證可執行所有操作

def test_permission_enforcement():
    """測試權限限制"""
    # 1. VIEWER 嘗試修改 → 失敗
    # 2. EDITOR 嘗試新增 OWNER → 失敗
    # 3. OWNER 執行任何操作 → 成功

整合測試重點

# tests/test_chat_room/test_api_endpoints.py

def test_full_room_lifecycle():
    """測試完整房間生命週期"""
    # 1. 建立 → 2. 新增成員 → 3. 更新 → 4. 解決 → 5. 封存

def test_ownership_transfer_api():
    """測試 API 層級的所有權轉移"""
    # 包含認證、權限檢查、業務邏輯

總結

Chat Room 模組提供了一個完整的事件協作管理系統,具備:

角色權限系統 - OWNER/EDITOR/VIEWER + ADMIN 覆寫 所有權轉移 - 支援動態變更房間擁有者 生命週期管理 - ACTIVE → RESOLVED → ARCHIVED 範本系統 - 快速建立標準化事件室 稽核追蹤 - 記錄所有關鍵操作 擴展性 - 為 WebSocket 與稽核日誌預留設計空間