From 503545fba651f37fa6ae9e5efbda7bf0e6b92b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E6=96=8C?= <259278618@qq.com> Date: Wed, 1 Jul 2026 15:50:13 +0800 Subject: [PATCH] 1 --- short_drama.db | Bin 86016 -> 86016 bytes shortdrama/agent/chat_model.go | 29 ++++--- shortdrama/agent/tools.go | 121 +++++++++++----------------- shortdrama/service/drama_service.go | 54 ++++--------- 4 files changed, 77 insertions(+), 127 deletions(-) diff --git a/short_drama.db b/short_drama.db index 0b2fb53d4418d83d681a6ebfa41d9893163850e4..9caef5694328560b3fc49c68e4a7b4aedc03bf22 100644 GIT binary patch delta 310 zcmZozz}m2Yb%M0uQw9bGQ6LroVh$iSov35X_;h2!5_vIpzRwK&Yx$q@pW*w=pT{rC zufwmycX6{|!gjvN7xkPOcTX18*Jsq7*r+)x%t0;KEIYV z9|I#JNH0ewZzCh8Y!MFiriPPe#QRK^kCfj0Kb|Q;oX4Mmm6?HonSuKs_XJKowv%ip z`RDV`-z+Hbf@#xXe@+&l+(sa8Ei)G{KQjX(?<5A^Nt?MY{Nw}5F#KK2eqT_Q9moO& Yg$NJ}0ylQ6LroVs;=lnW$sTcxPk65_vHWzRwK&Yx$q@pW*w=pT{50 zFUqgOcX6{|!gjvN7xkPOcTE=6*Vhu`EY8R;Dv3`iO3Y1+FHTO)OBH2fHsnASEhx&* zEhw3sr(eccxcRC6BYBHLZU#n9LtaTiU}Qi9dB5Yf!y_@vUzoRZAR4!nx>$wjF^Q&Qp+OB7rjLtH~VeO(n?T-_W)eL@tR zLxX}`{X*hFg25q 0 && visualDesc != "" { + scenes, err := dao.Scene.ListByDrama(ctx, dramaId) + if err == nil { + for _, sc := range scenes { + if sc.ImagePath == "" { + continue + } + // 匹配场景描述(双向包含匹配) + if strings.Contains(visualDesc, sc.Description) || strings.Contains(sc.Description, visualDesc) { + b64, err := readImageFileAsBase64(sc.ImagePath) + if err == nil { + imgBase64 = b64 + g.Log().Infof(ctx, "从场景库读取图片: %s (场景: %s)", sc.ImagePath, sc.Name) + } + break + } + } + } + } + + if imgBase64 == "" { + g.Log().Infof(ctx, "场景库中无匹配图片,返回空(episode=%d, scene=%d)", int(episodeIdx), int(sceneIdx)) } result, _ := json.Marshal(map[string]any{ @@ -329,75 +355,20 @@ func cleanEpisodeTitle(title string) string { return title } -func generateRealImage(ctx context.Context, prompt string) (string, error) { - imageModel := GetImageModel(ctx) - if imageModel == "" { - // 图片模型未配置,返回空字符串(不报错),避免 agent 反复重试 - return "", nil - } - - // 调用通义万相生成图片 - apiKey := GetAPIKey(ctx) - url := "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation" - - body := map[string]any{ - "model": imageModel, - "input": map[string]any{ - "messages": []map[string]any{ - { - "role": "user", - "content": []map[string]string{ - {"type": "text", "text": prompt}, - }, - }, - }, - }, - "parameters": map[string]any{ - "size": "1024*1364", - "n": 1, - "watermark": false, - }, - } - - payload, _ := json.Marshal(body) - httpClient := &http.Client{Timeout: 60 * time.Second} - req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(payload)) +func readImageFileAsBase64(path string) (string, error) { + data, err := os.ReadFile(path) if err != nil { return "", err } - req.Header.Set("Authorization", "Bearer "+apiKey) - req.Header.Set("Content-Type", "application/json") - - resp, err := httpClient.Do(req) - if err != nil { - return "", err + ext := strings.ToLower(filepath.Ext(path)) + mime := "image/png" + switch ext { + case ".jpg", ".jpeg": + mime = "image/jpeg" + case ".gif": + mime = "image/gif" + case ".webp": + mime = "image/webp" } - defer resp.Body.Close() - - data, _ := io.ReadAll(resp.Body) - - var result struct { - Output struct { - Choices []struct { - Message struct { - Content []struct { - Image string `json:"image"` - } `json:"content"` - } `json:"message"` - } `json:"choices"` - } `json:"workspace"` - Code string `json:"code"` - } - - err = json.Unmarshal(data, &result) - if err != nil || len(result.Output.Choices) == 0 || result.Code != "" { - return "", fmt.Errorf("生成图片失败: %s", string(data)) - } - - imgBase64 := result.Output.Choices[0].Message.Content[0].Image - if imgBase64 == "" { - return "", fmt.Errorf("图片内容为空") - } - - return imgBase64, nil + return "data:" + mime + ";base64," + base64.StdEncoding.EncodeToString(data), nil } diff --git a/shortdrama/service/drama_service.go b/shortdrama/service/drama_service.go index 2c89df4..ceabacc 100644 --- a/shortdrama/service/drama_service.go +++ b/shortdrama/service/drama_service.go @@ -209,6 +209,8 @@ func (s *dramaService) GenerateEpisode(ctx context.Context, dramaId, epId int64, APIKey: modelCfg.ChatApiKey, BaseURL: modelCfg.ChatBaseUrl, }) + genCtx = agent.WithDramaId(genCtx, d.Id) + genCtx = agent.WithDramaId(genCtx, d.Id) segDurs := calcSegDurs(d.EpisodeDuration, modelCfg) numSegments := len(segDurs) @@ -690,6 +692,7 @@ func (s *dramaService) FeedbackSegment(ctx context.Context, taskId int64, feedba APIKey: modelCfg.ChatApiKey, BaseURL: modelCfg.ChatBaseUrl, }) + genCtx = agent.WithDramaId(genCtx, d.Id) g.Log().Infof(genCtx, "第%d集第%d段根据反馈重新生成", ep.Index, task.SegmentIdx+1) if err := s.generateOneSegment(genCtx, d, ep, taskId, task.SegmentIdx, segDur, feedback); err != nil { g.Log().Errorf(genCtx, "第%d集第%d段重新生成失败: %v", ep.Index, task.SegmentIdx+1, err) @@ -759,40 +762,8 @@ func (s *dramaService) GetEpisodePollStatus(ctx context.Context, epId int64) (*d return v.Val().(*dto.EpisodePollRes), nil } - // 缓存未命中,查询 DB - tasks, err := dao.GenerationTask.ListByEpisode(ctx, epId) - if err != nil || len(tasks) == 0 { - return nil, nil - } - - status := "generating" - var errMsg string - var currentTaskId int64 - allDone := true - for _, t := range tasks { - if t.Status == "failed" { - status = "failed" - errMsg = t.ErrorMessage - break - } - if t.Status == "review" && currentTaskId == 0 { - currentTaskId = t.Id - status = "review" - } - if t.Status != "completed" { - allDone = false - } - } - if allDone { - status = "completed" - } - - return &dto.EpisodePollRes{ - Status: status, - ErrorMessage: errMsg, - Tasks: tasks, - CurrentTaskId: currentTaskId, - }, nil + // 缓存未命中 — 不查 DB,让前端继续轮询等待缓存写入 + return nil, nil } // StartVideoPoller 启动后台视频轮询器 @@ -873,12 +844,12 @@ func (s *dramaService) pollPendingVideos(ctx context.Context) { } else { g.Log().Warningf(ctx, "轮询器: 任务 %d 第%d段视频任务失败: %v(保留 task_id,不会重新提交消耗额度)", task.Id, task.SegmentIdx+1, err) } + } else if strings.Contains(err.Error(), "RUNNING") { + g.Log().Debugf(ctx, "轮询器: 任务 %d 第%d段视频正在生成中,继续等待...", task.Id, task.SegmentIdx+1) + } else { + g.Log().Warningf(ctx, "轮询器: 任务 %d 第%d段视频查询异常: %v", task.Id, task.SegmentIdx+1, err) } continue - if videoURL == "" { - g.Log().Debugf(ctx, "轮询器: 任务 %d 第%d段视频正在生成中,继续等待...", task.Id, task.SegmentIdx+1) - continue - } } // 视频就绪 — 下载到本地 @@ -950,7 +921,12 @@ func (s *dramaService) pollPendingVideos(ctx context.Context) { epUpdates[task.EpisodeId] = true } - // 更新有变化的 episode poll cache + // 刷新所有 generating 任务的 episode 缓存(不管状态有无变化,确保前端轮询不走 DB) + for _, t := range tasks { + epUpdates[t.EpisodeId] = true + } + + // 更新 episode poll cache for epId := range epUpdates { if ts, e := dao.GenerationTask.ListByEpisode(ctx, epId); e == nil { setPollCache(ctx, epId, ts)