import re from pydantic import BaseModel, Field, field_validator ZONGPAI_PATTERN = re.compile(r"^(\d{2}(B|C|T)\d+|\d{2}(BW|CW)\d{4})$") class BoxItemDetail(BaseModel): """箱号下某个总排号的明细""" box_item_id: int | None = None zongpai_no: str work_order_no: str | None = None quantity: int total_quantity: int | None = None class CurrentZongpaiBox(BaseModel): """当前总排号已经分配的箱号明细""" box_item_id: int box_no: int quantity: int class BoxDetail(BaseModel): """一个箱号的完整信息""" box_no: int items: list[BoxItemDetail] class BoxInfoResponse(BaseModel): """GET /box/info 响应""" zongpai_no: str paichan_no: str work_order_no: str | None = None quantity: int current_zongpai_boxes: list[CurrentZongpaiBox] = Field(default_factory=list) existing_boxes: list[BoxDetail] max_box_no: int suggested_box_no: int class BoxSaveRequest(BaseModel): """POST /box 请求""" zongpai_no: str box_no: int quantity: int @field_validator("zongpai_no") @classmethod def validate_zongpai_no(cls, v: str) -> str: v = v.strip().upper() if not ZONGPAI_PATTERN.match(v): raise ValueError("INVALID_ZONGPAI") return v @field_validator("box_no") @classmethod def validate_box_no(cls, v: int) -> int: if v <= 0: raise ValueError("INVALID_BOX_NO") return v @field_validator("quantity") @classmethod def validate_quantity(cls, v: int) -> int: if v <= 0: raise ValueError("INVALID_QUANTITY") return v class BoxSaveResponse(BaseModel): """POST /box 响应""" box_item_id: int paichan_no: str box_no: int zongpai_no: str quantity: int created_at: str class BoxUpdateRequest(BaseModel): """PATCH /box/{box_item_id} 请求""" box_no: int quantity: int @field_validator("box_no") @classmethod def validate_box_no(cls, v: int) -> int: if v <= 0: raise ValueError("INVALID_BOX_NO") return v @field_validator("quantity") @classmethod def validate_quantity(cls, v: int) -> int: if v <= 0: raise ValueError("INVALID_QUANTITY") return v class BoxUpdateResponse(BaseModel): """PATCH /box/{box_item_id} 响应""" box_item_id: int paichan_no: str box_no: int zongpai_no: str quantity: int updated_at: str class BoxDeleteResponse(BaseModel): """DELETE /box/{box_item_id} 响应""" box_item_id: int deleted: bool