RelayTaskSubmit 完成 task 提交的全部流程(每次尝试调用一次): 刷新渠道元数据 → 确定 platform/adaptor → 验证请求 → 估算计费(EstimateBilling) → 计算价格 → 预扣费(仅首次)→ 构建/发送/解析上游请求 → 提交后计费调整(AdjustBillingOnSubmit)。 控制器负责 defer Refund 和成功后 Settle。
(c *gin.Context, info *relaycommon.RelayInfo)
| 142 | // 构建/发送/解析上游请求 → 提交后计费调整(AdjustBillingOnSubmit)。 |
| 143 | // 控制器负责 defer Refund 和成功后 Settle。 |
| 144 | func RelayTaskSubmit(c *gin.Context, info *relaycommon.RelayInfo) (*TaskSubmitResult, *dto.TaskError) { |
| 145 | info.InitChannelMeta(c) |
| 146 | |
| 147 | // 1. 确定 platform → 创建适配器 → 验证请求 |
| 148 | platform := constant.TaskPlatform(c.GetString("platform")) |
| 149 | if platform == "" { |
| 150 | platform = GetTaskPlatform(c) |
| 151 | } |
| 152 | adaptor := GetTaskAdaptor(platform) |
| 153 | if adaptor == nil { |
| 154 | return nil, service.TaskErrorWrapperLocal(fmt.Errorf("invalid api platform: %s", platform), "invalid_api_platform", http.StatusBadRequest) |
| 155 | } |
| 156 | adaptor.Init(info) |
| 157 | if taskErr := adaptor.ValidateRequestAndSetAction(c, info); taskErr != nil { |
| 158 | return nil, taskErr |
| 159 | } |
| 160 | |
| 161 | // 2. 确定模型名称 |
| 162 | modelName := info.OriginModelName |
| 163 | if modelName == "" { |
| 164 | modelName = service.CoverTaskActionToModelName(platform, info.Action) |
| 165 | } |
| 166 | |
| 167 | // 2.5 应用渠道的模型映射(与同步任务对齐) |
| 168 | info.OriginModelName = modelName |
| 169 | info.UpstreamModelName = modelName |
| 170 | if err := helper.ModelMappedHelper(c, info, nil); err != nil { |
| 171 | return nil, service.TaskErrorWrapperLocal(err, "model_mapping_failed", http.StatusBadRequest) |
| 172 | } |
| 173 | |
| 174 | // 3. 预生成公开 task ID(仅首次) |
| 175 | if info.PublicTaskID == "" { |
| 176 | info.PublicTaskID = model.GenerateTaskID() |
| 177 | } |
| 178 | |
| 179 | // 4. 价格计算:基础模型价格 |
| 180 | info.OriginModelName = modelName |
| 181 | priceData, err := helper.ModelPriceHelperPerCall(c, info) |
| 182 | if err != nil { |
| 183 | return nil, service.TaskErrorWrapper(err, "model_price_error", http.StatusBadRequest) |
| 184 | } |
| 185 | info.PriceData = priceData |
| 186 | |
| 187 | // 5. 计费估算:让适配器根据用户请求提供 OtherRatios(时长、分辨率等) |
| 188 | // 必须在 ModelPriceHelperPerCall 之后调用(它会重建 PriceData)。 |
| 189 | // ResolveOriginTask 可能已在 remix 路径中预设了 OtherRatios,此处合并。 |
| 190 | if estimatedRatios := adaptor.EstimateBilling(c, info); len(estimatedRatios) > 0 { |
| 191 | for k, v := range estimatedRatios { |
| 192 | info.PriceData.AddOtherRatio(k, v) |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | // 6. 将 OtherRatios 应用到基础额度 |
| 197 | if !common.StringsContains(constant.TaskPricePatches, modelName) { |
| 198 | for _, ra := range info.PriceData.OtherRatios { |
| 199 | if ra != 1.0 { |
| 200 | info.PriceData.Quota = int(float64(info.PriceData.Quota) * ra) |
| 201 | } |
no test coverage detected