482 lines
14 KiB
Markdown
482 lines
14 KiB
Markdown
# 视图层(View)
|
||
|
||
视图层是模块开发的第四步,负责处理 HTTP 请求并调用 Service 层的业务逻辑。项目使用 Django 的类视图(Class-Based Views),每个视图类对应一个 API 端点。
|
||
|
||
## 文件位置
|
||
|
||
```
|
||
application/example/views.py
|
||
```
|
||
|
||
## 完整代码
|
||
|
||
```python
|
||
from django.utils.decorators import method_decorator
|
||
from django.views import View
|
||
|
||
from application.example import services
|
||
from config.env import DJANGO_DEMO
|
||
from middleware.login_middleware import check_login
|
||
from middleware.permission_middleware import PermissionRequired
|
||
from utils import R
|
||
from application.operation_log.constants import LogType
|
||
from application.operation_log.decorators import operation_log
|
||
|
||
|
||
# =============================================================
|
||
# 分页查询视图
|
||
# =============================================================
|
||
|
||
@method_decorator(check_login, name="get")
|
||
class ExamplePageView(PermissionRequired, View):
|
||
"""
|
||
案例分页数据查询视图
|
||
|
||
处理GET请求,返回案例列表的分页数据。
|
||
支持按案例名称、案例类型、案例状态等条件筛选。
|
||
|
||
URL: /example/page
|
||
请求方法: GET
|
||
权限要求:sys:example:page
|
||
"""
|
||
|
||
# 方法权限标识
|
||
permission_required = ("sys:example:page",)
|
||
|
||
@operation_log(
|
||
title="案例管理-查询分页列表",
|
||
log_type=LogType.QUERY
|
||
)
|
||
def get(self, request):
|
||
"""
|
||
处理GET请求,返回案例分页数据
|
||
|
||
Args:
|
||
request: HttpRequest对象,包含分页参数和筛选条件
|
||
- pageNo: 当前页码
|
||
- pageSize: 每页记录数
|
||
- name: 案例名称(可选,模糊查询)
|
||
- type: 案例类型(可选,精确匹配)
|
||
- status: 案例状态(可选,精确匹配)
|
||
|
||
Returns:
|
||
JsonResponse: 包含分页数据的JSON响应
|
||
"""
|
||
result = services.get_example_page(request)
|
||
return result
|
||
|
||
|
||
# =============================================================
|
||
# 列表查询视图(不分页)
|
||
# =============================================================
|
||
|
||
@method_decorator(check_login, name='get')
|
||
class ExampleListView(View):
|
||
"""
|
||
案例列表获取视图(支持筛选,不分页)
|
||
|
||
处理GET请求,返回所有案例的列表数据。
|
||
支持按案例名称、案例类型、案例状态等条件筛选。
|
||
|
||
URL: /example/list
|
||
请求方法: GET
|
||
权限要求:需要登录
|
||
"""
|
||
|
||
def get(self, request):
|
||
"""
|
||
处理GET请求,返回案例列表
|
||
|
||
Args:
|
||
request: HttpRequest对象
|
||
|
||
Returns:
|
||
JsonResponse: 包含案例列表的JSON响应
|
||
"""
|
||
result = services.get_example_list(request)
|
||
return R.ok(data=result)
|
||
|
||
|
||
# =============================================================
|
||
# 详情查询视图
|
||
# =============================================================
|
||
|
||
@method_decorator(check_login, name="get")
|
||
class ExampleDetailView(PermissionRequired, View):
|
||
"""
|
||
案例详情查询视图
|
||
|
||
处理GET请求,根据案例ID返回案例详细信息。
|
||
|
||
URL: /example/detail/<int:id>
|
||
请求方法: GET
|
||
权限要求:sys:example:detail
|
||
"""
|
||
|
||
# 方法权限标识
|
||
permission_required = ("sys:example:detail",)
|
||
|
||
@operation_log(
|
||
title="案例管理-查询记录详情",
|
||
log_type=LogType.QUERY
|
||
)
|
||
def get(self, request, id):
|
||
"""
|
||
处理GET请求,返回案例详情
|
||
|
||
Args:
|
||
request: HttpRequest对象
|
||
id: 案例ID(URL参数)
|
||
|
||
Returns:
|
||
JsonResponse: 包含案例详情的JSON响应
|
||
"""
|
||
data = services.get_example_detail(id)
|
||
return R.ok(data=data)
|
||
|
||
|
||
# =============================================================
|
||
# 添加视图
|
||
# =============================================================
|
||
|
||
@method_decorator(check_login, name="post")
|
||
class ExampleAddView(PermissionRequired, View):
|
||
"""
|
||
添加案例视图
|
||
|
||
处理POST请求,创建新的案例记录。
|
||
请求体需包含案例信息(name、avatar、type、status、sort等)。
|
||
|
||
URL: /example/add
|
||
请求方法: POST
|
||
权限要求:sys:example:add
|
||
"""
|
||
|
||
# 方法权限标识
|
||
permission_required = ("sys:example:add",)
|
||
|
||
@operation_log(
|
||
title="案例管理-添加记录",
|
||
log_type=LogType.ADD
|
||
)
|
||
def post(self, request):
|
||
"""
|
||
处理POST请求,添加案例
|
||
|
||
Args:
|
||
request: HttpRequest对象,请求体包含案例信息
|
||
|
||
Returns:
|
||
JsonResponse: 操作结果的JSON响应
|
||
"""
|
||
# 演示模式检查
|
||
if DJANGO_DEMO:
|
||
return R.failed("演示环境,暂无操作权限")
|
||
result = services.add_example(request)
|
||
return result
|
||
|
||
|
||
# =============================================================
|
||
# 更新视图
|
||
# =============================================================
|
||
|
||
@method_decorator(check_login, name="put")
|
||
class ExampleUpdateView(PermissionRequired, View):
|
||
"""
|
||
更新案例视图
|
||
|
||
处理PUT请求,更新现有案例信息。
|
||
请求体需包含案例ID和更新内容。
|
||
|
||
URL: /example/update
|
||
请求方法: PUT
|
||
权限要求:sys:example:update
|
||
"""
|
||
|
||
# 方法权限标识
|
||
permission_required = ('sys:example:update',)
|
||
|
||
@operation_log(
|
||
title="案例管理-更新记录",
|
||
log_type=LogType.UPDATE
|
||
)
|
||
def put(self, request):
|
||
"""
|
||
处理PUT请求,更新案例
|
||
|
||
Args:
|
||
request: HttpRequest对象,请求体包含更新信息
|
||
|
||
Returns:
|
||
JsonResponse: 操作结果的JSON响应
|
||
"""
|
||
# 演示模式检查
|
||
if DJANGO_DEMO:
|
||
return R.failed("演示环境,暂无操作权限")
|
||
result = services.update_example(request)
|
||
return result
|
||
|
||
|
||
# =============================================================
|
||
# 删除视图
|
||
# =============================================================
|
||
|
||
@method_decorator(check_login, name="delete")
|
||
class ExampleDeleteView(PermissionRequired, View):
|
||
"""
|
||
删除案例视图
|
||
|
||
处理DELETE请求,删除指定案例(逻辑删除)。
|
||
支持批量删除,多个ID用逗号分隔。
|
||
|
||
URL: /example/delete/<str:id>
|
||
请求方法: DELETE
|
||
权限要求:sys:example:delete
|
||
"""
|
||
|
||
# 方法权限标识
|
||
permission_required = ('sys:example:delete',)
|
||
|
||
@operation_log(
|
||
title="案例管理-删除记录",
|
||
log_type=LogType.DELETE
|
||
)
|
||
def delete(self, request, id):
|
||
"""
|
||
处理DELETE请求,删除案例
|
||
|
||
Args:
|
||
request: HttpRequest对象
|
||
id: 案例ID字符串,支持逗号分隔的多个ID(如 "1,2,3")
|
||
|
||
Returns:
|
||
JsonResponse: 操作结果的JSON响应
|
||
"""
|
||
# 演示模式检查
|
||
if DJANGO_DEMO:
|
||
return R.failed("演示环境,暂无操作权限")
|
||
result = services.delete_examples(id)
|
||
return result
|
||
|
||
|
||
# =============================================================
|
||
# 状态更新视图
|
||
# =============================================================
|
||
|
||
@method_decorator(check_login, name="put")
|
||
class ExampleStatusView(PermissionRequired, View):
|
||
"""
|
||
设置案例状态视图
|
||
|
||
处理PUT请求,更新案例的启用/禁用状态。
|
||
|
||
URL: /example/status
|
||
请求方法: PUT
|
||
权限要求:sys:example:status
|
||
"""
|
||
|
||
# 方法权限标识
|
||
permission_required = ('sys:example:status',)
|
||
|
||
def put(self, request):
|
||
"""
|
||
处理PUT请求,更新案例状态
|
||
|
||
Args:
|
||
request: HttpRequest对象,请求体包含案例ID和新状态
|
||
|
||
Returns:
|
||
JsonResponse: 操作结果的JSON响应
|
||
"""
|
||
# 演示模式检查
|
||
if DJANGO_DEMO:
|
||
return R.failed("演示环境,暂无操作权限")
|
||
result = services.update_example_status(request)
|
||
return result
|
||
|
||
|
||
# =============================================================
|
||
# 数据列表视图(下拉选择用)
|
||
# =============================================================
|
||
|
||
@method_decorator(check_login, name='get')
|
||
class ExampleDataView(View):
|
||
"""
|
||
案例列表获取视图
|
||
|
||
处理GET请求,返回所有启用案例的列表数据。
|
||
用于下拉选择等前端组件。
|
||
|
||
URL: /example/data
|
||
请求方法: GET
|
||
权限要求:需要登录
|
||
"""
|
||
|
||
def get(self, request):
|
||
"""
|
||
处理GET请求,返回案例数据列表
|
||
|
||
Args:
|
||
request: HttpRequest对象
|
||
|
||
Returns:
|
||
JsonResponse: 包含案例列表的JSON响应
|
||
"""
|
||
result = services.get_example_data()
|
||
return R.ok(data=result)
|
||
```
|
||
|
||
## 代码解析
|
||
|
||
### 视图类继承
|
||
|
||
```python
|
||
@method_decorator(check_login, name="get")
|
||
class ExamplePageView(PermissionRequired, View):
|
||
```
|
||
|
||
| 基类/装饰器 | 来源 | 提供能力 |
|
||
|:----|:----|:----|
|
||
| `View` | Django 内置 | 类视图基类,提供 get/post/put/delete 方法分发 |
|
||
| `PermissionRequired` | `middleware/permission_middleware.py` | 权限控制混入类 |
|
||
| `@method_decorator(check_login, name="get")` | `middleware/login_middleware.py` | JWT 登录认证 |
|
||
|
||
### 三层防护机制
|
||
|
||
```
|
||
请求 → check_login(登录校验) → PermissionRequired(权限校验) → operation_log(操作日志) → 业务逻辑
|
||
```
|
||
|
||
#### 1. 登录校验(check_login)
|
||
|
||
```python
|
||
@method_decorator(check_login, name="get")
|
||
```
|
||
|
||
`check_login` 装饰器从请求头 `Authorization: Bearer <token>` 中提取 JWT token,验证其有效性。验证失败返回 `401 Unauthorized`。
|
||
|
||
```python
|
||
def check_login(func):
|
||
def wrapper(request, *args, **kwargs):
|
||
ignoreURL = ['/login', '/captcha']
|
||
if request.path not in ignoreURL:
|
||
access_token = request.headers['Authorization']
|
||
access_token = access_token.replace('Bearer ', "")
|
||
result = parse_payload(access_token)
|
||
code = result['code']
|
||
if code != 0:
|
||
return R.failed(code=401, msg=result['msg'])
|
||
return func(request, *args, **kwargs)
|
||
return wrapper
|
||
```
|
||
|
||
#### 2. 权限校验(PermissionRequired)
|
||
|
||
```python
|
||
class ExamplePageView(PermissionRequired, View):
|
||
permission_required = ("sys:example:page",)
|
||
```
|
||
|
||
`PermissionRequired` 混入类重写了 `has_permission()` 方法,检查当前用户是否拥有所需的权限节点。用户 ID 为 1 的超级管理员自动放行。
|
||
|
||
```python
|
||
class PermissionRequired(PermissionRequiredMixin):
|
||
def has_permission(self):
|
||
permissions = self.get_permission_required()
|
||
user_id = get_user_id(self.request)
|
||
|
||
# 超级管理员自动放行
|
||
if user_id and user_id != 1:
|
||
from application.menu import services
|
||
permission_list = services.get_user_permissions(user_id)
|
||
for permission in permissions:
|
||
if permission not in permission_list:
|
||
return False
|
||
return True
|
||
|
||
def handle_no_permission(self):
|
||
return R.failed("暂无操作权限", 401)
|
||
```
|
||
|
||
#### 3. 操作日志(operation_log)
|
||
|
||
```python
|
||
@operation_log(title="案例管理-查询分页列表", log_type=LogType.QUERY)
|
||
def get(self, request):
|
||
...
|
||
```
|
||
|
||
`@operation_log` 装饰器自动记录操作日志,包括操作标题、操作类型、请求参数、响应结果、耗时等信息。
|
||
|
||
| 参数 | 说明 |
|
||
|:----|:----|
|
||
| `title` | 操作标题,如"案例管理-添加记录" |
|
||
| `log_type` | 操作类型,参考 `LogType` 常量 |
|
||
| `source` | 操作来源,0=后台系统 |
|
||
|
||
### 操作类型常量
|
||
|
||
```python
|
||
class LogType:
|
||
OTHER = 0 # 其他操作
|
||
QUERY = 1 # 查询操作
|
||
ADD = 2 # 新增操作
|
||
UPDATE = 3 # 修改操作
|
||
SAVE = 4 # 保存操作
|
||
DELETE = 5 # 删除操作
|
||
BATCH_DELETE = 6 # 批量删除
|
||
STATUS = 7 # 设置状态
|
||
RESET_PWD = 8 # 重置密码
|
||
IMPORT = 9 # 导入操作
|
||
EXPORT = 10 # 导出操作
|
||
UPLOAD = 11 # 上传操作
|
||
```
|
||
|
||
### 演示环境检查
|
||
|
||
```python
|
||
if DJANGO_DEMO:
|
||
return R.failed("演示环境,暂无操作权限")
|
||
```
|
||
|
||
写操作(add、update、delete、status)需要检查 `DJANGO_DEMO` 配置。当 `.env` 中设置 `DJANGO_DEMO=True` 时,所有写操作返回失败提示。
|
||
|
||
### 视图类分类
|
||
|
||
| 视图类 | HTTP 方法 | 权限 | 说明 |
|
||
|:----|:----|:----|:----|
|
||
| `ExamplePageView` | GET | `sys:example:page` | 分页查询 |
|
||
| `ExampleListView` | GET | 无(仅登录) | 列表查询(不分页) |
|
||
| `ExampleDetailView` | GET | `sys:example:detail` | 详情查询 |
|
||
| `ExampleAddView` | POST | `sys:example:add` | 添加记录 |
|
||
| `ExampleUpdateView` | PUT | `sys:example:update` | 更新记录 |
|
||
| `ExampleDeleteView` | DELETE | `sys:example:delete` | 删除记录 |
|
||
| `ExampleStatusView` | PUT | `sys:example:status` | 状态更新 |
|
||
| `ExampleDataView` | GET | 无(仅登录) | 数据列表(下拉选择) |
|
||
|
||
### 权限节点格式
|
||
|
||
```
|
||
sys:<module>:<action>
|
||
```
|
||
|
||
| 部分 | 说明 | 示例 |
|
||
|:----|:----|:----|
|
||
| `sys` | 固定前缀 | `sys` |
|
||
| `<module>` | 模块名称 | `example` |
|
||
| `<action>` | 操作类型 | `page`、`list`、`detail`、`add`、`update`、`delete`、`status` |
|
||
|
||
## 开发要点
|
||
|
||
1. **每个视图类只处理一个 HTTP 方法**:单一职责原则
|
||
2. **使用 `@method_decorator(check_login, name="方法名")` 进行登录校验**:确保用户已登录
|
||
3. **需要权限控制的视图继承 `PermissionRequired`**:并设置 `permission_required`
|
||
4. **写操作必须检查 `DJANGO_DEMO`**:演示环境禁止写操作
|
||
5. **使用 `@operation_log` 记录操作日志**:便于审计和追踪
|
||
6. **视图层只做请求分发**:业务逻辑全部委托给 Service 层
|
||
7. **直接返回 Service 层的结果**:不要在视图层处理业务逻辑
|
||
|
||
## 总结
|
||
|
||
视图层使用 Django 类视图,组合三层防护机制:登录校验(check_login)、权限控制(PermissionRequired)、操作日志(operation_log)。每个视图类只处理一个 HTTP 方法,写操作需要检查 DJANGO_DEMO 配置。视图层只做请求分发,业务逻辑全部委托给 Service 层。
|