"""业务岗位角色的请求/响应模型。""" import re from pydantic import BaseModel, ConfigDict, Field, field_validator from app.core.response import PageQuery # 岗位编码规则与系统角色一致:小写字母开头,后接小写字母/数字/下划线,长度 2–32。 # 编码会作为参数维度值(param_values.dimension_value)与核算模板绑定的锚点, # 保持单一风格避免大小写/全角混用造成对照困难 BUSINESS_ROLE_CODE_PATTERN = re.compile(r"^[a-z][a-z0-9_]{1,31}$") class BusinessRoleOut(BaseModel): """岗位响应(列表与详情共用)。""" model_config = ConfigDict(from_attributes=True) id: int role_code: str role_name: str calc_template: str description: str | None = None sort_order: int = 0 status: int = 1 is_builtin: int = 0 class TemplateParamOut(BaseModel): """模板声明的参数(供岗位表单展示,含当前口径说明)。""" code: str name: str class TemplateOptionOut(BaseModel): """公式模板下拉项。""" code: str name: str description: str params: list[TemplateParamOut] class BusinessRoleCreateIn(BaseModel): """新增岗位。""" model_config = ConfigDict(str_strip_whitespace=True) role_code: str = Field(min_length=2, max_length=32, description="岗位编码,创建后不可修改") role_name: str = Field(min_length=1, max_length=64, description="岗位名称") calc_template: str = Field(min_length=1, max_length=32, description="计提公式模板编码") description: str | None = Field(default=None, max_length=255) sort_order: int = Field(default=0, ge=0, description="显示排序,越小越靠前") @field_validator("role_code") @classmethod def _check_code(cls, value: str) -> str: if not BUSINESS_ROLE_CODE_PATTERN.match(value): raise ValueError("岗位编码须以小写字母开头,仅可含小写字母、数字与下划线") return value class BusinessRoleUpdateIn(BaseModel): """编辑岗位。 **不含 role_code** —— 编码是参数维度值与模板绑定的锚点(同系统角色的保护理由)。 前端表单全量提交,故为全量替换语义。 """ model_config = ConfigDict(str_strip_whitespace=True) role_name: str = Field(min_length=1, max_length=64, description="岗位名称") calc_template: str = Field(min_length=1, max_length=32, description="计提公式模板编码") description: str | None = Field(default=None, max_length=255) sort_order: int = Field(default=0, ge=0, description="显示排序,越小越靠前") class BusinessRoleStatusIn(BaseModel): """启用/禁用岗位。""" status: int = Field(ge=0, le=1, description="1启用 0禁用") class BusinessRoleQuery(PageQuery): """岗位列表查询条件。""" keyword: str | None = Field(default=None, max_length=64, description="编码或名称,模糊匹配") status: int | None = Field(default=None, ge=0, le=1, description="状态筛选")