feat: 新增告警、成本、配额、微信模块及缓存服务
All checks were successful
continuous-integration/drone/push Build is passing

- 新增告警模块 (alerts): 告警规则配置与触发
- 新增成本管理模块 (cost): 成本统计与分析
- 新增配额模块 (quota): 配额管理与限制
- 新增微信模块 (wechat): 微信相关功能接口
- 新增缓存服务 (cache): Redis 缓存封装
- 新增请求日志中间件 (request_logger)
- 新增异常处理和链路追踪中间件
- 更新 dashboard 前端展示
- 更新 SDK stats_client 功能
This commit is contained in:
111
2026-01-24 16:53:47 +08:00
parent eab2533c36
commit 6c6c48cf71
29 changed files with 4607 additions and 41 deletions

View File

@@ -0,0 +1,264 @@
"""企业微信JS-SDK路由"""
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy.orm import Session
from ..database import get_db
from ..models.tenant_app import TenantApp
from ..models.tenant_wechat_app import TenantWechatApp
from ..services.wechat import WechatService, get_wechat_service_by_id
router = APIRouter(prefix="/wechat", tags=["企业微信"])
class JssdkSignatureRequest(BaseModel):
"""JS-SDK签名请求"""
url: str # 当前页面URL不含#及其后面部分)
class JssdkSignatureResponse(BaseModel):
"""JS-SDK签名响应"""
appId: str
agentId: str
timestamp: int
nonceStr: str
signature: str
class OAuth2UrlRequest(BaseModel):
"""OAuth2授权URL请求"""
redirect_uri: str
scope: str = "snsapi_base"
state: str = ""
class UserInfoRequest(BaseModel):
"""用户信息请求"""
code: str
@router.post("/jssdk/signature")
async def get_jssdk_signature(
request: JssdkSignatureRequest,
tenant_id: str = Query(..., alias="tid"),
app_code: str = Query(..., alias="aid"),
db: Session = Depends(get_db)
):
"""获取JS-SDK签名
用于前端初始化企业微信JS-SDK
Args:
request: 包含当前页面URL
tenant_id: 租户ID
app_code: 应用代码
Returns:
JS-SDK签名信息
"""
# 查找租户应用配置
tenant_app = db.query(TenantApp).filter(
TenantApp.tenant_id == tenant_id,
TenantApp.app_code == app_code,
TenantApp.status == 1
).first()
if not tenant_app:
raise HTTPException(status_code=404, detail="租户应用配置不存在")
if not tenant_app.wechat_app_id:
raise HTTPException(status_code=400, detail="该应用未配置企业微信")
# 获取企微服务
wechat_service = await get_wechat_service_by_id(tenant_app.wechat_app_id, db)
if not wechat_service:
raise HTTPException(status_code=404, detail="企业微信应用不存在或已禁用")
# 生成签名
signature_data = await wechat_service.get_jssdk_signature(request.url)
if not signature_data:
raise HTTPException(status_code=500, detail="获取JS-SDK签名失败")
return signature_data
@router.get("/jssdk/signature")
async def get_jssdk_signature_get(
url: str = Query(..., description="当前页面URL"),
tenant_id: str = Query(..., alias="tid"),
app_code: str = Query(..., alias="aid"),
db: Session = Depends(get_db)
):
"""获取JS-SDK签名GET方式
方便前端JSONP调用
"""
# 查找租户应用配置
tenant_app = db.query(TenantApp).filter(
TenantApp.tenant_id == tenant_id,
TenantApp.app_code == app_code,
TenantApp.status == 1
).first()
if not tenant_app:
raise HTTPException(status_code=404, detail="租户应用配置不存在")
if not tenant_app.wechat_app_id:
raise HTTPException(status_code=400, detail="该应用未配置企业微信")
# 获取企微服务
wechat_service = await get_wechat_service_by_id(tenant_app.wechat_app_id, db)
if not wechat_service:
raise HTTPException(status_code=404, detail="企业微信应用不存在或已禁用")
# 生成签名
signature_data = await wechat_service.get_jssdk_signature(url)
if not signature_data:
raise HTTPException(status_code=500, detail="获取JS-SDK签名失败")
return signature_data
@router.post("/oauth2/url")
async def get_oauth2_url(
request: OAuth2UrlRequest,
tenant_id: str = Query(..., alias="tid"),
app_code: str = Query(..., alias="aid"),
db: Session = Depends(get_db)
):
"""获取OAuth2授权URL
用于企业微信内网页获取用户身份
"""
# 查找租户应用配置
tenant_app = db.query(TenantApp).filter(
TenantApp.tenant_id == tenant_id,
TenantApp.app_code == app_code,
TenantApp.status == 1
).first()
if not tenant_app:
raise HTTPException(status_code=404, detail="租户应用配置不存在")
if not tenant_app.wechat_app_id:
raise HTTPException(status_code=400, detail="该应用未配置企业微信")
# 获取企微服务
wechat_service = await get_wechat_service_by_id(tenant_app.wechat_app_id, db)
if not wechat_service:
raise HTTPException(status_code=404, detail="企业微信应用不存在或已禁用")
# 生成OAuth2 URL
oauth_url = wechat_service.get_oauth2_url(
redirect_uri=request.redirect_uri,
scope=request.scope,
state=request.state
)
return {"url": oauth_url}
@router.post("/oauth2/userinfo")
async def get_user_info(
request: UserInfoRequest,
tenant_id: str = Query(..., alias="tid"),
app_code: str = Query(..., alias="aid"),
db: Session = Depends(get_db)
):
"""通过OAuth2 code获取用户信息
在OAuth2回调后用code换取用户信息
"""
# 查找租户应用配置
tenant_app = db.query(TenantApp).filter(
TenantApp.tenant_id == tenant_id,
TenantApp.app_code == app_code,
TenantApp.status == 1
).first()
if not tenant_app:
raise HTTPException(status_code=404, detail="租户应用配置不存在")
if not tenant_app.wechat_app_id:
raise HTTPException(status_code=400, detail="该应用未配置企业微信")
# 获取企微服务
wechat_service = await get_wechat_service_by_id(tenant_app.wechat_app_id, db)
if not wechat_service:
raise HTTPException(status_code=404, detail="企业微信应用不存在或已禁用")
# 获取用户信息
user_info = await wechat_service.get_user_info_by_code(request.code)
if not user_info:
raise HTTPException(status_code=400, detail="获取用户信息失败code可能已过期")
return user_info
@router.get("/oauth2/userinfo")
async def get_user_info_get(
code: str = Query(..., description="OAuth2回调的code"),
tenant_id: str = Query(..., alias="tid"),
app_code: str = Query(..., alias="aid"),
db: Session = Depends(get_db)
):
"""通过OAuth2 code获取用户信息GET方式"""
# 查找租户应用配置
tenant_app = db.query(TenantApp).filter(
TenantApp.tenant_id == tenant_id,
TenantApp.app_code == app_code,
TenantApp.status == 1
).first()
if not tenant_app:
raise HTTPException(status_code=404, detail="租户应用配置不存在")
if not tenant_app.wechat_app_id:
raise HTTPException(status_code=400, detail="该应用未配置企业微信")
# 获取企微服务
wechat_service = await get_wechat_service_by_id(tenant_app.wechat_app_id, db)
if not wechat_service:
raise HTTPException(status_code=404, detail="企业微信应用不存在或已禁用")
# 获取用户信息
user_info = await wechat_service.get_user_info_by_code(code)
if not user_info:
raise HTTPException(status_code=400, detail="获取用户信息失败code可能已过期")
return user_info
@router.get("/user/{user_id}")
async def get_user_detail(
user_id: str,
tenant_id: str = Query(..., alias="tid"),
app_code: str = Query(..., alias="aid"),
db: Session = Depends(get_db)
):
"""获取企业微信成员详细信息"""
# 查找租户应用配置
tenant_app = db.query(TenantApp).filter(
TenantApp.tenant_id == tenant_id,
TenantApp.app_code == app_code,
TenantApp.status == 1
).first()
if not tenant_app:
raise HTTPException(status_code=404, detail="租户应用配置不存在")
if not tenant_app.wechat_app_id:
raise HTTPException(status_code=400, detail="该应用未配置企业微信")
# 获取企微服务
wechat_service = await get_wechat_service_by_id(tenant_app.wechat_app_id, db)
if not wechat_service:
raise HTTPException(status_code=404, detail="企业微信应用不存在或已禁用")
# 获取用户详情
user_detail = await wechat_service.get_user_detail(user_id)
if not user_detail:
raise HTTPException(status_code=404, detail="用户不存在")
return user_detail