修复subtitleStyle字段更新问题,添加测试用例

This commit is contained in:
wangzhiwei 2026-01-08 16:46:03 +08:00
parent 4455c2c3ed
commit de6a766ff9
8 changed files with 588 additions and 33 deletions

View File

@ -2,8 +2,13 @@ package top.continew.admin.business.model.req;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import top.continew.admin.business.model.entity.Effect;
import top.continew.admin.business.model.entity.SubtitleItem;
import top.continew.admin.business.model.entity.SubtitleStyle;
import top.continew.starter.extension.crud.model.req.BaseReq; import top.continew.starter.extension.crud.model.req.BaseReq;
import java.util.List;
/** /**
* 数字人请求参数对象 * 数字人请求参数对象
* *
@ -42,15 +47,15 @@ public class DigitalHumanReq extends BaseReq {
/** /**
* 字幕项列表冗余存储用于快速回显 * 字幕项列表冗余存储用于快速回显
*/ */
private String subtitle; private List<SubtitleItem> subtitles;
/** /**
* 字幕样式配置冗余或快照 * 字幕样式配置冗余或快照
*/ */
private String subtitleStyle; private SubtitleStyle subtitleStyle;
/** /**
* 特效列表冗余存储 * 特效列表冗余存储
*/ */
private String effects; private List<Effect> effects;
} }

View File

