diff --git a/.claude/settings.local.json b/.claude/settings.local.json index da8d46ba94..49db19d0d6 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -15,7 +15,9 @@ "Bash(git checkout:*)", "Bash(tree:*)", "Bash(ls:*)", - "Bash(mysql:*)" + "Bash(mysql:*)", + "Bash(npm run lint:*)", + "Bash(npx vue-tsc:*)" ], "deny": [], "ask": [] diff --git a/CLAUDE.md b/CLAUDE.md index 28c1fda488..4fae13ef1d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -25,6 +25,10 @@ - Pinia 3.0.3 状态管理 - TailwindCSS 4.1.14 样式 +## 代码简化 +- 只保留核心分支,移除重复校验 / 冗余注释”,例:“生成 Java 订单支付接口逻辑,仅包含参数非空校验、支付状态判断 2 个核心分支,无需异常场景的冗余兜底代码 +- 用三目运算符简化 if-else 冗余,避免单分支重复判断;变量仅定义必要的,移除未被调用的临时变量 + **数据库与基础设施:** - MySQL 8.0+(主要) - 支持 PostgreSQL、Oracle、SQL Server、DM、KingbaseES、OpenGauss、TiDB diff --git a/docs/ICE_SubmitMediaProducingJob_API.md b/docs/ICE_SubmitMediaProducingJob_API.md deleted file mode 100644 index 39eb690055..0000000000 --- a/docs/ICE_SubmitMediaProducingJob_API.md +++ /dev/null @@ -1,365 +0,0 @@ -# 阿里云智能媒体服务 - SubmitMediaProducingJob API文档 - -## 📋 API概述 - -**接口名称:** SubmitMediaProducingJob(提交剪辑合成作业) - -**服务名称:** Intelligent Media Services (IMS) - 智能媒体服务 - -**API版本:** 2020-11-09 - -### 业务说明 - -`SubmitMediaProducingJob` 是阿里云智能媒体服务的核心API接口,主要用于**提交媒体剪辑合成任务**。当用户需要对视频或音频素材进行剪辑、合成、添加特效、转码等后期制作时,可以通过调用此接口自动完成这些复杂的媒体处理工作。 - -#### 核心业务场景 - -1. **视频剪辑制作** - - 多个视频片段的拼接合成 - - 添加转场效果和过渡动画 - - 视频片段的裁剪和缩放 - -2. **音视频处理** - - 音频与视频的同步合成 - - 添加背景音乐和音效 - - 音频混合和音量调节 - -3. **多轨道编辑** - - 支持视频轨道、音频轨道、字幕轨道 - - 实现复杂的多层编辑效果 - - 视频叠加和水印添加 - -4. **模板化制作** - - 使用预定义模板快速生成视频 - - 批量内容生产 - - 统一风格的视频输出 - -5. **云端转码** - - 视频格式转换(MP4、AVI、MOV等) - - 分辨率和码率调整 - - 自适应码率输出 - ---- - -## 🔐 授权信息 - -| 操作名 | 访问级别 | 资源类型 | 条件键 | 关联操作 | -|--------|----------|----------|--------|----------| -| ice:SubmitMediaProducingJob | 写权限 | 所有资源 (`*`) | 无 | 无 | - ---- - -## 📡 接口调用 - -**请求方法:** POST - -**调用地址:** `https://ims.ap-southeast-1.aliyuncs.com/` - -**请求路径:** `/2020-11-09/submitMediaProducingJob` - -### ⚠️ 重要说明 - -- 此接口仅返回作业**提交结果**,作业提交后将在后台异步处理 -- 时间线中引用的素材可以是媒体库中的资产或OSS对象 -- **不支持**外部URL或CDN URL -- 生产完成后,输出文件会自动注册为媒体资产 -- 需要先分析媒体资产,才能查询时长和分辨率信息 - ---- - -## 🔒 调用限制 - -| 限制项 | 限制值 | 说明 | -|--------|--------|------| -| **QPS限制** | 30次/秒 | 超出限制会返回"Throttling.User"错误 | -| **视频轨道** | 最多100个 | 每个项目最多可创建100条视频轨道 | -| **图片轨道** | 最多100个 | 每个项目最多可创建100条图片轨道 | -| **字幕轨道** | 最多100个 | 每个项目最多可创建100条字幕轨道 | -| **素材总大小** | 不超过1TB | 项目中所有素材文件的总大小限制 | -| **输出分辨率** | 128px - 4096px | 宽度和高度都必须在128-4096像素之间 | -| **视频短边** | 不超过2160px | 视频的短边不能超过2160像素 | -| **区域限制** | 同一区域 | 素材和输出的OSS桶必须与IMS服务区域一致 | - ---- - -## 📝 请求参数 - -### 主要参数说明 - -| 参数名 | 类型 | 必填 | 描述 | 示例值 | -|--------|------|------|------|--------| -| **ProjectId** | string | 否 | 编辑项目的ID | `xxxxxfb2101cb318xxxxx` | -| **Timeline** | string | 否 | 在线编辑作业的时间线配置 | 详见时间线配置 | -| **TemplateId** | string | 否 | 模板ID(使用模板快速构建时间线) | `****96e8864746a0b6f3****` | -| **ClipsParam** | string | 否 | 模板素材参数(JSON格式) | - | -| **ProjectMetadata** | string | 否 | 编辑项目的元数据(JSON格式) | - | -| **OutputMediaTarget** | string | 否 | 输出文件类型:`oss-object`/`vod-media`/`S3` | `oss-object` | -| **OutputMediaConfig** | **Yes** | **是** | 输出文件配置(JSON格式) | 详见配置说明 | -| **UserData** | string | 否 | 用户自定义数据(最多512字节) | `{"NotifyAddress":"https://..."}` | -| **ClientToken** | string | 否 | 客户端令牌(确保请求幂等性) | `****12e8864746a0a398****` | -| **Source** | string | 否 | 请求来源:`OpenAPI`/`AliyunConsole`/`WebSDK` | `OPENAPI` | -| **EditingProduceConfig** | string | 否 | 编辑制作参数 | 详见配置说明 | -| **MediaMetadata** | string | 否 | 产出视频的元数据 | `{"Title":"test-title"}` | - -### 参数组合规则 - -**三选一参数:** `ProjectId`、`Timeline`、`TemplateId` 中必须指定一个,其余两个必须为空。 - -- 如果指定 `ProjectId`:使用现有编辑项目 -- 如果指定 `Timeline`:直接定义时间线 -- 如果指定 `TemplateId`:必须同时指定 `ClipsParam` - ---- - -## 💾 输出配置示例 - -### 示例1:输出到OSS - -```json -{ - "MediaURL": "https://my-test-bucket.oss-cn-shanghai.aliyuncs.com/test/xxxxxtest001xxxxx.mp4", - "Bitrate": 2000, - "Width": 800, - "Height": 680 -} -``` - -**配置说明:** -- `MediaURL`:OSS对象URL,格式为 `https://bucketname.oss-region-name.aliyuncs.com/xxx/yyy.ext` -- `Bitrate`:输出码率(Kbit/s),越高视频越清晰,最大值为5000 -- `Width`/`Height`:输出分辨率,留空则使用输入素材的最大分辨率 - -### 示例2:输出到ApsaraVideo VOD - -```json -{ - "StorageLocation": "outin-*xxxxxx7d2a3811eb83da00163exxxxxx.oss-cn-shanghai.aliyuncs.com", - "FileName": "output.mp4", - "Bitrate": 2000, - "Width": 800, - "Height": 680, - "VodTemplateGroupId": "VOD_NO_TRANSCODE" -} -``` - -**配置说明:** -- `StorageLocation`:VOD中的存储位置(不含http://前缀) -- `FileName`:输出文件名(包含扩展名) -- `VodTemplateGroupId`:VOD转码模板组ID,设为`VOD_NO_TRANSCODE`表示不转码 - -### OutputMediaConfig 参数详解 - -| 参数名 | 类型 | 说明 | -|--------|------|------| -| MediaURL | String | 输出文件URL(oss-object类型) | -| StorageLocation | String | VOD存储位置(vod-media类型) | -| FileName | String | 输出文件名(vod-media类型) | -| Width | Integer | 输出宽度(默认:输入素材最大宽度) | -| Height | Integer | 输出高度(默认:输入素材最大高度) | -| Bitrate | Integer | 输出码率(默认:输入素材最大码率) | -| VodTemplateGroupId | String | VOD转码模板组ID | - ---- - -## 📤 响应参数 - -| 参数名 | 类型 | 描述 | 示例值 | -|--------|------|------|--------| -| RequestId | string | 请求ID(唯一标识) | `****36-3C1E-4417-BDB2-1E034F****` | -| ProjectId | string | 编辑项目ID | `****b4549d46c88681030f6e****` | -| **JobId** | string | **作业ID(用于查询作业状态)** | `****d80e4e4044975745c14b****` | -| MediaId | string | 输出文件的媒体资产ID | `****c469e944b5a856828dc2****` | -| VodMediaId | string | 输出文件在VOD中的媒体资产ID(如适用) | `****d8s4h75ci975745c14b****` | - -### 响应示例 - -```json -{ - "RequestId": "****36-3C1E-4417-BDB2-1E034F****", - "ProjectId": "****b4549d46c88681030f6e****", - "JobId": "****d80e4e4044975745c14b****", - "MediaId": "****c469e944b5a856828dc2****", - "VodMediaId": "****d8s4h75ci975745c14b****" -} -``` - ---- - -## 🔧 EditingProduceConfig 配置 - -用于控制编辑制作过程的参数。 - -```json -{ - "AutoRegisterInputVodMedia": "true", - "OutputWebmTransparentChannel": "true", - "CoverConfig": { - "CustomThumbnail": "https://example.com/thumb.jpg" - } -} -``` - -**参数说明:** -- `AutoRegisterInputVodMedia`:是否自动注册时间线中的VOD媒体资产到IMS,默认 `true` -- `OutputWebmTransparentChannel`:输出视频是否包含Alpha通道(透明度),默认 `false` -- `CoverConfig`:自定义缩略图配置 - ---- - -## 🚨 错误码 - -| HTTP状态码 | 错误码 | 错误消息 | -|------------|--------|----------| -| 400 | InvalidParameter | 参数不合法 | -| 404 | ProjectNotFound | 指定的项目不存在 | -| 429 | Throttling.User | 请求频率超过限制(30 QPS) | - ---- - -## 💡 典型使用场景 - -### 场景1:视频片段拼接 - -```json -{ - "Timeline": { - "VideoTracks": [ - { - "VideoTrackClips": [ - {"MediaId": "****4d7cf14dc7b83b0e801c****"}, - {"MediaId": "****4d7cf14dc7b83b0e801c****"} - ] - } - ] - }, - "OutputMediaConfig": { - "MediaURL": "https://my-bucket.oss-cn-shanghai.aliyuncs.com/output.mp4", - "Bitrate": 2000 - } -} -``` - -**业务场景:** 将两个视频片段无缝拼接成一个完整视频 - -### 场景2:模板化视频生产 - -```json -{ - "TemplateId": "****template-id****", - "ClipsParam": { - "clips": [ - {"MediaId": "****video1****"}, - {"MediaId": "****video2****"} - ] - }, - "OutputMediaConfig": { - "MediaURL": "https://my-bucket.oss-cn-shanghai.aliyuncs.com/template-output.mp4" - } -} -``` - -**业务场景:** 使用预定义模板快速生成风格统一的视频内容 - -### 场景3:视频转码并上传VOD - -```json -{ - "Timeline": { - "VideoTracks": [ - { - "VideoTrackClips": [ - {"MediaId": "****source-video****"} - ] - } - ] - }, - "OutputMediaTarget": "vod-media", - "OutputMediaConfig": { - "StorageLocation": "outin-xxxxx.oss-cn-shanghai.aliyuncs.com", - "FileName": "transcoded-video.mp4", - "Bitrate": 1500, - "Width": 1920, - "Height": 1080 - }, - "UserData": { - "NotifyAddress": "https://your-callback-url.com" - } -} -``` - -**业务场景:** 将视频转码为不同分辨率和码率,并直接上传到VOD系统 - ---- - -## 📊 相关API - -- **GetMediaProducingJob**:查询媒体剪辑合成作业状态 -- **CancelMediaProducingJob**:取消媒体剪辑合成作业 -- **CreateEditingProject**:创建编辑项目 - ---- - -## 🔗 相关文档 - -- [时间线配置说明](https://www.alibabacloud.com/help/en/ims/developer-reference/timeline-configuration-description) -- [编辑制作参数说明](https://www.alibabacloud.com/help/en/ims/developer-reference/clip-composition-parameter-description) -- [模板创建和使用](https://www.alibabacloud.com/help/en/ims/user-guide/create-and-use-a-normal-template) -- [回调配置](https://www.alibabacloud.com/help/en/ims/use-cases/to-configure-a-callback-when-a-clip-completes) -- [常见问题FAQ](https://www.alibabacloud.com/help/en/ims/support/intelligent-production-making-faq) - ---- - -## 📌 注意事项 - -1. **异步处理:** 作业提交后立即返回,任务在后台异步执行 -2. **费用说明:** 按实际处理时长和输出文件大小计费 -3. **配额管理:** 建议使用 `ClientToken` 确保请求幂等性 -4. **回调通知:** 通过 `UserData.NotifyAddress` 设置完成回调通知 -5. **文件大小:** 单次处理的文件总大小建议不超过1GB,超过建议分段处理 -6. **格式支持:** 支持主流视频/音频格式(MP4、AVI、MOV、MP3、AAC等) -7. **转码速度:** 处理速度取决于输出质量设置,高质量处理时间较长 - ---- - -## 🎯 最佳实践 - -### 1. 幂等性保证 -```javascript -// 使用ClientToken确保同一请求不会被重复处理 -const clientToken = generateUUID(); -await submitMediaProducingJob({ - ClientToken: clientToken, - // ... 其他参数 -}); -``` - -### 2. 状态轮询 -```javascript -// 提交作业后,使用JobId轮询查询状态 -const jobId = response.JobId; -const status = await getMediaProducingJob({ JobId: jobId }); -``` - -### 3. 错误重试 -```javascript -// 针对网络错误或限流错误进行指数退避重试 -try { - await submitMediaProducingJob(params); -} catch (error) { - if (error.code === 'Throttling.User') { - // 等待后重试 - await sleep(1000); - await submitMediaProducingJob(params); - } -} -``` - -### 4. 资源清理 -```javascript -// 处理完成后,及时清理不必要的中间文件 -await deleteMediaAssets([tempMediaId1, tempMediaId2]); -``` - ---- - -*文档版本:v1.0* | *最后更新:2025-11-29* diff --git a/docs/kling-integration.md b/docs/kling-integration.md deleted file mode 100644 index 824b2a3ef7..0000000000 --- a/docs/kling-integration.md +++ /dev/null @@ -1,155 +0,0 @@ -# 可灵数字人功能集成报告 - -## 功能概述 - -基于302.ai的可灵API,我们成功集成了独立的人脸识别功能,为后续对口型服务提供基础支持。 - -## 后端开发 - -### 1. API客户端 -- **文件**: `yudao-module-tik/src/main/java/cn/iocoder/yudao/module/tik/kling/KlingClient.java` -- **功能**: 调用302.ai的Identify-Face接口 -- **复用**: 使用现有的LatentsyncProperties配置(API密钥已配置) - -### 2. 业务服务 -- **Service接口**: `KlingService.java` -- **Service实现**: `KlingServiceImpl.java` -- **Controller**: `KlingController.java` (路径: `/webApi/api/tik/kling/identify-face`) - -### 3. 数据传输对象 -- **请求VO**: `KlingIdentifyFaceReqVO.java` -- **响应VO**: `KlingIdentifyFaceRespVO.java` -- **DTO**: `KlingIdentifyFaceRequest.java`, `KlingIdentifyFaceResponse.java` - -### API接口 -``` -POST /webApi/api/tik/kling/identify-face -Content-Type: application/json - -Request: -{ - "videoUrl": "https://example.com/video.mp4" -} - -Response: -{ - "code": 0, - "data": { - "sessionId": "session_xxxxx", - "faceData": [ - { - "faceId": "face_001", - "faceImage": "https://...", - "startTime": 1000, - "endTime": 5000 - } - ] - } -} -``` - -## 前端开发 - -### 1. API服务 -- **文件**: `frontend/app/web-gold/src/api/kling.js` -- **功能**: `identifyFace()` 方法 - -### 2. 页面组件 -- **文件**: `frontend/app/web-gold/src/views/kling/IdentifyFace.vue` -- **功能**: - - 视频上传(支持 .mp4/.mov) - - 拖拽上传支持 - - 进度显示 - - 识别结果展示(人脸列表、时间段) - - 科技极简风格UI(黑蓝紫色调) - -### 3. 路由配置 -- **文件**: `frontend/app/web-gold/src/router/index.js` -- **路径**: `/digital-human/kling` -- **菜单项**: "可灵数字人"(位于"数字人"分组下) - -## 技术特点 - -### 1. 配置复用 -- 复用了现有的 `LatentsyncProperties` 配置 -- API Key: `sk-0IZJ2oo7VCkegFuF3JRsSRtyFUsIvLoHNK8OpulnlsStFN78` -- Base URL: `https://api.302.ai` - -### 2. 代码架构 -- 遵循Yudao框架的分层架构 -- 复用现有的客户端模式(参考LatentsyncClient) -- VO/DTO分层设计 - -### 3. 前端设计 -- 采用Vue 3 Composition API -- 响应式设计(支持移动端) -- 科技极简风格(渐变背景、毛玻璃效果) - -## 文件结构 - -``` -yudao-module-tik/src/main/java/cn/iocoder/yudao/module/tik/kling/ -├── KlingClient.java # API客户端 -├── KlingService.java # 服务接口 -├── KlingServiceImpl.java # 服务实现 -├── KlingController.java # REST控制器 -├── dto/ -│ ├── KlingIdentifyFaceRequest.java -│ └── KlingIdentifyFaceResponse.java -└── vo/ - ├── KlingIdentifyFaceReqVO.java - └── KlingIdentifyFaceRespVO.java - -frontend/app/web-gold/src/ -├── api/ -│ └── kling.js # 前端API -├── views/ -│ └── kling/ -│ └── IdentifyFace.vue # 识别页面 -└── router/ - └── index.js # 路由配置(含可灵路由) -``` - -## 使用说明 - -### 1. 访问页面 -- 登录系统后,点击左侧菜单 **数字人** > **可灵数字人** - -### 2. 上传视频 -- 支持格式:.mp4, .mov -- 文件大小:≤ 100MB -- 视频时长:2-60秒 -- 支持拖拽上传 - -### 3. 人脸识别 -- 点击"开始识别"按钮 -- 系统调用302.ai API进行分析 -- 显示识别结果: - - 会话ID(用于后续操作) - - 检测到的人脸列表 - - 每张人脸的可对口型时间段 - -## 注意事项 - -1. **视频URL**: 目前页面使用本地URL进行测试,生产环境需要先上传到OSS获取公网URL -2. **API密钥**: 已使用现有的302.ai配置,无需额外配置 -3. **跨域**: 确保前端已配置API代理到后端 - -## 后续扩展 - -1. **口型同步功能**: 基于sessionId进行口型同步 -2. **批量处理**: 支持多个视频的批量识别 -3. **历史记录**: 保存识别历史到数据库 -4. **结果导出**: 支持导出识别结果 - -## 测试建议 - -1. 测试不同格式的视频文件 -2. 测试大文件上传(接近100MB) -3. 测试网络异常情况 -4. 测试识别失败场景 - ---- - -**开发完成时间**: 2024-11-30 -**状态**: 后端API完成,前端页面完成,待测试联调 diff --git a/docs/kling-strategy-pattern.md b/docs/kling-strategy-pattern.md deleted file mode 100644 index 451c975a65..0000000000 --- a/docs/kling-strategy-pattern.md +++ /dev/null @@ -1,314 +0,0 @@ -# 数字人任务策略模式优化 - -## 概述 - -本次重构将数字人任务的口型同步逻辑从传统的 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 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 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)**:依赖抽象而非具体实现 diff --git a/docs/naming-conflict-fix.md b/docs/naming-conflict-fix.md deleted file mode 100644 index 5a9812c793..0000000000 --- a/docs/naming-conflict-fix.md +++ /dev/null @@ -1,213 +0,0 @@ -# 命名冲突问题修复 - -## 🚨 问题描述 - -在之前的重构中,我们遇到了一个严重的**命名歧义**问题: - -### 原始问题代码 - -```java -public class KlingLipSyncCreateResponse { - private Data data; // ❌ 成员变量类型是 Data - private String message; - - @Data - public static class Data { // ❌ 静态内部类也叫 Data - private String taskId; - } -} -``` - -### 问题分析 - -1. **歧义性**: - - `private Data data;` - 这里 `Data` 既是成员变量的**类型**,又是**静态内部类**的名称 - - 编译器需要推断 `Data` 是指内部类 `KlingLipSyncCreateResponse.Data` 还是其他包中的类 - -2. **可读性差**: - - `private Data data;` - 这样的命名不够清晰 - - 不明确 `data` 变量的具体含义 - -3. **潜在错误**: - - 如果有其他包的 `Data` 类被导入,可能会导致类型混淆 - - 代码维护困难 - ---- - -## ✅ 修复方案 - -### 修复后的代码 - -```java -public class KlingLipSyncCreateResponse { - private Data data; // ✅ 现在明确指向内部静态类 Data - private String message; - - @Data - public static class Data { // ✅ 静态内部类,名称清晰 - private String taskId; - } -} -``` - -### 为什么这样修复可行? - -1. **明确性**: - - 在类内部,`Data` 默认指向当前类的内部静态类 - - 编译器可以正确解析类型 - -2. **保持简洁**: - - 保持原有的简洁命名 - - 内部类的名称与变量名称不同,不会有歧义 - -3. **符合规范**: - - 静态内部类名称使用首字母大写的驼峰命名法 - - 成员变量名称使用小写开头的驼峰命名法 - ---- - -## 📋 受影响文件 - -以下文件都存在相同的问题,已全部修复: - -### DTO 包 -1. ✅ `KlingLipSyncCreateResponse.java` -2. ✅ `KlingLipSyncQueryResponse.java` - -### VO 包 -1. ✅ `KlingLipSyncCreateRespVO.java` -2. ✅ `KlingLipSyncQueryRespVO.java` - ---- - -## 🔍 代码检查清单 - -### 检查点 1:静态内部类命名 - -```java -// ✅ 正确:静态内部类使用首字母大写的驼峰命名 -public static class Data { } - -// ✅ 正确:静态内部类使用首字母大写的驼峰命名 -public static class TaskInfo { } - -// ❌ 错误:不应该使用小写命名 -public static class data { } -``` - -### 检查点 2:成员变量命名 - -```java -// ✅ 正确:成员变量使用小写开头的驼峰命名 -private Data data; -private String message; - -// ✅ 正确:变量名应该尽量描述性 -private ResponseData responseData; -``` - -### 检查点 3:类型引用 - -```java -// ✅ 在类内部,默认指向内部静态类 -private Data data; // 指向 KlingXxx.Data - -// ✅ 如果有歧义,可以显式指定 -private KlingLipSyncCreateResponse.Data data; - -// ✅ 跨包引用需要完整路径 -private com.example.OtherData otherData; -``` - ---- - -## 💡 最佳实践 - -### 1. 静态内部类命名规范 - -```java -// ✅ 推荐:使用有意义的名称 -public static class ResponseData { } -public static class RequestData { } - -// ✅ 推荐:即使简单也要遵循规范 -public static class Data { } -public static class Info { } -``` - -### 2. 避免歧义的策略 - -```java -// ✅ 方法1:使用描述性变量名 -private Data responseData; // 明确这是响应数据 - -// ✅ 方法2:使用显式类型 -private KlingLipSyncCreateResponse.Data data; - -// ✅ 方法3:重构类名 -public static class CreateResponseData { } -private CreateResponseData data; -``` - -### 3. 导入语句的注意事项 - -```java -// ✅ 如果导入了其他 Data 类,优先使用内部类 -import com.example.Data; // 可能冲突 - -// ✅ 解决方案:使用全限定名 -private com.example.Data externalData; -private Data internalData; // 指向内部静态类 -``` - ---- - -## 📝 测试建议 - -### 单元测试 - -```java -@Test -public void testResponseData() { - KlingLipSyncCreateResponse response = new KlingLipSyncCreateResponse(); - KlingLipSyncCreateResponse.Data data = response.getData(); - assertNotNull(data); -} -``` - -### 集成测试 - -```java -@Test -public void testJsonSerialization() { - KlingLipSyncCreateResponse response = new KlingLipSyncCreateResponse(); - String json = objectMapper.writeValueAsString(response); - assertNotNull(json); -} -``` - ---- - -## ✅ 验证清单 - -修复完成后,请检查以下项目: - -- [ ] 所有文件的命名冲突已修复 -- [ ] 编译无错误和警告 -- [ ] 单元测试通过 -- [ ] 集成测试通过 -- [ ] JSON 序列化正常 -- [ ] 类型转换正常 - ---- - -## 🎯 总结 - -本次修复解决了静态内部类与成员变量命名冲突的问题。通过遵循Java命名规范,我们确保了: - -1. **代码清晰** - 命名无歧义,易于理解 -2. **类型安全** - 编译器能正确解析类型 -3. **易于维护** - 遵循最佳实践,便于后续维护 -4. **向下兼容** - API 不变,不影响现有调用 - -这次修复提升了代码质量,为项目的长期维护奠定了坚实基础。 diff --git a/docs/vo-refactoring-final-summary.md b/docs/vo-refactoring-final-summary.md deleted file mode 100644 index 7be025282c..0000000000 --- a/docs/vo-refactoring-final-summary.md +++ /dev/null @@ -1,194 +0,0 @@ -# VO/DTO 静态内部类重构 - 最终总结 - -## 🎯 重构目标 - -将大量重复的独立VO/DTO类合并,使用静态内部类减少类数量。 - ---- - -## ✅ 已完成的修复 - -### 📁 DTO 包 (dto/) - -**重构前:** -- 13+ 个文件,包括大量重复的独立类 - -**重构后:5个核心文件** -``` -1. KlingIdentifyFaceRequest.java -2. KlingIdentifyFaceResponse.java -3. KlingLipSyncCreateRequest.java - └─ 静态内部类: FaceChoose -4. KlingLipSyncCreateResponse.java - └─ 静态内部类: Data, TaskInfo -5. KlingLipSyncQueryResponse.java - └─ 静态内部类: Data, TaskInfo, ParentVideo, TaskResult, Video -``` - -### 📁 VO 包 (vo/) - -**重构前:** -- 13+ 个文件,包括大量重复的独立类 - -**重构后:5个核心文件** -``` -1. KlingIdentifyFaceReqVO.java -2. KlingIdentifyFaceRespVO.java -3. KlingLipSyncCreateReqVO.java - └─ 静态内部类: FaceChooseVO -4. KlingLipSyncCreateRespVO.java - └─ 静态内部类: Data, TaskInfo -5. KlingLipSyncQueryRespVO.java - └─ 静态内部类: Data, TaskInfo, ParentVideo, TaskResult, Video -``` - ---- - -## 📊 重构成果 - -### 类数量对比 - -| 类别 | 重构前 | 重构后 | 减少 | -|------|--------|--------|------| -| DTO文件 | 13+ | 5 | **-62%** | -| VO文件 | 13+ | 5 | **-62%** | -| 总文件数 | 26+ | 10 | **-62%** | - -### 命名对比 - -| 场景 | 重构前 | 重构后 | -|------|--------|--------| -| 请求对象 | `KlingFaceChooseVO` (独立类) | `KlingLipSyncCreateReqVO.FaceChooseVO` (静态内部类) | -| 响应数据 | `KlingLipSyncCreateDataVO` (独立类) | `KlingLipSyncCreateRespVO.Data` (静态内部类) | -| 任务信息 | `KlingLipSyncTaskInfoVO` (独立类) | `KlingLipSyncCreateRespVO.TaskInfo` (静态内部类) | - ---- - -## 🔧 技术方案 - -### 方案选择:静态内部类 - -**为什么选择静态内部类?** -1. ✅ **减少类数量** - 避免创建大量独立的重复类 -2. ✅ **保持类型安全** - 通过静态内部类保持强类型 -3. ✅ **逻辑分组** - 相关类组织在一起,便于理解 -4. ✅ **API清晰** - 层次结构明确:`OuterClass.InnerClass` -5. ✅ **BeanUtils兼容** - 支持 DTO ↔ VO 转换 - -### 注意事项 - -1. **@Data 注解** - 静态内部类可以使用 @Data -2. **Lombok 配置** - 确保项目正确配置 Lombok -3. **Bean 转换** - 使用 `BeanUtils.toBean()` 进行对象转换 -4. **JSON 序列化** - @JsonProperty 注解确保正确的序列化 - ---- - -## 📝 使用示例 - -### 前端调用 (不变) - -```javascript -// 仍然使用相同的API -const response = await createLipSyncTask({ - sessionId: 'xxx', - faceChoose: [{ - faceId: 'xxx', - soundFile: 'audio.mp3' - }] -}) -``` - -### 后端转换 - -```java -// DTO -> VO 转换 -KlingLipSyncCreateRespVO vo = BeanUtils.toBean(dto, KlingLipSyncCreateRespVO.class); - -// 访问静态内部类 -String taskId = vo.getData().getTaskId(); -String externalTaskId = vo.getData().getTaskInfo().getExternalTaskId(); -``` - -### 策略模式 (不变) - -```java -// 仍然使用相同的策略 -LipSyncStrategy strategy = lipSyncStrategyFactory.getStrategyForTask(task); -return strategy.syncLip(task, audioUrl); -``` - ---- - -## 🎨 文件结构 - -### DTO 包结构 - -``` -cn.iocoder.yudao.module.tik.kling.dto -├── KlingIdentifyFaceRequest.java -├── KlingIdentifyFaceResponse.java -├── KlingLipSyncCreateRequest.java -│ └── FaceChoose (静态内部类) -├── KlingLipSyncCreateResponse.java -│ ├── Data (静态内部类) -│ └── TaskInfo (静态内部类) -└── KlingLipSyncQueryResponse.java - ├── Data (静态内部类) - ├── TaskInfo (静态内部类) - ├── ParentVideo (静态内部类) - ├── TaskResult (静态内部类) - └── Video (静态内部类) -``` - -### VO 包结构 - -``` -cn.iocoder.yudao.module.tik.kling.vo -├── KlingIdentifyFaceReqVO.java -├── KlingIdentifyFaceRespVO.java -├── KlingLipSyncCreateReqVO.java -│ └── FaceChooseVO (静态内部类) -├── KlingLipSyncCreateRespVO.java -│ ├── Data (静态内部类) -│ └── TaskInfo (静态内部类) -└── KlingLipSyncQueryRespVO.java - ├── Data (静态内部类) - ├── TaskInfo (静态内部类) - ├── ParentVideo (静态内部类) - ├── TaskResult (静态内部类) - └── Video (静态内部类) -``` - ---- - -## ✨ 优势总结 - -### 1. **代码简洁** -- 从 26+ 个文件减少到 10 个文件 -- 减少 62% 的类数量 - -### 2. **结构清晰** -- 相关类组织在同一个文件中 -- 层次结构明确 - -### 3. **易于维护** -- 减少重复代码 -- 便于理解代码逻辑 - -### 4. **类型安全** -- 保持强类型检查 -- 避免类型混淆 - -### 5. **向下兼容** -- API 接口不变 -- 前端调用不变 -- 策略模式逻辑不变 - ---- - -## 🎉 总结 - -本次重构成功将大量重复的VO/DTO类合并为静态内部类,大大简化了项目结构。从**26+个文件**减少到**10个文件**,减少**62%**的类数量,同时保持了所有功能的完整性和API的兼容性。 - -重构后的代码更加简洁、清晰、易于维护,为后续的功能扩展奠定了良好的基础。 diff --git a/docs/vo-refactoring-summary.md b/docs/vo-refactoring-summary.md deleted file mode 100644 index f532635fc1..0000000000 --- a/docs/vo-refactoring-summary.md +++ /dev/null @@ -1,177 +0,0 @@ -# VO/DTO 静态内部类重构总结 - -## 已完成的修复 - -### 1. KlingLipSyncCreateRespVO 和相关类 - -#### 原始问题 -在 `KlingLipSyncCreateRespVO` 中定义了静态内部类,导致使用不便且不符合最佳实践。 - -#### 修复方案 -✅ **已完成** -- 移除 `KlingLipSyncCreateRespVO.Data` 静态内部类 -- 移除 `KlingLipSyncCreateRespVO.TaskInfo` 静态内部类 -- 创建独立的 `KlingLipSyncCreateDataVO.java` -- 创建独立的 `KlingLipSyncTaskInfoVO.java` -- 更新 `KlingLipSyncCreateRespVO` 使用新的独立类 - -#### 文件变更 -``` -新增文件: -- KlingLipSyncCreateDataVO.java -- KlingLipSyncTaskInfoVO.java - -修改文件: -- KlingLipSyncCreateRespVO.java (移除了静态内部类) -``` - -### 2. KlingLipSyncCreateReqVO 和相关类 - -#### 原始问题 -在 `KlingLipSyncCreateReqVO` 中定义了 `FaceChooseVO` 静态内部类。 - -#### 修复方案 -✅ **已完成** -- 移除 `KlingLipSyncCreateReqVO.FaceChooseVO` 静态内部类 -- 创建独立的 `KlingFaceChooseVO.java` -- 更新 `KlingLipSyncCreateReqVO` 使用新的独立类 -- 更新 `KlingLipSyncStrategy.java` 引用新类 - -#### 文件变更 -``` -新增文件: -- KlingFaceChooseVO.java - -修改文件: -- KlingLipSyncCreateReqVO.java -- KlingLipSyncStrategy.java -``` - -### 3. DTO 类修复 - -#### 修复方案 -✅ **已完成** -- 移除 `KlingLipSyncCreateRequest.FaceChoose` 静态内部类 -- 创建独立的 `KlingFaceChoose.java` (DTO版本) -- 移除 `KlingLipSyncCreateResponse.Data` 静态内部类 -- 移除 `KlingLipSyncCreateResponse.TaskInfo` 静态内部类 -- 创建独立的 `KlingLipSyncCreateData.java` (DTO版本) -- 创建独立的 `KlingLipSyncTaskInfo.java` (DTO版本) -- 更新相关引用 - -#### 文件变更 -``` -新增文件: -- KlingFaceChoose.java (dto package) -- KlingLipSyncCreateData.java (dto package) -- KlingLipSyncTaskInfo.java (dto package) - -修改文件: -- KlingLipSyncCreateRequest.java -- KlingLipSyncCreateResponse.java -``` - -## 需要继续修复的文件 - -### 待修复 1: KlingLipSyncQueryResponse.java - -**问题**: 存在多层嵌套的静态内部类 -```java -public class KlingLipSyncQueryResponse { - private Data data; - - @Data - public static class Data { - private TaskInfo taskInfo; - private TaskResult taskResult; - - @Data - public static class TaskInfo { - private ParentVideo parentVideo; - - @Data - public static class ParentVideo { - private String id; - private String url; - private String duration; - } - } - - @Data - public static class TaskResult { - private List