feat: 优化
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
package cn.iocoder.yudao.module.tik.muye.pointrecord;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.yudao.module.tik.muye.pointrecord.dal.PointRecordDO;
|
||||
import cn.iocoder.yudao.module.tik.muye.pointrecord.service.PointRecordService;
|
||||
import cn.iocoder.yudao.module.tik.muye.pointrecord.vo.PointRecordPageReqVO;
|
||||
import cn.iocoder.yudao.module.tik.muye.pointrecord.vo.PointRecordRespVO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.object.BeanUtils.toBean;
|
||||
|
||||
/**
|
||||
* 用户 App - 积分记录
|
||||
*/
|
||||
@Tag(name = "用户 App - 积分记录")
|
||||
@RestController
|
||||
@RequestMapping("/api/tik/point-record")
|
||||
@Validated
|
||||
public class AppPointRecordController {
|
||||
|
||||
@Resource
|
||||
private PointRecordService pointRecordService;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获取当前用户积分记录分页")
|
||||
public CommonResult<PageResult<PointRecordRespVO>> getPointRecordPage(@Valid PointRecordPageReqVO pageReqVO) {
|
||||
// 强制使用当前登录用户ID,防止查询其他用户数据
|
||||
pageReqVO.setUserId(SecurityFrameworkUtils.getLoginUserId());
|
||||
PageResult<PointRecordDO> pageResult = pointRecordService.getPointRecordPage(pageReqVO);
|
||||
return success(toBean(pageResult, PointRecordRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -52,6 +52,10 @@ public class PointRecordRespVO {
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "状态:pending-预扣 confirmed-已确认 canceled-已取消", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("状态")
|
||||
private String status;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.tik.voice.vo.AppTikLatentsyncResultRespVO;
|
||||
import cn.iocoder.yudao.module.tik.kling.service.KlingService;
|
||||
import cn.iocoder.yudao.module.tik.kling.dto.KlingLipSyncQueryResponse;
|
||||
import cn.iocoder.yudao.module.tik.kling.vo.response.KlingLipSyncVideoVO;
|
||||
import cn.iocoder.yudao.module.tik.muye.points.service.PointsService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
@@ -35,6 +36,7 @@ public class LatentsyncPollingService {
|
||||
private final LatentsyncService latentsyncService;
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
private final KlingService klingService;
|
||||
private final PointsService pointsService;
|
||||
|
||||
// ========== 常量 ==========
|
||||
private static final String REDIS_POLLING_PREFIX = "latentsync:polling:";
|
||||
@@ -218,6 +220,10 @@ public class LatentsyncPollingService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
private void completeTask(Long taskId, String videoUrl, String requestId) {
|
||||
try {
|
||||
// 1. 先查询任务获取预扣记录ID
|
||||
TikDigitalHumanTaskDO task = TenantUtils.executeIgnore(() -> taskMapper.selectById(taskId));
|
||||
|
||||
// 2. 更新任务状态
|
||||
TikDigitalHumanTaskDO updateObj = new TikDigitalHumanTaskDO();
|
||||
updateObj.setId(taskId);
|
||||
updateObj.setStatus("SUCCESS");
|
||||
@@ -228,7 +234,17 @@ public class LatentsyncPollingService {
|
||||
|
||||
TenantUtils.executeIgnore(() -> taskMapper.updateById(updateObj));
|
||||
|
||||
// 缓存结果
|
||||
// 3. 确认预扣(任务成功,实际扣费)
|
||||
if (task != null && task.getPendingRecordId() != null) {
|
||||
try {
|
||||
pointsService.confirmPendingDeduct(task.getPendingRecordId());
|
||||
log.info("[completeTask][任务({})成功,确认扣费,预扣记录ID({})]", taskId, task.getPendingRecordId());
|
||||
} catch (Exception e) {
|
||||
log.error("[completeTask][确认扣费失败,taskId={},recordId={}]", taskId, task.getPendingRecordId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 缓存结果
|
||||
String resultKey = REDIS_RESULT_PREFIX + taskId;
|
||||
stringRedisTemplate.opsForValue().set(resultKey, videoUrl, RESULT_CACHE_TIME);
|
||||
|
||||
@@ -248,6 +264,10 @@ public class LatentsyncPollingService {
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
private void markTaskFailed(Long taskId, String errorMessage) {
|
||||
try {
|
||||
// 1. 先查询任务获取预扣记录ID
|
||||
TikDigitalHumanTaskDO task = TenantUtils.executeIgnore(() -> taskMapper.selectById(taskId));
|
||||
|
||||
// 2. 更新任务状态
|
||||
TikDigitalHumanTaskDO updateObj = new TikDigitalHumanTaskDO();
|
||||
updateObj.setId(taskId);
|
||||
updateObj.setStatus("FAILED");
|
||||
@@ -256,6 +276,16 @@ public class LatentsyncPollingService {
|
||||
|
||||
TenantUtils.executeIgnore(() -> taskMapper.updateById(updateObj));
|
||||
|
||||
// 3. 取消预扣(任务失败,不扣费)
|
||||
if (task != null && task.getPendingRecordId() != null) {
|
||||
try {
|
||||
pointsService.cancelPendingDeduct(task.getPendingRecordId());
|
||||
log.info("[markTaskFailed][任务({})失败,取消预扣,预扣记录ID({})]", taskId, task.getPendingRecordId());
|
||||
} catch (Exception e) {
|
||||
log.error("[markTaskFailed][取消预扣失败,taskId={},recordId={}]", taskId, task.getPendingRecordId(), e);
|
||||
}
|
||||
}
|
||||
|
||||
log.warn("[markTaskFailed][任务失败][taskId={}, error={}]", taskId, errorMessage);
|
||||
} catch (Exception e) {
|
||||
log.error("[markTaskFailed][标记任务失败失败][taskId={}]", taskId, e);
|
||||
@@ -266,6 +296,10 @@ public class LatentsyncPollingService {
|
||||
* 更新任务状态
|
||||
*/
|
||||
private void updateTaskStatus(Long taskId, String status, String currentStep, Integer progress, String errorMessage) {
|
||||
// 1. 先查询任务获取预扣记录ID
|
||||
TikDigitalHumanTaskDO task = TenantUtils.executeIgnore(() -> taskMapper.selectById(taskId));
|
||||
|
||||
// 2. 更新任务状态
|
||||
TikDigitalHumanTaskDO updateObj = new TikDigitalHumanTaskDO();
|
||||
updateObj.setId(taskId);
|
||||
updateObj.setStatus(status);
|
||||
@@ -274,11 +308,29 @@ public class LatentsyncPollingService {
|
||||
|
||||
if ("SUCCESS".equals(status)) {
|
||||
updateObj.setFinishTime(LocalDateTime.now());
|
||||
// 确认预扣(任务成功)
|
||||
if (task != null && task.getPendingRecordId() != null) {
|
||||
try {
|
||||
pointsService.confirmPendingDeduct(task.getPendingRecordId());
|
||||
log.info("[updateTaskStatus][任务({})成功,确认扣费]", taskId);
|
||||
} catch (Exception e) {
|
||||
log.error("[updateTaskStatus][确认扣费失败,taskId={}]", taskId, e);
|
||||
}
|
||||
}
|
||||
} else if ("PROCESSING".equals(status)) {
|
||||
updateObj.setStartTime(LocalDateTime.now());
|
||||
} else if ("FAILED".equals(status)) {
|
||||
updateObj.setErrorMessage(errorMessage);
|
||||
updateObj.setFinishTime(LocalDateTime.now());
|
||||
// 取消预扣(任务失败)
|
||||
if (task != null && task.getPendingRecordId() != null) {
|
||||
try {
|
||||
pointsService.cancelPendingDeduct(task.getPendingRecordId());
|
||||
log.info("[updateTaskStatus][任务({})失败,取消预扣]", taskId);
|
||||
} catch (Exception e) {
|
||||
log.error("[updateTaskStatus][取消预扣失败,taskId={}]", taskId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TenantUtils.executeIgnore(() -> taskMapper.updateById(updateObj));
|
||||
|
||||
@@ -391,10 +391,11 @@ public class TikUserVoiceServiceImpl implements TikUserVoiceService {
|
||||
transcriptionText = reqVO.getTranscriptionText();
|
||||
}
|
||||
|
||||
String finalText = determineSynthesisText(
|
||||
transcriptionText,
|
||||
reqVO.getInputText(),
|
||||
false);
|
||||
// transcriptionText 仅用于提高克隆质量,不拼接到合成文本
|
||||
String finalText = reqVO.getInputText();
|
||||
if (StrUtil.isBlank(finalText)) {
|
||||
throw exception(VOICE_TTS_FAILED, "请提供需要合成的文本内容");
|
||||
}
|
||||
|
||||
String cacheKey = buildCacheKey(SYNTH_CACHE_PREFIX,
|
||||
voiceId,
|
||||
@@ -444,7 +445,7 @@ public class TikUserVoiceServiceImpl implements TikUserVoiceService {
|
||||
log.error("[synthesizeVoice][积分扣减失败: {}]", e.getMessage());
|
||||
}
|
||||
|
||||
// 【安全方案】不暴露OSS链接,直接返回Base64编码的音频数据
|
||||
// 不暴露OSS链接,直接返回Base64编码的音频数据
|
||||
String audioBase64 = Base64.getEncoder().encodeToString(ttsResult.getAudio());
|
||||
log.info("[synthesizeVoice][合成成功,配音编号({}),voiceId({}),format({}),audioSize={}]",
|
||||
voiceConfigId, finalVoiceId, format, ttsResult.getAudio().length);
|
||||
@@ -617,9 +618,6 @@ public class TikUserVoiceServiceImpl implements TikUserVoiceService {
|
||||
|
||||
/**
|
||||
* 从URL中提取原始URL(去除查询参数和锚点)
|
||||
*
|
||||
* @param url 可能包含查询参数的URL
|
||||
* @return 原始URL(去除查询参数和锚点)
|
||||
*/
|
||||
private String extractRawUrl(String url) {
|
||||
if (StrUtil.isBlank(url)) {
|
||||
@@ -627,10 +625,8 @@ public class TikUserVoiceServiceImpl implements TikUserVoiceService {
|
||||
}
|
||||
try {
|
||||
java.net.URL urlObj = new java.net.URL(url);
|
||||
// 只使用协议、主机、路径部分,忽略查询参数和锚点
|
||||
return urlObj.getProtocol() + "://" + urlObj.getHost() + urlObj.getPath();
|
||||
} catch (Exception e) {
|
||||
// 如果URL解析失败,使用简单方式去除查询参数
|
||||
return url.split("\\?")[0].split("#")[0];
|
||||
}
|
||||
}
|
||||
@@ -644,18 +640,15 @@ public class TikUserVoiceServiceImpl implements TikUserVoiceService {
|
||||
String instruction,
|
||||
String audioFormat,
|
||||
Integer sampleRate) {
|
||||
// 构建标识符:优先使用voiceId,如果没有则使用fileUrl的稳定部分(去除查询参数)
|
||||
String identifier;
|
||||
if (StrUtil.isNotBlank(voiceId)) {
|
||||
identifier = voiceId;
|
||||
} else if (StrUtil.isNotBlank(fileUrl)) {
|
||||
// 对于fileUrl,提取稳定部分(去除预签名URL的查询参数,避免缓存key不稳定)
|
||||
identifier = extractRawUrl(fileUrl);
|
||||
} else {
|
||||
identifier = "no-voice";
|
||||
}
|
||||
|
||||
// 获取默认配置
|
||||
String defaultFormat = getDefaultFormat();
|
||||
Integer defaultSampleRate = getDefaultSampleRate();
|
||||
|
||||
@@ -667,8 +660,7 @@ public class TikUserVoiceServiceImpl implements TikUserVoiceService {
|
||||
instruction,
|
||||
StrUtil.blankToDefault(audioFormat, defaultFormat),
|
||||
sampleRate != null ? sampleRate : defaultSampleRate);
|
||||
String hash = cn.hutool.crypto.SecureUtil.sha256(payload);
|
||||
return prefix + hash;
|
||||
return prefix + cn.hutool.crypto.SecureUtil.sha256(payload);
|
||||
}
|
||||
|
||||
private PreviewCacheEntry getPreviewCache(String key) {
|
||||
|
||||
Reference in New Issue
Block a user