from __future__ import annotations from datetime import datetime from typing import List, Optional from uuid import uuid4 from pydantic import BaseModel, Field def generate_id() -> str: """Generate deterministic-looking ids for templates and categories.""" return uuid4().hex class VariableDefinition(BaseModel): """Schema describing each dynamic input value for a template.""" name: str = Field(..., description="变量唯一名称,用于在模板中引用") label: str = Field(..., description="变量可视化展示名称") description: Optional[str] = Field(None, description="变量说明提示") prerequisite: Optional[str] = Field(None, description="变量前提条件,如 rerank_reason=PIN_CONSECUTIVE") preview_value: Optional[str] = Field(None, description="预览时的示例值") data_type: str = Field( "string", description="变量类型, 例如 string/number/boolean/options", ) required: bool = Field(False, description="是否为必填字段") default: Optional[str] = Field(None, description="默认值") class TemplateBase(BaseModel): name: str category: str device_type: str = Field(..., description="设备类型:wash(洗涤)/dry(干衣)") machine_model: str = Field(..., description="机型标识,如 x11/hplus/ultra/f2_multi/seeker/x11_ultra") template_type: str = Field(..., description="模板类型:common/time_popular/time_custom/time_season/time_care/time_environmental/time_weather/time_health") type_id: Optional[str] = Field(None, description="机型硬件ID,如 201c51890c31c3080501002180083900000000000000000000000000000040") zone_ids: List[str] = Field(default_factory=list, description="区域ID列表,如 ['2','3']") card_type: str = Field("single_card", description="卡片类型:single_card/multi_card") redis_key_second: Optional[str] = Field(None, description="Redis二级key,如 broadcast-template-jinja:single_card:wash:common") redis_keys_first: List[str] = Field(default_factory=list, description="Redis一级key列表(由type_id+zone_id+card_type+recommend_type组合生成)") reusable_from: Optional[str] = Field(None, description="复用来源模板ID,如果为空则为独立模板") reuse_targets: List[str] = Field(default_factory=list, description="被哪些模板复用(目标模板ID列表)") description: Optional[str] = None output_description: Optional[str] = None call_method: Optional[str] = None input_variables: List[VariableDefinition] = Field(default_factory=list) template_body: str = Field(..., description="Jinja2 模板正文") class TemplateCreate(TemplateBase): pass class TemplateUpdate(BaseModel): name: Optional[str] = None category: Optional[str] = None device_type: Optional[str] = None machine_model: Optional[str] = None template_type: Optional[str] = None type_id: Optional[str] = None zone_ids: Optional[List[str]] = None card_type: Optional[str] = None description: Optional[str] = None output_description: Optional[str] = None call_method: Optional[str] = None input_variables: Optional[List[VariableDefinition]] = None template_body: Optional[str] = None class TemplateRecord(TemplateBase): id: str = Field(default_factory=generate_id) created_at: datetime = Field(default_factory=datetime.utcnow) updated_at: datetime = Field(default_factory=datetime.utcnow) class TemplateRenderRequest(BaseModel): template_id: str parameters: dict class TemplatePreviewRequest(BaseModel): template_body: str parameters: dict = Field(default_factory=dict) analyze_only: bool = Field(False, description="是否只分析变量,不渲染") common_body: Optional[str] = Field(None, description="关联的common模板正文(用于合并宏定义和列表变量)") class TemplateRenderResponse(BaseModel): rendered_text: str class MachineConfig(BaseModel): """机型硬件配置""" label: str = Field(..., description="显示名称,如 X11") type_ids: dict = Field(..., description="设备类型到type_id的映射,如 {'wash': 'xxx', 'dry': 'xxx'}") zone_ids: dict = Field(..., description="设备类型到zone_ids的映射,如 {'wash': ['2','3'], 'dry': ['0']}") card_type: str = Field("single_card", description="卡片类型:single_card/multi_card") class MachineConfigCreate(BaseModel): model_key: str = Field(..., description="机型标识,如 x12") label: str = Field(..., description="显示名称") type_ids: dict = Field(..., description="设备类型到type_id的映射") zone_ids: dict = Field(..., description="设备类型到zone_ids的映射") card_type: str = Field("single_card", description="卡片类型") reuse_from: Optional[str] = Field(None, description="复用来源机型,如 x11") class MachineConfigUpdate(BaseModel): label: Optional[str] = None type_ids: Optional[dict] = None zone_ids: Optional[dict] = None card_type: Optional[str] = None reuse_from: Optional[str] = None