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

202 lines
6.3 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.
# 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 处理。