From 48203e0d31441e2f55cebdaa70013294910de07e Mon Sep 17 00:00:00 2001 From: GeekMaster Date: Tue, 16 Sep 2025 20:35:53 +0800 Subject: [PATCH] optimize jimeng power config --- api/core/types/jimeng.go | 10 +- api/handler/admin/jimeng_handler.go | 20 ++-- api/handler/jimeng_handler.go | 54 ++++++--- web/src/assets/css/jimeng.scss | 1 + web/src/store/jimeng.js | 41 ++++--- web/src/views/Jimeng.vue | 2 +- web/src/views/admin/jimeng/JimengConfig.vue | 121 ++++++++------------ 7 files changed, 123 insertions(+), 126 deletions(-) diff --git a/api/core/types/jimeng.go b/api/core/types/jimeng.go index c56572ff..21ce3d54 100644 --- a/api/core/types/jimeng.go +++ b/api/core/types/jimeng.go @@ -8,15 +8,7 @@ type JimengConfig struct { // 火山引擎大模型专用的验证方式 ApiKey string `json:"api_key"` // 算力配置 - Power JimengPower `json:"power"` -} - -// JimengPower 即梦AI算力配置 -type JimengPower struct { - Image int `json:"image"` // 图片生成算力,单位:积分/张 - Video int `json:"video"` // 视频生成算力,单位:积分/秒 - VirtualHuman int `json:"virtual_human"` // 数字人视频生成算力,单位:积分/秒 - ActionTransfer int `json:"action_transfer"` // 视频动作迁移算力,单位:积分/秒 + Powers map[string]int `json:"powers"` } // JMTaskStatus 任务状态 diff --git a/api/handler/admin/jimeng_handler.go b/api/handler/admin/jimeng_handler.go index 7f884269..9a8bc372 100644 --- a/api/handler/admin/jimeng_handler.go +++ b/api/handler/admin/jimeng_handler.go @@ -231,21 +231,15 @@ func (h *AdminJimengHandler) UpdateConfig(c *gin.Context) { } // 验证算力配置 - if req.Power.Image <= 0 { - resp.ERROR(c, "图片生成算力必须大于0") + if len(req.Powers) == 0 { + resp.ERROR(c, "请至少配置一个模型的积分") return } - if req.Power.Video <= 0 { - resp.ERROR(c, "视频生成算力必须大于0") - return - } - if req.Power.VirtualHuman <= 0 { - resp.ERROR(c, "数字人生成算力必须大于0") - return - } - if req.Power.ActionTransfer <= 0 { - resp.ERROR(c, "视频动作迁移算力必须大于0") - return + for key, val := range req.Powers { + if val <= 0 { + resp.ERROR(c, fmt.Sprintf("模型 %s 的积分必须大于0", key)) + return + } } // 保存配置 diff --git a/api/handler/jimeng_handler.go b/api/handler/jimeng_handler.go index 79754ef9..06aea75c 100644 --- a/api/handler/jimeng_handler.go +++ b/api/handler/jimeng_handler.go @@ -39,12 +39,12 @@ func NewJimengHandler(app *core.AppServer, jimengService *jimeng.Service, db *go // RegisterRoutes 注册路由,新增统一任务接口 func (h *JimengHandler) RegisterRoutes() { group := h.App.Engine.Group("/api/jimeng/") + group.GET("power-config", h.GetPowerConfig) // 需要用户授权的接口 group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) { group.POST("task", h.CreateTask) - group.GET("power-config", h.GetPowerConfig) group.POST("jobs", h.Jobs) group.GET("remove", h.Remove) group.GET("retry", h.Retry) @@ -125,15 +125,31 @@ func (h *JimengHandler) CreateTask(c *gin.Context) { func (h *JimengHandler) getTaskRemark(req types.JimengTaskRequest, jobId uint) string { remark := fmt.Sprintf("即梦任务%s,任务ID:%d", req.ReqKey, jobId) + perUnit, ok := h.App.SysConfig.Jimeng.Powers[req.ReqKey] + if !ok || perUnit <= 0 { + return remark // Fallback if power not found or invalid + } switch req.TaskType { case types.JMTaskTypeImage: - remark = fmt.Sprintf("即梦图片生成,任务ID:%d,%d积分/张", jobId, h.App.SysConfig.Jimeng.Power.Image) + remark = fmt.Sprintf("即梦图片生成,任务ID:%d,%d积分/张", jobId, perUnit) case types.JMTaskTypeVideo: - remark = fmt.Sprintf("即梦视频生成,任务ID:%d,%d积分/秒, %d秒", jobId, h.App.SysConfig.Jimeng.Power.Video, req.Power/h.App.SysConfig.Jimeng.Power.Video) + seconds := 0 + if perUnit > 0 { + seconds = req.Power / perUnit + } + remark = fmt.Sprintf("即梦视频生成,任务ID:%d,%d积分/秒, %d秒", jobId, perUnit, seconds) case types.JMTaskTypeVirtualHuman: - remark = fmt.Sprintf("即梦数字人视频生成,任务ID:%d,%d积分/秒, %d秒", jobId, h.App.SysConfig.Jimeng.Power.VirtualHuman, req.Power/h.App.SysConfig.Jimeng.Power.VirtualHuman) + seconds := 0 + if perUnit > 0 { + seconds = req.Power / perUnit + } + remark = fmt.Sprintf("即梦数字人视频生成,任务ID:%d,%d积分/秒, %d秒", jobId, perUnit, seconds) case types.JMTaskTypeActionTransfer: - remark = fmt.Sprintf("即梦视频动作迁移,任务ID:%d,%d积分/秒, %d秒", jobId, h.App.SysConfig.Jimeng.Power.ActionTransfer, req.Power/h.App.SysConfig.Jimeng.Power.ActionTransfer) + seconds := 0 + if perUnit > 0 { + seconds = req.Power / perUnit + } + remark = fmt.Sprintf("即梦视频动作迁移,任务ID:%d,%d积分/秒, %d秒", jobId, perUnit, seconds) } return remark } @@ -299,20 +315,22 @@ func (h *JimengHandler) Retry(c *gin.Context) { resp.SUCCESS(c, gin.H{"message": "重试任务已提交"}) } -// getPowerFromConfig 从配置中获取指定类型的算力消耗 func (h *JimengHandler) getTaskPower(req types.JimengTaskRequest) (int, error) { logger.Debugf("getTaskPower req: %+v", req) config := h.App.SysConfig.Jimeng + basePower, ok := config.Powers[req.ReqKey] + if !ok || basePower <= 0 { + return 0, errors.New("未配置模型积分或配置不合法") + } switch req.TaskType { case types.JMTaskTypeImage: - return config.Power.Image, nil + return basePower, nil case types.JMTaskTypeVideo: if req.Duration == 0 { return 0, errors.New("视频时长不能为0") } - return config.Power.Video * req.Duration, nil + return basePower * req.Duration, nil case types.JMTaskTypeVirtualHuman: - // TODO 计算音频时长 if req.AudioURL == "" { return 0, errors.New("音频URL不能为空") } @@ -320,9 +338,12 @@ func (h *JimengHandler) getTaskPower(req types.JimengTaskRequest) (int, error) { if err != nil { return 0, err } - return config.Power.VirtualHuman * int(audioDuration.Seconds()), nil + seconds := int(audioDuration.Seconds()) + if seconds <= 0 { + return 0, errors.New("音频时长无效") + } + return basePower * seconds, nil case types.JMTaskTypeActionTransfer: - // TODO 计算视频时长 if req.VideoURL == "" { return 0, errors.New("视频URL不能为空") } @@ -330,7 +351,11 @@ func (h *JimengHandler) getTaskPower(req types.JimengTaskRequest) (int, error) { if err != nil { return 0, err } - return config.Power.ActionTransfer * int(videoDuration.Seconds()), nil + seconds := int(videoDuration.Seconds()) + if seconds <= 0 { + return 0, errors.New("视频时长无效") + } + return basePower * seconds, nil default: return 0, errors.New("任务类型不支持") } @@ -340,9 +365,6 @@ func (h *JimengHandler) getTaskPower(req types.JimengTaskRequest) (int, error) { func (h *JimengHandler) GetPowerConfig(c *gin.Context) { config := h.App.SysConfig.Jimeng resp.SUCCESS(c, gin.H{ - "image": config.Power.Image, - "video": config.Power.Video, - "virtual_human": config.Power.VirtualHuman, - "action_transfer": config.Power.ActionTransfer, + "powers": config.Powers, }) } diff --git a/web/src/assets/css/jimeng.scss b/web/src/assets/css/jimeng.scss index 8697a304..32b86273 100644 --- a/web/src/assets/css/jimeng.scss +++ b/web/src/assets/css/jimeng.scss @@ -341,6 +341,7 @@ // 提示词指南样式 .prompt-guide { margin: 12px 0 16px; + background-color: var(--el-fill-color-blank); .guide-title { display: flex; diff --git a/web/src/store/jimeng.js b/web/src/store/jimeng.js index 311170e0..ee154a50 100644 --- a/web/src/store/jimeng.js +++ b/web/src/store/jimeng.js @@ -36,7 +36,7 @@ export const useJimengStore = defineStore('jimeng', () => { const shareStore = useSharedStore() // 积分消耗配置 - const powerConfig = reactive({}) + const powerConfig = reactive({ powers: {} }) const currentPowerCost = ref('0积分') // 功能配置 @@ -83,12 +83,10 @@ export const useJimengStore = defineStore('jimeng', () => { // 获取状态类型 const getTaskType = (type) => { const typeMap = { - text_to_image: 'primary', - image_to_image: 'primary', - image_edit: 'primary', - image_effects: 'primary', - text_to_video: 'success', - image_to_video: 'success', + image: 'info', + video: 'primary', + virtual_human: 'success', + action_transfer: 'warning', } return typeMap[type] || 'primary' } @@ -124,7 +122,7 @@ export const useJimengStore = defineStore('jimeng', () => { } total.value = data.total || 0 - if (data.items.length < pageSize.value) { + if (!data.items || data.items.length < pageSize.value) { isOver.value = true } if (pageNum === 1) { @@ -150,7 +148,7 @@ export const useJimengStore = defineStore('jimeng', () => { page_size: 20, }) const data = response.data - if (data.items.length === 0) { + if (!data.items || data.items.length === 0) { stopPolling() return } @@ -184,7 +182,6 @@ export const useJimengStore = defineStore('jimeng', () => { shareStore.setShowLoginDialog(true) return } - console.log(formData.value) for (const key in requiredKeys.value) { if (!formData.value[key]) { showMessageError('缺少参数:' + requiredKeys.value[key].label) @@ -284,20 +281,32 @@ export const useJimengStore = defineStore('jimeng', () => { } const setFunctionPowers = () => { - if (activeFunction.value === 'image') { - currentPowerCost.value = `${powerConfig.image}积分/张` - } else { - currentPowerCost.value = `${powerConfig.video}积分/秒` - } + nextTick(() => { + const key = formData.value.req_key + const perUnit = key ? powerConfig.powers[key] : 0 + if (!perUnit) { + currentPowerCost.value = '未配置积分' + return + } + currentPowerCost.value = + activeFunction.value === 'image' ? `${perUnit}积分/张` : `${perUnit}积分/秒` + }) } + watch( + () => formData.value, + () => { + setFunctionPowers() + } + ) + // 初始化方法 const init = async () => { try { // 获取积分消耗配置 const powerRes = await httpGet('/api/jimeng/power-config') if (powerRes.data) { - Object.assign(powerConfig, powerRes.data) + powerConfig.powers = powerRes.data.powers || {} setFunctionPowers() } const user = await checkSession() diff --git a/web/src/views/Jimeng.vue b/web/src/views/Jimeng.vue index 828e1124..1496b597 100644 --- a/web/src/views/Jimeng.vue +++ b/web/src/views/Jimeng.vue @@ -19,7 +19,7 @@ -
+