@ -20,6 +20,7 @@ import top.continew.admin.business.service.DigitalHumanService;
import top.continew.starter.extension.crud.model.resp.PageResp; import top.continew.starter.extension.crud.model.resp.PageResp;
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl; import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -96,25 +97,136 @@ public class DigitalHumanServiceImpl extends BaseServiceImpl<DigitalHumanMapper,
@Override @Override
public Long add(DigitalHumanReq req) { public Long add(DigitalHumanReq req) {
DigitalHuman digitalHuman = new DigitalHuman(); DigitalHuman digitalHuman = new DigitalHuman();
BeanUtils.copyProperties(req, digitalHuman); // 只复制基本字段不包括关联的列表字段
digitalHuman.setUserId(req.getUserId());
digitalHuman.setCopywriting(req.getCopywriting());
digitalHuman.setGeneratedVoiceUrl(req.getGeneratedVoiceUrl());
digitalHuman.setGeneratedFrameImageUrl(req.getGeneratedFrameImageUrl());
digitalHuman.setGeneratedVideoUrl(req.getGeneratedVideoUrl());
digitalHuman.setCreateTime(java.time.LocalDateTime.now()); digitalHuman.setCreateTime(java.time.LocalDateTime.now());
digitalHuman.setUpdateTime(java.time.LocalDateTime.now()); digitalHuman.setUpdateTime(java.time.LocalDateTime.now());
digitalHumanMapper.insert(digitalHuman); digitalHumanMapper.insert(digitalHuman);
// 保存字幕样式
if (req.getSubtitleStyle() != null) {
SubtitleStyle subtitleStyle = req.getSubtitleStyle();
subtitleStyle.setDigitalHumanId(digitalHuman.getId());
subtitleStyle.setCreateTime(java.time.LocalDateTime.now());
subtitleStyle.setUpdateTime(java.time.LocalDateTime.now());
subtitleStyleMapper.insert(subtitleStyle);
}
// 保存字幕项列表
if (req.getSubtitles() != null && !req.getSubtitles().isEmpty()) {
LocalDateTime now = LocalDateTime.now();
for (SubtitleItem subtitleItem : req.getSubtitles()) {
subtitleItem.setDigitalHumanId(digitalHuman.getId());
subtitleItem.setCreateTime(now);
subtitleItem.setUpdateTime(now);
subtitleItemMapper.insert(subtitleItem);
}
}
// 保存特效列表
if (req.getEffects() != null && !req.getEffects().isEmpty()) {
LocalDateTime now = LocalDateTime.now();
for (Effect effect : req.getEffects()) {
effect.setDigitalHumanId(digitalHuman.getId());
effect.setCreateTime(now);
effect.setUpdateTime(now);
effectMapper.insert(effect);
}
}
return digitalHuman.getId(); return digitalHuman.getId();
} }
@Override @Override
public void delete(List<Long> ids) { public void delete(List<Long> ids) {
for (Long id : ids) {
// 删除关联的特效
LambdaQueryWrapper<Effect> effectWrapper = new LambdaQueryWrapper<>();
effectWrapper.eq(Effect::getDigitalHumanId, id);
effectMapper.delete(effectWrapper);
// 删除关联的字幕项
LambdaQueryWrapper<SubtitleItem> subtitleItemWrapper = new LambdaQueryWrapper<>();
subtitleItemWrapper.eq(SubtitleItem::getDigitalHumanId, id);
subtitleItemMapper.delete(subtitleItemWrapper);
// 删除关联的字幕样式
LambdaQueryWrapper<SubtitleStyle> subtitleStyleWrapper = new LambdaQueryWrapper<>();
subtitleStyleWrapper.eq(SubtitleStyle::getDigitalHumanId, id);
subtitleStyleMapper.delete(subtitleStyleWrapper);
}
// 删除数字人
digitalHumanMapper.deleteBatchIds(ids); digitalHumanMapper.deleteBatchIds(ids);
} }
@Override @Override
public void update(DigitalHumanReq req, Long id) { public void update(DigitalHumanReq req, Long id) {
DigitalHuman digitalHuman = new DigitalHuman(); DigitalHuman digitalHuman = new DigitalHuman();
BeanUtils.copyProperties(req, digitalHuman); // 只复制基本字段不包括关联的列表字段
digitalHuman.setId(id); digitalHuman.setId(id);
digitalHuman.setUserId(req.getUserId());
digitalHuman.setCopywriting(req.getCopywriting());
digitalHuman.setGeneratedVoiceUrl(req.getGeneratedVoiceUrl());
digitalHuman.setGeneratedFrameImageUrl(req.getGeneratedFrameImageUrl());
digitalHuman.setGeneratedVideoUrl(req.getGeneratedVideoUrl());
digitalHuman.setUpdateTime(java.time.LocalDateTime.now()); digitalHuman.setUpdateTime(java.time.LocalDateTime.now());
digitalHumanMapper.updateById(digitalHuman); digitalHumanMapper.updateById(digitalHuman);
// 更新或保存字幕样式
if (req.getSubtitleStyle() != null) {
LambdaQueryWrapper<SubtitleStyle> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SubtitleStyle::getDigitalHumanId, id);
List<SubtitleStyle> existingStyles = subtitleStyleMapper.selectList(wrapper);
SubtitleStyle subtitleStyle = req.getSubtitleStyle();
subtitleStyle.setDigitalHumanId(id);
subtitleStyle.setUpdateTime(java.time.LocalDateTime.now());
if (existingStyles != null && !existingStyles.isEmpty()) {
// 如果已存在样式记录更新第一个记录
subtitleStyle.setId(existingStyles.get(0).getId());
subtitleStyleMapper.updateById(subtitleStyle);
} else {
// 如果不存在样式记录创建新记录
subtitleStyle.setCreateTime(java.time.LocalDateTime.now());
subtitleStyleMapper.insert(subtitleStyle);
}
}
// 删除旧的字幕项然后保存新的字幕项
LambdaQueryWrapper<SubtitleItem> subtitleItemWrapper = new LambdaQueryWrapper<>();
subtitleItemWrapper.eq(SubtitleItem::getDigitalHumanId, id);
subtitleItemMapper.delete(subtitleItemWrapper);
if (req.getSubtitles() != null && !req.getSubtitles().isEmpty()) {
LocalDateTime now = LocalDateTime.now();
for (SubtitleItem subtitleItem : req.getSubtitles()) {
subtitleItem.setDigitalHumanId(id);
subtitleItem.setCreateTime(now);
subtitleItem.setUpdateTime(now);
subtitleItemMapper.insert(subtitleItem);
}
}
// 删除旧的特效然后保存新的特效
LambdaQueryWrapper<Effect> effectWrapper = new LambdaQueryWrapper<>();
effectWrapper.eq(Effect::getDigitalHumanId, id);
effectMapper.delete(effectWrapper);
if (req.getEffects() != null && !req.getEffects().isEmpty()) {
LocalDateTime now = LocalDateTime.now();
for (Effect effect : req.getEffects()) {
effect.setDigitalHumanId(id);
effect.setCreateTime(now);
effect.setUpdateTime(now);
effectMapper.insert(effect);
}
}
} }
private DigitalHumanResp convertToResp(DigitalHuman digitalHuman) { private DigitalHumanResp convertToResp(DigitalHuman digitalHuman) {

View File

@ -73,6 +73,13 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- H2 内存数据库(用于测试) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>cn.dev33</groupId> <groupId>cn.dev33</groupId>
<artifactId>sa-token-core</artifactId> <artifactId>sa-token-core</artifactId>

View File

@ -15,7 +15,7 @@ spring.datasource:
## 动态数据源配置可配多主多从m1、s1...纯粹多库mysql、oracle...混合配置m1、s1、oracle... ## 动态数据源配置可配多主多从m1、s1...纯粹多库mysql、oracle...混合配置m1、s1、oracle...
dynamic: dynamic:
# 是否启用 P6SpySQL 性能分析组件,该插件有性能损耗,不建议生产环境使用) # 是否启用 P6SpySQL 性能分析组件,该插件有性能损耗,不建议生产环境使用)
p6spy: true p6spy: false
# 设置默认的数据源或者数据源组默认master # 设置默认的数据源或者数据源组默认master
primary: digital_human primary: digital_human
# 严格匹配数据源true未匹配到指定数据源时抛异常false使用默认数据源默认 false # 严格匹配数据源true未匹配到指定数据源时抛异常false使用默认数据源默认 false
@ -23,31 +23,38 @@ spring.datasource:
datasource: datasource:
# 主库配置(可配多个,构成多主) # 主库配置(可配多个,构成多主)
digital_human: digital_human:
url: jdbc:mysql://${DB_HOST:localhost}:${DB_PORT:3306}/${DB_NAME:digital_human}?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false&allowPublicKeyRetrieval=true url: jdbc:h2:mem:digital_human;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=MYSQL;DATABASE_TO_UPPER=FALSE
#url: jdbc:mysql://106.54.11.219:3306/digital_human?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false&allowMultiQueries=true&autoReconnect=true&maxReconnects=10&failOverReadOnly=false username: sa
username: ${DB_USER:root} password:
# password: ${DB_PWD:147369Wan} driver-class-name: org.h2.Driver
password: ${DB_PWD:123456}
# password: ${DB_PWD:C9MUjc5ChtqHeCtQ}
driver-class-name: com.mysql.cj.jdbc.Driver
type: ${spring.datasource.type} type: ${spring.datasource.type}
hikari: spring.jpa:
# 最大连接数量(默认 10根据实际环境调整 hibernate:
# 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒 ddl-auto: create-drop
max-pool-size: 20 show-sql: true
# 获取连接超时时间(默认 30000 毫秒30 秒) database-platform: org.hibernate.dialect.H2Dialect
connection-timeout: 30000
# 空闲连接最大存活时间(默认 600000 毫秒10 分钟) spring.h2.console:
idle-timeout: 600000 enabled: true
# 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime默认 0禁用 path: /h2-console
keepaliveTime: 30000
# 连接最大生存时间(默认 1800000 毫秒30 分钟) spring.datasource.dynamic.hikari:
max-lifetime: 1800000 # 最大连接数量(默认 10根据实际环境调整
# 注意:当连接达到上限,并且没有空闲连接可用时,获取连接将在超时前阻塞最多 connectionTimeout 毫秒
max-pool-size: 20
# 获取连接超时时间(默认 30000 毫秒30 秒)
connection-timeout: 30000
# 空闲连接最大存活时间(默认 600000 毫秒10 分钟)
idle-timeout: 600000
# 保持连接活动的频率,以防止它被数据库或网络基础设施超时。该值必须小于 maxLifetime默认 0禁用
keepaliveTime: 30000
# 连接最大生存时间(默认 1800000 毫秒30 分钟)
max-lifetime: 1800000
## Liquibase 配置 ## Liquibase 配置
spring.liquibase: spring.liquibase:
# 是否启用 # 是否启用
enabled: false enabled: true
# 配置文件路径 # 配置文件路径
change-log: classpath:/db/changelog/db.changelog-master.yaml change-log: classpath:/db/changelog/db.changelog-master.yaml
@ -62,7 +69,7 @@ spring.data:
port: 6379 port: 6379
# port: 16380 # port: 16380
# 密码(未设置密码时请注释掉) # 密码(未设置密码时请注释掉)
password: 123456 # password: 123456
# 数据库索引 # 数据库索引
database: ${REDIS_DB:11} database: ${REDIS_DB:11}
# 连接超时时间 # 连接超时时间
@ -72,7 +79,7 @@ spring.data:
enabled: false enabled: false
## Redisson 配置 ## Redisson 配置
redisson: redisson:
enabled: true enabled: false
mode: SINGLE mode: SINGLE
## JetCache 配置 ## JetCache 配置
jetcache: jetcache:

View File

@ -0,0 +1,235 @@
package top.continew.admin.business.service.impl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.transaction.annotation.Transactional;
import top.continew.admin.business.mapper.DigitalHumanMapper;
import top.continew.admin.business.mapper.EffectMapper;
import top.continew.admin.business.mapper.SubtitleItemMapper;
import top.continew.admin.business.mapper.SubtitleStyleMapper;
import top.continew.admin.business.model.entity.DigitalHuman;
import top.continew.admin.business.model.entity.Effect;
import top.continew.admin.business.model.entity.SubtitleItem;
import top.continew.admin.business.model.entity.SubtitleStyle;
import top.continew.admin.business.model.req.DigitalHumanReq;
import top.continew.admin.business.service.DigitalHumanService;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* 数字人服务实现类测试
*
* @author 王志维
* @since 2025/12/29
*/
@SpringBootTest
@ActiveProfiles("test")
@Transactional
@Rollback(true)
class DigitalHumanServiceImplTest {
@Autowired
private DigitalHumanService digitalHumanService;
@Autowired
private DigitalHumanMapper digitalHumanMapper;
@Autowired
private SubtitleStyleMapper subtitleStyleMapper;
@Autowired
private SubtitleItemMapper subtitleItemMapper;
@Autowired
private EffectMapper effectMapper;
private DigitalHumanReq createDigitalHumanReq() {
DigitalHumanReq req = new DigitalHumanReq();
req.setUserId("1");
req.setCopywriting("测试文案");
req.setGeneratedVoiceUrl("http://test.com/voice.mp3");
req.setGeneratedFrameImageUrl("http://test.com/frame.jpg");
req.setGeneratedVideoUrl("http://test.com/video.mp4");
// 添加字幕样式
SubtitleStyle subtitleStyle = new SubtitleStyle();
subtitleStyle.setFontSize(16);
subtitleStyle.setColor("#FFFFFF");
subtitleStyle.setStrokeColor("#000000");
subtitleStyle.setStrokeWidth(2);
req.setSubtitleStyle(subtitleStyle);
// 添加字幕项
SubtitleItem subtitleItem1 = new SubtitleItem();
subtitleItem1.setStartTime(0.0);
subtitleItem1.setEndTime(1.0);
subtitleItem1.setText("测试字幕1");
SubtitleItem subtitleItem2 = new SubtitleItem();
subtitleItem2.setStartTime(1.0);
subtitleItem2.setEndTime(2.0);
subtitleItem2.setText("测试字幕2");
req.setSubtitles(List.of(subtitleItem1, subtitleItem2));
// 添加特效
Effect effect1 = new Effect();
effect1.setType("text");
effect1.setName("测试特效1");
effect1.setSourceUrl("http://test.com/effect1.mp4");
effect1.setLength(5.0);
Effect effect2 = new Effect();
effect2.setType("image");
effect2.setName("测试特效2");
effect2.setSourceUrl("http://test.com/effect2.jpg");
effect2.setLength(3.0);
req.setEffects(List.of(effect1, effect2));
return req;
}
@Test
void testAdd() {
DigitalHumanReq req = createDigitalHumanReq();
Long id = digitalHumanService.add(req);
assertNotNull(id);
// 验证数字人基本信息
DigitalHuman digitalHuman = digitalHumanMapper.selectById(id);
assertNotNull(digitalHuman);
assertEquals(req.getUserId(), digitalHuman.getUserId());
assertEquals(req.getCopywriting(), digitalHuman.getCopywriting());
// 验证字幕样式
List<SubtitleStyle> subtitleStyles = subtitleStyleMapper.selectList(null);
assertFalse(subtitleStyles.isEmpty());
SubtitleStyle savedStyle = subtitleStyles.stream()
.filter(style -> style.getDigitalHumanId().equals(id))
.findFirst()
.orElse(null);
assertNotNull(savedStyle);
assertEquals(req.getSubtitleStyle().getFontSize(), savedStyle.getFontSize());
assertEquals(req.getSubtitleStyle().getColor(), savedStyle.getColor());
// 验证字幕项
List<SubtitleItem> subtitleItems = subtitleItemMapper.selectList(null);
assertFalse(subtitleItems.isEmpty());
long subtitleCount = subtitleItems.stream().filter(item -> item.getDigitalHumanId().equals(id)).count();
assertEquals(2, subtitleCount);
// 验证特效
List<Effect> effects = effectMapper.selectList(null);
assertFalse(effects.isEmpty());
long effectCount = effects.stream().filter(effect -> effect.getDigitalHumanId().equals(id)).count();
assertEquals(2, effectCount);
}
@Test
void testUpdate() {
// 先添加一个数字人
DigitalHumanReq addReq = createDigitalHumanReq();
Long id = digitalHumanService.add(addReq);
assertNotNull(id);
// 准备更新数据
DigitalHumanReq updateReq = new DigitalHumanReq();
updateReq.setUserId("1");
updateReq.setCopywriting("更新后的文案");
updateReq.setGeneratedVoiceUrl("http://test.com/voice-updated.mp3");
updateReq.setGeneratedFrameImageUrl("http://test.com/frame-updated.jpg");
updateReq.setGeneratedVideoUrl("http://test.com/video-updated.mp4");
// 更新字幕样式
SubtitleStyle updatedStyle = new SubtitleStyle();
updatedStyle.setFontSize(20);
updatedStyle.setColor("#FF0000");
updatedStyle.setStrokeColor("#0000FF");
updatedStyle.setStrokeWidth(3);
updateReq.setSubtitleStyle(updatedStyle);
// 更新字幕项只保留一个
SubtitleItem updatedSubtitle = new SubtitleItem();
updatedSubtitle.setStartTime(0.0);
updatedSubtitle.setEndTime(3.0);
updatedSubtitle.setText("更新后的字幕");
updateReq.setSubtitles(List.of(updatedSubtitle));
// 更新特效只保留一个
Effect updatedEffect = new Effect();
updatedEffect.setType("music");
updatedEffect.setName("更新后的特效");
updatedEffect.setSourceUrl("http://test.com/effect-updated.mp3");
updatedEffect.setLength(4.0);
updateReq.setEffects(List.of(updatedEffect));
// 执行更新
digitalHumanService.update(updateReq, id);
// 验证数字人基本信息更新
DigitalHuman updatedDigitalHuman = digitalHumanMapper.selectById(id);
assertNotNull(updatedDigitalHuman);
assertEquals(updateReq.getCopywriting(), updatedDigitalHuman.getCopywriting());
assertEquals(updateReq.getGeneratedVoiceUrl(), updatedDigitalHuman.getGeneratedVoiceUrl());
// 验证字幕样式更新
List<SubtitleStyle> subtitleStyles = subtitleStyleMapper.selectList(null);
SubtitleStyle savedStyle = subtitleStyles.stream()
.filter(style -> style.getDigitalHumanId().equals(id))
.findFirst()
.orElse(null);
assertNotNull(savedStyle);
assertEquals(updatedStyle.getFontSize(), savedStyle.getFontSize());
assertEquals(updatedStyle.getColor(), savedStyle.getColor());
assertEquals(updatedStyle.getStrokeColor(), savedStyle.getStrokeColor());
// 验证字幕项更新数量应为1
List<SubtitleItem> subtitleItems = subtitleItemMapper.selectList(null);
long subtitleCount = subtitleItems.stream().filter(item -> item.getDigitalHumanId().equals(id)).count();
assertEquals(1, subtitleCount);
// 验证特效更新数量应为1
List<Effect> effects = effectMapper.selectList(null);
long effectCount = effects.stream().filter(effect -> effect.getDigitalHumanId().equals(id)).count();
assertEquals(1, effectCount);
}
@Test
void testDelete() {
// 先添加一个数字人
DigitalHumanReq req = createDigitalHumanReq();
Long id = digitalHumanService.add(req);
assertNotNull(id);
// 验证添加成功
assertNotNull(digitalHumanMapper.selectById(id));
// 执行删除
digitalHumanService.delete(List.of(id));
// 验证数字人已删除
assertNull(digitalHumanMapper.selectById(id));
// 验证关联的字幕样式已删除
List<SubtitleStyle> subtitleStyles = subtitleStyleMapper.selectList(null);
long styleCount = subtitleStyles.stream().filter(style -> style.getDigitalHumanId().equals(id)).count();
assertEquals(0, styleCount);
// 验证关联的字幕项已删除
List<SubtitleItem> subtitleItems = subtitleItemMapper.selectList(null);
long subtitleCount = subtitleItems.stream().filter(item -> item.getDigitalHumanId().equals(id)).count();
assertEquals(0, subtitleCount);
// 验证关联的特效已删除
List<Effect> effects = effectMapper.selectList(null);
long effectCount = effects.stream().filter(effect -> effect.getDigitalHumanId().equals(id)).count();
assertEquals(0, effectCount);
}
}

View File

@ -0,0 +1,189 @@
package top.continew.admin.business.service.impl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import top.continew.admin.business.mapper.DigitalHumanMapper;
import top.continew.admin.business.mapper.EffectMapper;
import top.continew.admin.business.mapper.SubtitleItemMapper;
import top.continew.admin.business.mapper.SubtitleStyleMapper;
import top.continew.admin.business.model.entity.DigitalHuman;
import top.continew.admin.business.model.entity.Effect;
import top.continew.admin.business.model.entity.SubtitleItem;
import top.continew.admin.business.model.entity.SubtitleStyle;
import top.continew.admin.business.model.req.DigitalHumanReq;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
/**
* 数字人服务实现类单元测试
*
* @author 王志维
* @since 2025/12/29
*/
class DigitalHumanServiceImplUnitTest {
@Mock
private DigitalHumanMapper digitalHumanMapper;
@Mock
private SubtitleStyleMapper subtitleStyleMapper;
@Mock
private SubtitleItemMapper subtitleItemMapper;
@Mock
private EffectMapper effectMapper;
@InjectMocks
private DigitalHumanServiceImpl digitalHumanService;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
private DigitalHumanReq createDigitalHumanReq() {
DigitalHumanReq req = new DigitalHumanReq();
req.setUserId("1");
req.setCopywriting("测试文案");
req.setGeneratedVoiceUrl("http://test.com/voice.mp3");
req.setGeneratedFrameImageUrl("http://test.com/frame.jpg");
req.setGeneratedVideoUrl("http://test.com/video.mp4");
// 添加字幕样式
SubtitleStyle subtitleStyle = new SubtitleStyle();
subtitleStyle.setFontSize(16);
subtitleStyle.setColor("#FFFFFF");
subtitleStyle.setStrokeColor("#000000");
subtitleStyle.setStrokeWidth(2);
req.setSubtitleStyle(subtitleStyle);
// 添加字幕项
SubtitleItem subtitleItem1 = new SubtitleItem();
subtitleItem1.setStartTime(0.0);
subtitleItem1.setEndTime(1.0);
subtitleItem1.setText("测试字幕1");
SubtitleItem subtitleItem2 = new SubtitleItem();
subtitleItem2.setStartTime(1.0);
subtitleItem2.setEndTime(2.0);
subtitleItem2.setText("测试字幕2");
req.setSubtitles(List.of(subtitleItem1, subtitleItem2));
// 添加特效
Effect effect1 = new Effect();
effect1.setType("text");
effect1.setName("测试特效1");
effect1.setSourceUrl("http://test.com/effect1.mp4");
effect1.setLength(5.0);
Effect effect2 = new Effect();
effect2.setType("image");
effect2.setName("测试特效2");
effect2.setSourceUrl("http://test.com/effect2.jpg");
effect2.setLength(3.0);
req.setEffects(List.of(effect1, effect2));
return req;
}
@Test
void testAdd() {
// 准备测试数据
DigitalHumanReq req = createDigitalHumanReq();
// 模拟mapper行为设置id
when(digitalHumanMapper.insert(any(DigitalHuman.class))).thenAnswer(invocation -> {
DigitalHuman digitalHuman = invocation.getArgument(0);
digitalHuman.setId(1L); // 手动设置id
return 1;
});
// 执行测试
Long id = digitalHumanService.add(req);
// 验证结果
assertNotNull(id);
// 验证调用次数
verify(digitalHumanMapper, times(1)).insert(any(DigitalHuman.class));
verify(subtitleStyleMapper, times(1)).insert(any(SubtitleStyle.class));
verify(subtitleItemMapper, times(2)).insert(any(SubtitleItem.class));
verify(effectMapper, times(2)).insert(any(Effect.class));
}
@Test
void testUpdate() {
// 准备测试数据
DigitalHumanReq req = createDigitalHumanReq();
Long id = 1L;
// 模拟现有字幕样式
SubtitleStyle existingStyle = new SubtitleStyle();
existingStyle.setId(100L);
existingStyle.setDigitalHumanId(id);
List<SubtitleStyle> existingStyles = new ArrayList<>();
existingStyles.add(existingStyle);
when(subtitleStyleMapper.selectList(any())).thenReturn(existingStyles);
// 执行测试
digitalHumanService.update(req, id);
// 验证调用次数
verify(digitalHumanMapper, times(1)).updateById(any(DigitalHuman.class));
verify(subtitleStyleMapper, times(1)).selectList(any());
verify(subtitleStyleMapper, times(1)).updateById(any(SubtitleStyle.class));
verify(subtitleItemMapper, times(1)).delete(any());
verify(subtitleItemMapper, times(2)).insert(any(SubtitleItem.class));
verify(effectMapper, times(1)).delete(any());
verify(effectMapper, times(2)).insert(any(Effect.class));
}
@Test
void testUpdateWithNewSubtitleStyle() {
// 准备测试数据
DigitalHumanReq req = createDigitalHumanReq();
Long id = 1L;
// 模拟没有现有字幕样式
when(subtitleStyleMapper.selectList(any())).thenReturn(new ArrayList<>());
// 执行测试
digitalHumanService.update(req, id);
// 验证调用次数
verify(digitalHumanMapper, times(1)).updateById(any(DigitalHuman.class));
verify(subtitleStyleMapper, times(1)).selectList(any());
verify(subtitleStyleMapper, times(1)).insert(any(SubtitleStyle.class)); // 应该调用insert而不是update
verify(subtitleItemMapper, times(1)).delete(any());
verify(subtitleItemMapper, times(2)).insert(any(SubtitleItem.class));
verify(effectMapper, times(1)).delete(any());
verify(effectMapper, times(2)).insert(any(Effect.class));
}
@Test
void testDelete() {
// 准备测试数据
Long id = 1L;
List<Long> ids = List.of(id);
// 执行测试
digitalHumanService.delete(ids);
// 验证调用次数
verify(effectMapper, times(1)).delete(any());
verify(subtitleItemMapper, times(1)).delete(any());
verify(subtitleStyleMapper, times(1)).delete(any());
verify(digitalHumanMapper, times(1)).deleteBatchIds(ids);
}
}

View File

@ -113,8 +113,8 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>
<!-- 跳过单元测试 --> <!-- 跳过单元测试,可通过-DskipTests=false覆盖 -->
<skip>true</skip> <skip>${skipTests}</skip>
</configuration> </configuration>
</plugin> </plugin>
<!-- 代码格式化插件 --> <!-- 代码格式化插件 -->