前端优化

This commit is contained in:
2025-11-22 18:30:02 +08:00
parent 307c90f93e
commit fee84ce822
8 changed files with 198 additions and 167 deletions

View File

@@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.tik.userprompt.controller.app;
package cn.iocoder.yudao.module.tik.userprompt.controller;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -23,7 +23,7 @@ import static cn.iocoder.yudao.module.tik.enums.ErrorCodeConstants.USER_PROMPT_N
@Tag(name = "用户 App - 用户提示词")
@RestController
@RequestMapping("/ai/user-prompt")
@RequestMapping("/api/ai/user-prompt")
@Validated
public class AppUserPromptController {

View File

@@ -29,17 +29,16 @@ public class UserPromptServiceImpl implements UserPromptService {
@Override
public Long createUserPrompt(UserPromptSaveReqVO createReqVO) {
// 插入
UserPromptDO userPrompt = BeanUtils.toBean(createReqVO, UserPromptDO.class);
if (userPrompt.getSort() == null) userPrompt.setSort(0);
if (userPrompt.getUseCount() == null) userPrompt.setUseCount(0);
if (userPrompt.getIsPublic() == null) userPrompt.setIsPublic(false);
userPromptMapper.insert(userPrompt);
// 返回
return userPrompt.getId();
}
@Override
public void updateUserPrompt(UserPromptSaveReqVO updateReqVO) {
// 1. 手动验证前端表单字段(与前端表单对应)
if (updateReqVO.getName() == null || updateReqVO.getName().trim().isEmpty()) {
throw new IllegalArgumentException("提示词名称不能为空");
}
@@ -49,42 +48,32 @@ public class UserPromptServiceImpl implements UserPromptService {
if (updateReqVO.getStatus() == null) {
throw new IllegalArgumentException("状态不能为空");
}
// 2. 校验存在并获取现有记录
UserPromptDO existing = validateUserPromptExists(updateReqVO.getId());
// 3. 手动设置要更新的字段(只更新前端表单中的字段)
UserPromptDO updateObj = new UserPromptDO();
updateObj.setId(updateReqVO.getId());
updateObj.setName(updateReqVO.getName().trim());
updateObj.setContent(updateReqVO.getContent().trim());
updateObj.setCategory(updateReqVO.getCategory() != null ? updateReqVO.getCategory().trim() : null);
updateObj.setStatus(updateReqVO.getStatus());
// 4. 自动填充前端表单中没有的字段(从数据库获取)
updateObj.setSort(existing.getSort());
updateObj.setUseCount(existing.getUseCount());
updateObj.setIsPublic(existing.getIsPublic());
updateObj.setUserId(existing.getUserId()); // 保持用户ID不变
// 5. 执行更新
updateObj.setUserId(existing.getUserId());
userPromptMapper.updateById(updateObj);
}
@Override
public void deleteUserPrompt(Long id) {
// 校验存在
validateUserPromptExists(id);
// 删除
userPromptMapper.deleteById(id);
}
@Override
public void deleteUserPromptListByIds(List<Long> ids) {
// 删除
public void deleteUserPromptListByIds(List<Long> ids) {
userPromptMapper.deleteByIds(ids);
}
}
private UserPromptDO validateUserPromptExists(Long id) {
UserPromptDO userPrompt = userPromptMapper.selectById(id);
@@ -103,5 +92,4 @@ public class UserPromptServiceImpl implements UserPromptService {
public PageResult<UserPromptDO> getUserPromptPage(UserPromptPageReqVO pageReqVO) {
return userPromptMapper.selectPage(pageReqVO);
}
}

View File

@@ -27,16 +27,13 @@ public class UserPromptSaveReqVO {
@Schema(description = "分类/标签")
private String category;
@Schema(description = "是否公开0-私有1-公开)", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "是否公开0-私有1-公开)不能为空")
@Schema(description = "是否公开0-私有1-公开)")
private Boolean isPublic;
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "排序不能为空")
@Schema(description = "排序")
private Integer sort;
@Schema(description = "使用次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "22185")
@NotNull(message = "使用次数不能为空")
@Schema(description = "使用次数")
private Integer useCount;
@Schema(description = "状态0-禁用1-启用)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")

View File

@@ -16,7 +16,9 @@ import cn.iocoder.yudao.module.tik.file.dal.dataobject.TikUserFileDO;
import cn.iocoder.yudao.module.tik.file.dal.mysql.TikUserFileMapper;
import cn.iocoder.yudao.module.tik.file.service.TikOssInitService;
import cn.iocoder.yudao.module.tik.voice.dal.dataobject.TikDigitalHumanTaskDO;
import cn.iocoder.yudao.module.tik.voice.dal.dataobject.TikUserVoiceDO;
import cn.iocoder.yudao.module.tik.voice.dal.mysql.TikDigitalHumanTaskMapper;
import cn.iocoder.yudao.module.tik.voice.dal.mysql.TikUserVoiceMapper;
import cn.iocoder.yudao.module.tik.voice.enums.DigitalHumanTaskStatusEnum;
import cn.iocoder.yudao.module.tik.voice.enums.DigitalHumanTaskStepEnum;
import cn.iocoder.yudao.module.tik.voice.vo.*;
@@ -48,6 +50,7 @@ public class DigitalHumanTaskServiceImpl implements DigitalHumanTaskService {
private final TikDigitalHumanTaskMapper taskMapper;
private final TikUserFileMapper userFileMapper;
private final TikUserVoiceMapper userVoiceMapper;
private final FileMapper fileMapper;
private final FileApi fileApi;
private final TikUserVoiceService userVoiceService;
@@ -224,9 +227,21 @@ public class DigitalHumanTaskServiceImpl implements DigitalHumanTaskService {
throw new IllegalArgumentException("文案不能为空");
}
// 验证音色ID必填
if (StrUtil.isBlank(reqVO.getVoiceId())) {
throw new IllegalArgumentException("音色ID不能为空");
// 验证音色参数二选一voiceId用于系统音色voiceConfigId用于用户音色
boolean hasVoiceId = StrUtil.isNotBlank(reqVO.getVoiceId());
boolean hasVoiceConfigId = reqVO.getVoiceConfigId() != null;
if (!hasVoiceId && !hasVoiceConfigId) {
throw new IllegalArgumentException("必须提供音色IDvoiceId或voiceConfigId");
}
if (hasVoiceId && hasVoiceConfigId) {
throw new IllegalArgumentException("voiceId和voiceConfigId不能同时提供");
}
// 如果是用户音色验证voiceConfigId对应的用户音色是否存在
if (hasVoiceConfigId) {
validateUserVoice(reqVO.getVoiceConfigId(), userId);
}
// 验证视频文件(必填)
@@ -253,17 +268,44 @@ public class DigitalHumanTaskServiceImpl implements DigitalHumanTaskService {
}
}
/**
* 验证用户音色
*/
private void validateUserVoice(Long voiceConfigId, Long userId) {
TikUserVoiceDO userVoice = userVoiceMapper.selectById(voiceConfigId);
if (userVoice == null) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.GENERAL_NOT_EXISTS, "用户音色不存在");
}
if (!userVoice.getUserId().equals(userId)) {
throw ServiceExceptionUtil.exception(ErrorCodeConstants.GENERAL_FORBIDDEN, "无权访问该音色");
}
if (StrUtil.isBlank(userVoice.getVoiceId())) {
throw new IllegalArgumentException("该音色配置无效缺少voiceId");
}
}
/**
* 创建任务记录
*/
private TikDigitalHumanTaskDO createTaskRecord(AppTikDigitalHumanCreateReqVO reqVO, Long userId) {
// 如果是用户音色需要从voiceConfigId获取voiceId
String voiceId = reqVO.getVoiceId();
if (voiceId == null && reqVO.getVoiceConfigId() != null) {
TikUserVoiceDO userVoice = userVoiceMapper.selectById(reqVO.getVoiceConfigId());
if (userVoice != null) {
voiceId = userVoice.getVoiceId();
}
}
return TikDigitalHumanTaskDO.builder()
.userId(userId)
.taskName(reqVO.getTaskName())
.aiProvider(StrUtil.blankToDefault(reqVO.getAiProvider(), "302ai"))
.videoFileId(reqVO.getVideoFileId())
.videoUrl(reqVO.getVideoUrl())
.voiceId(reqVO.getVoiceId())
.voiceId(voiceId)
.inputText(reqVO.getInputText())
.speechRate(reqVO.getSpeechRate() != null ? reqVO.getSpeechRate() : 1.0f)
.volume(reqVO.getVolume() != null ? reqVO.getVolume() : 0f)

View File

@@ -33,9 +33,12 @@ public class AppTikDigitalHumanCreateReqVO {
@Size(max = 1024, message = "视频URL不能超过1024个字符")
private String videoUrl;
@Schema(description = "音色IDCosyVoice voiceId", example = "cosyvoice-v3-flash-sys-xxx")
@Schema(description = "音色IDCosyVoice voiceId,系统音色使用", example = "cosyvoice-v3-flash-sys-xxx")
private String voiceId;
@Schema(description = "用户音色配置IDtik_user_voice.id用户音色使用", example = "123")
private Long voiceConfigId;
@Schema(description = "输入文本(用于语音合成,文案必填)", example = "您好,欢迎体验数字人")
@NotBlank(message = "文案不能为空")
@Size(max = 4000, message = "文本不能超过4000个字符")

View File

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.tik.voice.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.eclipse.angus.mail.iap.ByteArray;
/**
* Latentsync 任务结果响应 VO
@@ -21,6 +22,10 @@ public class AppTikLatentsyncResultRespVO {
@Schema(description = "视频信息")
private VideoInfo video;
@Schema(description = "视频流信息")
private ByteArray value;
@Schema(description = "视频信息")
@Data
public static class VideoInfo {