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>
179 lines
5.8 KiB
Python
179 lines
5.8 KiB
Python
"""Template service for room templates
|
|
|
|
Handles business logic for room template operations
|
|
"""
|
|
from sqlalchemy.orm import Session
|
|
from typing import List, Optional
|
|
import json
|
|
from datetime import datetime
|
|
|
|
from app.modules.chat_room.models import RoomTemplate, IncidentRoom, RoomMember, IncidentType, SeverityLevel, MemberRole
|
|
from app.modules.chat_room.services.room_service import room_service
|
|
from app.modules.chat_room.services.membership_service import membership_service
|
|
|
|
|
|
class TemplateService:
|
|
"""Service for room template operations"""
|
|
|
|
def get_templates(self, db: Session) -> List[RoomTemplate]:
|
|
"""Get all available templates
|
|
|
|
Args:
|
|
db: Database session
|
|
|
|
Returns:
|
|
List of templates
|
|
"""
|
|
return db.query(RoomTemplate).all()
|
|
|
|
def get_template_by_name(
|
|
self,
|
|
db: Session,
|
|
template_name: str
|
|
) -> Optional[RoomTemplate]:
|
|
"""Get template by name
|
|
|
|
Args:
|
|
db: Database session
|
|
template_name: Template name
|
|
|
|
Returns:
|
|
Template or None if not found
|
|
"""
|
|
return db.query(RoomTemplate).filter(
|
|
RoomTemplate.name == template_name
|
|
).first()
|
|
|
|
def create_room_from_template(
|
|
self,
|
|
db: Session,
|
|
template_id: int,
|
|
user_id: str,
|
|
title: str,
|
|
location: Optional[str] = None,
|
|
description: Optional[str] = None
|
|
) -> Optional[IncidentRoom]:
|
|
"""Create a room from a template
|
|
|
|
Args:
|
|
db: Database session
|
|
template_id: Template ID
|
|
user_id: User creating the room
|
|
title: Room title
|
|
location: Optional location override
|
|
description: Optional description override
|
|
|
|
Returns:
|
|
Created room or None if template not found
|
|
"""
|
|
# Get template
|
|
template = db.query(RoomTemplate).filter(
|
|
RoomTemplate.template_id == template_id
|
|
).first()
|
|
|
|
if not template:
|
|
return None
|
|
|
|
# Create room with template defaults
|
|
room = IncidentRoom(
|
|
title=title,
|
|
incident_type=template.incident_type,
|
|
severity=template.default_severity,
|
|
location=location,
|
|
description=description or template.description,
|
|
created_by=user_id,
|
|
status="active",
|
|
created_at=datetime.utcnow(),
|
|
last_activity_at=datetime.utcnow(),
|
|
last_updated_at=datetime.utcnow(),
|
|
member_count=1
|
|
)
|
|
db.add(room)
|
|
db.flush() # Get room_id
|
|
|
|
# Add creator as owner
|
|
owner = RoomMember(
|
|
room_id=room.room_id,
|
|
user_id=user_id,
|
|
role=MemberRole.OWNER,
|
|
added_by=user_id,
|
|
added_at=datetime.utcnow()
|
|
)
|
|
db.add(owner)
|
|
|
|
# Add default members from template
|
|
if template.default_members:
|
|
try:
|
|
default_members = json.loads(template.default_members)
|
|
for member_config in default_members:
|
|
if member_config.get("user_id") != user_id: # Don't duplicate owner
|
|
member = RoomMember(
|
|
room_id=room.room_id,
|
|
user_id=member_config["user_id"],
|
|
role=member_config.get("role", MemberRole.VIEWER),
|
|
added_by=user_id,
|
|
added_at=datetime.utcnow()
|
|
)
|
|
db.add(member)
|
|
room.member_count += 1
|
|
except (json.JSONDecodeError, KeyError):
|
|
# Invalid template configuration, skip default members
|
|
pass
|
|
|
|
db.commit()
|
|
db.refresh(room)
|
|
return room
|
|
|
|
def initialize_default_templates(self, db: Session) -> None:
|
|
"""Initialize default templates if none exist
|
|
|
|
Args:
|
|
db: Database session
|
|
"""
|
|
# Check if templates already exist
|
|
existing = db.query(RoomTemplate).count()
|
|
if existing > 0:
|
|
return
|
|
|
|
# Create default templates
|
|
templates = [
|
|
RoomTemplate(
|
|
name="equipment_failure",
|
|
description="Equipment failure incident requiring immediate attention",
|
|
incident_type=IncidentType.EQUIPMENT_FAILURE,
|
|
default_severity=SeverityLevel.HIGH,
|
|
default_members=json.dumps([
|
|
{"user_id": "maintenance_team@panjit.com.tw", "role": "editor"},
|
|
{"user_id": "engineering@panjit.com.tw", "role": "viewer"}
|
|
])
|
|
),
|
|
RoomTemplate(
|
|
name="material_shortage",
|
|
description="Material shortage affecting production",
|
|
incident_type=IncidentType.MATERIAL_SHORTAGE,
|
|
default_severity=SeverityLevel.MEDIUM,
|
|
default_members=json.dumps([
|
|
{"user_id": "procurement@panjit.com.tw", "role": "editor"},
|
|
{"user_id": "logistics@panjit.com.tw", "role": "editor"}
|
|
])
|
|
),
|
|
RoomTemplate(
|
|
name="quality_issue",
|
|
description="Quality control issue requiring investigation",
|
|
incident_type=IncidentType.QUALITY_ISSUE,
|
|
default_severity=SeverityLevel.HIGH,
|
|
default_members=json.dumps([
|
|
{"user_id": "quality_team@panjit.com.tw", "role": "editor"},
|
|
{"user_id": "production_manager@panjit.com.tw", "role": "viewer"}
|
|
])
|
|
)
|
|
]
|
|
|
|
for template in templates:
|
|
db.add(template)
|
|
|
|
db.commit()
|
|
|
|
|
|
# Create singleton instance
|
|
template_service = TemplateService() |