"""业务岗位角色管理接口(需求 #28:岗位可扩展 + 绑定公式模板)。 保护规则(内置岗位禁删禁停、编码不可改、模板须在注册表内)见 `business_role_service` 模块 docstring。前端会禁用相应按钮,但**后端校验是唯一防线**。 全部要求管理员权限;写操作经 `RequestMetaDep` 带入请求元信息由 service 层写入操作日志。 """ from typing import Annotated from fastapi import APIRouter, Path, Query from app.api.deps import AdminUser, DbSession, RequestMetaDep from app.core.response import ApiResponse, PageResult, ok from app.schemas.business_role import ( BusinessRoleCreateIn, BusinessRoleOut, BusinessRoleQuery, BusinessRoleStatusIn, BusinessRoleUpdateIn, TemplateOptionOut, ) from app.services import business_role_service router = APIRouter(prefix="/business-roles", tags=["系统管理 · 岗位角色"]) @router.get("", response_model=ApiResponse[PageResult[BusinessRoleOut]], summary="岗位列表") def list_roles( session: DbSession, _: AdminUser, query: Annotated[BusinessRoleQuery, Query()], ) -> ApiResponse[PageResult[BusinessRoleOut]]: """分页查询业务岗位角色(含停用)。""" return ok(business_role_service.list_roles(session, query)) @router.post("", response_model=ApiResponse[BusinessRoleOut], summary="新增岗位") def create_role( session: DbSession, current_user: AdminUser, payload: BusinessRoleCreateIn, meta: RequestMetaDep, ) -> ApiResponse[BusinessRoleOut]: """新增岗位。编码全局唯一、创建后不可修改;模板须在注册表内;新岗位默认启用。""" return ok( business_role_service.create_role(session, payload, operator=current_user, meta=meta), message="岗位已创建", ) @router.get( "/meta/templates", response_model=ApiResponse[list[TemplateOptionOut]], summary="公式模板下拉", ) def list_template_options(session: DbSession, _: AdminUser) -> ApiResponse[list[TemplateOptionOut]]: """取公式模板下拉(含声明参数,供岗位表单展示)。 定义在 `/{role_id}` 之前:静态路径优先,避免 "meta" 被当作路径参数解析。 """ return ok(business_role_service.list_template_options()) @router.get("/{role_id}", response_model=ApiResponse[BusinessRoleOut], summary="岗位详情") def get_role( session: DbSession, _: AdminUser, role_id: Annotated[int, Path(ge=1)], ) -> ApiResponse[BusinessRoleOut]: """按主键查询岗位详情。""" return ok(business_role_service.get_role(session, role_id)) @router.put("/{role_id}", response_model=ApiResponse[BusinessRoleOut], summary="编辑岗位") def update_role( session: DbSession, current_user: AdminUser, role_id: Annotated[int, Path(ge=1)], payload: BusinessRoleUpdateIn, meta: RequestMetaDep, ) -> ApiResponse[BusinessRoleOut]: """编辑岗位名称/模板/说明/排序。 **不含 role_code** —— 编码是参数维度值与模板绑定的锚点。 """ return ok( business_role_service.update_role( session, role_id, payload, operator=current_user, meta=meta ), message="已保存", ) @router.put( "/{role_id}/status", response_model=ApiResponse[BusinessRoleOut], summary="启用/禁用岗位" ) def set_status( session: DbSession, current_user: AdminUser, role_id: Annotated[int, Path(ge=1)], payload: BusinessRoleStatusIn, meta: RequestMetaDep, ) -> ApiResponse[BusinessRoleOut]: """启用或禁用岗位。内置岗位不可停用。""" result = business_role_service.set_status( session, role_id, payload.status, operator=current_user, meta=meta ) return ok(result, message="已启用" if payload.status == 1 else "已禁用") @router.delete("/{role_id}", response_model=ApiResponse[None], summary="删除岗位") def delete_role( session: DbSession, current_user: AdminUser, role_id: Annotated[int, Path(ge=1)], meta: RequestMetaDep, ) -> ApiResponse[None]: """删除岗位。内置岗位不可删除;有人员引用的岗位不可删除。""" business_role_service.delete_role(session, role_id, operator=current_user, meta=meta) return ok(message="岗位已删除")