feat(*): 短剧创作工作台 - 完善章节分镜、资产管理、技能运行与生成任务恢复
This commit is contained in:
1 parent
4f07daae9e
commit
2f6832f99c
113 files changed
+7016
-723
No files matched your search
@@ -463,6 +463,12 @@ func (s *Service) RegisterTaskOutputFromTask(task model.Task) error {
|
||||
input.Role, _ = input.Metadata["role"].(string)
|
||||
}
|
||||
}
|
||||
if strings.TrimSpace(input.ResourceID) == "" {
|
||||
input.ResourceID, input.MediaType = taskOutputResource(task.ResultJSON, task.Type)
|
||||
}
|
||||
if strings.TrimSpace(input.MediaType) == "" && strings.TrimSpace(input.ResourceID) != "" {
|
||||
input.MediaType = taskOutputMediaType(task.Type)
|
||||
}
|
||||
if strings.TrimSpace(input.WorkflowStepID) == "" {
|
||||
return nil
|
||||
}
|
||||
@@ -475,10 +481,129 @@ func (s *Service) RegisterTaskOutputFromTask(task model.Task) error {
|
||||
if projectID == "" {
|
||||
return errors.New("任务未提供短剧项目 ID,无法登记产物")
|
||||
}
|
||||
if strings.TrimSpace(input.ResourceID) != "" && strings.TrimSpace(input.AssetVersionID) == "" && strings.TrimSpace(input.ShotID) != "" {
|
||||
assetVersionID, assetErr := s.ensureGeneratedProjectAsset(task, projectID, input.ShotID, input.ResourceID, input.MediaType)
|
||||
if assetErr != nil {
|
||||
return assetErr
|
||||
}
|
||||
input.AssetVersionID = assetVersionID
|
||||
}
|
||||
_, err = s.RegisterTaskOutput(task.UserID, projectID, input.WorkflowStepID, RegisterTaskOutputRequest{TaskID: task.ID, CanvasID: input.CanvasID, UnitID: input.UnitID, ShotID: input.ShotID, ArtifactType: input.ArtifactType, AssetVersionID: input.AssetVersionID, ResourceID: input.ResourceID, MediaType: input.MediaType, Role: input.Role, OutputJSON: task.ResultJSON})
|
||||
return err
|
||||
}
|
||||
|
||||
func taskOutputMediaType(taskType string) string {
|
||||
if strings.Contains(strings.ToLower(taskType), "video") {
|
||||
return "video"
|
||||
}
|
||||
return "image"
|
||||
}
|
||||
|
||||
func taskOutputResource(raw string, taskType string) (string, string) {
|
||||
if strings.TrimSpace(raw) == "" {
|
||||
return "", ""
|
||||
}
|
||||
var value any
|
||||
if json.Unmarshal([]byte(raw), &value) != nil {
|
||||
return "", ""
|
||||
}
|
||||
return findTaskOutputResource(value, taskOutputMediaType(taskType))
|
||||
}
|
||||
|
||||
func findTaskOutputResource(value any, mediaType string) (string, string) {
|
||||
switch item := value.(type) {
|
||||
case []any:
|
||||
for _, child := range item {
|
||||
if id, kind := findTaskOutputResource(child, mediaType); id != "" {
|
||||
return id, kind
|
||||
}
|
||||
}
|
||||
case map[string]any:
|
||||
for _, key := range []string{"resourceId", "storageKey", "url", "dataUrl", "resultUrl", "outputUrl"} {
|
||||
if raw, ok := item[key].(string); ok {
|
||||
text := strings.TrimSpace(raw)
|
||||
if strings.HasPrefix(text, "resource:") {
|
||||
return strings.TrimPrefix(text, "resource:"), mediaType
|
||||
}
|
||||
if strings.HasPrefix(text, "/api/resources/") {
|
||||
id := strings.TrimPrefix(text, "/api/resources/")
|
||||
if slash := strings.IndexByte(id, '/'); slash >= 0 {
|
||||
id = id[:slash]
|
||||
}
|
||||
if id != "" {
|
||||
return id, mediaType
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, child := range item {
|
||||
if id, kind := findTaskOutputResource(child, mediaType); id != "" {
|
||||
return id, kind
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func (s *Service) ensureGeneratedProjectAsset(task model.Task, projectID string, shotID string, resourceID string, mediaType string) (string, error) {
|
||||
representations, err := s.repo.AssetRepresentationsForTask(task.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, representation := range representations {
|
||||
if representation.Role == "output" && representation.ResourceID == resourceID && representation.AssetVersionID != "" {
|
||||
return representation.AssetVersionID, nil
|
||||
}
|
||||
}
|
||||
resource, err := s.repo.ResourceForUser(task.UserID, resourceID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if resource.Status != model.ResourceStatusReady {
|
||||
return "", BadAuthRequest("生成资源尚未就绪,无法登记项目素材")
|
||||
}
|
||||
shot, err := s.repo.ShotForProject(projectID, shotID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
now := time.Now()
|
||||
assetID := "workflow-asset-" + task.ID
|
||||
versionID := "workflow-version-" + task.ID
|
||||
label := "图片"
|
||||
if mediaType == "video" {
|
||||
label = "视频"
|
||||
}
|
||||
title := strings.TrimSpace(shot.Title)
|
||||
if title == "" {
|
||||
title = "镜头产物"
|
||||
}
|
||||
title += " · " + label
|
||||
payload, _ := json.Marshal(map[string]any{
|
||||
"id": assetID, "kind": mediaType, "category": model.AssetCategoryOther, "status": model.AssetVersionStatusConfirmed,
|
||||
"primaryVersionId": versionID, "title": title,
|
||||
"data": map[string]any{"storageKey": "resource:" + resourceID, "url": "/api/resources/" + resourceID + "/file", "mimeType": resource.MimeType, "bytes": resource.Size},
|
||||
"metadata": map[string]any{"source": "short-drama-workflow", "taskId": task.ID, "shotId": shot.ID, "projectIds": []string{projectID}},
|
||||
})
|
||||
asset := &model.Asset{ID: assetID, UserID: task.UserID, Kind: mediaType, Category: model.AssetCategoryOther, Status: model.AssetVersionStatusConfirmed, PrimaryVersionID: versionID, Title: title, PayloadJSON: string(payload), CreatedAt: now, UpdatedAt: now}
|
||||
version := &model.AssetVersion{ID: versionID, AssetID: assetID, Version: 1, Status: model.AssetVersionStatusConfirmed, DefinitionJSON: "{}", Prompt: task.Prompt, Note: "工作流生成产物", CreatedAt: now, UpdatedAt: now}
|
||||
folderID, err := s.resolveProjectAssetFolderID(projectID, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
position, err := s.repo.NextProjectAssetPosition(projectID, folderID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
link := &model.ProjectAssetLink{ID: newID(), ProjectID: projectID, AssetID: assetID, FolderID: folderID, Position: position, CreatedAt: now}
|
||||
if err := s.repo.UpsertAsset(asset); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := s.repo.LinkProjectAsset(asset, version, link); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return versionID, nil
|
||||
}
|
||||
|
||||
func validWorkflowStepStatus(status model.WorkflowStepStatus) bool {
|
||||
switch status {
|
||||
case model.WorkflowStepStatusPending, model.WorkflowStepStatusReady, model.WorkflowStepStatusRunning, model.WorkflowStepStatusReview, model.WorkflowStepStatusCompleted, model.WorkflowStepStatusFailed, model.WorkflowStepStatusSkipped:
|
||||
|
||||
Reference in new issue
Block a user