Commit b29011ef authored by yaoke.yk's avatar yaoke.yk

fix(storyboard): harden prompt persistence and invalidation

parent a19ab7b9
package com.yaoai.pipeline.service.impl; package com.yaoai.pipeline.service.impl;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.yaoai.ai.core.model.ChatRequest; import com.yaoai.ai.core.model.ChatRequest;
import com.yaoai.ai.core.service.LlmService; import com.yaoai.ai.core.service.LlmService;
import com.yaoai.billing.service.BillingService; import com.yaoai.billing.service.BillingService;
import com.yaoai.common.context.UserContext; import com.yaoai.common.context.UserContext;
import com.yaoai.common.exception.BizException;
import com.yaoai.domain.entity.Episode; import com.yaoai.domain.entity.Episode;
import com.yaoai.domain.entity.Scene; import com.yaoai.domain.entity.Scene;
import com.yaoai.domain.entity.Storyboard; import com.yaoai.domain.entity.Storyboard;
...@@ -18,16 +21,22 @@ import org.junit.jupiter.api.Test; ...@@ -18,16 +21,22 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.InOrder;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.transaction.annotation.Transactional;
import java.util.List; import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
...@@ -78,14 +87,15 @@ class StoryboardPipelineServiceImplTest { ...@@ -78,14 +87,15 @@ class StoryboardPipelineServiceImplTest {
episode.setSummary("summary"); episode.setSummary("summary");
when(episodeMapper.selectById(11L)).thenReturn(episode); when(episodeMapper.selectById(11L)).thenReturn(episode);
when(characterMapper.findByProject(22L, 33L)).thenReturn(List.of()); when(characterMapper.findByProject(22L, 33L)).thenReturn(List.of());
when(sceneMapper.findByProject(22L, 33L)).thenReturn(List.of(scene("cafe"), scene("street"))); when(sceneMapper.findByProject(22L, 33L)).thenReturn(List.of(scene("cafe", null), scene("street", "outdoor")));
when(storyboardMapper.insert(any(Storyboard.class))).thenAnswer(invocation -> 1);
when(llmService.chat(any(ChatRequest.class))).thenReturn(""" when(llmService.chat(any(ChatRequest.class))).thenReturn("""
[{ [{
"sequence_num": 1, "sequence_num": 1,
"scene_number": "001", "scene_number": "001",
"short_description": "shot", "short_description": "shot",
"detailed_description": "detail", "detailed_description": "detail",
"characters": "@hero, @cafe, @friend", "characters": "@hero, @street, @cafe, @friend",
"dialogues": "", "dialogues": "",
"camera_direction": "push", "camera_direction": "push",
"composition_guide": "medium", "composition_guide": "medium",
...@@ -98,8 +108,14 @@ class StoryboardPipelineServiceImplTest { ...@@ -98,8 +108,14 @@ class StoryboardPipelineServiceImplTest {
List<Storyboard> storyboards = service.generateStoryboards(11L, 22L, 33L); List<Storyboard> storyboards = service.generateStoryboards(11L, 22L, 33L);
assertEquals(1, storyboards.size()); assertEquals(1, storyboards.size());
assertEquals("@cafe", storyboards.get(0).getSceneRef()); assertEquals("@street", storyboards.get(0).getSceneRef());
assertEquals("@hero,@friend", storyboards.get(0).getCharacters()); assertEquals("@hero,@friend", storyboards.get(0).getCharacters());
ArgumentCaptor<ChatRequest> requestCaptor = ArgumentCaptor.forClass(ChatRequest.class);
verify(llmService).chat(requestCaptor.capture());
String systemMessage = requestCaptor.getValue().getMessages().get(0).getContent();
assertTrue(systemMessage.contains("- @cafe ():"));
assertTrue(!systemMessage.contains("- @cafe (outdoor):"));
} }
@Test @Test
...@@ -119,6 +135,7 @@ class StoryboardPipelineServiceImplTest { ...@@ -119,6 +135,7 @@ class StoryboardPipelineServiceImplTest {
storyboard.setCompositionGuide("medium"); storyboard.setCompositionGuide("medium");
storyboard.setDurationSeconds(5); storyboard.setDurationSeconds(5);
when(storyboardMapper.selectById(7L)).thenReturn(storyboard); when(storyboardMapper.selectById(7L)).thenReturn(storyboard);
when(storyboardMapper.updateById(any(Storyboard.class))).thenReturn(1);
when(episodeMapper.selectById(11L)).thenReturn(new Episode()); when(episodeMapper.selectById(11L)).thenReturn(new Episode());
when(characterMapper.findByProject(22L, 33L)).thenReturn(List.of()); when(characterMapper.findByProject(22L, 33L)).thenReturn(List.of());
when(sceneMapper.findByProject(22L, 33L)).thenReturn(List.of()); when(sceneMapper.findByProject(22L, 33L)).thenReturn(List.of());
...@@ -134,6 +151,10 @@ class StoryboardPipelineServiceImplTest { ...@@ -134,6 +151,10 @@ class StoryboardPipelineServiceImplTest {
assertEquals(7L, persisted.getId()); assertEquals(7L, persisted.getId());
assertEquals("persisted prompt", persisted.getVideoPrompt()); assertEquals("persisted prompt", persisted.getVideoPrompt());
InOrder inOrder = inOrder(billingService, storyboardMapper);
inOrder.verify(billingService).charge(any());
inOrder.verify(storyboardMapper).updateById(any(Storyboard.class));
ArgumentCaptor<ChatRequest> requestCaptor = ArgumentCaptor.forClass(ChatRequest.class); ArgumentCaptor<ChatRequest> requestCaptor = ArgumentCaptor.forClass(ChatRequest.class);
verify(llmService).chat(requestCaptor.capture()); verify(llmService).chat(requestCaptor.capture());
String userMessage = requestCaptor.getValue().getMessages().get(1).getContent(); String userMessage = requestCaptor.getValue().getMessages().get(1).getContent();
...@@ -156,9 +177,10 @@ class StoryboardPipelineServiceImplTest { ...@@ -156,9 +177,10 @@ class StoryboardPipelineServiceImplTest {
storyboard.setCompositionGuide("wide"); storyboard.setCompositionGuide("wide");
storyboard.setDurationSeconds(6); storyboard.setDurationSeconds(6);
when(storyboardMapper.selectById(8L)).thenReturn(storyboard); when(storyboardMapper.selectById(8L)).thenReturn(storyboard);
when(storyboardMapper.updateById(any(Storyboard.class))).thenReturn(1);
when(episodeMapper.selectById(11L)).thenReturn(new Episode()); when(episodeMapper.selectById(11L)).thenReturn(new Episode());
when(characterMapper.findByProject(22L, 33L)).thenReturn(List.of()); when(characterMapper.findByProject(22L, 33L)).thenReturn(List.of());
when(sceneMapper.findByProject(22L, 33L)).thenReturn(List.of(scene("cafe"))); when(sceneMapper.findByProject(22L, 33L)).thenReturn(List.of(scene("cafe", null)));
when(llmService.chat(any(ChatRequest.class))).thenReturn("legacy prompt"); when(llmService.chat(any(ChatRequest.class))).thenReturn("legacy prompt");
service.generatePrompt(8L, 33L); service.generatePrompt(8L, 33L);
...@@ -168,6 +190,8 @@ class StoryboardPipelineServiceImplTest { ...@@ -168,6 +190,8 @@ class StoryboardPipelineServiceImplTest {
String userMessage = requestCaptor.getValue().getMessages().get(1).getContent(); String userMessage = requestCaptor.getValue().getMessages().get(1).getContent();
assertTrue(userMessage.contains("Character refs: @hero")); assertTrue(userMessage.contains("Character refs: @hero"));
assertTrue(userMessage.contains("Scene refs: @cafe")); assertTrue(userMessage.contains("Scene refs: @cafe"));
assertTrue(userMessage.contains("- @cafe ():"));
assertTrue(!userMessage.contains("- @cafe (outdoor):"));
} }
@Test @Test
...@@ -180,6 +204,8 @@ class StoryboardPipelineServiceImplTest { ...@@ -180,6 +204,8 @@ class StoryboardPipelineServiceImplTest {
Storyboard refreshed = new Storyboard(); Storyboard refreshed = new Storyboard();
refreshed.setId(9L); refreshed.setId(9L);
refreshed.setTenantId(33L); refreshed.setTenantId(33L);
when(storyboardMapper.updateById(any(Storyboard.class))).thenReturn(1);
when(storyboardMapper.update(org.mockito.ArgumentMatchers.isNull(), any())).thenReturn(1);
when(storyboardMapper.selectById(9L)).thenReturn(existing, refreshed); when(storyboardMapper.selectById(9L)).thenReturn(existing, refreshed);
Storyboard patch = new Storyboard(); Storyboard patch = new Storyboard();
...@@ -195,10 +221,16 @@ class StoryboardPipelineServiceImplTest { ...@@ -195,10 +221,16 @@ class StoryboardPipelineServiceImplTest {
assertEquals(9L, updated.getId()); assertEquals(9L, updated.getId());
assertEquals(33L, updated.getTenantId()); assertEquals(33L, updated.getTenantId());
ArgumentCaptor<com.baomidou.mybatisplus.core.conditions.Wrapper<Storyboard>> wrapperCaptor = ArgumentCaptor<UpdateWrapper<Storyboard>> wrapperCaptor = ArgumentCaptor.forClass(UpdateWrapper.class);
ArgumentCaptor.forClass(com.baomidou.mybatisplus.core.conditions.Wrapper.class);
verify(storyboardMapper).update(org.mockito.ArgumentMatchers.isNull(), wrapperCaptor.capture()); verify(storyboardMapper).update(org.mockito.ArgumentMatchers.isNull(), wrapperCaptor.capture());
assertTrue(wrapperCaptor.getValue().getSqlSet().contains("video_prompt")); UpdateWrapper<Storyboard> wrapper = wrapperCaptor.getValue();
assertTrue(wrapper.getSqlSet().contains("video_prompt"));
assertTrue(wrapper.getSqlSegment().contains("id"));
assertTrue(wrapper.getSqlSegment().contains("tenant_id"));
Map<String, Object> paramPairs = ((AbstractWrapper<Storyboard, ?, ?>) wrapper).getParamNameValuePairs();
assertTrue(paramPairs.containsValue(9L));
assertTrue(paramPairs.containsValue(33L));
} }
@Test @Test
...@@ -207,6 +239,7 @@ class StoryboardPipelineServiceImplTest { ...@@ -207,6 +239,7 @@ class StoryboardPipelineServiceImplTest {
existing.setId(9L); existing.setId(9L);
existing.setTenantId(33L); existing.setTenantId(33L);
existing.setShortDescription("same"); existing.setShortDescription("same");
when(storyboardMapper.updateById(any(Storyboard.class))).thenReturn(1);
when(storyboardMapper.selectById(9L)).thenReturn(existing, existing); when(storyboardMapper.selectById(9L)).thenReturn(existing, existing);
Storyboard patch = new Storyboard(); Storyboard patch = new Storyboard();
...@@ -220,6 +253,45 @@ class StoryboardPipelineServiceImplTest { ...@@ -220,6 +253,45 @@ class StoryboardPipelineServiceImplTest {
} }
@Test @Test
void update_shouldBeTransactionalAndFailWhenPromptInvalidationFails() throws Exception {
Storyboard existing = new Storyboard();
existing.setId(9L);
existing.setTenantId(33L);
existing.setShortDescription("old");
when(storyboardMapper.selectById(9L)).thenReturn(existing);
when(storyboardMapper.updateById(any(Storyboard.class))).thenReturn(1);
when(storyboardMapper.update(org.mockito.ArgumentMatchers.isNull(), any())).thenReturn(0);
Storyboard patch = new Storyboard();
patch.setShortDescription("new");
assertThrows(BizException.class, () -> service.update(9L, 33L, patch));
assertTrue(StoryboardPipelineServiceImpl.class
.getMethod("update", Long.class, Long.class, Storyboard.class)
.isAnnotationPresent(Transactional.class));
}
@Test
void generatePrompt_shouldNotPersistVideoPromptWhenChargeFails() throws Exception {
Storyboard storyboard = promptStoryboard(7L, 11L, 22L, 33L);
storyboard.setSceneRef("@cafe");
when(storyboardMapper.selectById(7L)).thenReturn(storyboard);
when(episodeMapper.selectById(11L)).thenReturn(new Episode());
when(characterMapper.findByProject(22L, 33L)).thenReturn(List.of());
when(sceneMapper.findByProject(22L, 33L)).thenReturn(List.of());
when(llmService.chat(any(ChatRequest.class))).thenReturn("persisted prompt");
doThrow(new BizException("TEST", "charge failed")).when(billingService).charge(any());
assertThrows(BizException.class, () -> service.generatePrompt(7L, 33L));
verify(storyboardMapper, never()).updateById(org.mockito.ArgumentMatchers.<Storyboard>argThat(storyboardArg ->
"persisted prompt".equals(storyboardArg.getVideoPrompt())
));
assertTrue(StoryboardPipelineServiceImpl.class
.getMethod("generatePrompt", Long.class, Long.class)
.isAnnotationPresent(Transactional.class));
}
@Test
void populateMissingVideoPromptsByEpisode_shouldFillOnlyMissingRows() { void populateMissingVideoPromptsByEpisode_shouldFillOnlyMissingRows() {
Storyboard missingA = new Storyboard(); Storyboard missingA = new Storyboard();
missingA.setId(1L); missingA.setId(1L);
...@@ -227,6 +299,7 @@ class StoryboardPipelineServiceImplTest { ...@@ -227,6 +299,7 @@ class StoryboardPipelineServiceImplTest {
missingB.setId(2L); missingB.setId(2L);
Storyboard full = new Storyboard(); Storyboard full = new Storyboard();
full.setId(3L); full.setId(3L);
when(storyboardMapper.updateById(any(Storyboard.class))).thenReturn(1);
when(storyboardMapper.findMissingVideoPromptByEpisode(44L, 33L)).thenReturn(List.of(missingA, missingB)); when(storyboardMapper.findMissingVideoPromptByEpisode(44L, 33L)).thenReturn(List.of(missingA, missingB));
when(storyboardMapper.findByEpisode(44L, 33L)).thenReturn(List.of(missingA, missingB, full)); when(storyboardMapper.findByEpisode(44L, 33L)).thenReturn(List.of(missingA, missingB, full));
when(storyboardMapper.selectById(1L)).thenReturn(promptStoryboard(1L, 44L, 22L, 33L)); when(storyboardMapper.selectById(1L)).thenReturn(promptStoryboard(1L, 44L, 22L, 33L));
...@@ -239,12 +312,16 @@ class StoryboardPipelineServiceImplTest { ...@@ -239,12 +312,16 @@ class StoryboardPipelineServiceImplTest {
List<Storyboard> result = service.populateMissingVideoPromptsByEpisode(44L, 33L); List<Storyboard> result = service.populateMissingVideoPromptsByEpisode(44L, 33L);
assertEquals(3, result.size()); assertEquals(3, result.size());
verify(storyboardMapper, times(2)).updateById(any(Storyboard.class)); verify(storyboardMapper, never()).selectById(3L);
ArgumentCaptor<Storyboard> updateCaptor = ArgumentCaptor.forClass(Storyboard.class);
verify(storyboardMapper, times(2)).updateById(updateCaptor.capture());
assertEquals(List.of(1L, 2L), updateCaptor.getAllValues().stream().map(Storyboard::getId).toList());
} }
private static Scene scene(String name) { private static Scene scene(String name, String sceneType) {
Scene scene = new Scene(); Scene scene = new Scene();
scene.setName(name); scene.setName(name);
scene.setSceneType(sceneType);
return scene; return scene;
} }
......
...@@ -23,6 +23,7 @@ import com.yaoai.pipeline.service.StoryboardPipelineService; ...@@ -23,6 +23,7 @@ import com.yaoai.pipeline.service.StoryboardPipelineService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
...@@ -193,6 +194,7 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService ...@@ -193,6 +194,7 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService
} }
@Override @Override
@Transactional
public Storyboard update(Long id, Long tenantId, Storyboard patch) { public Storyboard update(Long id, Long tenantId, Storyboard patch) {
Storyboard existing = storyboardMapper.selectById(id); Storyboard existing = storyboardMapper.selectById(id);
if (existing == null || !tenantId.equals(existing.getTenantId())) { if (existing == null || !tenantId.equals(existing.getTenantId())) {
...@@ -202,7 +204,10 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService ...@@ -202,7 +204,10 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService
boolean bodyFieldsChanged = hasStoryboardBodyChanges(existing, patch); boolean bodyFieldsChanged = hasStoryboardBodyChanges(existing, patch);
patch.setId(id); patch.setId(id);
patch.setTenantId(tenantId); patch.setTenantId(tenantId);
storyboardMapper.updateById(patch); int updatedRows = storyboardMapper.updateById(patch);
if (updatedRows != 1) {
throw new BizException(ErrorCode.INTERNAL_ERROR, "Failed to update storyboard");
}
if (bodyFieldsChanged) { if (bodyFieldsChanged) {
clearVideoPrompt(id, tenantId); clearVideoPrompt(id, tenantId);
} }
...@@ -226,6 +231,7 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService ...@@ -226,6 +231,7 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService
} }
@Override @Override
@Transactional
public String generatePrompt(Long storyboardId, Long tenantId) { public String generatePrompt(Long storyboardId, Long tenantId) {
Storyboard storyboard = storyboardMapper.selectById(storyboardId); Storyboard storyboard = storyboardMapper.selectById(storyboardId);
if (storyboard == null || !tenantId.equals(storyboard.getTenantId())) { if (storyboard == null || !tenantId.equals(storyboard.getTenantId())) {
...@@ -271,7 +277,7 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService ...@@ -271,7 +277,7 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService
userMsg.append(String.format( userMsg.append(String.format(
"- @%s (%s): %s%n", "- @%s (%s): %s%n",
nullSafe(scene.getName()), nullSafe(scene.getName()),
"indoor".equals(scene.getSceneType()) ? "indoor" : "outdoor", sceneTypeLabel(scene.getSceneType()),
nullSafe(scene.getDescription()) nullSafe(scene.getDescription())
)); ));
} }
...@@ -331,11 +337,6 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService ...@@ -331,11 +337,6 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService
String prompt = llmService.chat(request); String prompt = llmService.chat(request);
Storyboard promptUpdate = new Storyboard();
promptUpdate.setId(storyboard.getId());
promptUpdate.setVideoPrompt(prompt);
storyboardMapper.updateById(promptUpdate);
billingService.charge(BillingChargeRequest.builder() billingService.charge(BillingChargeRequest.builder()
.tenantId(tenantId) .tenantId(tenantId)
.userId(UserContext.require()) .userId(UserContext.require())
...@@ -348,6 +349,14 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService ...@@ -348,6 +349,14 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService
.unitCount(1) .unitCount(1)
.refId(String.valueOf(storyboardId)) .refId(String.valueOf(storyboardId))
.build()); .build());
Storyboard promptUpdate = new Storyboard();
promptUpdate.setId(storyboard.getId());
promptUpdate.setVideoPrompt(prompt);
int updatedRows = storyboardMapper.updateById(promptUpdate);
if (updatedRows != 1) {
throw new BizException(ErrorCode.INTERNAL_ERROR, "Failed to persist storyboard prompt");
}
return prompt; return prompt;
} }
...@@ -388,7 +397,7 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService ...@@ -388,7 +397,7 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService
context.append(String.format( context.append(String.format(
"- @%s (%s): %s%n", "- @%s (%s): %s%n",
nullSafe(scene.getName()), nullSafe(scene.getName()),
"indoor".equals(scene.getSceneType()) ? "indoor" : "outdoor", sceneTypeLabel(scene.getSceneType()),
nullSafe(scene.getDescription()) nullSafe(scene.getDescription())
)); ));
} }
...@@ -412,17 +421,19 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService ...@@ -412,17 +421,19 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService
private StoryboardRefs splitStoryboardRefs(String rawRefs, Set<String> sceneRefs) { private StoryboardRefs splitStoryboardRefs(String rawRefs, Set<String> sceneRefs) {
List<String> refs = parseRefs(rawRefs); List<String> refs = parseRefs(rawRefs);
List<String> characterRefs = new ArrayList<>(); List<String> characterRefs = new ArrayList<>();
List<String> matchedSceneRefs = new ArrayList<>(); String sceneRef = null;
for (String ref : refs) { for (String ref : refs) {
if (sceneRefs.contains(ref)) { if (sceneRefs.contains(ref)) {
matchedSceneRefs.add(ref); if (sceneRef == null) {
sceneRef = ref;
}
} else { } else {
characterRefs.add(ref); characterRefs.add(ref);
} }
} }
return new StoryboardRefs(joinRefs(characterRefs), joinRefs(matchedSceneRefs)); return new StoryboardRefs(joinRefs(characterRefs), sceneRef);
} }
private StoryboardRefs promptRefsForCurrentShot(Storyboard storyboard, List<Scene> projectScenes) { private StoryboardRefs promptRefsForCurrentShot(Storyboard storyboard, List<Scene> projectScenes) {
...@@ -459,13 +470,16 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService ...@@ -459,13 +470,16 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService
} }
private void clearVideoPrompt(Long storyboardId, Long tenantId) { private void clearVideoPrompt(Long storyboardId, Long tenantId) {
storyboardMapper.update( int updatedRows = storyboardMapper.update(
null, null,
new UpdateWrapper<Storyboard>() new UpdateWrapper<Storyboard>()
.eq("id", storyboardId) .eq("id", storyboardId)
.eq("tenant_id", tenantId) .eq("tenant_id", tenantId)
.set("video_prompt", null) .set("video_prompt", null)
); );
if (updatedRows != 1) {
throw new BizException(ErrorCode.INTERNAL_ERROR, "Failed to clear storyboard prompt");
}
} }
private boolean hasStoryboardBodyChanges(Storyboard existing, Storyboard patch) { private boolean hasStoryboardBodyChanges(Storyboard existing, Storyboard patch) {
...@@ -508,6 +522,16 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService ...@@ -508,6 +522,16 @@ public class StoryboardPipelineServiceImpl implements StoryboardPipelineService
return value != null ? value : ""; return value != null ? value : "";
} }
private String sceneTypeLabel(String sceneType) {
if ("indoor".equals(sceneType)) {
return "indoor";
}
if ("outdoor".equals(sceneType)) {
return "outdoor";
}
return "";
}
private record StoryboardRefs(String characters, String sceneRef) { private record StoryboardRefs(String characters, String sceneRef) {
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment