8.6 KiB
8.6 KiB
数字人任务策略模式优化
概述
本次重构将数字人任务的口型同步逻辑从传统的 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
- 要求:
klingSessionId和klingFaceId - 如果参数缺失,自动回退到 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.javacn.iocoder.yudao.module.tik.voice.strategy.impl.LatentsyncLipSyncStrategy.java
修改文件
1. DigitalHumanTaskServiceImpl.java
- ✅ 移除
syncWithLatentsync()方法 - ✅ 移除
syncWithKling()方法 - ✅ 重构
syncLip()方法使用策略模式 - ✅ 注入
LipSyncStrategyFactory和KlingService
策略选择逻辑
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 → 支持 → 使用通用接口
性能优化
- 策略缓存:工厂类使用
@Cacheable缓存策略选择结果 - 延迟加载:策略按需创建和初始化
- 优先级排序:一次性排序,避免重复计算
测试建议
单元测试
@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):依赖抽象而非具体实现