199 lines
6.7 KiB
Markdown
199 lines
6.7 KiB
Markdown
# 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/<int:id>', 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/<int:id>', 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 函数使用延迟导入,避免循环引用。
|