Files
1818web-hoduan/RUNNINGHUB_IMPLEMENTATION_TODO.md
2025-11-14 17:41:15 +08:00

9.3 KiB
Raw Blame History

RunningHub集成实现清单

已完成

  1. 创建Provider接口和DTO
  2. 数据库表扩展V5迁移脚本
  3. 配置文件扩展
  4. 实体类更新

🔨 待实现(按优先级)

1. 实现OpenAIProvider适配器

文件: src/main/java/com/dora/service/provider/impl/OpenAIProviderImpl.java

@Service
@Slf4j
@RequiredArgsConstructor
public class OpenAIProviderImpl implements AIProvider {
    
    private final ThirdPartyApiService thirdPartyApiService;
    
    @Override
    public ProviderTaskResponse submitTask(ProviderTaskRequest request) {
        // 调用现有的 thirdPartyApiService
        // 同步返回结果
    }
    
    @Override
    public String getProviderName() {
        return "openai";
    }
    
    @Override
    public boolean isAsyncProvider() {
        return false; // OpenAI是同步API
    }
}

2. 实现RunningHubProvider适配器

文件: src/main/java/com/dora/service/provider/impl/RunningHubProviderImpl.java

关键逻辑:

@Override
public ProviderTaskResponse submitTask(ProviderTaskRequest request) {
    // 1. 从providerConfig中获取webappId
    // 2. 构建nodeInfoList
    //    - prompt节点
    //    - model节点portrait/landscape等
    //    - duration_seconds节点
    // 3. POST到 /task/openapi/ai-app/run
    // 4. 解析响应获取taskId
    // 5. 返回ProviderTaskResponsestatus=PROCESSING
}

@Override
public ProviderTaskStatus queryTaskStatus(String providerTaskId) {
    // POST到 /task/openapi/status
    // 解析响应QUEUED/RUNNING/FAILED/SUCCESS
}

@Override
public ProviderTaskResult getTaskResult(String providerTaskId) {
    // POST到 /task/openapi/outputs
    // 解析data数组获取fileUrl
}

RunningHub请求DTO

@Data
class RunningHubSubmitRequest {
    private String webappId;
    private String apiKey;
    private List<RunningHubNodeInfo> nodeInfoList;
}

@Data
class RunningHubNodeInfo {
    private String nodeId;
    private String fieldName;
    private String fieldValue;
    private String fieldData;  // 可选
    private String description;
}

3. 创建AIProviderService路由服务

文件: src/main/java/com/dora/service/AIProviderService.java

@Service
@Slf4j
@RequiredArgsConstructor
public class AIProviderService {
    
    private final Map<String, AIProvider> providerMap;
    private final PointsConfigMapper pointsConfigMapper;
    
    @PostConstruct
    public void init() {
        // 初始化providerMapkey为providerType
    }
    
    public AIProvider getProvider(String modelName) {
        // 1. 从points_config表查询模型配置
        // 2. 获取providerType
        // 3. 从providerMap中获取对应的Provider
        PointsConfig config = pointsConfigMapper.findByModelName(modelName);
        String providerType = config.getProviderType();
        return providerMap.get(providerType);
    }
}

4. 添加RunningHub轮询定时器

文件: src/main/java/com/dora/scheduler/RunningHubPollingScheduler.java

@Component
@Slf4j
@RequiredArgsConstructor
public class RunningHubPollingScheduler {
    
    private final AiTaskMapper aiTaskMapper;
    private final AIProviderService providerService;
    private final AiTaskService aiTaskService;
    
    @Scheduled(fixedDelay = 5000)  // 每5秒执行一次
    public void pollRunningHubTasks() {
        // 1. 查询 status='processing' 且 provider_type='runninghub' 的任务
        List<AiTask> tasks = aiTaskMapper.findProcessingTasksByProvider("runninghub");
        
        for (AiTask task : tasks) {
            try {
                AIProvider provider = providerService.getProvider(task.getModelName());
                
                // 2. 查询任务状态
                ProviderTaskStatus status = provider.queryTaskStatus(task.getProviderTaskId());
                
                // 3. 根据状态更新
                if (status.getStatus() == Status.SUCCESS) {
                    // 获取结果
                    ProviderTaskResult result = provider.getTaskResult(task.getProviderTaskId());
                    // 更新任务为completed
                    aiTaskService.markTaskCompleted(task.getTaskNo(), result.getFiles().get(0).getFileUrl());
                } else if (status.getStatus() == Status.FAILED) {
                    // 标记为失败
                    aiTaskService.markTaskFailed(task.getTaskNo(), status.getErrorMessage());
                }
            } catch (Exception e) {
                log.error("轮询RunningHub任务失败: {}", task.getTaskNo(), e);
            }
        }
    }
}

5. 更新AiTaskMapper

