Files
2026-09-12 23:12:15 +08:00

288 lines
7.8 KiB
Markdown
Raw Permalink 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 层(Flask Blueprint)
Router 层是 HTTP 接口层,负责定义路由、权限装饰器和请求参数解析。每个业务模块对应一个 router 文件,所有业务逻辑委托给 Service 层处理。
## 文件位置
```
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.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():
"""
查询岗位分页数据
Returns:
JSON: 岗位分页数据,包含 records(数据列表)、total(总记录数)等
"""
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):
"""
查询岗位详情
Args:
id: 岗位ID
Returns:
JSON: 岗位详细信息
"""
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():
"""
添加岗位
Returns:
JSON: 操作结果
"""
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():
"""
更新岗位
Returns:
JSON: 操作结果
"""
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):
"""
删除岗位
Args:
id: 岗位ID(支持逗号分隔的多个ID)
Returns:
JSON: 操作结果
"""
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():
"""
批量删除岗位
Request Body:
[1, 2, 3] # 岗位ID数组
Returns:
JSON: 操作结果
"""
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():
"""
设置岗位状态
Returns:
JSON: 操作结果
"""
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():
"""
获取岗位数据列表(无分页)
用于下拉选择框,支持 name 模糊匹配。
Returns:
JSON: 岗位列表数据
"""
result = position.get_position_list(request.args)
return R.ok(data=result)
```
## 代码解析
### Blueprint 实例
```python
position_blue = Blueprint('position', __name__, url_prefix='/position')
```
每个 router 文件创建一个独立的 `Blueprint` 实例,后续在 `src/routes/v1.py` 中统一注册。
### 装饰器说明
| 装饰器 | 作用 | 位置 |
|:---|:---|:---|
| `@position_blue.route()` | 定义 HTTP 方法和路径 | 最外层 |
| `@check_permission('sys:position:page')` | RBAC 权限校验 | 路由装饰器之后 |
| `@check_demo` | 演示模式拦截(写操作) | 权限装饰器之后 |
| `@operation_log("岗位管理", "查询")` | 操作日志记录 | 最内层 |
**装饰器顺序必须严格遵守**:路由 → 权限 → 演示模式 → 操作日志。
### 权限字符串命名规范
```
sys:{module}:{action}
```
| 动作 | HTTP 方法 | 说明 |
|:---|:---|:---|
| `page` | GET | 分页查询 |
| `detail` | GET | 详情查询 |
| `list` | GET | 列表查询(无分页) |
| `add` | POST | 新增 |
| `update` | PUT | 更新 |
| `delete` | DELETE | 单条删除 |
| `batchDelete` | DELETE | 批量删除 |
| `status` | PUT | 状态变更 |
### 参数解析
```python
# 解析请求体 JSON(POST/PUT)
json_data, err = parse_request_body()
if err:
return R.failed(msg=err)
# 解析批量删除的 ID 数组(DELETE)
id_str, err = parse_batch_ids()
if err:
return err
# 获取查询参数(GET)
params = request.args # 如 request.args.get('name')
```
### 响应封装
```python
# 成功响应
R.ok(data=data) # 返回数据
R.ok(msg="添加成功") # 返回消息
R.ok(data=data, msg="ok") # 同时返回数据和消息
# 失败响应
R.failed(msg="参数错误") # 返回错误消息
```
## 开发要点
1. **只需两步注册**:一行导入 Blueprint,一行添加到 `BLUEPRINTS` 列表
2. **prefix 与模块目录名保持一致**:便于代码导航
3. **所有业务蓝图都挂在 `v1` 路由组下**:自动享受 `login_required` 认证保护
4. **装饰器顺序**:路由 → `@check_permission` → `@check_demo` → `@operation_log`
5. **Router 层不处理业务逻辑**:只做参数解析和响应封装,业务逻辑委托 Service 层
## 总结
Router 层通过 Flask Blueprint 定义路由,使用 `@check_permission` 实现 RBAC 权限控制,`@check_demo` 拦截演示模式写操作,`@operation_log` 自动记录操作日志。参数解析通过 `parse_request_body()` / `parse_batch_ids()` 完成,响应封装通过 `R.ok()` / `R.failed()` 统一格式。