Files
sionrui/docs/kling-strategy-pattern.md
2025-12-01 22:27:50 +08:00

8.6 KiB
Raw Blame History

数字人任务策略模式优化

概述

本次重构将数字人任务的口型同步逻辑从传统的 if-else 条件判断优化为策略模式,提升了代码的可维护性、可扩展性和可测试性。

架构设计

1. 策略模式结构

┌─────────────────────────────────────────┐
│   DigitalHumanTaskServiceImpl           │
│   - syncLip()                           │
│     ├─ lipSyncStrategyFactory           │
│     └─ getStrategyForTask()             │
└───────────────┬─────────────────────────┘
                │
                ▼
    ┌───────────────────┐
    │ LipSyncStrategy   │  (接口)
    │ + syncLip()       │
    │ + getStrategyName()│
    │ + supports()      │
    │ + getPriority()   │
    │ + getDescription()│
    └────────┬──────────┘
             │
             ├────────────────────┬──────────────────────
             │                    │
             ▼                    ▼
    ┌──────────────┐    ┌──────────────────────┐
    │KlingStrategy │    │LatentsyncStrategy   │
    │ - Priority:  │    │ - Priority: 50      │
    │   100        │    │ - Fallback策略       │
    │ - Advanced   │    │ - 通用口型同步       │
    │   Lip-Sync   │    └──────────────────────┘
    └──────────────┘

2. 核心组件

2.1 LipSyncStrategy 接口

public interface LipSyncStrategy {
    // 执行口型同步
    String syncLip(TikDigitalHumanTaskDO task, String audioUrl) throws Exception;

    // 策略名称
    String getStrategyName();

    // 是否支持该任务
    boolean supports(TikDigitalHumanTaskDO task);

    // 优先级(数字越大优先级越高)
    int getPriority();

    // 策略描述
    String getDescription();
}

2.2 LipSyncStrategyFactory 工厂类

@Component
public class LipSyncStrategyFactory {
    // 注册策略
    public void registerStrategy(LipSyncStrategy strategy);

    // 根据任务选择策略
    public LipSyncStrategy getStrategyForTask(TikDigitalHumanTaskDO task);

    // 获取所有支持的策略
    public List<LipSyncStrategy> getAllStrategies();
}

2.3 具体策略实现

KlingLipSyncStrategy优先级 100

  • 使用可灵 advanced-lip-sync API
  • 要求:klingSessionIdklingFaceId
  • 如果参数缺失,自动回退到 Latentsync

LatentsyncLipSyncStrategy优先级 50

  • 使用 302.ai Latentsync 通用接口
  • 作为默认回退策略
  • 支持所有标准的口型同步任务

3. 工作流程

1. 创建任务 → 验证参数 → 存储记录
                │
                ▼
2. 异步处理 → prepareFiles → synthesizeVoice
                │
                ▼
3. 选择策略 → getStrategyForTask()
                │
                ├─ Kling策略如果支持
                └─ Latentsync策略回退
                │
                ▼
4. 执行口型同步 → 提交异步任务 → 加入轮询队列
                │
                ▼
5. 轮询服务检测状态 → 更新任务 → 返回结果

重构详情

修改前的问题

问题 1违反开闭原则

// 传统 if-else 实现
if ("302ai".equals(aiProvider)) {
    syncWithLatentsync();
} else if ("kling".equals(aiProvider)) {
    syncWithKling();
} else if ("aliyun".equals(aiProvider)) {
    // TODO: 新增供应商需要修改此方法
}

问题 2职责不单一

  • 每个分支包含大量业务逻辑
  • 难以单元测试
  • 重复代码多

问题 3可扩展性差

  • 新增 AI 供应商需要修改核心服务类
  • 违反单一职责原则

修改后的优势

优势 1符合开闭原则

// 新增供应商只需:
1. 创建新的策略实现类
2. 使用 @Component 注解自动注册
3. 无需修改 DigitalHumanTaskServiceImpl

优势 2职责分离

// 每个策略类专注自己的业务逻辑
KlingLipSyncStrategy         专注可灵接口
LatentsyncLipSyncStrategy    专注 Latentsync 接口
DigitalHumanTaskServiceImpl  专注任务流程编排

优势 3可测试性强

