from __future__ import annotations from typing import Any, Literal from pydantic import BaseModel, Field Role = Literal["student", "admin"] OrderStatus = Literal["已提交", "待处理", "处理中", "待上门", "已完成", "已确认", "返工申请中", "已取消"] Urgency = Literal["低", "中", "高", "紧急"] class LoginRequest(BaseModel): username: str = Field(min_length=1, max_length=50) password: str = Field(min_length=1, max_length=100) class UserProfile(BaseModel): id: int username: str display_name: str role: Role class LoginResponse(BaseModel): token: str user: UserProfile class DiagnosisStartRequest(BaseModel): message: str = Field(min_length=2, max_length=300) class DiagnosisQuestionAnswer(BaseModel): question_id: str answer: str = Field(min_length=1, max_length=200) class DiagnosisAnswerRequest(BaseModel): session_id: str answers: list[DiagnosisQuestionAnswer] class DiagnosisQuestion(BaseModel): id: str prompt: str class DiagnosisDraft(BaseModel): category: str urgency: Urgency summary: str safety_risk: bool suggested_worker: str notes: list[str] class DiagnosisResponse(BaseModel): session_id: str stage: Literal["questions", "draft"] initial_message: str suggested_categories: list[str] questions: list[DiagnosisQuestion] draft: DiagnosisDraft | None = None class SavedAddressOut(BaseModel): id: int campus: str building: str room: str last_used_at: str class OrderCreateRequest(BaseModel): campus: str = Field(min_length=1, max_length=50) building: str = Field(min_length=1, max_length=50) room: str = Field(min_length=1, max_length=50) category: str = Field(min_length=1, max_length=50) raw_description: str = Field(min_length=2, max_length=300) structured_summary: str = Field(min_length=2, max_length=400) urgency: Urgency expected_date: str | None = Field(default=None, max_length=20) expected_time_segment: str | None = Field(default=None, max_length=50) diagnosis_session_id: str | None = Field(default=None, max_length=80) allow_room_entry: bool = False class FeedbackRequest(BaseModel): rating: int = Field(ge=1, le=5) comment: str = Field(min_length=1, max_length=300) class ReworkRequest(BaseModel): reason: str = Field(min_length=2, max_length=300) class AdminOrderUpdateRequest(BaseModel): status: OrderStatus | None = None assignee_name: str | None = Field(default=None, max_length=50) expected_arrival_at: str | None = Field(default=None, max_length=50) admin_note: str | None = Field(default=None, max_length=300) class OrderEventOut(BaseModel): id: int actor_role: str actor_name: str event_type: str title: str detail: str | None from_status: str | None to_status: str | None created_at: str class AttachmentOut(BaseModel): id: int file_name: str file_path: str mime_type: str created_at: str class OrderSummaryOut(BaseModel): id: int order_no: str campus: str building: str room: str category: str status: str urgency: str submission_time: str expected_repair_time: str | None assignee_name: str | None class OrderDetailOut(BaseModel): id: int order_no: str campus: str building: str room: str category: str status: str urgency: str raw_description: str structured_summary: str allow_room_entry: bool expected_date: str | None expected_time_segment: str | None assignee_name: str | None expected_arrival_at: str | None admin_note: str | None rework_reason: str | None created_at: str updated_at: str attachments: list[AttachmentOut] events: list[OrderEventOut] feedback: dict[str, Any] | None