Files
iot-device-management-service/app/yudao_compat.py
16337 1ddc23e0d3 feat(yudao): 添加芋道兼容层和基础路由
- 新增 yudao_compat.py:芋道标准响应格式、权限校验
- 新增 yudao_auth.py:登录认证、权限信息、租户等系统接口
- 新增 yudao_alert.py:告警管理和摄像头汇总的芋道兼容路由
- 新增 routers/__init__.py:统一导出路由模块

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 16:38:54 +08:00

150 lines
4.3 KiB
Python
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.

"""
芋道平台兼容层
保持与芋道主平台一致的接口规范,便于后续无缝对接。
设计原则:
- 响应格式符合芋道标准: {"code": 0, "data": ..., "msg": "success"}
- 分页参数: pageNo, pageSize
- HTTP 状态码始终 200业务错误通过 code 区分
"""
from typing import Any, Optional
from fastapi import HTTPException, Header, Request
from fastapi.responses import JSONResponse
from app.config import settings
class YudaoResponse:
"""芋道标准响应格式"""
@staticmethod
def success(data: Any = None, msg: str = "success") -> dict:
"""成功响应"""
return {"code": 0, "data": data, "msg": msg}
@staticmethod
def error(code: int, msg: str) -> dict:
"""错误响应"""
return {"code": code, "data": None, "msg": msg}
@staticmethod
def page(list_data: list, total: int, page_no: int, page_size: int) -> dict:
"""
分页响应格式
Args:
list_data: 数据列表
total: 总记录数
page_no: 当前页码
page_size: 每页大小
"""
return {
"code": 0,
"data": {
"list": list_data,
"total": total,
"pageNo": page_no,
"pageSize": page_size
},
"msg": "success"
}
async def yudao_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
"""
统一异常处理器
芋道规范HTTP 状态码始终返回 200业务错误通过 code 区分
"""
return JSONResponse(
status_code=200,
content=YudaoResponse.error(exc.status_code, exc.detail)
)
def get_current_user(authorization: Optional[str] = Header(None, alias="Authorization")) -> dict:
"""
获取当前用户
测试阶段 (dev_mode=True)
- 跳过 Token 验证
- 返回模拟的超级管理员用户
- 拥有 ["*:*:*"] 全部权限
生产阶段 (dev_mode=False)
- 验证 Authorization Header
- 调用芋道主平台验证 Token
- 返回真实用户信息
Returns:
dict: 用户信息,包含 userId, username, permissions
"""
if settings.app.dev_mode:
# 测试阶段:返回模拟的超级管理员
return {
"userId": 1,
"username": "admin",
"nickname": "超级管理员",
"permissions": ["*:*:*"]
}
# 生产阶段:验证 Token
if not authorization or not authorization.startswith("Bearer "):
raise HTTPException(status_code=401, detail="请先登录")
token = authorization.replace("Bearer ", "")
# TODO: 生产阶段调用芋道主平台验证 Token
# response = requests.get(
# f"{settings.yudao.base_url}/system/auth/check-token",
# headers={"Authorization": f"Bearer {token}"}
# )
# if response.status_code != 200:
# raise HTTPException(status_code=401, detail="Token 无效或已过期")
# return response.json()["data"]["user"]
# 临时:生产模式未实现时返回模拟用户
return {
"userId": 1,
"username": "admin",
"nickname": "超级管理员",
"permissions": ["*:*:*"]
}
def check_permission(required_permission: str):
"""
权限检查装饰器
测试阶段:始终通过(超级用户拥有 *:*:* 权限)
生产阶段:检查用户是否拥有指定权限
Usage:
@router.get("/alert/page")
@check_permission("ai-alert:alert:query")
async def get_alert_page(...):
...
"""
def decorator(func):
async def wrapper(*args, current_user: dict = None, **kwargs):
if current_user is None:
raise HTTPException(status_code=401, detail="请先登录")
permissions = current_user.get("permissions", [])
# 超级权限检查
if "*:*:*" in permissions:
return await func(*args, current_user=current_user, **kwargs)
# 具体权限检查
if required_permission not in permissions:
raise HTTPException(status_code=403, detail="没有操作权限")
return await func(*args, current_user=current_user, **kwargs)
return wrapper
return decorator