# 多厂商AI API适配器设计方案

## 📋 目标

支持接入多个AI服务提供商，包括：
1. **OpenAI格式API**（当前使用的api.apiyi.com）
2. **RunningHub API**
3. 未来的其他厂商

## 🏗️ 架构设计

```
┌─────────────────────────────────────────────────────────┐
│                    AiTaskService                         │
│                  (业务逻辑层，不变)                        │
└────────────────────┬────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────┐
│              AIProviderService (新增)                     │
│           根据模型配置选择对应的Provider                    │
└────────────────────┬────────────────────────────────────┘
                     │
         ┌───────────┴───────────┐
         ▼                       ▼
┌──────────────────┐    ┌──────────────────┐
│ OpenAIProvider   │    │ RunningHubProvider│
│   (适配器1)       │    │    (适配器2)      │
└──────────────────┘    └──────────────────┘
         │                       │
         ▼                       ▼
┌──────────────────┐    ┌──────────────────┐
│  api.apiyi.com   │    │ www.runninghub.cn│
│   (第三方API)     │    │   (第三方API)     │
└──────────────────┘    └──────────────────┘
```

## 📝 实现步骤

### 1. 定义统一的Provider接口

```java
public interface AIProvider {
    /**
     * 提交任务到AI服务商
     * @return 统一的任务响应对象
     */
    ProviderTaskResponse submitTask(ProviderTaskRequest request);
    
    /**
     * 查询任务状态
     */
    ProviderTaskStatus queryTaskStatus(String providerTaskId);
    
    /**
     * 获取任务结果
     */
    ProviderTaskResult getTaskResult(String providerTaskId);
    
    /**
     * 获取服务商名称
     */
    String getProviderName();
}
```

### 2. 扩展数据库表

#### points_config表新增字段
```sql
ALTER TABLE `points_config`
ADD COLUMN `provider_type` VARCHAR(50) NOT NULL DEFAULT 'openai' 
    COMMENT 'AI服务提供商类型：openai, runninghub',
ADD COLUMN `provider_config` TEXT NULL 
    COMMENT '服务商特定配置（JSON格式）';
```

#### ai_task表新增字段
```sql
ALTER TABLE `ai_task`
ADD COLUMN `provider_type` VARCHAR(50) NULL 
    COMMENT 'AI服务提供商类型',
ADD COLUMN `provider_task_id` VARCHAR(100) NULL 
    COMMENT '服务商返回的任务ID',
ADD COLUMN `provider_response` TEXT NULL 
    COMMENT '服务商原始响应（JSON）';
```

### 3. 配置文件扩展

```yaml
# application.yml
ai:
  providers:
    openai:
      enabled: true
      base-url: https://api.apiyi.com/v1/chat/completions
      api-key: sk-xxx
      timeout: 300000
    runninghub:
      enabled: true
      base-url: https://www.runninghub.cn
      submit-url: /task/openapi/ai-app/run
      status-url: /task/openapi/status
      output-url: /task/openapi/outputs
      default-webapp-id: "1973555977595301890"
      api-key: 5c44cef12da3470e9f24da70c63787dc
      polling-interval: 5000  # 轮询间隔（毫秒）
      max-polling-times: 120  # 最大轮询次数（10分钟）
```

## 🔧 RunningHub特殊处理

### 模型映射配置

在`points_config`表中配置RunningHub模型：

```sql
INSERT INTO `points_config` 
(model_name, points_cost, description, is_enabled, provider_type, provider_config) 
VALUES 
('rh_sora2_portrait', 160, 'RunningHub Sora2 竖屏视频', 1, 'runninghub', 
 '{"webappId":"1973555977595301890","duration":10,"model":"portrait"}'),
 
('rh_sora2_landscape', 160, 'RunningHub Sora2 横屏视频', 1, 'runninghub', 
 '{"webappId":"1973555977595301890","duration":10,"model":"landscape"}'),
 
('rh_sora2_portrait_hd', 420, 'RunningHub Sora2 高清竖屏', 1, 'runninghub', 
 '{"webappId":"1973555977595301890","duration":10,"model":"portrait-hd"}');
```

### 异步任务处理流程

RunningHub是异步API，需要特殊处理：

```
1. 提交任务 → 获得taskId
2. 更新数据库：provider_task_id = taskId, status = 'processing'
3. 启动轮询任务：
   - 每5秒查询一次状态
   - RUNNING → 继续轮询
   - SUCCESS → 获取结果 → 更新status='completed'
   - FAILED → 更新status='failed'
   - 超时 → 更新status='failed'
```

## 📦 核心类设计

### ProviderTaskRequest（统一请求）
```java
@Data
public class ProviderTaskRequest {
    private String modelName;
    private String prompt;
    private String imageUrl;
    private String imageBase64;
    private String aspectRatio;
    private Integer duration;  // 视频时长
    private Map<String, Object> extraParams;  // 扩展参数
}
```

### ProviderTaskResponse（统一响应）
```java
@Data
public class ProviderTaskResponse {
    private String providerTaskId;  // 服务商任务ID
    private TaskSubmitStatus status;  // SUBMITTED, PROCESSING, COMPLETED, FAILED
    private String resultUrl;  // 如果是同步返回，直接有结果
    private String errorMessage;
    private Map<String, Object> rawResponse;  // 原始响应
}
```

### RunningHubNodeInfo（RunningHub请求节点）
```java
@Data
public class RunningHubNodeInfo {
    private String nodeId;
    private String fieldName;
    private String fieldValue;
    private String fieldData;  // 可选
    private String description;
}
```

## 🔄 任务轮询设计

### 新增定时任务
```java
@Scheduled(fixedDelay = 5000)  // 每5秒执行一次
public void pollAsyncTasks() {
    // 1. 查询所有 status='processing' 且 provider_type='runninghub' 的任务
    // 2. 对每个任务调用 queryTaskStatus
    // 3. 根据状态更新数据库
    // 4. 如果完成，调用 getTaskResult 获取结果
}
```

## 📊 数据流转

### OpenAI格式（同步）
```
用户提交 → OpenAIProvider.submitTask() 
        → 直接返回结果URL
        → 更新status='completed'
```

### RunningHub格式（异步）
```
用户提交 → RunningHubProvider.submitTask() 
        → 返回taskId，status='RUNNING'
        → 定时任务轮询
        → 检测到SUCCESS
        → getTaskResult()获取结果URL
        → 更新status='completed'
```

## ⚠️ 注意事项

1. **配置隔离**：不同厂商的配置独立管理
2. **错误处理**：统一异常类型，便于业务层处理
3. **日志记录**：记录每次API调用的原始请求和响应
4. **超时控制**：异步任务需要设置最大轮询次数
5. **并发控制**：轮询任务需要考虑并发和限流
6. **配置热更新**：支持动态切换服务商

## 🎯 优势

1. ✅ **扩展性**：新增厂商只需实现AIProvider接口
2. ✅ **解耦**：业务层无需关心底层API差异
3. ✅ **灵活性**：同一个模型类型可以配置多个厂商
4. ✅ **可维护性**：每个厂商的逻辑独立封装
5. ✅ **容错性**：某个厂商故障不影响其他厂商

## 📈 未来扩展

- 支持厂商负载均衡
- 支持厂商降级和熔断
- 支持厂商价格对比和智能选择
- 支持多厂商并行调用（取最快）

