仓储层的 list_*/count_* 此前以 keyword/status/offset/limit 四五个散参数 传参,调用点重复铺开、易漏传且不可扩展;按项目「参数 >4 封装 DTO」的 约定统一收敛为各模块的 Query 对象(如 RoleQuery、BusinessQuery), 服务层与测试调用同步调整,行为不变。 - repositories 层查询方法签名改为 (session, query),分页与筛选条件 由 Query 承载,_apply_filters 同步收口 - schemas 层补齐各模块 Query 模型(approval/business_role/report 等) - services 层调用点同步改为传 Query - tests 随之调整;新增用例:待办角色流转(财务通过后领导出现待办)、 参数预览顺序稳定 验证:全量 pytest 900 passed(11:08);单元 526 passed;ruff check 绿。 Co-Authored-By: Claude Code <noreply@anthropic.com>
104 lines
3.5 KiB
Python
104 lines
3.5 KiB
Python
"""业务岗位角色的请求/响应模型。"""
|
||
|
||
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 BusinessRoleOptionOut(BaseModel):
|
||
"""岗位下拉选项(人员档案与报表筛选共用)。
|
||
|
||
与 ``BusinessRoleOut`` 分开:下拉只需渲染用的最小字段集,不必顺带
|
||
暴露计提模板等管理字段。字段名直出蛇形(前端按同名取值)。
|
||
"""
|
||
|
||
id: int = Field(description="岗位ID")
|
||
role_code: str = Field(description="岗位编码")
|
||
role_name: str = Field(description="岗位名称")
|
||
status: int = Field(description="状态:1启用 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="状态筛选")
|