优化更新

This commit is contained in:
2026-09-14 10:33:29 +08:00
parent e8b5d55603
commit 11c46addfa
61 changed files with 1025 additions and 896 deletions
@@ -471,37 +471,73 @@ def permission_required(permission: str):
### 权限缓存
权限缓存采用「用户级缓存 + 全局版本号」双层策略(`src/utils/perm_cache.py`):
```python
# src/modules/system/menu/service.py
async def get_permissions_list(user_id: int) -> list:
"""获取用户权限列表(异步,走 Redis 缓存)"""
cache_key = f"perm:user:{user_id}"
# src/utils/perm_cache.py
_VERSION_KEY = 'perm:version' # 全局版本号
_PERM_PREFIX = 'perm:list:' # 用户权限列表前缀
_MENU_PREFIX = 'perm:menu:' # 用户菜单树前缀
_CACHE_TTL = 7200 # 缓存过期时间(2小时)
async def get_cached_permissions(user_id: int):
"""获取缓存的用户权限列表,未命中返回 None"""
redis = get_redis()
cached = await redis.get(cache_key)
if cached:
return json.loads(cached)
# 从数据库查询
permissions = query_user_permissions(user_id)
await redis.set(cache_key, json.dumps(permissions), ex=3600)
return permissions
def get_permissions_list_sync(user_id: int) -> list:
"""获取用户权限列表(同步,直查DB,供线程池场景使用)"""
return query_user_permissions(user_id)
def invalidate_perm_cache(user_id: int):
"""失效用户权限缓存(角色/菜单变更时调用)"""
from core.redis_client import get_redis
import asyncio
ver = await _get_version()
key = f'{_PERM_PREFIX}{user_id}:{ver}'
data = await redis.get(key)
if data is not None:
return json.loads(data)
return None
async def set_cached_permissions(user_id: int, perm_list: list):
"""缓存用户权限列表(空列表不缓存,避免缓存空壳后无法感知后续数据变更)"""
if not perm_list:
return
redis = get_redis()
asyncio.get_event_loop().run_until_complete(
redis.delete(f"perm:user:{user_id}")
)
ver = await _get_version()
key = f'{_PERM_PREFIX}{user_id}:{ver}'
await redis.set(key, json.dumps(perm_list), ex=_CACHE_TTL)
```
缓存失效策略:
```python
# src/utils/perm_cache.py
async def invalidate_menu_change():
"""菜单增删改时调用,递增全局版本号使所有用户缓存自然失效"""
await _incr_version()
async def invalidate_user_perms(user_id: int):
"""用户角色变更时调用,删除该用户的权限和菜单缓存"""
redis = get_redis()
ver = await _get_version()
keys = [
f'{_PERM_PREFIX}{user_id}:{ver}',
f'{_MENU_PREFIX}{user_id}:{ver}',
]
await redis.delete(*keys)
async def invalidate_role_perms(role_id: int):
"""角色菜单变更时调用,清理该角色下所有用户的权限和菜单缓存"""
# 查询该角色下所有用户 ID
user_ids = await asyncio.to_thread(_query_user_ids, role_id)
if not user_ids:
return
redis = get_redis()
ver = await _get_version()
keys = []
for uid in user_ids:
keys.append(f'{_PERM_PREFIX}{uid}:{ver}')
keys.append(f'{_MENU_PREFIX}{uid}:{ver}')
if keys:
await redis.delete(*keys)
```
:::tip 版本号策略的优势
传统方案在菜单变更时需要遍历所有用户删除缓存,版本号方案只需递增一个全局 key,所有用户的旧版本缓存自然失效(key 中包含版本号,读取时用新版本号拼 key,旧 key 不再被访问,等 TTL 过期自动清除)。
:::
### check_demo 装饰器
```python
@@ -600,7 +636,7 @@ def sanitize_html(content: str) -> str:
### 上传体积限制
```python
# src/middleware/upload_size.py
# src/middleware/upload_size_limit.py
async def upload_size_limit_middleware(request: Request, call_next):
content_length = request.headers.get('content-length')
if content_length and int(content_length) > MAX_UPLOAD_SIZE:
@@ -695,7 +731,7 @@ async def operation_log_middleware(request: Request, call_next):
| 配置项 | 环境变量 | 默认值 | 说明 |
|:---|:---|:---|:---|
| JWT 密钥 | `JWT_SALT` | 必须配置 | HS256 签名密钥 |
| JWT 过期时间 | `JWT_EXPIRE_MINUTES` | 120 | Token 有效期(分钟) |
| JWT 过期时间 | `JWT_EXPIRE_MINUTES` | 20 | Token 有效期(分钟) |
| 密码错误锁定 | — | 5 次/10 分钟 | 连续失败后锁定 |
| 验证码有效期 | — | 120 秒 | 验证码 Redis TTL |
| 限流开关 | `RATE_LIMIT_ENABLED` | False | 滑动窗口限流 |