Files
sionrui/docs/kling-strategy-pattern.md

315 lines
8.6 KiB
Markdown
Raw Normal View History

2025-12-01 22:27:50 +08:00
# 数字人任务策略模式优化
## 概述
本次重构将数字人任务的口型同步逻辑从传统的 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 接口
```java
public interface LipSyncStrategy {
// 执行口型同步
String syncLip(TikDigitalHumanTaskDO task, String audioUrl) throws Exception;
// 策略名称
String getStrategyName();
// 是否支持该任务
boolean supports(TikDigitalHumanTaskDO task);
// 优先级(数字越大优先级越高)
int getPriority();
// 策略描述
String getDescription();
}
```
#### 2.2 LipSyncStrategyFactory 工厂类
```java
@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违反开闭原则**
```java
// 传统 if-else 实现
if ("302ai".equals(aiProvider)) {
syncWithLatentsync();
} else if ("kling".equals(aiProvider)) {
syncWithKling();
} else if ("aliyun".equals(aiProvider)) {
// TODO: 新增供应商需要修改此方法
}
```
**问题 2职责不单一**
- 每个分支包含大量业务逻辑
- 难以单元测试
- 重复代码多
**问题 3可扩展性差**
- 新增 AI 供应商需要修改核心服务类
- 违反单一职责原则
### 修改后的优势
**优势 1符合开闭原则**
```java
// 新增供应商只需:
1. 创建新的策略实现类
2. 使用 @Component 注解自动注册
3. 无需修改 DigitalHumanTaskServiceImpl
```
**优势 2职责分离**
```java
// 每个策略类专注自己的业务逻辑
KlingLipSyncStrategy → 专注可灵接口
LatentsyncLipSyncStrategy → 专注 Latentsync 接口
DigitalHumanTaskServiceImpl → 专注任务流程编排
```
**优势 3可测试性强**
```java
// 可以独立测试每个策略
@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()` 方法使用策略模式
- ✅ 注入 `LipSyncStrategyFactory``KlingService`
## 策略选择逻辑
```java
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创建策略类**
```java
@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. **优先级排序**:一次性排序,避免重复计算
## 测试建议
### 单元测试
```java
@Test
public void testKlingStrategySupports() {
// 测试支持条件
}
@Test
public void testKlingStrategySync() {
// 测试口型同步逻辑
}
@Test
public void testLatentsyncStrategySupports() {
// 测试回退逻辑
}
@Test
public void testStrategyFactory() {
// 测试策略选择逻辑
}
```
### 集成测试
```java
@Test
public void testEndToEndWithKling() {
// 测试完整的可灵流程
}
@Test
public void testEndToEndWithLatentsync() {
// 测试完整的 Latentsync 流程
}
```
## 总结
通过策略模式的引入,我们实现了:
**高内聚低耦合** - 每个策略专注自己的业务
**易于扩展** - 新增供应商无需修改核心代码
**易于维护** - 策略之间相互独立,修改互不影响
**易于测试** - 每个策略可以独立单元测试
**代码复用** - 移除重复代码,统一处理流程
这套架构设计遵循了 SOLID 原则,特别是:
- **单一职责原则SRP**:每个策略只负责一种 AI 供应商
- **开放封闭原则OCP**:对扩展开放,对修改封闭
- **依赖倒置原则DIP**:依赖抽象而非具体实现