优化更新
This commit is contained in:
@@ -2,134 +2,234 @@
|
||||
|
||||
## 概述
|
||||
|
||||
Router 层是 HTTP 接口层,负责定义路由、权限装饰器和请求参数绑定。每个业务模块对应一个 router 文件,所有业务逻辑委托给 Service 层处理。
|
||||
Router 层是 HTTP 接口层,负责定义路由、权限装饰器和请求参数解析。每个业务模块对应一个 router 文件,所有业务逻辑委托给 Service 层处理。
|
||||
|
||||
:::tip 设计原则
|
||||
- Router 层不含业务逻辑,仅做路由定义、权限校验和参数绑定
|
||||
- 装饰器顺序固定为:路由 → 权限
|
||||
- Service 函数使用延迟导入,避免循环引用
|
||||
- Router 层不含业务逻辑,仅做路由定义、权限校验和参数解析
|
||||
- 装饰器顺序固定为:路由 → 权限 → 演示模式 → 操作日志
|
||||
- Service 模块在文件顶部统一导入(`from src.modules.system.position import service as position`)
|
||||
- Router 负责调用 `R.ok()` / `R.failed()` 封装响应,Service 不返回 R 对象
|
||||
:::
|
||||
|
||||
## 完整示例
|
||||
|
||||
以岗位模块为例,`src/modules/system/position/router.py` 完整内容:
|
||||
以岗位模块为例,以下为 `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.core.response import R
|
||||
from src.common.utils.request import parse_request_body, parse_id_list
|
||||
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
|
||||
|
||||
# ============================================================
|
||||
# 创建 Blueprint 实例
|
||||
# ============================================================
|
||||
position_blue = Blueprint('position', __name__, url_prefix='/position')
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 查询分页数据
|
||||
# 岗位分页查询
|
||||
# ============================================================
|
||||
@position_blue.route('/page', methods=['GET'])
|
||||
@check_permission('sys:position:page')
|
||||
|
||||
@position_blue.route('/page', methods=["GET"])
|
||||
@check_permission("sys:position:page")
|
||||
@operation_log("岗位管理", "查询")
|
||||
def page():
|
||||
"""获取岗位分页列表"""
|
||||
from src.modules.system.position.service import get_position_page
|
||||
return get_position_page()
|
||||
"""查询岗位分页数据"""
|
||||
return R.ok(data=position.get_position_page(request.args))
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 查询岗位详情
|
||||
# 岗位详情查询
|
||||
# ============================================================
|
||||
@position_blue.route('/detail/<int:id>', methods=['GET'])
|
||||
@check_permission('sys:position:detail')
|
||||
|
||||
@position_blue.route('/detail/<int:id>', methods=["GET"])
|
||||
@check_permission("sys:position:detail")
|
||||
@operation_log("岗位管理", "详情")
|
||||
def detail(id):
|
||||
"""根据岗位ID查询详细信息"""
|
||||
from src.modules.system.position.service import get_position_detail
|
||||
return R.ok(data=get_position_detail(id))
|
||||
"""查询岗位详情"""
|
||||
data = position.get_position_detail(id)
|
||||
return R.ok(data=data)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 获取岗位列表
|
||||
# 岗位添加
|
||||
# ============================================================
|
||||
@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')
|
||||
@position_blue.route('/add', methods=["POST"])
|
||||
@check_permission("sys:position:add")
|
||||
@check_demo
|
||||
@operation_log("岗位管理", "新增")
|
||||
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)
|
||||
"""添加岗位"""
|
||||
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')
|
||||
|
||||
@position_blue.route('/update', methods=["PUT"])
|
||||
@check_permission("sys:position:update")
|
||||
@check_demo
|
||||
@operation_log("岗位管理", "修改")
|
||||
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)
|
||||
"""更新岗位"""
|
||||
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/<int:id>', methods=['DELETE'])
|
||||
@check_permission('sys:position:delete')
|
||||
|
||||
@position_blue.route('/delete/<string:id>', methods=["DELETE"])
|
||||
@check_permission("sys:position:delete")
|
||||
@check_demo
|
||||
@operation_log("岗位管理", "删除")
|
||||
def delete(id):
|
||||
"""根据ID删除单个岗位"""
|
||||
from src.modules.system.position.service import delete_position
|
||||
return delete_position(id)
|
||||
"""删除岗位(支持逗号分隔的多个ID)"""
|
||||
count = position.delete_positions(id)
|
||||
return R.ok(msg=f"本次共删除{count}条数据")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 设置岗位状态
|
||||
# 岗位批量删除
|
||||
# ============================================================
|
||||
@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')
|
||||
@position_blue.route('/batchDelete', methods=["DELETE"])
|
||||
@check_permission("sys:position:batchDelete")
|
||||
@check_demo
|
||||
@operation_log("岗位管理", "批量删除")
|
||||
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)
|
||||
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 权限校验 | 路由装饰器之后 |
|
||||
| `@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()` 响应。
|
||||
:::
|
||||
|
||||
## 权限字符串命名规范
|
||||
|
||||
@@ -148,28 +248,6 @@ sys:{module}:{action}
|
||||
| `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` 中注册模块:
|
||||
@@ -186,13 +264,13 @@ BLUEPRINTS = [
|
||||
|
||||
## 开发要点
|
||||
|
||||
1. **Router 层不含业务逻辑**:仅做路由定义、权限校验和参数绑定
|
||||
2. **装饰器顺序**:路由 → 权限
|
||||
3. **使用 `parse_request_body` 解析请求体**:自动执行 WTForms 校验
|
||||
4. **使用 `parse_id_list` 解析批量操作的 ID 数组**
|
||||
5. **Service 函数使用延迟导入**:避免循环引用
|
||||
6. **权限字符串与代码完全一致**:确保权限校验正确
|
||||
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 请求的入口,负责路由定义、权限校验和参数绑定,不含业务逻辑。装饰器顺序固定为路由 → 权限,使用 `parse_request_body` 解析请求体,使用 `parse_id_list` 解析批量操作的 ID 数组。Service 函数使用延迟导入,避免循环引用。
|
||||
Router 层是 HTTP 请求的入口,负责路由定义、权限校验和参数解析,不含业务逻辑。装饰器顺序固定为路由 → 权限 → 演示模式 → 操作日志。Service 模块在文件顶部统一导入,Router 负责调用 `R.ok()` / `R.failed()` 封装响应,Service 不返回 R 对象。
|
||||
|
||||
Reference in New Issue
Block a user