feat: Add AI report generation with DIFY integration

- Add Users table for display name resolution from AD authentication
- Integrate DIFY AI service for report content generation
- Create docx assembly service with image embedding from MinIO
- Add REST API endpoints for report generation and download
- Add WebSocket notifications for generation progress
- Add frontend UI with progress modal and download functionality
- Add integration tests for report generation flow

Report sections (Traditional Chinese):
- 事件摘要 (Summary)
- 時間軸 (Timeline)
- 參與人員 (Participants)
- 處理過程 (Resolution Process)
- 目前狀態 (Current Status)
- 最終處置結果 (Final Resolution)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
egg
2025-12-04 18:32:40 +08:00
parent 77091eefb5
commit 3927441103
32 changed files with 4374 additions and 8 deletions

View File

@@ -0,0 +1,100 @@
"""SQLAlchemy models for report generation
Tables:
- generated_reports: Stores report metadata and generation status
"""
from sqlalchemy import Column, String, Text, DateTime, Integer, ForeignKey, Index, JSON
from sqlalchemy.orm import relationship
from datetime import datetime
import uuid
import enum
from app.core.database import Base
class ReportStatus(str, enum.Enum):
"""Report generation status"""
PENDING = "pending"
COLLECTING_DATA = "collecting_data"
GENERATING_CONTENT = "generating_content"
ASSEMBLING_DOCUMENT = "assembling_document"
COMPLETED = "completed"
FAILED = "failed"
class GeneratedReport(Base):
"""Generated report model for incident reports"""
__tablename__ = "generated_reports"
report_id = Column(
String(36), primary_key=True, default=lambda: str(uuid.uuid4()),
comment="Unique report identifier (UUID)"
)
room_id = Column(
String(36), ForeignKey("incident_rooms.room_id", ondelete="CASCADE"),
nullable=False, comment="Reference to incident room"
)
# Generation metadata
generated_by = Column(
String(255), nullable=False,
comment="User email who triggered report generation"
)
generated_at = Column(
DateTime, default=datetime.utcnow, nullable=False,
comment="Report generation timestamp"
)
# Status tracking
status = Column(
String(30), default=ReportStatus.PENDING.value, nullable=False,
comment="Current generation status"
)
error_message = Column(
Text, nullable=True,
comment="User-friendly error message if generation failed"
)
# DIFY AI metadata
dify_message_id = Column(
String(100), nullable=True,
comment="DIFY API message ID for tracking"
)
dify_conversation_id = Column(
String(100), nullable=True,
comment="DIFY conversation ID"
)
prompt_tokens = Column(
Integer, nullable=True,
comment="Number of prompt tokens used"
)
completion_tokens = Column(
Integer, nullable=True,
comment="Number of completion tokens used"
)
# Report content
report_title = Column(
String(255), nullable=True,
comment="Generated report title"
)
report_json = Column(
JSON, nullable=True,
comment="Parsed AI output as JSON"
)
docx_storage_path = Column(
String(500), nullable=True,
comment="Path to generated .docx file in MinIO or local storage"
)
# Relationship
room = relationship("IncidentRoom", backref="reports")
# Indexes
__table_args__ = (
Index("ix_generated_reports_room_date", "room_id", "generated_at"),
Index("ix_generated_reports_status", "status"),
)
def __repr__(self):
return f"<GeneratedReport {self.report_id} status={self.status}>"