202 lines
6.3 KiB
Markdown
202 lines
6.3 KiB
Markdown
# Endpoint层规范
|
||
|
||
## 概述
|
||
|
||
Endpoint 层是 HTTP 接口层,定义路由和请求处理。每个模块的 Endpoint 文件位于 `src/api/v1/endpoints/{name}.py`,使用 FastAPI `APIRouter` 定义接口。
|
||
|
||
:::tip 设计原则
|
||
- Endpoint 只做请求分发,不含业务逻辑
|
||
- 通过装饰器控制权限和演示模式
|
||
- 委托 Service 处理业务逻辑
|
||
- 返回 R 封装的响应
|
||
:::
|
||
|
||
## 装饰器顺序
|
||
|
||
```python
|
||
@router.post('/add') # 1. 路由定义
|
||
@permission_required("sys:position:add") # 2. 权限校验
|
||
@check_demo # 3. 演示模式拦截
|
||
def add(request: Request, data: PositionForm):
|
||
return position_service.add(request, data)
|
||
```
|
||
|
||
:::warning 装饰器顺序规则
|
||
1. `@router.get/post/put/delete`:路由定义(最外层)
|
||
2. `@permission_required("权限标识")`:RBAC 权限校验
|
||
3. `@check_demo`:演示环境写操作拦截(仅写接口需要)
|
||
|
||
顺序不可调换,否则权限校验或演示拦截可能失效。
|
||
:::
|
||
|
||
## 读操作(无需 @check_demo)
|
||
|
||
```python
|
||
# 分页查询
|
||
@router.get('/page', summary='查询分页数据')
|
||
@permission_required("sys:position:page")
|
||
def page(request: Request):
|
||
return position_service.get_page(request)
|
||
|
||
# 详情查询
|
||
@router.get('/detail/{id}', summary='查询岗位详情')
|
||
@permission_required("sys:position:detail")
|
||
def detail(request: Request, id):
|
||
return R.ok(data=position_service.get_detail(id))
|
||
|
||
# 列表查询(无分页)
|
||
@router.get('/list', summary='获取岗位列表')
|
||
@permission_required("sys:position:list")
|
||
def get_position_list(request: Request):
|
||
return R.ok(data=position_service.get_position_list(request))
|
||
```
|
||
|
||
## 写操作(需要 @check_demo)
|
||
|
||
```python
|
||
# 添加
|
||
@router.post('/add', summary='添加岗位')
|
||
@permission_required("sys:position:add")
|
||
@check_demo
|
||
def add(request: Request, data: PositionForm):
|
||
return position_service.add(request, data)
|
||
|
||
# 编辑
|
||
@router.put('/update', summary='更新岗位')
|
||
@permission_required("sys:position:update")
|
||
@check_demo
|
||
def update(request: Request, data: PositionForm):
|
||
return position_service.update(request, data)
|
||
|
||
# 删除
|
||
@router.delete('/delete/{id}', summary='删除岗位')
|
||
@permission_required("sys:position:delete")
|
||
@check_demo
|
||
def delete(request: Request, id):
|
||
return position_service.delete(id)
|
||
|
||
# 设置状态
|
||
@router.put('/status', summary='设置岗位状态')
|
||
@permission_required("sys:position:status")
|
||
@check_demo
|
||
def status(request: Request, data: PositionStatusForm):
|
||
return position_service.update_status(request, data)
|
||
|
||
# 批量删除(必须 async def)
|
||
@router.delete('/batchDelete', summary='批量删除岗位')
|
||
@permission_required("sys:position:batchDelete")
|
||
@check_demo
|
||
async def batch_delete(request: Request):
|
||
return await position_service.batch_delete(request)
|
||
```
|
||
|
||
:::warning 批量删除必须 async
|
||
`batch_delete` 端点必须为 `async def`,因为 `parse_batch_ids(request)` 需要异步读取请求体。其他端点使用同步 `def` 即可。
|
||
:::
|
||
|
||
## 同步/异步选择
|
||
|
||
| 场景 | 端点类型 | 原因 |
|
||
|------|---------|------|
|
||
| 纯 CRUD(增删改查) | `def` | Service 为同步,FastAPI 自动线程池执行 |
|
||
| 批量删除 | `async def` | 需 `await parse_batch_ids(request)` |
|
||
| 含 Redis/文件 IO | `async def` | Service 为异步,需 await |
|
||
|
||
## 权限标识规范
|
||
|
||
权限标识格式:`sys:{module}:{action}`
|
||
|
||
| 操作 | 权限标识 | HTTP 方法 |
|
||
|------|---------|----------|
|
||
| 分页查询 | `sys:{module}:page` | GET |
|
||
| 详情查询 | `sys:{module}:detail` | GET |
|
||
| 列表查询 | `sys:{module}:list` | GET |
|
||
| 添加 | `sys:{module}:add` | POST |
|
||
| 编辑 | `sys:{module}:update` | PUT |
|
||
| 删除 | `sys:{module}:delete` | DELETE |
|
||
| 批量删除 | `sys:{module}:batchDelete` | DELETE |
|
||
| 状态设置 | `sys:{module}:status` | PUT |
|
||
|
||
:::tip admin 用户
|
||
用户 ID 为 1 的管理员账号跳过所有 `permission_required` 检查。
|
||
:::
|
||
|
||
## 完整 Endpoint 文件模板
|
||
|
||
```python
|
||
from fastapi import APIRouter, Request
|
||
from modules.system.position.schemas import PositionForm, PositionStatusForm
|
||
from core.access_decorators import check_demo, permission_required
|
||
from modules.system.position.service import position_service
|
||
from core import R
|
||
|
||
router = APIRouter()
|
||
|
||
|
||
@router.get('/page', summary='查询分页数据')
|
||
@permission_required("sys:position:page")
|
||
def page(request: Request):
|
||
return position_service.get_page(request)
|
||
|
||
|
||
@router.get('/detail/{id}', summary='查询详情')
|
||
@permission_required("sys:position:detail")
|
||
def detail(request: Request, id):
|
||
return R.ok(data=position_service.get_detail(id))
|
||
|
||
|
||
@router.get('/list', summary='获取列表')
|
||
@permission_required("sys:position:list")
|
||
def get_list(request: Request):
|
||
return R.ok(data=position_service.get_position_list(request))
|
||
|
||
|
||
@router.post('/add', summary='添加')
|
||
@permission_required("sys:position:add")
|
||
@check_demo
|
||
def add(request: Request, data: PositionForm):
|
||
return position_service.add(request, data)
|
||
|
||
|
||
@router.put('/update', summary='更新')
|
||
@permission_required("sys:position:update")
|
||
@check_demo
|
||
def update(request: Request, data: PositionForm):
|
||
return position_service.update(request, data)
|
||
|
||
|
||
@router.delete('/delete/{id}', summary='删除')
|
||
@permission_required("sys:position:delete")
|
||
@check_demo
|
||
def delete(request: Request, id):
|
||
return position_service.delete(id)
|
||
|
||
|
||
@router.put('/status', summary='设置状态')
|
||
@permission_required("sys:position:status")
|
||
@check_demo
|
||
def status(request: Request, data: PositionStatusForm):
|
||
return position_service.update_status(request, data)
|
||
|
||
|
||
@router.delete('/batchDelete', summary='批量删除')
|
||
@permission_required("sys:position:batchDelete")
|
||
@check_demo
|
||
async def batch_delete(request: Request):
|
||
return await position_service.batch_delete(request)
|
||
```
|
||
|
||
## 路由注册
|
||
|
||
在 `src/api/v1/router.py` 中注册模块路由:
|
||
|
||
```python
|
||
from api.v1.endpoints import position
|
||
|
||
v1.include_router(position.router, prefix="/position", tags=["岗位管理"])
|
||
```
|
||
|
||
## 总结
|
||
|
||
Endpoint 层是 HTTP 接口定义层,只做请求分发和装饰器配置。核心规则:装饰器顺序为 `@router` → `@permission_required` → `@check_demo`;读操作用同步 `def`,批量删除用 `async def`;权限标识格式 `sys:{module}:{action}`;所有逻辑委托 Service 处理。
|