修复
This commit is contained in:
@@ -70,20 +70,19 @@ async function handleAnalyzeUser() {
|
|||||||
hasMore.value = false
|
hasMore.value = false
|
||||||
await clearData()
|
await clearData()
|
||||||
|
|
||||||
const req = await TikhubService.postTikHup({
|
|
||||||
type: InterfaceType.DOUYIN_WEB_USER_POST_VIDEOS,
|
|
||||||
methodType: MethodType.GET,
|
|
||||||
urlParams: {
|
|
||||||
sec_user_id,
|
|
||||||
max_cursor: 0,
|
|
||||||
type: 'tik-app',
|
|
||||||
sort_type: form.value.sort_type,
|
|
||||||
count: form.value.count || 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await req
|
const resp = await TikhubService.postTikHup({
|
||||||
|
type: InterfaceType.DOUYIN_WEB_USER_POST_VIDEOS,
|
||||||
|
methodType: MethodType.GET,
|
||||||
|
urlParams: {
|
||||||
|
sec_user_id,
|
||||||
|
max_cursor: 0,
|
||||||
|
type: 'tik-app',
|
||||||
|
sort_type: form.value.sort_type,
|
||||||
|
count: form.value.count || 20,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const result = processApiResponse(resp, form.value.platform)
|
const result = processApiResponse(resp, form.value.platform)
|
||||||
maxCursor.value = result.maxCursor
|
maxCursor.value = result.maxCursor
|
||||||
hasMore.value = result.hasMore
|
hasMore.value = result.hasMore
|
||||||
@@ -190,20 +189,19 @@ async function handleLoadMore() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadingMore.value = true
|
loadingMore.value = true
|
||||||
const req = await TikhubService.postTikHup({
|
|
||||||
type: InterfaceType.DOUYIN_WEB_USER_POST_VIDEOS,
|
|
||||||
methodType: MethodType.GET,
|
|
||||||
urlParams: {
|
|
||||||
sec_user_id,
|
|
||||||
max_cursor: maxCursor.value,
|
|
||||||
type: 'tik-app',
|
|
||||||
sort_type: form.value.sort_type,
|
|
||||||
count: form.value.count || 20,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await req
|
const resp = await TikhubService.postTikHup({
|
||||||
|
type: InterfaceType.DOUYIN_WEB_USER_POST_VIDEOS,
|
||||||
|
methodType: MethodType.GET,
|
||||||
|
urlParams: {
|
||||||
|
sec_user_id,
|
||||||
|
max_cursor: maxCursor.value,
|
||||||
|
type: 'tik-app',
|
||||||
|
sort_type: form.value.sort_type,
|
||||||
|
count: form.value.count || 20,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
if (form.value.platform === '抖音') {
|
if (form.value.platform === '抖音') {
|
||||||
const { mapFromDouyin } = await import('./utils/benchmarkUtils')
|
const { mapFromDouyin } = await import('./utils/benchmarkUtils')
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class DifyServiceImpl implements DifyService {
|
|||||||
: AiModelTypeEnum.DIFY_WRITING_PRO;
|
: AiModelTypeEnum.DIFY_WRITING_PRO;
|
||||||
AiModelConfigDO config = pointsService.getConfig(
|
AiModelConfigDO config = pointsService.getConfig(
|
||||||
AiPlatformEnum.DIFY.getPlatform(),
|
AiPlatformEnum.DIFY.getPlatform(),
|
||||||
modelTypeEnum.getModelType());
|
modelTypeEnum.getModelCode());
|
||||||
|
|
||||||
// 3. 预检积分
|
// 3. 预检积分
|
||||||
pointsService.checkPoints(userId, config.getConsumePoints());
|
pointsService.checkPoints(userId, config.getConsumePoints());
|
||||||
|
|||||||
@@ -17,27 +17,27 @@ import java.util.Arrays;
|
|||||||
public enum AiModelTypeEnum implements ArrayValuable<String> {
|
public enum AiModelTypeEnum implements ArrayValuable<String> {
|
||||||
|
|
||||||
// ========== Dify 写作模型 ==========
|
// ========== Dify 写作模型 ==========
|
||||||
DIFY_WRITING_PRO("writing_pro", "Pro深度版", AiPlatformEnum.DIFY),
|
DIFY_WRITING_PRO("writing_pro", "Pro深度版", AiPlatformEnum.DIFY, "text"),
|
||||||
DIFY_WRITING_STANDARD("writing_standard", "标准版", AiPlatformEnum.DIFY),
|
DIFY_WRITING_STANDARD("writing_standard", "标准版", AiPlatformEnum.DIFY, "text"),
|
||||||
|
|
||||||
// ========== 数字人模型 ==========
|
// ========== 数字人模型 ==========
|
||||||
DIGITAL_HUMAN_LATENTSYNC("latentsync", "LatentSync", AiPlatformEnum.DIGITAL_HUMAN),
|
DIGITAL_HUMAN_LATENTSYNC("latentsync", "LatentSync", AiPlatformEnum.DIGITAL_HUMAN, "video"),
|
||||||
DIGITAL_HUMAN_KLING("kling", "可灵", AiPlatformEnum.DIGITAL_HUMAN),
|
DIGITAL_HUMAN_KLING("kling", "可灵", AiPlatformEnum.DIGITAL_HUMAN, "video"),
|
||||||
|
|
||||||
// ========== TikHub 爬虫 ==========
|
// ========== TikHub 爬虫 ==========
|
||||||
TIKHUB_CRAWLER("crawler", "爬虫", AiPlatformEnum.TIKHUB),
|
TIKHUB_CRAWLER("crawler", "爬虫", AiPlatformEnum.TIKHUB, "third"),
|
||||||
|
|
||||||
// ========== 阿里云语音服务 ==========
|
// ========== 阿里云语音服务 ==========
|
||||||
ALICLOUD_VOICE_TO_TEXT("voice_to_text", "语音转文字", AiPlatformEnum.ALICLOUD),
|
ALICLOUD_VOICE_TO_TEXT("voice_to_text", "语音转文字", AiPlatformEnum.ALICLOUD, "audio"),
|
||||||
|
|
||||||
// ========== SiliconFlow 语音服务 ==========
|
// ========== SiliconFlow 语音服务 ==========
|
||||||
SILICONFLOW_INDEXTTS("indextts", "IndexTTS", AiPlatformEnum.SILICONFLOW),
|
SILICONFLOW_INDEXTTS("indextts", "IndexTTS", AiPlatformEnum.SILICONFLOW, "audio"),
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模型类型标识
|
* 模型标识(业务类型)
|
||||||
*/
|
*/
|
||||||
private final String modelType;
|
private final String modelCode;
|
||||||
/**
|
/**
|
||||||
* 模型类型名称
|
* 模型类型名称
|
||||||
*/
|
*/
|
||||||
@@ -46,8 +46,12 @@ public enum AiModelTypeEnum implements ArrayValuable<String> {
|
|||||||
* 所属平台
|
* 所属平台
|
||||||
*/
|
*/
|
||||||
private final AiPlatformEnum platform;
|
private final AiPlatformEnum platform;
|
||||||
|
/**
|
||||||
|
* 模型类型(媒体分类)
|
||||||
|
*/
|
||||||
|
private final String modelType;
|
||||||
|
|
||||||
public static final String[] ARRAYS = Arrays.stream(values()).map(AiModelTypeEnum::getModelType).toArray(String[]::new);
|
public static final String[] ARRAYS = Arrays.stream(values()).map(AiModelTypeEnum::getModelCode).toArray(String[]::new);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] array() {
|
public String[] array() {
|
||||||
@@ -55,11 +59,11 @@ public enum AiModelTypeEnum implements ArrayValuable<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据模型类型标识获取枚举
|
* 根据模型标识获取枚举
|
||||||
*/
|
*/
|
||||||
public static AiModelTypeEnum valueOfModelType(String modelType) {
|
public static AiModelTypeEnum valueOfModelCode(String modelCode) {
|
||||||
return Arrays.stream(values())
|
return Arrays.stream(values())
|
||||||
.filter(e -> e.getModelType().equals(modelType))
|
.filter(e -> e.getModelCode().equals(modelCode))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,12 +40,12 @@ public interface AiModelConfigMapper extends BaseMapperX<AiModelConfigDO> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据平台和模型类型查询配置
|
* 根据平台和模型标识查询配置
|
||||||
*/
|
*/
|
||||||
default AiModelConfigDO selectByPlatformAndModelType(String platform, String modelType) {
|
default AiModelConfigDO selectByPlatformAndModelCode(String platform, String modelCode) {
|
||||||
return selectOne(new LambdaQueryWrapperX<AiModelConfigDO>()
|
return selectOne(new LambdaQueryWrapperX<AiModelConfigDO>()
|
||||||
.eq(AiModelConfigDO::getPlatform, platform)
|
.eq(AiModelConfigDO::getPlatform, platform)
|
||||||
.eq(AiModelConfigDO::getModelType, modelType)
|
.eq(AiModelConfigDO::getModelCode, modelCode)
|
||||||
.eq(AiModelConfigDO::getStatus, 1));
|
.eq(AiModelConfigDO::getStatus, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,16 +55,13 @@ public class AiModelConfigSaveReqVO {
|
|||||||
@Schema(description = "最大文本数量")
|
@Schema(description = "最大文本数量")
|
||||||
private Integer maxTextLength;
|
private Integer maxTextLength;
|
||||||
|
|
||||||
@Schema(description = "图片最大像素", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "图片最大像素")
|
||||||
@NotEmpty(message = "图片最大像素不能为空")
|
|
||||||
private String maxImageSize;
|
private String maxImageSize;
|
||||||
|
|
||||||
@Schema(description = "视频最大时长(秒)", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "视频最大时长(秒)")
|
||||||
@NotNull(message = "视频最大时长(秒)不能为空")
|
|
||||||
private Integer maxVideoDuration;
|
private Integer maxVideoDuration;
|
||||||
|
|
||||||
@Schema(description = "视频最大质量", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "视频最大质量")
|
||||||
@NotEmpty(message = "视频最大质量不能为空")
|
|
||||||
private String maxVideoQuality;
|
private String maxVideoQuality;
|
||||||
|
|
||||||
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
|
@Schema(description = "备注", requiredMode = Schema.RequiredMode.REQUIRED, example = "随便")
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ public interface PointsService {
|
|||||||
* 获取积分配置
|
* 获取积分配置
|
||||||
*
|
*
|
||||||
* @param platform 平台标识(dify/tikhub/voice/digital_human)
|
* @param platform 平台标识(dify/tikhub/voice/digital_human)
|
||||||
* @param modelType 模型类型
|
* @param modelCode 模型标识
|
||||||
* @return 积分配置
|
* @return 积分配置
|
||||||
*/
|
*/
|
||||||
AiModelConfigDO getConfig(String platform, String modelType);
|
AiModelConfigDO getConfig(String platform, String modelCode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 预检积分(余额不足抛异常)
|
* 预检积分(余额不足抛异常)
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ public class PointsServiceImpl implements PointsService {
|
|||||||
private PointRecordMapper pointRecordMapper;
|
private PointRecordMapper pointRecordMapper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AiModelConfigDO getConfig(String platform, String modelType) {
|
public AiModelConfigDO getConfig(String platform, String modelCode) {
|
||||||
AiModelConfigDO config = aiModelConfigMapper.selectByPlatformAndModelType(platform, modelType);
|
AiModelConfigDO config = aiModelConfigMapper.selectByPlatformAndModelCode(platform, modelCode);
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
throw exception(POINTS_CONFIG_NOT_FOUND);
|
throw exception(POINTS_CONFIG_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,13 @@ import io.reactivex.Flowable;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
@@ -43,7 +48,11 @@ public class TikHupServiceImpl implements TikHupService {
|
|||||||
|
|
||||||
// 平台和模型标识
|
// 平台和模型标识
|
||||||
private static final String PLATFORM_TIKHUB = "tikhub";
|
private static final String PLATFORM_TIKHUB = "tikhub";
|
||||||
private static final String MODEL_TYPE_CRAWLER = "crawler";
|
private static final String MODEL_CODE_CRAWLER = "crawler";
|
||||||
|
|
||||||
|
// 缓存配置
|
||||||
|
private static final String CACHE_KEY_PREFIX = "tikhub:cache:";
|
||||||
|
private static final long CACHE_EXPIRE_HOURS = 24;
|
||||||
|
|
||||||
// API 配置(建议后续迁移到配置文件)
|
// API 配置(建议后续迁移到配置文件)
|
||||||
private static final String ALIYUN_API_URL = "https://dashscope.aliyuncs.com/api/v1";
|
private static final String ALIYUN_API_URL = "https://dashscope.aliyuncs.com/api/v1";
|
||||||
@@ -63,6 +72,7 @@ public class TikHupServiceImpl implements TikHupService {
|
|||||||
private final TikTokenMapper tikTokenMapper;
|
private final TikTokenMapper tikTokenMapper;
|
||||||
private final TikPromptMapper tikPromptMapper;
|
private final TikPromptMapper tikPromptMapper;
|
||||||
private final PointsService pointsService;
|
private final PointsService pointsService;
|
||||||
|
private final StringRedisTemplate stringRedisTemplate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object postTikHup(String type, String methodType, String urlParams, String paramType) {
|
public Object postTikHup(String type, String methodType, String urlParams, String paramType) {
|
||||||
@@ -81,25 +91,39 @@ public class TikHupServiceImpl implements TikHupService {
|
|||||||
String userId = loginUserId != null ? loginUserId.toString() : "1";
|
String userId = loginUserId != null ? loginUserId.toString() : "1";
|
||||||
AiModelConfigDO config;
|
AiModelConfigDO config;
|
||||||
try {
|
try {
|
||||||
config = pointsService.getConfig(PLATFORM_TIKHUB, MODEL_TYPE_CRAWLER);
|
config = pointsService.getConfig(PLATFORM_TIKHUB, MODEL_CODE_CRAWLER);
|
||||||
pointsService.checkPoints(userId, config.getConsumePoints());
|
pointsService.checkPoints(userId, config.getConsumePoints());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("[postTikHup] 积分预检失败: {}", e.getMessage());
|
log.error("[postTikHup] 积分预检失败: {}", e.getMessage());
|
||||||
return CommonResult.error(400, e.getMessage());
|
return CommonResult.error(400, e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 从 muye_ai_model_config 获取 apiKey
|
// 3. 尝试从缓存获取结果(缓存命中也要扣积分)
|
||||||
|
String cacheKey = buildCacheKey(type, methodType, urlParams, paramType);
|
||||||
|
String cachedResult = stringRedisTemplate.opsForValue().get(cacheKey);
|
||||||
|
if (StringUtils.isNotBlank(cachedResult)) {
|
||||||
|
log.info("[postTikHup] 命中缓存, cacheKey: {}", cacheKey);
|
||||||
|
// 缓存命中也扣积分
|
||||||
|
deductPointsSafely(userId, config.getConsumePoints(), type);
|
||||||
|
try {
|
||||||
|
return JSON.parseObject(cachedResult);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return cachedResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 从 muye_ai_model_config 获取 apiKey
|
||||||
String authorization = config.getApiKey();
|
String authorization = config.getApiKey();
|
||||||
if (StringUtils.isBlank(authorization)) {
|
if (StringUtils.isBlank(authorization)) {
|
||||||
log.error("postTikHup: TikHub 配置的 apiKey 为空");
|
log.error("postTikHup: TikHub 配置的 apiKey 为空");
|
||||||
return CommonResult.error(500, "接口配置错误:apiKey 为空");
|
return CommonResult.error(500, "接口配置错误:apiKey 为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 获取接口 URL
|
// 5. 获取接口 URL(从 tik_token 表)
|
||||||
TikTokenVO tikTokenVO = tikTokenMapper.getInterfaceUrl(type);
|
TikTokenVO tikTokenVO = tikTokenMapper.getInterfaceUrl(type);
|
||||||
if (tikTokenVO == null) {
|
if (tikTokenVO == null) {
|
||||||
log.error("postTikHup: 未找到接口类型 {} 的 URL 配置", type);
|
log.error("postTikHup: 未找到接口类型 {} 的 URL 配置", type);
|
||||||
return CommonResult.error(404, "未找到接口类型 " + type + " 的 URL 配置");
|
return CommonResult.error(404, "未找到接口类型 " + type + " URL 配置");
|
||||||
}
|
}
|
||||||
String url = tikTokenVO.getPlatformUrl();
|
String url = tikTokenVO.getPlatformUrl();
|
||||||
if (StringUtils.isBlank(url)) {
|
if (StringUtils.isBlank(url)) {
|
||||||
@@ -107,7 +131,7 @@ public class TikHupServiceImpl implements TikHupService {
|
|||||||
return CommonResult.error(500, "接口配置错误:URL 为空");
|
return CommonResult.error(500, "接口配置错误:URL 为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 统一转换为小写进行比较
|
// 6. 统一转换为小写进行比较
|
||||||
String methodTypeLower = methodType.toLowerCase();
|
String methodTypeLower = methodType.toLowerCase();
|
||||||
String paramTypeLower = paramType != null ? paramType.toLowerCase() : "";
|
String paramTypeLower = paramType != null ? paramType.toLowerCase() : "";
|
||||||
|
|
||||||
@@ -115,8 +139,8 @@ public class TikHupServiceImpl implements TikHupService {
|
|||||||
Unirest.setTimeouts(0, 0);
|
Unirest.setTimeouts(0, 0);
|
||||||
HttpResponse<String> response = executeRequest(url, urlParams, authorization, methodTypeLower, paramTypeLower);
|
HttpResponse<String> response = executeRequest(url, urlParams, authorization, methodTypeLower, paramTypeLower);
|
||||||
|
|
||||||
// 6. 处理响应
|
// 7. 处理响应并缓存
|
||||||
return handleResponse(response, url, userId, config.getConsumePoints(), type);
|
return handleResponse(response, url, userId, config.getConsumePoints(), type, cacheKey);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("postTikHup: 接口调用异常, URL: {}, 错误信息: {}", url, e.getMessage(), e);
|
log.error("postTikHup: 接口调用异常, URL: {}, 错误信息: {}", url, e.getMessage(), e);
|
||||||
return CommonResult.error(500, "接口调用异常: " + e.getMessage());
|
return CommonResult.error(500, "接口调用异常: " + e.getMessage());
|
||||||
@@ -154,7 +178,7 @@ public class TikHupServiceImpl implements TikHupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Object handleResponse(HttpResponse<String> response, String url, String userId,
|
private Object handleResponse(HttpResponse<String> response, String url, String userId,
|
||||||
Integer consumePoints, String type) {
|
Integer consumePoints, String type, String cacheKey) {
|
||||||
int statusCode = response.getStatus();
|
int statusCode = response.getStatus();
|
||||||
String responseBody = response.getBody();
|
String responseBody = response.getBody();
|
||||||
|
|
||||||
@@ -172,6 +196,10 @@ public class TikHupServiceImpl implements TikHupService {
|
|||||||
// 扣减积分
|
// 扣减积分
|
||||||
deductPointsSafely(userId, consumePoints, type);
|
deductPointsSafely(userId, consumePoints, type);
|
||||||
|
|
||||||
|
// 缓存成功响应(24小时)
|
||||||
|
stringRedisTemplate.opsForValue().set(cacheKey, responseBody, CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
|
||||||
|
log.info("[postTikHup] 结果已缓存, cacheKey: {}, 有效期: {}小时", cacheKey, CACHE_EXPIRE_HOURS);
|
||||||
|
|
||||||
// 尝试解析 JSON,失败则返回原始字符串
|
// 尝试解析 JSON,失败则返回原始字符串
|
||||||
try {
|
try {
|
||||||
return JSON.parseObject(responseBody);
|
return JSON.parseObject(responseBody);
|
||||||
@@ -189,6 +217,34 @@ public class TikHupServiceImpl implements TikHupService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建缓存 Key
|
||||||
|
* 格式: tikhub:cache:{type}:{methodType}:{paramType}:{paramsHash}
|
||||||
|
*/
|
||||||
|
private String buildCacheKey(String type, String methodType, String urlParams, String paramType) {
|
||||||
|
String rawKey = type + ":" + methodType + ":" + (paramType != null ? paramType : "") + ":" + (urlParams != null ? urlParams : "");
|
||||||
|
String hash = md5(rawKey);
|
||||||
|
return CACHE_KEY_PREFIX + type + ":" + hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MD5 哈希
|
||||||
|
*/
|
||||||
|
private String md5(String input) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (byte b : digest) {
|
||||||
|
sb.append(String.format("%02x", b));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 降级为直接使用原始字符串的 hashCode
|
||||||
|
return String.valueOf(input.hashCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object videoToCharacters(String fileLink) {
|
public Object videoToCharacters(String fileLink) {
|
||||||
log.info("[videoToCharacters] 开始识别,文件链接: {}", fileLink);
|
log.info("[videoToCharacters] 开始识别,文件链接: {}", fileLink);
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
package cn.iocoder.yudao.module.tik.tikhup.vo;
|
package cn.iocoder.yudao.module.tik.tikhup.vo;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
|
||||||
import com.baomidou.mybatisplus.annotation.TableField;
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
@TableName("tik_token")
|
@TableName("tik_token")
|
||||||
@Data
|
@Data
|
||||||
public class TikTokenVO {
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class TikTokenVO extends TenantBaseDO {
|
||||||
|
|
||||||
|
@TableField(value = "ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
@TableField(value = "platform_url")
|
@TableField(value = "platform_url")
|
||||||
private String platformUrl;
|
private String platformUrl;
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ public class DigitalHumanTaskServiceImpl implements DigitalHumanTaskService {
|
|||||||
|
|
||||||
/** 积分平台和类型常量 */
|
/** 积分平台和类型常量 */
|
||||||
private static final String PLATFORM_DIGITAL_HUMAN = "digital_human";
|
private static final String PLATFORM_DIGITAL_HUMAN = "digital_human";
|
||||||
private static final String MODEL_TYPE_LATENTSYNC = "latentsync";
|
private static final String MODEL_CODE_LATENTSYNC = "latentsync";
|
||||||
private static final String MODEL_TYPE_KLING = "kling";
|
private static final String MODEL_CODE_KLING = "kling";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
@@ -97,8 +97,8 @@ public class DigitalHumanTaskServiceImpl implements DigitalHumanTaskService {
|
|||||||
|
|
||||||
// 2. 积分预检和预扣
|
// 2. 积分预检和预扣
|
||||||
String aiProvider = StrUtil.blankToDefault(reqVO.getAiProvider(), "302ai");
|
String aiProvider = StrUtil.blankToDefault(reqVO.getAiProvider(), "302ai");
|
||||||
String modelType = "kling".equalsIgnoreCase(aiProvider) ? MODEL_TYPE_KLING : MODEL_TYPE_LATENTSYNC;
|
String modelCode = "kling".equalsIgnoreCase(aiProvider) ? MODEL_CODE_KLING : MODEL_CODE_LATENTSYNC;
|
||||||
AiModelConfigDO config = pointsService.getConfig(PLATFORM_DIGITAL_HUMAN, modelType);
|
AiModelConfigDO config = pointsService.getConfig(PLATFORM_DIGITAL_HUMAN, modelCode);
|
||||||
pointsService.checkPoints(userId.toString(), config.getConsumePoints());
|
pointsService.checkPoints(userId.toString(), config.getConsumePoints());
|
||||||
Long pendingRecordId = pointsService.createPendingDeduct(
|
Long pendingRecordId = pointsService.createPendingDeduct(
|
||||||
userId.toString(),
|
userId.toString(),
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ public class TikUserVoiceServiceImpl implements TikUserVoiceService {
|
|||||||
|
|
||||||
/** 积分平台和类型常量 */
|
/** 积分平台和类型常量 */
|
||||||
private static final String PLATFORM_VOICE = "voice";
|
private static final String PLATFORM_VOICE = "voice";
|
||||||
private static final String MODEL_TYPE_TTS = "tts";
|
private static final String MODEL_CODE_TTS = "tts";
|
||||||
private static final String MODEL_TYPE_CLONE = "clone";
|
private static final String MODEL_CODE_CLONE = "clone";
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private PointsService pointsService;
|
private PointsService pointsService;
|
||||||
@@ -162,7 +162,7 @@ public class TikUserVoiceServiceImpl implements TikUserVoiceService {
|
|||||||
if (StrUtil.isNotBlank(createReqVO.getText())) {
|
if (StrUtil.isNotBlank(createReqVO.getText())) {
|
||||||
try {
|
try {
|
||||||
// 4.1 获取积分配置并预检
|
// 4.1 获取积分配置并预检
|
||||||
AiModelConfigDO config = pointsService.getConfig(PLATFORM_VOICE, MODEL_TYPE_CLONE);
|
AiModelConfigDO config = pointsService.getConfig(PLATFORM_VOICE, MODEL_CODE_CLONE);
|
||||||
pointsService.checkPoints(userId.toString(), config.getConsumePoints());
|
pointsService.checkPoints(userId.toString(), config.getConsumePoints());
|
||||||
|
|
||||||
log.info("[createVoice][开始语音复刻,配音编号({}),文件ID({}),供应商({})]",
|
log.info("[createVoice][开始语音复刻,配音编号({}),文件ID({}),供应商({})]",
|
||||||
@@ -472,7 +472,7 @@ public class TikUserVoiceServiceImpl implements TikUserVoiceService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取积分配置并预检(缓存未命中,需要实际调用 TTS)
|
// 获取积分配置并预检(缓存未命中,需要实际调用 TTS)
|
||||||
AiModelConfigDO ttsConfig = pointsService.getConfig(PLATFORM_VOICE, MODEL_TYPE_TTS);
|
AiModelConfigDO ttsConfig = pointsService.getConfig(PLATFORM_VOICE, MODEL_CODE_TTS);
|
||||||
pointsService.checkPoints(userId.toString(), ttsConfig.getConsumePoints());
|
pointsService.checkPoints(userId.toString(), ttsConfig.getConsumePoints());
|
||||||
|
|
||||||
// 使用 Provider 接口进行 TTS 合成(支持前端选择供应商,不传则使用默认)
|
// 使用 Provider 接口进行 TTS 合成(支持前端选择供应商,不传则使用默认)
|
||||||
|
|||||||
@@ -103,17 +103,12 @@ const formRules = reactive({
|
|||||||
modelName: [{ required: true, message: '模型名称不能为空', trigger: 'blur' }],
|
modelName: [{ required: true, message: '模型名称不能为空', trigger: 'blur' }],
|
||||||
modelCode: [{ required: true, message: '模型标识/编码不能为空', trigger: 'blur' }],
|
modelCode: [{ required: true, message: '模型标识/编码不能为空', trigger: 'blur' }],
|
||||||
platform: [{ required: true, message: '所属平台不能为空', trigger: 'blur' }],
|
platform: [{ required: true, message: '所属平台不能为空', trigger: 'blur' }],
|
||||||
apiKey: [{ required: false, message: 'API秘钥不能为空', trigger: 'blur' }],
|
|
||||||
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
|
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }],
|
||||||
temperature: [{ required: false, message: '温度参数不能为空', trigger: 'blur' }],
|
temperature: [{ required: true, message: '温度参数不能为空', trigger: 'blur' }],
|
||||||
maxTokens: [{ required: true, message: '回复数Token数不能为空', trigger: 'blur' }],
|
maxTokens: [{ required: true, message: '回复数Token数不能为空', trigger: 'blur' }],
|
||||||
dailyLimit: [{ required: true, message: '每日请求次数不能为空', trigger: 'blur' }],
|
dailyLimit: [{ required: true, message: '每日请求次数不能为空', trigger: 'blur' }],
|
||||||
modelType: [{ required: true, message: '模型类型(image-图像 text-文本 video-视频 audio-音频)不能为空', trigger: 'change' }],
|
modelType: [{ required: true, message: '模型类型不能为空', trigger: 'change' }],
|
||||||
consumePoints: [{ required: true, message: '消耗积分不能为空', trigger: 'blur' }],
|
consumePoints: [{ required: true, message: '消耗积分不能为空', trigger: 'blur' }],
|
||||||
maxTextLength: [{ required: false, message: '最大文本数量不能为空', trigger: 'blur' }],
|
|
||||||
maxImageSize: [{ required: false, message: '图片最大像素不能为空', trigger: 'blur' }],
|
|
||||||
maxVideoDuration: [{ required: false, message: '视频最大时长(秒)不能为空', trigger: 'blur' }],
|
|
||||||
maxVideoQuality: [{ required: false, message: '视频最大质量不能为空', trigger: 'blur' }],
|
|
||||||
remark: [{ required: true, message: '备注不能为空', trigger: 'blur' }],
|
remark: [{ required: true, message: '备注不能为空', trigger: 'blur' }],
|
||||||
})
|
})
|
||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
|
|||||||
Reference in New Issue
Block a user