This commit is contained in:
2026-07-29 09:31:13 +08:00
parent 68a548a1ff
commit c7fe438213
4 changed files with 29 additions and 75 deletions
+1 -1
View File
@@ -98,7 +98,7 @@ func processField(name string, def map[string]any, input map[string]any) (any, e
rawVal, exists := input[name]
if !exists {
if required {
return nil, fmt.Errorf("'%s' is required", name)
return nil, fmt.Errorf("%s", def["description"])
}
if dflt, ok := def["default"]; ok {
return convertDefault(dflt, fieldType), nil
BIN
View File
Binary file not shown.
+14 -62
View File
@@ -1238,7 +1238,6 @@ func buildCharacterGuide(charNames []string, chars []*entity.Character, labelOf
// extractAndSaveEntities 从镜头数组中提取演员/场景/道具,去重后写入数据库。
// 如果同 drama 下已存在同名记录,跳过插入(避免重复)。
// 描述信息从镜头的 event 画面描述中提取,保证是视觉/外观描述。
func extractAndSaveEntities(ctx context.Context, dramaId int64, shots []domain.Shot) {
if len(shots) == 0 {
return
@@ -1247,39 +1246,22 @@ func extractAndSaveEntities(ctx context.Context, dramaId int64, shots []domain.S
charSet := make(map[string]bool)
sceneSet := make(map[string]bool)
propSet := make(map[string]bool)
// name → []event 片段
charEvents := make(map[string][]string)
sceneEvents := make(map[string][]string)
propEvents := make(map[string][]string)
for _, sh := range shots {
for _, c := range sh.Characters {
if c == "" {
continue
}
if !charSet[c] {
charSet[c] = true
}
if sh.Event != "" {
charEvents[c] = append(charEvents[c], sh.Event)
}
charSet[c] = true
}
if sh.Scene != "" {
sceneSet[sh.Scene] = true
if sh.Event != "" {
sceneEvents[sh.Scene] = append(sceneEvents[sh.Scene], sh.Event)
}
}
for _, p := range sh.Props {
if p == "" {
continue
}
if !propSet[p] {
propSet[p] = true
}
if sh.Event != "" {
propEvents[p] = append(propEvents[p], sh.Event)
}
propSet[p] = true
}
}
@@ -1312,43 +1294,17 @@ func extractAndSaveEntities(ctx context.Context, dramaId int64, shots []domain.S
return false
}
buildDesc := func(events []string) string {
seen := make(map[string]bool)
var parts []string
for _, e := range events {
if e == "" || seen[e] {
continue
}
seen[e] = true
parts = append(parts, e)
if len(parts) >= 3 {
break
}
}
if len(parts) == 0 {
return ""
}
result := strings.Join(parts, "")
runes := []rune(result)
if len(runes) > 300 {
result = string(runes[:300]) + "……"
}
return result
}
now := time.Now().Format("2006-01-02 15:04:05")
for name := range charSet {
if existName(existingChars, name) {
continue
}
desc := buildDesc(charEvents[name])
_, _ = g.DB().Model(consts.TableNameCharacter).Ctx(ctx).Data(g.Map{
"drama_id": dramaId,
"name": name,
"description": desc,
"created_at": now,
"updated_at": now,
"drama_id": dramaId,
"name": name,
"created_at": now,
"updated_at": now,
}).Insert()
}
@@ -1356,13 +1312,11 @@ func extractAndSaveEntities(ctx context.Context, dramaId int64, shots []domain.S
if existName(existingScenes, name) {
continue
}
desc := buildDesc(sceneEvents[name])
_, _ = g.DB().Model(consts.TableNameScene).Ctx(ctx).Data(g.Map{
"drama_id": dramaId,
"name": name,
"description": desc,
"created_at": now,
"updated_at": now,
"drama_id": dramaId,
"name": name,
"created_at": now,
"updated_at": now,
}).Insert()
}
@@ -1370,13 +1324,11 @@ func extractAndSaveEntities(ctx context.Context, dramaId int64, shots []domain.S
if existName(existingProps, name) {
continue
}
desc := buildDesc(propEvents[name])
_, _ = g.DB().Model(consts.TableNameProp).Ctx(ctx).Data(g.Map{
"drama_id": dramaId,
"name": name,
"description": desc,
"created_at": now,
"updated_at": now,
"drama_id": dramaId,
"name": name,
"created_at": now,
"updated_at": now,
}).Insert()
}
}
+14 -12
View File
@@ -4,6 +4,7 @@ import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"math"
@@ -71,7 +72,17 @@ func (s *generationService) GenerateEpisode(ctx context.Context, dramaId, epId i
if videoCfg.ApiKey == "" || videoCfg.BaseUrl == "" || videoCfg.ModelName == "" {
return fmt.Errorf("video model not configured")
}
// 客户身份:余额检查并预扣费
// ============ 检查待生成任务的 media 文件是否存在 ============
if !continueIfMissing {
if missingNames := checkTaskMediaExistence(ctx, epId); len(missingNames) > 0 {
return errors.New("至少需要包含一个人物形象")
}
} else {
rebuildTasksMedia(ctx, epId)
}
// 客户身份:余额检查并预扣费(放在 media 检查之后,确认可生成再扣费)
if r := g.RequestFromCtx(ctx); r != nil {
if role := r.GetCtxVar("role").String(); role == "customer" {
userId := r.GetCtxVar("userId").Int64()
@@ -100,15 +111,6 @@ func (s *generationService) GenerateEpisode(ctx context.Context, dramaId, epId i
g.Log().Infof(ctx, "预加载引用数据: %d个演员, %d个场景, %d个道具, %d个引用",
len(genCtx2.Characters), len(genCtx2.Scenes), len(genCtx2.Props), len(genCtx2.OrderedRefs))
// ============ 检查待生成任务的 media 文件是否存在 ============
if !continueIfMissing {
if missingNames := checkTaskMediaExistence(ctx, epId); len(missingNames) > 0 {
return fmt.Errorf("missing_character_media:%s", strings.Join(missingNames, ","))
}
} else {
rebuildTasksMedia(ctx, epId)
}
genCtx := agent.WithDramaID(ctx, d.Id)
segDurs := calcSegDurs(d.EpisodeDuration, videoCfg.ModelConfig)
@@ -2125,7 +2127,7 @@ func (s *generationService) submitVideoTask(ctx context.Context, d *entity.Drama
taskId, requestJSON, err := submitVideoGenRequest(ctx, modelCfg.ApiKey, modelCfg.BaseUrl, modelCfg.ModelName,
prompt, negativePrompt, refURLs, effectiveDur, d.Resolution, d.AspectRatio, modelCfg.Schema, seed, segIdx, genTaskId, ep.Id, d.Title, ep.Title, modelCfg.SchemaMapping)
if err != nil {
return "", "", fmt.Errorf("video composition request failed: %w", err)
return "", "", fmt.Errorf("%s", err)
}
if taskId == "" {
@@ -2225,7 +2227,7 @@ func submitVideoGenRequest(ctx context.Context, apiKey, baseURL, modelName, prom
bodyDef, _ := nested(vs, "body").(map[string]any)
body, err := common.BuildSchemaRequest(bodyDef, input)
if err != nil {
return "", "", fmt.Errorf("build request body failed: %w", err)
return "", "", fmt.Errorf("%s", err)
}
if body == nil {
body = map[string]any{}