All checks were successful
continuous-integration/drone/push Build is passing
- 新增 platform_tenant_wechat_apps 表(租户企微应用配置) - platform_apps 增加 require_jssdk 字段 - platform_tenant_apps 增加 wechat_app_id 关联字段 - 新增企微应用管理 API 和页面 - 应用管理页面增加 JS-SDK 开关 - 应用配置页面增加企微应用选择
209 lines
6.6 KiB
Python
209 lines
6.6 KiB
Python
"""租户应用配置路由"""
|
|
import json
|
|
import secrets
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from pydantic import BaseModel
|
|
from typing import Optional, List
|
|
from sqlalchemy.orm import Session
|
|
|
|
from ..database import get_db
|
|
from ..models.tenant_app import TenantApp
|
|
from .auth import get_current_user, require_operator
|
|
from ..models.user import User
|
|
|
|
router = APIRouter(prefix="/tenant-apps", tags=["应用配置"])
|
|
|
|
|
|
# Schemas
|
|
|
|
class TenantAppCreate(BaseModel):
|
|
tenant_id: str
|
|
app_code: str = "tools"
|
|
app_name: Optional[str] = None
|
|
wechat_app_id: Optional[int] = None # 关联的企微应用ID
|
|
access_token: Optional[str] = None # 如果不传则自动生成
|
|
allowed_origins: Optional[List[str]] = None
|
|
allowed_tools: Optional[List[str]] = None
|
|
|
|
|
|
class TenantAppUpdate(BaseModel):
|
|
app_name: Optional[str] = None
|
|
wechat_app_id: Optional[int] = None # 关联的企微应用ID
|
|
access_token: Optional[str] = None
|
|
allowed_origins: Optional[List[str]] = None
|
|
allowed_tools: Optional[List[str]] = None
|
|
status: Optional[int] = None
|
|
|
|
|
|
# API Endpoints
|
|
|
|
@router.get("")
|
|
async def list_tenant_apps(
|
|
page: int = Query(1, ge=1),
|
|
size: int = Query(20, ge=1, le=100),
|
|
tenant_id: Optional[str] = None,
|
|
app_code: Optional[str] = None,
|
|
user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""获取应用配置列表"""
|
|
query = db.query(TenantApp)
|
|
|
|
if tenant_id:
|
|
query = query.filter(TenantApp.tenant_id == tenant_id)
|
|
if app_code:
|
|
query = query.filter(TenantApp.app_code == app_code)
|
|
|
|
total = query.count()
|
|
apps = query.order_by(TenantApp.id.desc()).offset((page - 1) * size).limit(size).all()
|
|
|
|
return {
|
|
"total": total,
|
|
"page": page,
|
|
"size": size,
|
|
"items": [format_tenant_app(app, mask_secret=True, db=db) for app in apps]
|
|
}
|
|
|
|
|
|
@router.get("/{app_id}")
|
|
async def get_tenant_app(
|
|
app_id: int,
|
|
user: User = Depends(get_current_user),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""获取应用配置详情"""
|
|
app = db.query(TenantApp).filter(TenantApp.id == app_id).first()
|
|
if not app:
|
|
raise HTTPException(status_code=404, detail="应用配置不存在")
|
|
|
|
return format_tenant_app(app, mask_secret=True, db=db)
|
|
|
|
|
|
@router.post("")
|
|
async def create_tenant_app(
|
|
data: TenantAppCreate,
|
|
user: User = Depends(require_operator),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""创建应用配置"""
|
|
# 检查是否重复
|
|
exists = db.query(TenantApp).filter(
|
|
TenantApp.tenant_id == data.tenant_id,
|
|
TenantApp.app_code == data.app_code
|
|
).first()
|
|
if exists:
|
|
raise HTTPException(status_code=400, detail="该租户应用配置已存在")
|
|
|
|
# 自动生成 access_token
|
|
access_token = data.access_token or secrets.token_hex(32)
|
|
|
|
app = TenantApp(
|
|
tenant_id=data.tenant_id,
|
|
app_code=data.app_code,
|
|
app_name=data.app_name,
|
|
wechat_app_id=data.wechat_app_id,
|
|
access_token=access_token,
|
|
allowed_origins=json.dumps(data.allowed_origins) if data.allowed_origins else None,
|
|
allowed_tools=json.dumps(data.allowed_tools) if data.allowed_tools else None,
|
|
status=1
|
|
)
|
|
db.add(app)
|
|
db.commit()
|
|
db.refresh(app)
|
|
|
|
return {"success": True, "id": app.id, "access_token": access_token}
|
|
|
|
|
|
@router.put("/{app_id}")
|
|
async def update_tenant_app(
|
|
app_id: int,
|
|
data: TenantAppUpdate,
|
|
user: User = Depends(require_operator),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""更新应用配置"""
|
|
app = db.query(TenantApp).filter(TenantApp.id == app_id).first()
|
|
if not app:
|
|
raise HTTPException(status_code=404, detail="应用配置不存在")
|
|
|
|
update_data = data.model_dump(exclude_unset=True)
|
|
|
|
# 处理 JSON 字段
|
|
if 'allowed_origins' in update_data:
|
|
update_data['allowed_origins'] = json.dumps(update_data['allowed_origins']) if update_data['allowed_origins'] else None
|
|
if 'allowed_tools' in update_data:
|
|
update_data['allowed_tools'] = json.dumps(update_data['allowed_tools']) if update_data['allowed_tools'] else None
|
|
|
|
for key, value in update_data.items():
|
|
setattr(app, key, value)
|
|
|
|
db.commit()
|
|
return {"success": True}
|
|
|
|
|
|
@router.delete("/{app_id}")
|
|
async def delete_tenant_app(
|
|
app_id: int,
|
|
user: User = Depends(require_operator),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""删除应用配置"""
|
|
app = db.query(TenantApp).filter(TenantApp.id == app_id).first()
|
|
if not app:
|
|
raise HTTPException(status_code=404, detail="应用配置不存在")
|
|
|
|
db.delete(app)
|
|
db.commit()
|
|
|
|
return {"success": True}
|
|
|
|
|
|
@router.post("/{app_id}/regenerate-token")
|
|
async def regenerate_token(
|
|
app_id: int,
|
|
user: User = Depends(require_operator),
|
|
db: Session = Depends(get_db)
|
|
):
|
|
"""重新生成 access_token"""
|
|
app = db.query(TenantApp).filter(TenantApp.id == app_id).first()
|
|
if not app:
|
|
raise HTTPException(status_code=404, detail="应用配置不存在")
|
|
|
|
new_token = secrets.token_hex(32)
|
|
app.access_token = new_token
|
|
db.commit()
|
|
|
|
return {"success": True, "access_token": new_token}
|
|
|
|
|
|
def format_tenant_app(app: TenantApp, mask_secret: bool = True, db: Session = None) -> dict:
|
|
"""格式化应用配置"""
|
|
# 获取关联的企微应用信息
|
|
wechat_app_info = None
|
|
if app.wechat_app_id and db:
|
|
from ..models.tenant_wechat_app import TenantWechatApp
|
|
wechat_app = db.query(TenantWechatApp).filter(TenantWechatApp.id == app.wechat_app_id).first()
|
|
if wechat_app:
|
|
wechat_app_info = {
|
|
"id": wechat_app.id,
|
|
"name": wechat_app.name,
|
|
"corp_id": wechat_app.corp_id,
|
|
"agent_id": wechat_app.agent_id
|
|
}
|
|
|
|
result = {
|
|
"id": app.id,
|
|
"tenant_id": app.tenant_id,
|
|
"app_code": app.app_code,
|
|
"app_name": app.app_name,
|
|
"wechat_app_id": app.wechat_app_id,
|
|
"wechat_app": wechat_app_info,
|
|
"access_token": "******" if mask_secret and app.access_token else app.access_token,
|
|
"allowed_origins": json.loads(app.allowed_origins) if app.allowed_origins else [],
|
|
"allowed_tools": json.loads(app.allowed_tools) if app.allowed_tools else [],
|
|
"status": app.status,
|
|
"created_at": app.created_at,
|
|
"updated_at": app.updated_at
|
|
}
|
|
return result
|