# Router层规范 ## 概述 Router 层是 HTTP 接口层,负责定义路由、权限装饰器和请求参数解析。每个业务模块对应一个 router 文件,所有业务逻辑委托给 Service 层处理。 :::tip 设计原则 - Router 层不含业务逻辑,仅做路由定义、权限校验和参数解析 - 装饰器顺序固定为:路由 → 权限 → 演示模式 → 操作日志 - Service 模块在文件顶部统一导入(`from src.modules.system.position import service as position`) - Router 负责调用 `R.ok()` / `R.failed()` 封装响应,Service 不返回 R 对象 ::: ## 完整示例 以岗位模块为例,以下为 `src/modules/system/position/router.py` 的核心代码(省略版权头): ```python """ 岗位管理控制器模块 ============================================================= 提供岗位(Position)相关的API接口,包括: - 岗位分页查询 - 岗位详情查询 - 岗位添加、更新、删除 - 岗位状态设置 - 岗位数据列表获取 """ from flask import Blueprint, request from src.middleware import check_demo, operation_log from src.core.permission import check_permission from src.modules.system.position import service as position from src.common.utils.request import parse_batch_ids, parse_request_body from src.core import response as R position_blue = Blueprint('position', __name__, url_prefix='/position') # ============================================================ # 岗位分页查询 # ============================================================ @position_blue.route('/page', methods=["GET"]) @check_permission("sys:position:page") @operation_log("岗位管理", "查询") def page(): """查询岗位分页数据""" return R.ok(data=position.get_position_page(request.args)) # ============================================================ # 岗位详情查询 # ============================================================ @position_blue.route('/detail/', methods=["GET"]) @check_permission("sys:position:detail") @operation_log("岗位管理", "详情") def detail(id): """查询岗位详情""" data = position.get_position_detail(id) return R.ok(data=data) # ============================================================ # 岗位添加 # ============================================================ @position_blue.route('/add', methods=["POST"]) @check_permission("sys:position:add") @check_demo @operation_log("岗位管理", "新增") def add(): """添加岗位""" json_data, err = parse_request_body() if err: return R.failed(msg=err) position.add_position(json_data) return R.ok(msg="添加成功") # ============================================================ # 岗位更新 # ============================================================ @position_blue.route('/update', methods=["PUT"]) @check_permission("sys:position:update") @check_demo @operation_log("岗位管理", "修改") def update(): """更新岗位""" json_data, err = parse_request_body() if err: return R.failed(msg=err) position.update_position(json_data) return R.ok(msg="更新成功") # ============================================================ # 岗位删除 # ============================================================ @position_blue.route('/delete/', methods=["DELETE"]) @check_permission("sys:position:delete") @check_demo @operation_log("岗位管理", "删除") def delete(id): """删除岗位(支持逗号分隔的多个ID)""" count = position.delete_positions(id) return R.ok(msg=f"本次共删除{count}条数据") # ============================================================ # 岗位批量删除 # ============================================================ @position_blue.route('/batchDelete', methods=["DELETE"]) @check_permission("sys:position:batchDelete") @check_demo @operation_log("岗位管理", "批量删除") def batch_delete(): """批量删除岗位""" id_str, err = parse_batch_ids() if err: return err count = position.delete_positions(id_str) return R.ok(msg=f"本次共删除{count}条数据") # ============================================================ # 岗位状态设置 # ============================================================ @position_blue.route('/status', methods=["PUT"]) @check_permission("sys:position:status") @check_demo @operation_log("岗位管理", "状态变更") def status(): """设置岗位状态""" json_data, err = parse_request_body() if err: return R.failed(msg=err) position.update_position_status(json_data) return R.ok(msg="设置成功") # ============================================================ # 岗位列表获取(无分页) # ============================================================ @position_blue.route('/list', methods=["GET"]) @check_permission("sys:position:list") @operation_log("岗位管理", "查询") def get_position_list(): """获取岗位数据列表(无分页,用于下拉选择)""" result = position.get_position_list(request.args) return R.ok(data=result) ``` ## 装饰器说明 | 装饰器 | 作用 | 适用场景 | |:----|:----|:----| | `@position_blue.route()` | 定义 HTTP 方法和路径 | 所有接口 | | `@check_permission('sys:position:page')` | RBAC 权限校验 | 所有接口 | | `@check_demo` | 演示模式拦截(禁止写操作) | 写操作(add/update/delete/status/batchDelete) | | `@operation_log("岗位管理", "查询")` | 操作日志自动记录 | 所有接口 | **装饰器顺序必须严格遵守**:路由 → 权限 → 演示模式 → 操作日志。 ## 导入规范 ```python # 模块顶部统一导入 from src.middleware import check_demo, operation_log from src.core.permission import check_permission from src.modules.system.position import service as position from src.common.utils.request import parse_batch_ids, parse_request_body from src.core import response as R ``` | 导入项 | 来源 | 说明 | |:----|:----|:----| | `check_demo` | `src.middleware` | 演示模式装饰器 | | `operation_log` | `src.middleware` | 操作日志装饰器 | | `check_permission` | `src.core.permission` | 权限校验装饰器 | | `service as position` | 业务模块 | Service 层代理函数 | | `parse_request_body` | `src.common.utils.request` | 解析请求体 JSON | | `parse_batch_ids` | `src.common.utils.request` | 解析批量删除的 ID 数组 | | `R` | `src.core` | 统一响应封装 | ## 请求参数解析 ```python # 解析请求体(返回元组:数据, 错误信息) json_data, err = parse_request_body() if err: return R.failed(msg=err) # 解析批量删除的ID数组(返回元组:ID字符串, 错误信息) id_str, err = parse_batch_ids() if err: return err ``` | 函数 | 作用 | 返回值 | |:----|:----|:----| | `parse_request_body()` | 从请求体解析 JSON | `(dict, None)` 或 `(None, error_msg)` | | `parse_batch_ids()` | 从请求体解析 ID 数组 | `(id_str, None)` 或 `(None, R.failed(...))` | ## 响应封装 Router 层负责调用 `R.ok()` / `R.failed()` 封装响应: ```python # 查询类:Service 返回数据,Router 用 R.ok(data=...) 包装 return R.ok(data=position.get_position_page(request.args)) # 写操作类:Service 执行成功后无返回值,Router 返回成功消息 position.add_position(json_data) return R.ok(msg="添加成功") # 删除类:Service 返回删除数量,Router 格式化消息 count = position.delete_positions(id) return R.ok(msg=f"本次共删除{count}条数据") ``` :::warning Service 不返回 R 对象 Service 层入参为数据字典、出参为纯数据(或 None),业务失败通过 `raise BizError` 表达。Router 层负责封装 `R.ok()` / `R.failed()` 响应。 ::: ## 权限字符串命名规范 ``` sys:{module}:{action} ``` | 动作 | HTTP 方法 | 说明 | |:----|:----|:----| | `page` | GET | 分页查询 | | `detail` | GET | 详情查询 | | `list` | GET | 列表查询 | | `add` | POST | 新增 | | `update` | PUT | 更新 | | `delete` | DELETE | 单条删除 | | `batchDelete` | DELETE | 批量删除 | | `status` | PUT | 状态变更 | ## 模块注册 在 `src/routes/v1.py` 中注册模块: ```python from src.modules.system.position.router import position_blue BLUEPRINTS = [ # ... 其他模块 position_blue, # ... 其他模块 ] ``` ## 开发要点 1. **Router 层不含业务逻辑**:仅做路由定义、权限校验和参数解析 2. **装饰器顺序**:路由 → 权限 → 演示模式 → 操作日志 3. **Service 模块顶部统一导入**:`from src.modules.system.position import service as position` 4. **使用 `parse_request_body()` 解析请求体**:返回 `(data, err)` 元组 5. **使用 `parse_batch_ids()` 解析批量操作的 ID 数组** 6. **Router 负责封装响应**:Service 不返回 R 对象 ## 总结 Router 层是 HTTP 请求的入口,负责路由定义、权限校验和参数解析,不含业务逻辑。装饰器顺序固定为路由 → 权限 → 演示模式 → 操作日志。Service 模块在文件顶部统一导入,Router 负责调用 `R.ok()` / `R.failed()` 封装响应,Service 不返回 R 对象。