""" Pydantic 数据模型定义 """ from pydantic import BaseModel, Field from typing import List, Dict, Optional, Any class TableStructure(BaseModel): """表格结构""" horizontal_lines: List[int] = Field(default_factory=list, description="横线Y坐标列表") vertical_lines: List[int] = Field(default_factory=list, description="竖线X坐标列表") table_bbox: Optional[List[int]] = Field(default=None, description="表格边界框 [x1, y1, x2, y2]") row_height: Optional[int] = Field(default=None, description="标准行高") col_widths: Optional[List[int]] = Field(default=None, description="列宽列表") total_rows: Optional[int] = Field(default=None, description="总行数") total_cols: Optional[int] = Field(default=None, description="总列数") mode: Optional[str] = Field(default="hybrid", description="分析模式") modified_h_lines: List[int] = Field(default_factory=list, description="被修改的横线索引") modified_v_lines: List[int] = Field(default_factory=list, description="被修改的竖线索引") image_rotation_angle: float = Field(default=0.0, description="图片旋转角度") skew_angle: float = Field(default=0.0, description="倾斜角度") is_skew_corrected: bool = Field(default=False, description="是否已校正倾斜") class ImageSize(BaseModel): """图片尺寸""" width: int height: int class UploadResponse(BaseModel): """上传响应""" success: bool image_base64: str = Field(description="Base64 编码的图片") structure: TableStructure = Field(description="表格结构") image_size: ImageSize = Field(description="图片尺寸") scale_factor: float = Field(default=1.0, description="缩放比例 (1.0 表示未缩放)") ocr_data: Optional[Dict[str, Any]] = Field(default=None, description="原始 OCR 数据 (用于重新分析)") suggested_filename: Optional[str] = Field(default=None, description="建议的文件名") message: Optional[str] = None class AnalyzeParams(BaseModel): """分析参数""" y_tolerance: int = Field(default=5, ge=1, le=50, description="Y轴聚类容差") x_tolerance: int = Field(default=10, ge=1, le=100, description="X轴聚类容差") min_row_height: int = Field(default=20, ge=5, le=100, description="最小行高") method: str = Field(default="auto", description="分析方法: auto/cluster/mineru") class AnalyzeRequest(BaseModel): """重新分析请求""" ocr_data: Dict[str, Any] = Field(description="OCR 数据") params: AnalyzeParams = Field(default_factory=AnalyzeParams) class AnalyzeResponse(BaseModel): """分析响应""" success: bool structure: TableStructure message: Optional[str] = None class SaveRequest(BaseModel): """保存请求""" structure: TableStructure = Field(description="表格结构") image_base64: Optional[str] = Field(default=None, description="Base64 编码的原始图片(可选,saveImage=false时不需要)") output_dir: str = Field(description="输出目录路径") filename: str = Field(description="结构文件名 (不含扩展名)") image_filename: Optional[str] = Field(default=None, description="图片文件名 (不含扩展名)") overwrite_mode: str = Field(default="overwrite", description="覆盖策略: overwrite/skip/new") structure_suffix: str = Field(default="_structure", description="结构文件后缀(不含.json扩展名)") image_suffix: str = Field(default="", description="图片文件后缀(不含.png扩展名)") line_width: int = Field(default=2, description="线条宽度") line_color: List[int] = Field(default=[0, 0, 0], description="线条颜色 RGB") class SaveResponse(BaseModel): """保存响应""" success: bool structure_path: Optional[str] = Field(default=None, description="结构文件路径") image_path: Optional[str] = Field(default=None, description="图片文件路径(可选)") message: Optional[str] = None class ErrorResponse(BaseModel): """错误响应""" success: bool = False error: str detail: Optional[str] = None class LoadByPathRequest(BaseModel): """按路径加载请求""" json_path: str = Field(description="JSON 文件路径") image_path: str = Field(description="图片文件路径") output_dir: Optional[str] = Field(default=None, description="输出目录路径(可选,用于查找已保存的标注结构)") structure_suffix: str = Field(default="_structure", description="结构文件后缀(不含.json)") image_suffix: str = Field(default="", description="图片文件后缀(不含.png)") class PreviewApplyRequest(BaseModel): """预览应用模板请求""" template_name: str = Field(description="模板名称") target_image_size: ImageSize = Field(description="目标图片尺寸") target_table_bbox: Optional[List[int]] = Field(default=None, description="目标表格边界框 [x1, y1, x2, y2]") class ApplyTemplateRequest(BaseModel): """应用模板请求""" template_name: str = Field(description="模板名称") structure: TableStructure = Field(description="原始表格结构") target_table_bbox: Optional[List[int]] = Field(default=None, description="目标表格边界框") class CreateTemplateRequest(BaseModel): """创建模板请求""" name: str = Field(description="模板名称") description: Optional[str] = Field(default=None, description="模板描述") structure: TableStructure = Field(description="表格结构") class TemplateInfo(BaseModel): """模板信息""" name: str description: Optional[str] created_at: str row_count: int col_count: int class TemplateListResponse(BaseModel): """模板列表响应""" success: bool templates: List[TemplateInfo] message: Optional[str] = None class TemplateResponse(BaseModel): """单个模板响应""" success: bool data: Dict message: Optional[str] = None class DeleteTemplateRequest(BaseModel): """删除模板请求""" name: str = Field(description="模板名称") class DeleteTemplateResponse(BaseModel): """删除模板响应""" success: bool message: str class CreateTemplateResponse(BaseModel): """创建模板响应""" success: bool data: Dict = Field(description="包含 name, path, stats, created_at") message: Optional[str] = None class GetTemplateResponse(BaseModel): """获取模板响应""" success: bool data: Dict = Field(description="模板完整信息") message: Optional[str] = None class TemplateApplyResponse(BaseModel): """预览/应用模板响应""" success: bool data: Dict = Field(description="应用后的表格结构") message: Optional[str] = None class HealthResponse(BaseModel): """健康检查响应""" status: str = Field(description="服务状态") service: str = Field(description="服务名称") class HomeDirectoryResponse(BaseModel): """主目录响应""" path: str = Field(description="用户主目录路径")