fix(manager): align frontend and backend field contracts

This commit is contained in:
Tyke Chen
2026-07-24 10:21:05 +08:00
parent 27e57631a7
commit 18e5c47a06
20 changed files with 455 additions and 64 deletions
@@ -34,7 +34,7 @@ public class TimbreDataDTO {
@Schema(description = "排序")
@Min(value = 0, message = "{sort.number}")
private long sort;
private Long sort;
@Schema(description = "对应 TTS 模型主键")
@NotBlank(message = "{timbre.ttsModelId.require}")
@@ -46,4 +46,4 @@ public class TimbreDataDTO {
@Schema(description = "音频播放地址")
private String voiceDemo;
}
}
@@ -3,6 +3,7 @@ package xiaozhi.modules.timbre.entity;
import java.util.Date;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -41,7 +42,8 @@ public class TimbreEntity {
private String referenceText;
@Schema(description = "排序")
private long sort;
@TableField(updateStrategy = FieldStrategy.NOT_NULL)
private Long sort;
@Schema(description = "对应 TTS 模型主键")
private String ttsModelId;
@@ -68,4 +70,4 @@ public class TimbreEntity {
@TableField(fill = FieldFill.INSERT)
private Date createDate;
}
}
@@ -99,6 +99,9 @@ public class TimbreServiceImpl extends BaseServiceImpl<TimbreDao, TimbreEntity>
@Transactional(rollbackFor = Exception.class)
public void save(TimbreDataDTO dto) {
isTtsModelId(dto.getTtsModelId());
if (dto.getSort() == null) {
dto.setSort(0L);
}
TimbreEntity timbreEntity = ConvertUtils.sourceToTarget(dto, TimbreEntity.class);
baseDao.insert(timbreEntity);
}
@@ -32,7 +32,7 @@ public class TimbreDetailsVO implements Serializable {
private String referenceText;
@Schema(description = "排序")
private long sort;
private Long sort;
@Schema(description = "对应 TTS 模型主键")
private String ttsModelId;
@@ -2,16 +2,20 @@ package xiaozhi.modules.timbre.service.impl;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import xiaozhi.common.redis.RedisUtils;
import xiaozhi.modules.timbre.dao.TimbreDao;
import xiaozhi.modules.timbre.dto.TimbreDataDTO;
import xiaozhi.modules.timbre.entity.TimbreEntity;
import xiaozhi.modules.timbre.vo.TimbreDetailsVO;
import xiaozhi.modules.voiceclone.dao.VoiceCloneDao;
import xiaozhi.modules.voiceclone.entity.VoiceCloneEntity;
@@ -54,4 +58,74 @@ class TimbreServiceImplTest {
assertNull(service.getDefaultLanguageById("voice-id"));
}
@Test
void updateLeavesSortOutOfTheUpdateWhenRequestOmitsIt() {
TimbreDao timbreDao = mock(TimbreDao.class);
RedisUtils redisUtils = mock(RedisUtils.class);
TimbreServiceImpl service = new TimbreServiceImpl(timbreDao, mock(VoiceCloneDao.class), redisUtils);
ReflectionTestUtils.setField(service, "baseDao", timbreDao);
TimbreDataDTO dto = validTimbreData();
service.update("voice-id", dto);
verify(timbreDao, never()).selectById("voice-id");
verify(timbreDao).updateById(argThat((TimbreEntity entity) ->
"voice-id".equals(entity.getId()) && entity.getSort() == null));
verify(redisUtils).delete("timbre:details:voice-id");
}
@Test
void updateUsesExplicitSortWithoutLoadingExistingTimbre() {
TimbreDao timbreDao = mock(TimbreDao.class);
TimbreServiceImpl service = new TimbreServiceImpl(
timbreDao, mock(VoiceCloneDao.class), mock(RedisUtils.class));
ReflectionTestUtils.setField(service, "baseDao", timbreDao);
TimbreDataDTO dto = validTimbreData();
dto.setSort(0L);
service.update("voice-id", dto);
verify(timbreDao, never()).selectById("voice-id");
verify(timbreDao).updateById(argThat((TimbreEntity entity) -> entity.getSort() == 0L));
}
@Test
void saveDefaultsOmittedSortToZero() {
TimbreDao timbreDao = mock(TimbreDao.class);
TimbreServiceImpl service = new TimbreServiceImpl(
timbreDao, mock(VoiceCloneDao.class), mock(RedisUtils.class));
ReflectionTestUtils.setField(service, "baseDao", timbreDao);
service.save(validTimbreData());
verify(timbreDao).insert(argThat((TimbreEntity entity) ->
"测试音色".equals(entity.getName()) && entity.getSort() == 0L));
}
@Test
void getSupportsLegacyRowsWithNullSort() {
TimbreDao timbreDao = mock(TimbreDao.class);
RedisUtils redisUtils = mock(RedisUtils.class);
TimbreServiceImpl service = new TimbreServiceImpl(
timbreDao, mock(VoiceCloneDao.class), redisUtils);
ReflectionTestUtils.setField(service, "baseDao", timbreDao);
TimbreEntity entity = new TimbreEntity();
entity.setId("voice-id");
entity.setSort(null);
when(timbreDao.selectById("voice-id")).thenReturn(entity);
TimbreDetailsVO details = service.get("voice-id");
assertNull(details.getSort());
}
private TimbreDataDTO validTimbreData() {
TimbreDataDTO dto = new TimbreDataDTO();
dto.setLanguages("中文");
dto.setName("测试音色");
dto.setTtsModelId("TTS_Test");
dto.setTtsVoice("test-voice");
return dto;
}
}