# Design: Fix Chat UX Issues ## Technical Design ### 1. 發文者顯示名稱 (Sender Display Name) #### 後端改動 **Schema 變更 (`app/modules/realtime/schemas.py`):** ```python class MessageResponse(BaseModel): message_id: str room_id: str sender_id: str sender_display_name: Optional[str] = None # 新增欄位 content: str # ... 其他欄位 ``` **訊息查詢修改 (`app/modules/realtime/services/message_service.py`):** ```python from app.modules.auth.models import User # 在 get_messages() 中 JOIN users 表 messages = ( db.query(Message, User.display_name) .outerjoin(User, Message.sender_id == User.user_id) .filter(Message.room_id == room_id) .order_by(desc(Message.created_at)) .limit(limit) .all() ) # 轉換為 response for msg, display_name in messages: msg_response = MessageResponse.from_orm(msg) msg_response.sender_display_name = display_name or msg.sender_id ``` **WebSocket 廣播修改 (`app/modules/realtime/schemas.py`):** ```python class MessageBroadcast(BaseModel): type: str = "message" message_id: str sender_id: str sender_display_name: Optional[str] = None # 新增 # ... 其他欄位 ``` #### 前端改動 **RoomDetail.tsx:** ```tsx // 將 message.sender_id 改為 {message.sender_display_name || message.sender_id} ``` ### 2. 統一時區為 GMT+8 #### 前端工具函數 (`frontend/src/utils/datetime.ts`) ```typescript /** * 格式化日期時間為 GMT+8 (台灣時間) */ export function formatDateTimeGMT8(date: Date | string): string { const d = typeof date === 'string' ? new Date(date) : date return d.toLocaleString('zh-TW', { timeZone: 'Asia/Taipei', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', }) } /** * 格式化時間為 GMT+8 (僅時:分) */ export function formatTimeGMT8(date: Date | string): string { const d = typeof date === 'string' ? new Date(date) : date return d.toLocaleString('zh-TW', { timeZone: 'Asia/Taipei', hour: '2-digit', minute: '2-digit', }) } ``` #### 使用位置 - `RoomDetail.tsx` - 訊息時間 - `RoomList.tsx` - 房間最後更新時間 - `Reports.tsx` - 報告生成時間 ### 3. AI 報告生成問題修復 #### 新增健康檢查端點 (`app/modules/report_generation/router.py`) ```python @router.get("/health", response_model=schemas.HealthCheckResponse) async def check_dify_health(): """檢查 DIFY AI 服務連線狀態""" if not settings.DIFY_API_KEY: return {"status": "error", "message": "DIFY_API_KEY 未設定"} try: # 測試 API 連線 result = await dify_service.test_connection() return {"status": "ok", "message": "AI 服務正常"} except Exception as e: return {"status": "error", "message": str(e)} ``` #### 啟動時檢查 (`app/main.py`) ```python @app.on_event("startup") async def startup_event(): # 檢查 DIFY API Key if not settings.DIFY_API_KEY: logger.warning("DIFY_API_KEY not configured - AI report generation will be unavailable") ``` #### 前端改善 (`frontend/src/hooks/useReports.ts`) ```typescript // 新增輪詢直到報告完成或失敗 const pollReportStatus = async (reportId: string) => { const maxAttempts = 60 // 最多輪詢 2 分鐘 let attempts = 0 while (attempts < maxAttempts) { const status = await api.get(`/rooms/${roomId}/reports/${reportId}`) if (status.data.status === 'completed' || status.data.status === 'failed') { return status.data } await new Promise(resolve => setTimeout(resolve, 2000)) attempts++ } throw new Error('報告生成超時') } ``` ## Data Flow ### Message Flow with Display Name ``` 1. User sends message via WebSocket 2. Backend creates message in DB 3. Backend queries User table for sender's display_name 4. Backend broadcasts MessageBroadcast with sender_display_name 5. Frontend displays sender_display_name in chat bubble ``` ### Report Generation Flow (Fixed) ``` 1. User clicks "Generate Report" 2. Frontend: POST /api/rooms/{id}/reports/generate 3. Backend: Creates report record (status=pending) 4. Backend: Returns report_id immediately 5. Frontend: Starts polling GET /api/rooms/{id}/reports/{report_id} 6. Backend: Background task updates status (collecting_data → generating_content → assembling_document → completed) 7. Backend: Broadcasts WebSocket updates for each status change 8. Frontend: Updates UI based on poll response OR WebSocket message 9. If completed: Enable download button 10. If failed: Show error message ``` ## Database Changes 無資料庫結構變更。僅新增 JOIN 查詢。 ## Configuration Changes 無新增設定項目。僅改善現有 DIFY_API_KEY 的檢查機制。