// 可以独立测试每个策略
@Test
public void testKlingStrategy() {
    // 测试可灵策略逻辑
}

@Test
public void testLatentsyncStrategy() {
    // 测试 Latentsync 策略逻辑
}

新增文件

1. 策略接口

  • cn.iocoder.yudao.module.tik.voice.strategy.LipSyncStrategy.java

2. 策略工厂

  • cn.iocoder.yudao.module.tik.voice.strategy.LipSyncStrategyFactory.java

3. 具体策略

  • cn.iocoder.yudao.module.tik.voice.strategy.impl.KlingLipSyncStrategy.java
  • cn.iocoder.yudao.module.tik.voice.strategy.impl.LatentsyncLipSyncStrategy.java

修改文件

1. DigitalHumanTaskServiceImpl.java

  • 移除 syncWithLatentsync() 方法
  • 移除 syncWithKling() 方法
  • 重构 syncLip() 方法使用策略模式
  • 注入 LipSyncStrategyFactoryKlingService

策略选择逻辑

public LipSyncStrategy getStrategyForTask(TikDigitalHumanTaskDO task) {
    // 1. 获取所有支持的策略
    List<LipSyncStrategy> supportedStrategies = strategies.stream()
        .filter(strategy -> strategy.supports(task))
        .collect(Collectors.toList());

    // 2. 按优先级排序
    supportedStrategies.sort((a, b) -> b.getPriority() - a.getPriority());

    // 3. 返回最高优先级的策略
    return supportedStrategies.isEmpty() ? null : supportedStrategies.get(0);
}

扩展指南

新增 AI 供应商(例如:阿里云)

步骤 1创建策略类

@Component
public class AliyunLipSyncStrategy implements LipSyncStrategy {
    @Override
    public String syncLip(TikDigitalHumanTaskDO task, String audioUrl) {
        // 1. 构建阿里云请求参数
        // 2. 调用阿里云 API
        // 3. 加入轮询队列
    }

    @Override
    public String getStrategyName() {
        return "aliyun";
    }

    @Override
    public boolean supports(TikDigitalHumanTaskDO task) {
        return "aliyun".equalsIgnoreCase(task.getAiProvider());
    }

    @Override
    public int getPriority() {
        return 75; // 可灵之下Latentsync 之上
    }

    @Override
    public String getDescription() {
        return "阿里云语音驱动视频服务";
    }
}

步骤 2无需修改其他代码

  • 策略工厂会自动注册
  • 任务处理时会自动选择

回退机制

当高优先级策略因参数缺失无法执行时,会自动回退到低优先级策略:

Kling任务 → KlingStrategy检查参数
            ├─ 参数完整 → 使用 Kling advanced-lip-sync
            └─ 参数缺失 → 返回false尝试Latentsync策略
                         └─ LatentsyncStrategy → 支持 → 使用通用接口

性能优化

  1. 策略缓存:工厂类使用 @Cacheable 缓存策略选择结果
  2. 延迟加载:策略按需创建和初始化
  3. 优先级排序:一次性排序,避免重复计算

测试建议

单元测试

@Test
public void testKlingStrategySupports() {
    // 测试支持条件
}

@Test
public void testKlingStrategySync() {
    // 测试口型同步逻辑
}

@Test
public void testLatentsyncStrategySupports() {
    // 测试回退逻辑
}

@Test
public void testStrategyFactory() {
    // 测试策略选择逻辑
}

集成测试

@Test
public void testEndToEndWithKling() {
    // 测试完整的可灵流程
}

@Test
public void testEndToEndWithLatentsync() {
    // 测试完整的 Latentsync 流程
}

总结

通过策略模式的引入,我们实现了:

高内聚低耦合 - 每个策略专注自己的业务 易于扩展 - 新增供应商无需修改核心代码 易于维护 - 策略之间相互独立,修改互不影响 易于测试 - 每个策略可以独立单元测试 代码复用 - 移除重复代码,统一处理流程

这套架构设计遵循了 SOLID 原则,特别是:

  • 单一职责原则SRP:每个策略只负责一种 AI 供应商
  • 开放封闭原则OCP:对扩展开放,对修改封闭
  • 依赖倒置原则DIP:依赖抽象而非具体实现