Add ids/columns support and unify toolbar export UX. Co-authored-by: Cursor <cursoragent@cursor.com>
212 lines
10 KiB
Python
212 lines
10 KiB
Python
"""CRM 产品模块 Pydantic Schemas"""
|
||
|
||
from decimal import Decimal
|
||
from typing import List, Optional
|
||
|
||
from pydantic import AliasChoices, BaseModel, ConfigDict, Field
|
||
|
||
|
||
# ── 产品分类 Schemas ──────────────────────────────────────────
|
||
|
||
|
||
class ProductCategoryCreateBody(BaseModel):
|
||
"""产品分类新增请求体"""
|
||
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
category_name: str = Field(..., alias="categoryName", description="分类名称")
|
||
parent_id: str = Field(default="0", alias="parentId", description="父级分类ID,0为顶级")
|
||
category_code: Optional[str] = Field(default=None, alias="categoryCode", description="分类编码")
|
||
level: int = Field(default=1, description="层级")
|
||
status: str = Field(default="1", description="状态 1启用 0停用")
|
||
order_num: Optional[int] = Field(default=None, alias="orderNum", description="排序号")
|
||
remark: Optional[str] = Field(default=None, description="备注")
|
||
|
||
|
||
class ProductCategoryUpdateBody(BaseModel):
|
||
"""产品分类更新请求体"""
|
||
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
id: str = Field(..., description="分类ID")
|
||
category_name: Optional[str] = Field(default=None, alias="categoryName", description="分类名称")
|
||
parent_id: Optional[str] = Field(default=None, alias="parentId", description="父级分类ID")
|
||
category_code: Optional[str] = Field(default=None, alias="categoryCode", description="分类编码")
|
||
level: Optional[int] = Field(default=None, description="层级")
|
||
status: Optional[str] = Field(default=None, description="状态")
|
||
order_num: Optional[int] = Field(default=None, alias="orderNum", description="排序号")
|
||
remark: Optional[str] = Field(default=None, description="备注")
|
||
|
||
|
||
class CategoryDeleteBody(BaseModel):
|
||
"""分类删除请求体"""
|
||
|
||
id: str = Field(..., description="分类ID")
|
||
|
||
|
||
class ProductCategoryTreeBody(BaseModel):
|
||
"""产品分类树查询(密文解密后须用 Pydantic 绑定,裸 dict=None 在部分链路下会丢 body)"""
|
||
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
category_name: Optional[str] = Field(default=None, alias="categoryName", description="分类名称(模糊)")
|
||
status: Optional[str] = Field(default=None, description="状态")
|
||
|
||
|
||
class ProductUpdateStatusBody(BaseModel):
|
||
"""产品批量上下架请求体"""
|
||
|
||
ids: List[str] = Field(..., description="产品ID列表")
|
||
status: str = Field(..., description="目标状态:on=上架 off=下架")
|
||
|
||
|
||
# ── 产品 Schemas ──────────────────────────────────────────────
|
||
|
||
|
||
class ProductListBody(BaseModel):
|
||
"""产品列表查询请求体(分页)"""
|
||
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
page: int = Field(default=1, ge=1, description="页码")
|
||
pageSize: int = Field(default=10, ge=1, le=500, description="每页条数")
|
||
keyword: Optional[str] = Field(
|
||
default=None, description="搜索关键词(默认:产品名称/编号/拼音首字母)"
|
||
)
|
||
category_id: Optional[str] = Field(default=None, alias="categoryId", description="分类ID")
|
||
status: Optional[str] = Field(default=None, description="状态")
|
||
supplier_id: Optional[str] = Field(
|
||
default=None, alias="supplierId", description="报价/合同主体ID,传入则按危化品能力过滤"
|
||
)
|
||
match_product_no: Optional[bool] = Field(
|
||
default=True,
|
||
alias="matchProductNo",
|
||
description="keyword 是否匹配产品编号(选择器建议 false)",
|
||
)
|
||
match_model: Optional[bool] = Field(
|
||
default=False,
|
||
alias="matchModel",
|
||
description="keyword 是否匹配规格型号(选择器建议 true)",
|
||
)
|
||
ids: Optional[List[str]] = Field(default=None, description="导出时指定产品ID列表")
|
||
columns: Optional[List[str]] = Field(default=None, description="导出列(前端 visibleColumns prop)")
|
||
|
||
|
||
class ProductBrandItem(BaseModel):
|
||
"""产品品牌项"""
|
||
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
id: Optional[str] = None
|
||
brand_name: str = Field(..., alias="brandName", max_length=100, description="品牌名称")
|
||
status: str = Field(default="on", description="状态 on/off")
|
||
order_num: Optional[int] = Field(default=None, alias="orderNum", description="排序号")
|
||
|
||
|
||
class ProductAliasItem(BaseModel):
|
||
"""产品别名项"""
|
||
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
id: Optional[str] = None
|
||
alias_name: str = Field(..., alias="aliasName", max_length=200, description="别名")
|
||
order_num: Optional[int] = Field(default=None, alias="orderNum", description="排序号")
|
||
|
||
|
||
class ProductCreateBody(BaseModel):
|
||
"""产品新增请求体"""
|
||
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
product_no: Optional[str] = Field(default=None, alias="productNo", description="产品编号(不传则自动生成,格式 SXWL000001)")
|
||
product_name: str = Field(..., alias="productName", description="产品名称")
|
||
name_pinyin_abbr: Optional[str] = Field(
|
||
default=None, alias="namePinyinAbbr", description="产品名称拼音首字母(小写)"
|
||
)
|
||
category_id: Optional[str] = Field(default=None, alias="categoryId", description="分类ID")
|
||
category_name: Optional[str] = Field(default=None, alias="categoryName", description="分类名称")
|
||
model: Optional[str] = Field(default=None, description="规格型号")
|
||
unit: Optional[str] = Field(default=None, description="单位(产品模块不维护,留空)")
|
||
barcode: Optional[str] = Field(default=None, description="条码(不传则自动生成)")
|
||
price: Optional[Decimal] = Field(default=None, description="标准单价(产品模块不维护,留空)")
|
||
cost_price: Optional[Decimal] = Field(
|
||
default=None,
|
||
validation_alias=AliasChoices("costPrice", "cost"),
|
||
serialization_alias="costPrice",
|
||
description="成本价",
|
||
)
|
||
stock: int = Field(default=0, description="库存数量")
|
||
low_stock: int = Field(default=10, alias="lowStock", description="库存预警值")
|
||
tax_rate: Decimal = Field(default=Decimal("13.00"), alias="taxRate", description="税率(%)")
|
||
is_toxic: int = Field(default=0, alias="isToxic", description="是否易制毒 0-否 1-是")
|
||
is_explosive: int = Field(default=0, alias="isExplosive", description="是否易制爆 0-否 1-是")
|
||
status: str = Field(default="on", description="状态 on=上架 off=下架")
|
||
description: Optional[str] = Field(default=None, description="产品描述")
|
||
images: Optional[str] = Field(default=None, description="产品图片(逗号分隔 URL)")
|
||
sale_count: int = Field(default=0, alias="saleCount", description="销售次数")
|
||
order_num: Optional[int] = Field(default=None, alias="orderNum", description="排序号")
|
||
remark: Optional[str] = Field(default=None, description="备注")
|
||
brands: Optional[List[ProductBrandItem]] = Field(default=None, description="产品品牌列表")
|
||
aliases: Optional[List[ProductAliasItem]] = Field(default=None, description="产品别名列表")
|
||
|
||
|
||
class ProductUpdateBody(BaseModel):
|
||
"""产品更新请求体"""
|
||
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
id: str = Field(..., description="产品ID")
|
||
product_no: Optional[str] = Field(default=None, alias="productNo", description="产品编号")
|
||
product_name: Optional[str] = Field(default=None, alias="productName", description="产品名称")
|
||
name_pinyin_abbr: Optional[str] = Field(
|
||
default=None, alias="namePinyinAbbr", description="产品名称拼音首字母(小写)"
|
||
)
|
||
category_id: Optional[str] = Field(default=None, alias="categoryId", description="分类ID")
|
||
category_name: Optional[str] = Field(default=None, alias="categoryName", description="分类名称")
|
||
model: Optional[str] = Field(default=None, description="规格型号")
|
||
unit: Optional[str] = Field(default=None, description="单位")
|
||
barcode: Optional[str] = Field(default=None, description="条码")
|
||
price: Optional[Decimal] = Field(default=None, description="标准单价")
|
||
cost_price: Optional[Decimal] = Field(
|
||
default=None,
|
||
validation_alias=AliasChoices("costPrice", "cost"),
|
||
serialization_alias="costPrice",
|
||
description="成本价",
|
||
)
|
||
stock: Optional[int] = Field(default=None, description="库存数量")
|
||
low_stock: Optional[int] = Field(default=None, alias="lowStock", description="库存预警值")
|
||
tax_rate: Optional[Decimal] = Field(default=None, alias="taxRate", description="税率(%)")
|
||
is_toxic: Optional[int] = Field(default=None, alias="isToxic", description="是否易制毒")
|
||
is_explosive: Optional[int] = Field(default=None, alias="isExplosive", description="是否易制爆")
|
||
status: Optional[str] = Field(default=None, description="状态")
|
||
description: Optional[str] = Field(default=None, description="产品描述")
|
||
images: Optional[str] = Field(default=None, description="产品图片(逗号分隔 URL)")
|
||
sale_count: Optional[int] = Field(default=None, alias="saleCount", description="销售次数")
|
||
order_num: Optional[int] = Field(default=None, alias="orderNum", description="排序号")
|
||
remark: Optional[str] = Field(default=None, description="备注")
|
||
brands: Optional[List[ProductBrandItem]] = Field(default=None, description="产品品牌列表")
|
||
aliases: Optional[List[ProductAliasItem]] = Field(default=None, description="产品别名列表")
|
||
|
||
|
||
class ProductDetailBody(BaseModel):
|
||
"""产品详情请求体(不分页)"""
|
||
|
||
id: str = Field(..., description="产品ID")
|
||
|
||
|
||
class ProductDeleteBody(BaseModel):
|
||
"""产品删除请求体"""
|
||
|
||
id: Optional[str] = Field(default=None, description="产品ID(单条删除)")
|
||
ids: Optional[List[str]] = Field(default=None, description="产品ID列表(批量删除)")
|
||
|
||
|
||
class ProductOptionsBody(BaseModel):
|
||
"""产品下拉选项请求体(不分页,返回全量)"""
|
||
|
||
model_config = ConfigDict(populate_by_name=True)
|
||
|
||
keyword: Optional[str] = Field(default=None, description="搜索关键词(仅产品名称模糊匹配)")
|
||
category_id: Optional[str] = Field(default=None, alias="categoryId", description="分类ID")
|
||
limit: Optional[int] = Field(default=None, ge=1, le=500, description="返回条数上限(空则全量)")
|