Files
djangoadmin-wiki/DjangoAdmin_Flask_EleVue/zh/volume7/router.md
T
2026-09-13 20:29:19 +08:00

277 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/<int:id>', 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/<string:id>', 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 对象。