""" 睿美云代理路由 提供统一的睿美云接口代理能力,支持: - 多租户配置隔离 - 接口权限控制 - 统一日志记录 - 错误处理 """ import logging from typing import Optional, Dict, Any, List from fastapi import APIRouter, Depends, HTTPException, Query from pydantic import BaseModel from sqlalchemy.orm import Session from ..database import get_db from ..services.ruimeiyun import RuimeiyunClient, RUIMEIYUN_APIS, get_api_definition from ..services.ruimeiyun.client import RuimeiyunError from ..services.ruimeiyun.registry import get_all_modules, get_api_list_by_module, get_api_summary from .auth import get_current_user from ..models.user import User logger = logging.getLogger(__name__) router = APIRouter(prefix="/ruimeiyun", tags=["睿美云代理"]) # ======================================== # Schemas # ======================================== class RuimeiyunCallRequest(BaseModel): """睿美云接口调用请求""" params: Optional[Dict[str, Any]] = None # URL 参数 body: Optional[Dict[str, Any]] = None # 请求体 class RuimeiyunRawCallRequest(BaseModel): """睿美云原始接口调用请求""" method: str # HTTP 方法 path: str # API 路径 params: Optional[Dict[str, Any]] = None body: Optional[Dict[str, Any]] = None # ======================================== # API Endpoints # ======================================== @router.post("/call/{api_name}") async def call_ruimeiyun_api( api_name: str, request: RuimeiyunCallRequest, tenant_id: str = Query(..., description="租户ID"), user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """ 调用睿美云接口(通过接口名称) Args: api_name: 接口名称,如 customer.search, order.list request: 请求参数 tenant_id: 租户ID Returns: 睿美云接口返回的数据 示例: POST /api/ruimeiyun/call/customer.search?tenant_id=xxx Body: {"params": {"keyword": "13800138000", "page": 1, "size": 20}} """ try: client = RuimeiyunClient(tenant_id, db) result = await client.call( api_name=api_name, params=request.params, body=request.body ) if result.success: return { "success": True, "data": result.data } else: return { "success": False, "error": result.error, "raw": result.raw_response } except RuimeiyunError as e: logger.error(f"睿美云调用失败: {api_name}, {e}") raise HTTPException(status_code=e.status_code, detail=str(e)) except Exception as e: logger.exception(f"睿美云调用异常: {api_name}") raise HTTPException(status_code=500, detail=f"调用失败: {str(e)}") @router.post("/call-raw") async def call_ruimeiyun_raw( request: RuimeiyunRawCallRequest, tenant_id: str = Query(..., description="租户ID"), user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """ 直接调用睿美云接口(通过路径) 用于调用未在注册表中定义的接口 Args: request: 请求参数(包含 method, path, params, body) tenant_id: 租户ID Returns: 睿美云接口返回的数据 示例: POST /api/ruimeiyun/call-raw?tenant_id=xxx Body: { "method": "GET", "path": "/api/v1/tpos/customer/customer-search", "params": {"keyword": "13800138000"} } """ try: client = RuimeiyunClient(tenant_id, db) result = await client.call_raw( method=request.method, path=request.path, params=request.params, body=request.body ) if result.success: return { "success": True, "data": result.data } else: return { "success": False, "error": result.error, "raw": result.raw_response } except RuimeiyunError as e: logger.error(f"睿美云调用失败: {request.path}, {e}") raise HTTPException(status_code=e.status_code, detail=str(e)) except Exception as e: logger.exception(f"睿美云调用异常: {request.path}") raise HTTPException(status_code=500, detail=f"调用失败: {str(e)}") # ======================================== # 接口元数据 # ======================================== @router.get("/apis") async def list_apis( module: Optional[str] = None, user: User = Depends(get_current_user) ): """ 获取可用的接口列表 Args: module: 模块名称(可选),如 customer, order Returns: 接口列表 """ if module: apis = get_api_list_by_module(module) return { "module": module, "count": len(apis), "apis": apis } else: return { "count": len(RUIMEIYUN_APIS), "summary": get_api_summary(), "apis": RUIMEIYUN_APIS } @router.get("/apis/{api_name}") async def get_api_info( api_name: str, user: User = Depends(get_current_user) ): """ 获取接口详情 Args: api_name: 接口名称,如 customer.search Returns: 接口定义 """ api_def = get_api_definition(api_name) if not api_def: raise HTTPException(status_code=404, detail=f"接口不存在: {api_name}") return { "name": api_name, **api_def } @router.get("/modules") async def list_modules( user: User = Depends(get_current_user) ): """ 获取所有模块列表 Returns: 模块名称列表和每个模块的接口数量 """ modules = get_all_modules() summary = get_api_summary() return { "modules": [ {"name": m, "count": summary.get(m, 0)} for m in modules ] } # ======================================== # 健康检查 # ======================================== @router.get("/health/{tenant_id}") async def check_ruimeiyun_health( tenant_id: str, user: User = Depends(get_current_user), db: Session = Depends(get_db) ): """ 检查租户的睿美云连接状态 Args: tenant_id: 租户ID Returns: 连接状态信息 """ try: client = RuimeiyunClient(tenant_id, db) # 调用门店列表接口测试连接 result = await client.call("tenant.list") if result.success: return { "status": "connected", "tenant_id": tenant_id, "base_url": client.config.base_url, "account": client.config.account, "message": "连接正常" } else: return { "status": "error", "tenant_id": tenant_id, "message": result.error } except RuimeiyunError as e: return { "status": "error", "tenant_id": tenant_id, "message": str(e) } except Exception as e: return { "status": "error", "tenant_id": tenant_id, "message": f"检查失败: {str(e)}" }