修复subtitleStyle字段更新问题,添加测试用例
This commit is contained in:
parent
4455c2c3ed
commit
de6a766ff9
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ spring.datasource:
|
||||||
## 动态数据源配置(可配多主多从:m1、s1...;纯粹多库:mysql、oracle...;混合配置:m1、s1、oracle...)
|
## 动态数据源配置(可配多主多从:m1、s1...;纯粹多库:mysql、oracle...;混合配置:m1、s1、oracle...)
|
||||||
dynamic:
|
dynamic:
|
||||||
# 是否启用 P6Spy(SQL 性能分析组件,该插件有性能损耗,不建议生产环境使用)
|
# 是否启用 P6Spy(SQL 性能分析组件,该插件有性能损耗,不建议生产环境使用)
|
||||||
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:
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
4
pom.xml
4
pom.xml
|
|
@ -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>
|
||||||
<!-- 代码格式化插件 -->
|
<!-- 代码格式化插件 -->
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue