# createGenTaskPayload 梳理 本文只说明 `src/layouts/globalPromptPanel/globalPromptPanel.tsx` 里的 `createGenTaskPayload`。 它是创建任务接口 `createGenTask()` 的请求体。 它也会传给 `useGenerateCost()`,作为普通任务价格计算的同源参数。 ## 1. 它解决什么问题 `createGenTaskPayload` 把创作面板当前状态整理成后端需要的任务参数。 它主要合并 4 类数据: - 当前任务类型:`activeTaskSubType` - 当前模型:`activeModelItem` - 当前参数:`modelParamFields` 和 `selectedParamValues` - 当前输入内容:`chatPrompt`、上传媒体、音色、动作模仿示例、参考主体 最终结果会用于两个地方: - `useGenerateCost({ taskPayload: createGenTaskPayload })` - `createGenTask(createGenTaskPayload)` ## 2. 整体流程 ```txt mediaItems └─ splitUploadMediaItems() ├─ uploadImages ├─ uploadVideos └─ uploadVoice activeModelItem + activeTaskSubType + modelParamFields + selectedParamValues └─ buildComposerTaskSubmitPayload() ├─ subType ├─ type ├─ 模型字段 ├─ 普通参数 └─ extraTaskParams createGenTaskPayload ├─ 基础任务参数 ├─ chatPrompt ├─ images / videos / voices ├─ voiceId ├─ templateVideoMaterialId └─ referenceSubjectList ``` ## 3. 基础任务参数从哪里来 基础参数来自 `buildComposerTaskSubmitPayload()`。 这个函数会返回: - `subType`:当前任务子类型 - `type`:任务大类。当前代码里 `1` 是图片类,`2` 是视频类,`3` 是音频类 - 模型字段:来自 `buildGenTaskModelPayload(activeModelItem)` - 普通参数:来自模型参数和固定参数 - `extraTaskParams`:滑块参数,比如 `speed / vol / pitch` ### 3.1 模型字段 `buildGenTaskModelPayload()` 会把同一个模型信息写成多个字段。 这是后端接口约定,不是前端重复写错。 当前会写入: - `aiModelName` - `aiPlatform` - `aiModelId` - `model` - `aiModelCode` - `aiModelCodeAlias` 其中 `aiPlatform`、`model`、`aiModelCode` 都来自 `activeModelItem.code`。 ### 3.2 参数字段 参数字段来自 `buildComposerTaskParamPayload()`。 它会做这些事: - 用 `buildPreferredParamValues()` 给缺失参数补默认值 - 找到比例字段,比如 `ratio` - 找到分辨率字段,比如 `resolution` - 根据比例和分辨率推导 `width / height` - 保证请求体里有接口约定的 `ratio` 字段 ### 3.3 滑块参数 `splitRangeParamsForTaskSubmit()` 会把滑块参数从顶层移到 `extraTaskParams`。 例子: ```json { "resolution": "2K", "ratio": "9:16", "extraTaskParams": { "speed": 1, "vol": 0.8, "pitch": 0 } } ``` 这样做是为了让普通参数留在顶层,同时让 `speed / vol / pitch` 这类范围值走 `extraTaskParams`。 ## 4. 媒体字段怎么来 上传媒体来自 `splitUploadMediaItems(mediaItems, composerUploadLimits)`。 它会按类型拆成三列: - 图片:`uploadImages`,最后写入 `images` - 视频:`uploadVideos`,最后写入 `videos` - 音频:`uploadVoice`,最后写入 `voices` 它也会按模型上传上限截断对应类型。 例如模型最多只收 1 个视频时,后面的上传视频不会进入请求体。 空数组不会提交。 所以代码最后会删除这些空字段: - `images` - `videos` - `voices` ## 5. 不同任务的额外处理 `createGenTaskPayload` 在基础参数之外,会按任务类型补几个字段。 ### 5.1 通用任务 所有任务都会写入: - `chatPrompt: chatPrompt.trim()` - `images` - `videos` - `voices` 如果 `chatPrompt` 为空,创建任务前会有校验。 动作模仿任务例外,因为它可以只依赖动作示例。 ### 5.2 文本转语音任务 当 `isVoiceTask` 为 `true` 时,会写入: - `voiceId` 取值优先级是: ```txt selectedVoiceItem?.voiceId > voiceId ``` 如果最终值为空,代码会删除 `voiceId`。 创建任务前也会再校验一次,确保文本转语音必须有音色。 ### 5.3 动作模仿任务 当 `isActionMimicTask` 为 `true` 时,`videos` 会特殊处理。 如果用户选了动作模仿示例,代码会把示例视频放到 `videos` 第一位: ```txt [selectedActionMimicItem.url, ...uploadMediaPayload.uploadVideos] ``` 如果选中的是官方示例,还会写入: - `templateVideoMaterialId` 它来自 `selectedActionMimicItem.id`。 如果不是官方示例,这个字段会被删除。 ### 5.4 参考生视频任务 当 `isReferenceTask` 为 `true` 时,会写入: - `referenceSubjectList` 这个字段来自 `referenceStructure.referenceSubjectList`。 它保存参考主体列表,不只是普通上传图片。 ## 6. 当前请求体大概长什么样 一个普通图像或视频任务的请求体大概是: ```json { "subType": 102, "type": 1, "aiModelName": "模型名", "aiPlatform": "模型 code", "aiModelId": 123, "model": "模型 code", "aiModelCode": "模型 code", "aiModelCodeAlias": "别名", "resolution": "2K", "ratio": "9:16", "width": 720, "height": 1280, "batchSize": 1, "chatPrompt": "用户输入的提示词", "images": ["https://example.com/a.png"], "videos": ["https://example.com/a.mp4"], "voices": ["https://example.com/a.mp3"], "extraTaskParams": { "speed": 1 } } ``` 实际字段会根据任务类型、模型参数、上传内容变化。 空的 `images / videos / voices` 不会出现在最终请求体里。 ## 7. 创建任务前还有哪些校验 `createGenTaskPayload` 只负责组装请求体。 它不负责全部业务校验。 点击创建任务时,`handleCreateGenTaskClick()` 还会检查: - 非动作模仿任务必须有提示词 - 当前任务是否满足最少上传数量 - 音频素材不能单独提交,必须搭配图片或视频 - 动作模仿任务必须选择示例 - 文本转语音任务必须选择音色 - 用户积分是否足够 这些校验通过后,才会调用: ```ts await createGenTask(createGenTaskPayload); ``` ## 8. 阅读这段代码时的顺序 建议按这个顺序看: 1. 先看 `createGenTaskPayload` 本体,知道最终字段有哪些。 2. 再看 `buildComposerTaskSubmitPayload()`,理解模型和参数怎么进请求体。 3. 再看 `buildComposerTaskParamPayload()`,理解默认参数、比例、分辨率、尺寸。 4. 再看 `splitRangeParamsForTaskSubmit()`,理解滑块参数为什么进 `extraTaskParams`。 5. 最后看 `handleCreateGenTaskClick()`,理解请求体提交前的校验。 ## 9. 为什么现在看起来杂乱 这段逻辑承担了两个职责: - 生成创建任务的请求体 - 给价格计算提供同一份任务参数 同时它还要兼容多种任务类型。 所以它会同时碰到模型、参数、媒体、音色、动作示例、参考主体。 如果后续要继续整理,可以把它拆成一个纯函数,例如: ```ts buildCreateGenTaskPayload({ activeTaskSubType, activeModelItem, modelParamFields, selectedParamValues, chatPrompt, uploadMediaPayload, selectedVoiceItem, voiceId, selectedActionMimicItem, referenceSubjectList, isVoiceTask, isActionMimicTask, isReferenceTask, }) ``` 这样 `globalPromptPanel.tsx` 只负责传状态。 请求体规则可以放到 `utils.ts` 或单独文件里测试。