文件: src/main/resources/mapper/AiTaskMapper.xml

<!-- 新增查询指定provider的processing任务 -->
<select id="findProcessingTasksByProvider" parameterType="string" resultType="com.dora.entity.AiTask">
    SELECT * FROM ai_task
    WHERE status = 'processing'
      AND provider_type = #{providerType}
      AND is_deleted = 0
    ORDER BY update_time ASC
    LIMIT 100
</select>

<!-- 更新insert语句添加provider字段 -->
<insert id="insert" parameterType="com.dora.entity.AiTask" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO ai_task (
        task_no, user_id, model_name, task_type, provider_type, provider_task_id, provider_response,
        prompt, image_url, image_base64, aspect_ratio,
        status, progress, progress_message, points_frozen, points_consumed, result_url, 
        error_message, queue_time, start_time, complete_time, expire_time
    )
    VALUES (
        #{taskNo}, #{userId}, #{modelName}, #{taskType}, #{providerType}, #{providerTaskId}, #{providerResponse},
        #{prompt}, #{imageUrl}, #{imageBase64}, #{aspectRatio},
        #{status}, #{progress}, #{progressMessage}, #{pointsFrozen}, #{pointsConsumed}, #{resultUrl}, 
        #{errorMessage}, #{queueTime}, #{startTime}, #{completeTime}, #{expireTime}
    )
</insert>

6. 更新AiTaskServiceImpl

文件: src/main/java/com/dora/service/impl/AiTaskServiceImpl.java

@Override
@Transactional(rollbackFor = Exception.class)
public AiTask createTask(CreateTaskDto createTaskDto) {
    // 1. 验证模型并获取价格
    PointsConfig pointsConfig = pointsConfigMapper.findByModelName(createTaskDto.getModelName());
    
    // 2. 扣除积分
    // ...
    
    // 3. 创建任务
    AiTask task = new AiTask();
    // ... 设置基本字段
    task.setProviderType(pointsConfig.getProviderType());  // 设置provider类型
    aiTaskMapper.insert(task);
    
    // 4. 判断provider类型
    if ("runninghub".equals(pointsConfig.getProviderType())) {
        // RunningHub直接提交到服务商
        submitToRunningHub(task, pointsConfig);
    } else {
        // OpenAI加入队列由原有流程处理
        queueService.enqueue(task.getModelName(), task.getTaskNo());
        updateTaskStatus(task.getTaskNo(), "queued", "任务已进入等待队列");
    }
    
    return task;
}

private void submitToRunningHub(AiTask task, PointsConfig config) {
    try {
        AIProvider provider = aiProviderService.getProvider(task.getModelName());
        
        // 构建请求
        ProviderTaskRequest request = ProviderTaskRequest.builder()
                .modelName(task.getModelName())
                .prompt(task.getPrompt())
                .imageUrl(task.getImageUrl())
                .imageBase64(task.getImageBase64())
                .providerConfig(parseProviderConfig(config.getProviderConfig()))
                .build();
        
        // 提交任务
        ProviderTaskResponse response = provider.submitTask(request);
        
        // 更新任务
        task.setProviderTaskId(response.getProviderTaskId());
        task.setProviderResponse(JSON.toJSONString(response.getRawResponse()));
        task.setStatus("processing");
        task.setStartTime(LocalDateTime.now());
        aiTaskMapper.update(task);
        
    } catch (Exception e) {
        log.error("提交RunningHub任务失败: {}", task.getTaskNo(), e);
        markTaskFailed(task.getTaskNo(), "提交失败: " + e.getMessage());
    }
}

🧪 测试步骤

1. 数据库迁移

mysql -u root -p your_database < V5__add_provider_support.sql

2. 配置RunningHub API Key

修改application.yml中的ai.providers.runninghub.api-key

3. 测试RunningHub模型

curl -X POST "http://localhost:8081/user/ai/tasks/submit" \
  -H "Authorization: Bearer YOUR_JWT_OR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "modelName": "rh_sora2_portrait",
    "prompt": "测试视频生成"
  }'

4. 查看轮询日志

tail -f logs/application.log | grep "RunningHub"

📝 注意事项

  1. 配置管理RunningHub的webappId需要从数据库的provider_config中读取
  2. 错误处理RunningHub API可能返回各种错误需要完善异常处理
  3. 超时控制:设置最大轮询次数,防止任务无限轮询
  4. 并发限制轮询时要控制并发数避免给RunningHub造成压力
  5. 日志记录详细记录每次API调用的请求和响应便于排查问题

🎯 预期效果

完成后,系统将支持:

  • OpenAI格式的同步API原有功能无影响
  • RunningHub的异步API新功能
  • 用户无感切换,根据模型自动选择服务商
  • 统一的任务管理和状态追踪