Files
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

585 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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** | 擁有者 | - 完全控制權<br>- 可更新房間資訊<br>- 可管理所有成員<br>- 可轉移所有權<br>- 可刪除房間 |
| **EDITOR** | 編輯者 | - 讀寫權限<br>- 可新增 VIEWER 成員<br>- 不可變更房間狀態<br>- 不可管理 OWNER/EDITOR |
| **VIEWER** | 檢視者 | - 僅讀取權限<br>- 可查看房間資訊與成員<br>- 無法進行任何修改 |
| **ADMIN** | 系統管理員 | - 覆寫所有限制<br>- 可存取所有房間<br>- 執行任何操作 |
#### 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 權限檢查流程
```python
# 在 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 程式碼範例
```python
# 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 使用範例
```bash
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 軟刪除機制
成員採用軟刪除設計,不實際刪除記錄:
```python
# 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` 欄位自動同步:
```python
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 預設範本
系統提供三個預設範本,在應用啟動時自動初始化:
```python
# 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 使用範本建立房間
```python
# 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: 在其他模組中查詢使用者的房間
```python
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: 檢查使用者是否有特定房間的權限
```python
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: 建立帶有自訂成員的房間
```python
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
```
---
## 資料庫查詢優化
### 索引策略
```sql
-- 房間查詢優化
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);
```
### 常見查詢模式
```python
# 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 即時通訊預留設計空間:
```python
# 未來可實作:
# - 房間訊息廣播
# - 成員上線狀態
# - 即時通知
# - 協作編輯
# 建議架構:
# 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
```
### 稽核日誌
建議新增完整的稽核日誌表:
```python
# 未來可實作:
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)
```
---
## 測試建議
### 單元測試重點
```python
# 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 執行任何操作 → 成功
```
### 整合測試重點
```python
# 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 與稽核日誌預留設計空間