# Endpoint 层 Endpoint 层是 HTTP 接口层,负责定义路由、权限装饰器和请求参数绑定。每个业务模块对应一个 endpoint 文件,所有业务逻辑委托给 Service 层处理。 ## 文件位置 ``` src/api/v1/endpoints/position.py ``` ## 完整代码 ```python from fastapi import APIRouter, Request from modules.system.position.schemas import PositionForm, PositionStatusForm from core.access_decorators import check_demo from core.access_decorators import permission_required from modules.system.position.service import position_service from core import R # ============================================================ # 创建路由实例 # ============================================================ router = APIRouter() # ============================================================ # 查询分页数据 # ============================================================ @router.get('/page', summary='查询分页数据') @permission_required("sys:position:page") def page(request: Request): """ 获取岗位分页列表 :param request: FastAPI请求对象 :return: 分页数据响应 """ return position_service.get_page(request) # ============================================================ # 查询岗位详情 # ============================================================ @router.get('/detail/{id}', summary='查询岗位详情') @permission_required("sys:position:detail") def detail(request: Request, id): """ 根据岗位ID查询详细信息 :param request: FastAPI请求对象 :param id: 岗位ID(路径参数) :return: 岗位详情数据 """ return R.ok(data=position_service.get_detail(id)) # ============================================================ # 获取岗位列表 # ============================================================ @router.get('/list', summary='获取岗位列表') @permission_required("sys:position:list") def get_position_list(request: Request): """ 获取所有岗位列表(无分页,用于下拉选择) :param request: FastAPI请求对象 :return: 岗位列表数据 """ return R.ok(data=position_service.get_position_list(request)) # ============================================================ # 添加岗位 # ============================================================ @router.post('/add', summary='添加岗位') @permission_required("sys:position:add") @check_demo def add(request: Request, data: PositionForm): """ 新增岗位 :param request: FastAPI请求对象 :param data: 岗位表单数据 :return: 添加结果响应 """ return position_service.add(request, data) # ============================================================ # 更新岗位 # ============================================================ @router.put('/update', summary='更新岗位') @permission_required("sys:position:update") @check_demo def update(request: Request, data: PositionForm): """ 更新岗位信息 :param request: FastAPI请求对象 :param data: 岗位表单数据(必须包含 id 字段) :return: 更新结果响应 """ return position_service.update(request, data) # ============================================================ # 删除岗位 # ============================================================ @router.delete('/delete/{id}', summary='删除岗位') @permission_required("sys:position:delete") @check_demo def delete(request: Request, id): """ 根据ID删除单个岗位 :param request: FastAPI请求对象 :param id: 岗位ID(路径参数) :return: 删除结果响应 """ return position_service.delete(id) # ============================================================ # 设置岗位状态 # ============================================================ @router.put('/status', summary='设置岗位状态') @permission_required("sys:position:status") @check_demo def status(request: Request, data: PositionStatusForm): """ 修改岗位状态(启用/禁用) :param request: FastAPI请求对象 :param data: 状态表单数据 :return: 状态更新结果响应 """ return position_service.update_status(request, data) # ============================================================ # 批量删除岗位 # ============================================================ @router.delete('/batchDelete', summary='批量删除岗位') @permission_required("sys:position:batchDelete") @check_demo async def batch_delete(request: Request): """ 批量删除岗位(请求体需传入 ID 数组) Request Body 示例: [1, 2, 3] # 岗位ID数组 :param request: FastAPI请求对象 :return: 批量删除结果响应 """ return await position_service.batch_delete(request) ``` ## 代码解析 ### 路由实例 ```python router = APIRouter() ``` 每个 endpoint 文件创建一个独立的 `APIRouter` 实例,后续在 `router.py` 中统一注册。 ### 装饰器说明 | 装饰器 | 作用 | 位置 | |:----|:----|:----| | `@router.get/post/put/delete` | 定义 HTTP 方法和路径 | 最外层 | | `@permission_required("sys:position:page")` | RBAC 权限校验 | 路由装饰器之后 | | `@check_demo` | 演示模式拦截(仅写操作) | 权限装饰器之后 | **装饰器顺序必须严格遵守**:路由 → 权限 → 演示模式。顺序错误会导致功能异常。 ### 权限字符串命名规范 ``` sys:{module}:{action} ``` | 动作 | HTTP 方法 | 说明 | |:----|:----|:----| | `page` | GET | 分页查询 | | `detail` | GET | 详情查询 | | `list` | GET | 列表查询 | | `add` | POST | 新增 | | `update` | PUT | 更新 | | `delete` | DELETE | 单条删除 | | `batchDelete` | DELETE | 批量删除 | | `status` | PUT | 状态变更 | ### request 参数 ```python def page(request: Request): ``` 所有 endpoint 都接收 `request: Request` 参数,即使当前不直接使用。原因: 1. Service 层需要通过 `request.query_params` 读取分页参数 2. 中间件通过 `request.state` 传递用户信息 3. 保持接口一致性 ### Pydantic 表单参数 ```python def add(request: Request, data: PositionForm): ``` `data: PositionForm` 声明为函数参数后,FastAPI 自动: 1. 从请求体读取 JSON 2. 解析为 `PositionForm` 实例 3. 执行 Pydantic 校验 4. 校验失败返回标准错误响应 ### 响应封装 - **Service 返回 R 对象的**(如 `add`、`update`、`delete`):直接 `return` - **Service 返回原始数据的**(如 `get_detail`、`get_position_list`):用 `R.ok(data=...)` 包装 ### 批量删除的异步处理 ```python async def batch_delete(request: Request): return await position_service.batch_delete(request) ``` `batch_delete` 端点必须是 `async def`,因为需要 `await parse_batch_ids(request)` 异步读取请求体。 ## 开发要点 1. **endpoint 层不含业务逻辑**:仅做路由定义、权限校验和参数绑定 2. **装饰器顺序**:路由 → 权限 → 演示模式 3. **所有端点都需要 `request: Request` 参数** 4. **写操作必须加 `@check_demo`**:防止演示环境被修改 5. **批量删除端点必须是 `async def`**:异步读取请求体 ## 总结 Endpoint 层是 HTTP 请求的入口,负责路由定义、权限校验和参数绑定,不含业务逻辑。装饰器顺序固定为路由 → 权限 → 演示模式,写操作必须加 `@check_demo`,批量删除端点必须是 `async def`。