# Router层规范 ## 概述 Router 层是 HTTP 接口层,负责定义路由、权限装饰器和请求参数绑定。每个业务模块对应一个 router 文件,所有业务逻辑委托给 Service 层处理。 :::tip 设计原则 - Router 层不含业务逻辑,仅做路由定义、权限校验和参数绑定 - 装饰器顺序固定为:路由 → 权限 - Service 函数使用延迟导入,避免循环引用 ::: ## 完整示例 以岗位模块为例,`src/modules/system/position/router.py` 完整内容: ```python from flask import Blueprint, request from src.core.permission import check_permission from src.core.response import R from src.common.utils.request import parse_request_body, parse_id_list # ============================================================ # 创建 Blueprint 实例 # ============================================================ position_blue = Blueprint('position', __name__, url_prefix='/position') # ============================================================ # 查询分页数据 # ============================================================ @position_blue.route('/page', methods=['GET']) @check_permission('sys:position:page') def page(): """获取岗位分页列表""" from src.modules.system.position.service import get_position_page return get_position_page() # ============================================================ # 查询岗位详情 # ============================================================ @position_blue.route('/detail/', methods=['GET']) @check_permission('sys:position:detail') def detail(id): """根据岗位ID查询详细信息""" from src.modules.system.position.service import get_position_detail return R.ok(data=get_position_detail(id)) # ============================================================ # 获取岗位列表 # ============================================================ @position_blue.route('/list', methods=['GET']) @check_permission('sys:position:list') def get_position_list(): """获取所有岗位列表(无分页,用于下拉选择)""" from src.modules.system.position.service import get_position_list return R.ok(data=get_position_list()) # ============================================================ # 添加岗位 # ============================================================ @position_blue.route('/add', methods=['POST']) @check_permission('sys:position:add') def add(): """新增岗位""" from src.modules.system.position.service import add_position from src.modules.system.position.schema import PositionForm data = parse_request_body(request, PositionForm) return add_position(data) # ============================================================ # 更新岗位 # ============================================================ @position_blue.route('/update', methods=['PUT']) @check_permission('sys:position:update') def update(): """更新岗位信息""" from src.modules.system.position.service import update_position from src.modules.system.position.schema import PositionForm data = parse_request_body(request, PositionForm) return update_position(data) # ============================================================ # 删除岗位 # ============================================================ @position_blue.route('/delete/', methods=['DELETE']) @check_permission('sys:position:delete') def delete(id): """根据ID删除单个岗位""" from src.modules.system.position.service import delete_position return delete_position(id) # ============================================================ # 设置岗位状态 # ============================================================ @position_blue.route('/status', methods=['PUT']) @check_permission('sys:position:status') def status(): """修改岗位状态(启用/禁用)""" from src.modules.system.position.service import update_position_status from src.modules.system.position.schema import PositionStatusForm data = parse_request_body(request, PositionStatusForm) return update_position_status(data) # ============================================================ # 批量删除岗位 # ============================================================ @position_blue.route('/batchDelete', methods=['DELETE']) @check_permission('sys:position:batchDelete') def batch_delete(): """批量删除岗位""" from src.modules.system.position.service import batch_delete_positions id_list = parse_id_list(request) return batch_delete_positions(id_list) ``` ## 装饰器说明 | 装饰器 | 作用 | 位置 | |:----|:----|:----| | `@position_blue.route()` | 定义 HTTP 方法和路径 | 最外层 | | `@check_permission('sys:position:page')` | RBAC 权限校验 | 路由装饰器之后 | **装饰器顺序必须严格遵守**:路由 → 权限。顺序错误会导致功能异常。 ## 权限字符串命名规范 ``` sys:{module}:{action} ``` | 动作 | HTTP 方法 | 说明 | |:----|:----|:----| | `page` | GET | 分页查询 | | `detail` | GET | 详情查询 | | `list` | GET | 列表查询 | | `add` | POST | 新增 | | `update` | PUT | 更新 | | `delete` | DELETE | 单条删除 | | `batchDelete` | DELETE | 批量删除 | | `status` | PUT | 状态变更 | ## 请求参数解析 ```python from src.common.utils.request import parse_request_body, parse_id_list # 解析请求体 data = parse_request_body(request, PositionForm) # 解析ID列表 id_list = parse_id_list(request) ``` | 函数 | 作用 | |:----|:----| | `parse_request_body(request, FormClass)` | 从请求体解析 JSON 并校验 | | `parse_id_list(request)` | 从请求体解析 ID 数组 | ## 响应封装 - **Service 返回 R 对象的**(如 `add_position`、`update_position`):直接 `return` - **Service 返回原始数据的**(如 `get_position_detail`):用 `R.ok(data=...)` 包装 ## 模块注册 在 `src/routes/v1.py` 中注册模块: ```python from src.modules.system.position.router import position_blue BLUEPRINTS = [ # ... 其他模块 position_blue, # ... 其他模块 ] ``` ## 开发要点 1. **Router 层不含业务逻辑**:仅做路由定义、权限校验和参数绑定 2. **装饰器顺序**:路由 → 权限 3. **使用 `parse_request_body` 解析请求体**:自动执行 WTForms 校验 4. **使用 `parse_id_list` 解析批量操作的 ID 数组** 5. **Service 函数使用延迟导入**:避免循环引用 6. **权限字符串与代码完全一致**:确保权限校验正确 ## 总结 Router 层是 HTTP 请求的入口,负责路由定义、权限校验和参数绑定,不含业务逻辑。装饰器顺序固定为路由 → 权限,使用 `parse_request_body` 解析请求体,使用 `parse_id_list` 解析批量操作的 ID 数组。Service 函数使用延迟导入,避免循环引用。