Files
workphone-sdk/sdk/app/routers/connection_provider.py

139 lines
5.5 KiB
Python
Raw 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.

"""
设备连接方案 · 可切换驱动 API
供存客宝四端(存客宝/触客宝/AI数智员工/SuperAdmin与超管 UI 切换「设备连接解决方案」:
- 列出所有方案 / 当前生效方案
- 一键切换(全局 / 按项目 / 按设备)
- 注册/更新/删除自定义方案(预留接口,新方案无需改代码)
- 健康检查 + 统一执行(验证切换后功能可用)
真源sdk/app/services/connection_provider.py
文档:开发文档/1、需求/修改/工作手机_设备Agent与基础设施_20260529.md §3.6
"""
from __future__ import annotations
from typing import Any, Dict, Optional
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field
from services.connection_provider import (
connection_provider_manager as mgr,
SCOPE_GLOBAL,
)
router = APIRouter()
class SwitchRequest(BaseModel):
provider: str = Field(..., description="目标连接方案 idjiqing / aochuang / legacy / custom_*")
scope: str = Field(default=SCOPE_GLOBAL, description="作用域global / project / device")
project_id: str = ""
device_id: str = ""
class RegisterProviderRequest(BaseModel):
id: str = Field(..., description="方案 id自定义建议 custom_ 前缀)")
name: str = ""
kind: str = Field(default="http", description="native / http")
enabled: bool = True
desc: str = ""
base_url: str = ""
base_url_env: str = ""
auth: Optional[Dict[str, Any]] = None
health: Optional[Dict[str, Any]] = None
endpoints: Optional[Dict[str, Any]] = None
class ProviderExecuteRequest(BaseModel):
device_id: str
script: str = Field(default="wechat", description="平台wechat/douyin/xhs/...")
action: str = Field(..., description="规范化动作或平台 action")
params: Dict[str, Any] = Field(default_factory=dict)
provider: str = Field(default="", description="强制指定方案;空则按开关解析")
project_id: str = ""
timeout: int = 120
@router.get("/connection/providers")
async def list_connection_providers() -> Dict[str, Any]:
"""列出所有设备连接方案 + 当前开关。"""
return {
"code": 200,
"data": {
"providers": mgr.list_providers(),
"active": mgr.active_config(),
"scopes": ["global", "project", "device"],
"doc": "开发文档/1、需求/修改/工作手机_设备Agent与基础设施_20260529.md §3.6",
},
}
@router.get("/connection/provider/active")
async def get_active_provider(device_id: str = "", project_id: str = "") -> Dict[str, Any]:
"""解析指定作用域当前生效的连接方案(设备 > 项目 > 全局)。"""
pid = mgr.resolve_active_id(device_id=device_id, project_id=project_id)
prov = mgr.get(pid)
return {"code": 200, "data": {
"active_provider": pid,
"device_id": device_id,
"project_id": project_id,
"meta": prov.meta() if prov else None,
}}
@router.post("/connection/provider/switch")
async def switch_connection_provider(req: SwitchRequest) -> Dict[str, Any]:
"""一键切换设备连接方案(开关)。"""
try:
result = mgr.switch(req.provider, scope=req.scope,
project_id=req.project_id, device_id=req.device_id)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
return {"code": 200, "message": f"已切换到连接方案 {req.provider}", "data": result}
@router.post("/connection/provider/register")
async def register_connection_provider(req: RegisterProviderRequest) -> Dict[str, Any]:
"""注册/更新连接方案(预留接口:自定义工作手机方案无需改代码即可接入)。"""
try:
meta = mgr.register(req.model_dump(exclude_none=True))
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
return {"code": 200, "message": f"连接方案 {req.id} 已登记", "data": meta}
@router.delete("/connection/provider/{provider_id}")
async def delete_connection_provider(provider_id: str) -> Dict[str, Any]:
"""删除自定义连接方案(内置方案不可删,改用禁用)。"""
try:
result = mgr.remove(provider_id)
except ValueError as e:
raise HTTPException(status_code=400, detail=str(e))
return {"code": 200, "message": f"连接方案 {provider_id} 已删除", "data": result}
@router.get("/connection/provider/{provider_id}/health")
async def provider_health(provider_id: str) -> Dict[str, Any]:
"""连接方案健康/连通性检查。"""
prov = mgr.get(provider_id)
if not prov:
raise HTTPException(status_code=404, detail=f"未知连接方案: {provider_id}")
return {"code": 200, "data": await prov.health()}
@router.post("/connection/provider/execute")
async def provider_execute(req: ProviderExecuteRequest) -> Dict[str, Any]:
"""经当前(或指定)连接方案统一执行——验证切换后功能可用。"""
if req.provider:
prov = mgr.get(req.provider)
if not prov:
raise HTTPException(status_code=404, detail=f"未知连接方案: {req.provider}")
else:
prov = mgr.resolve_active(device_id=req.device_id, project_id=req.project_id)
result = await prov.execute(req.device_id, req.script, req.action, req.params, timeout=req.timeout)
if isinstance(result, dict):
result.setdefault("provider", prov.id)
return {"code": result.get("code", 200) if isinstance(result, dict) else 200, "data": result}