tik集成

This commit is contained in:
wing
2025-10-14 23:27:38 +08:00
parent 4bdc6337dd
commit 3dfc167fc3
10 changed files with 205 additions and 24 deletions

View File

@@ -15,7 +15,7 @@
<!-- 各种 module 拓展 --> <!-- 各种 module 拓展 -->
<module>yudao-module-system</module> <module>yudao-module-system</module>
<module>yudao-module-infra</module> <module>yudao-module-infra</module>
<!-- <module>yudao-module-member</module>--> <module>yudao-module-member</module>
<!-- <module>yudao-module-bpm</module>--> <!-- <module>yudao-module-bpm</module>-->
<!-- <module>yudao-module-report</module>--> <!-- <module>yudao-module-report</module>-->
<!-- <module>yudao-module-mp</module>--> <!-- <module>yudao-module-mp</module>-->

View File

@@ -149,6 +149,43 @@
<version>${alibaba-ai.version}</version> <version>${alibaba-ai.version}</version>
</dependency> </dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dashscope-sdk-java</artifactId>
<version>2.21.11</version>
<exclusions>
<exclusion>
<groupId>com.github.victools</groupId>
<artifactId>jsonschema-generator</artifactId>
</exclusion>
<!-- 排除 SDK 自带的 OkHttp 和 Okio -->
<exclusion>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</exclusion>
<exclusion>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.12.0</version> <!-- 使用较新的稳定版本 -->
</dependency>
<dependency>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
<version>3.8.0</version> <!-- 与 OkHttp 4.12.0 严格兼容 -->
</dependency>
<!-- 3. 引入 Kotlin 运行时OkHttp 4.x+ 必须) -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.9.20</version> <!-- 与 OkHttp 4.12.0 兼容的版本 -->
</dependency>
<dependency> <dependency>
<!-- 文心一言 --> <!-- 文心一言 -->
<groupId>org.springaicommunity</groupId> <groupId>org.springaicommunity</groupId>

View File

@@ -1,10 +1,23 @@
package cn.iocoder.yudao.module.ai.controller.admin.tikhup; package cn.iocoder.yudao.module.ai.controller.admin.tikhup;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.ai.dal.mysql.tikhup.TikPromptMapper;
import cn.iocoder.yudao.module.ai.service.tikhup.TikHupService; import cn.iocoder.yudao.module.ai.service.tikhup.TikHupService;
import com.alibaba.dashscope.app.Application;
import com.alibaba.dashscope.app.ApplicationParam;
import com.alibaba.dashscope.app.ApplicationResult;
import com.alibaba.dashscope.app.FlowStreamMode;
import com.alibaba.dashscope.utils.Constants;
import com.alibaba.dashscope.utils.JsonUtils;
import com.alibaba.fastjson.JSON;
import io.reactivex.Flowable;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.scheduler.Schedulers;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -13,10 +26,13 @@ import java.util.Map;
@RestController @RestController
@RequestMapping("/ai/tikHup") @RequestMapping("/ai/tikHup")
@RequiredArgsConstructor @RequiredArgsConstructor
@Slf4j
public class TikHupController { public class TikHupController {
private final TikHupService tikHupService; private final TikHupService tikHupService;
private final TikPromptMapper tikPromptMapper;
@GetMapping("/fetch_user_post_videos") @GetMapping("/fetch_user_post_videos")
@Operation( @Operation(
summary = "获取用户主页作品数据", summary = "获取用户主页作品数据",
@@ -44,4 +60,68 @@ public class TikHupController {
return tikHupService.videoToCharacters2(fileLinkList); return tikHupService.videoToCharacters2(fileLinkList);
} }
@GetMapping("/getPromptTypeList")
@Operation(summary = "获取提示词类型列表", description = "获取提示词类型列表")
public Object getPromptTypeList(){
return CommonResult.success(tikPromptMapper.getTikPromptVOList());
}
@PostMapping("/deepseekAnalysis")
@Operation(summary = "deepseek文案重写", description = "deepseek文案重写")
public Object deepseekAnalysis(@RequestBody Map<String,Object> contentMap){
String content = (String) contentMap.get("content");
String promptType = (String) contentMap.get("promptType");
return tikHupService.deepseekAnalysis(promptType,content);
}
@PostMapping("/callWorkflow")
@Operation(summary = "调用阿里工作流", description = "调用阿里工作流 文档地址https://help.aliyun.com/zh/model-studio/invoke-workflow-application?mode=pure#8f4b4d2ce9fry")
public Flux<String> callWorkflow(@RequestBody Map<String,Object> params){
Constants.baseHttpApiUrl = "https://dashscope.aliyuncs.com/api/v1";
ApplicationParam param = ApplicationParam.builder()
// 若没有配置环境变量可用百炼API Key将下行替换为.apiKey("sk-xxx")。但不建议在生产环境中直接将API Key硬编码到代码中以减少API Key泄露风险。
.apiKey("sk-10c746f8cb8640738f8d6b71af699003")
.appId("bb746b74bfb0439e841cb309dc9f945c")
.flowStreamMode(FlowStreamMode.MESSAGE_FORMAT)
.bizParams(JsonUtils.parse(JSON.toJSONString(params)))
.prompt((String) params.get("audio_prompt"))
.hasThoughts(false)
.incrementalOutput(true)
.build();
Application application = new Application();
try{
//ApplicationResult result = application.call(param);
//return CommonResult.success(result.getOutput().getText());
Flowable<ApplicationResult> flowableResult = application.streamCall(param);
// 关键RxJava Flowable 转 WebFlux Flux指定非阻塞线程调度
// 4.2 将 RxJava Flowable 转换为 WebFlux Flux响应式转换无阻塞
return Flux.from(flowableResult)
// 关键:切换到非阻塞线程执行(避免阻塞 WebFlux 事件循环)
.subscribeOn(Schedulers.boundedElastic())
// 4.3 处理每一条流式数据:提取 thoughts 字段(按你的需求,也可提取 text
.map(applicationResult -> {
// 校验结果非 null阿里 SDK 可能返回空结果)
if (applicationResult == null || applicationResult.getOutput() == null) {
log.warn("收到空的流式结果");
return "空结果";
}
return applicationResult.getOutput().getWorkflowMessage().getMessage().getContent();
})
// 4.4 流处理异常捕获(如网络中断、阿里 SDK 内部错误)
.onErrorMap(e -> {
log.error("流式处理异常", e); // 打印完整异常栈,便于排查
return new RuntimeException("流式处理错误:" + e.getMessage());
})
// 4.5 异常时返回默认提示(前端不会断连,而是接收错误信息)
.onErrorReturn("错误:流式处理中断:" + "请重试或联系管理员");
}catch (Exception e){
// 捕获 SDK 调用时的同步异常(如初始化失败、网络连接超时)
log.error("调用阿里 SDK 失败", e);
return Flux.just("错误SDK 调用失败:" + e.getMessage());
}
}
} }

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.ai.controller.admin.tikhup.vo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@TableName("tik_prompt")
@Data
public class TikPromptVO {
@TableField(value = "prompt_type")
private String promptType;
@TableField(value = "prompt_name")
private String promptName;
@TableField(value = "prompt")
private String prompt;
}

View File

@@ -2,29 +2,15 @@ package cn.iocoder.yudao.module.ai.controller.admin.tikhup.vo;
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 io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
@Schema(description = "TikToken - TikToken管理 Request VO")
@TableName("tik_token") @TableName("tik_token")
@Data @Data
public class TikTokenVO { public class TikTokenVO {
@Schema(
description = "TikHup平台API地址",
requiredMode = Schema.RequiredMode.REQUIRED,
example = "https://api.tikhub.io/api/v1/xiaohongshu/app/get_user_info",
title = "平台API地址"
)
@TableField(value = "platform_url") @TableField(value = "platform_url")
private String platformUrl; private String platformUrl;
@Schema(
description = "TikHup平台访问Token用于API认证",
requiredMode = Schema.RequiredMode.REQUIRED,
example = "y15aJOgphU+dik4QGaCW3CL2bR7eQ6ZA2Tz3tUe8c6ycoW27OX+zRTO6Ow==",
title = "平台访问Token"
)
@TableField(value = "platform_token") @TableField(value = "platform_token")
private String platformToken; private String platformToken;

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.ai.dal.mysql.tikhup;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.ai.controller.admin.tikhup.vo.TikPromptVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface TikPromptMapper extends BaseMapperX<TikPromptVO> {
default TikPromptVO getTikPromptVO(String promptType) {
return selectOne("prompt_type",promptType);
}
default List<TikPromptVO> getTikPromptVOList() {
return selectList();
}
}

View File

@@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.tikhup.vo.TikTokenVO;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
@Mapper @Mapper
public interface TikHupMapper extends BaseMapperX<TikTokenVO> { public interface TikTokenMapper extends BaseMapperX<TikTokenVO> {
default TikTokenVO getPlatformToken(String platformUrl) { default TikTokenVO getPlatformToken(String platformUrl) {
return selectOne("platform_url",platformUrl); return selectOne("platform_url",platformUrl);

View File

@@ -1,6 +1,9 @@
package cn.iocoder.yudao.module.ai.service.tikhup; package cn.iocoder.yudao.module.ai.service.tikhup;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List; import java.util.List;
import java.util.Map;
public interface TikHupService { public interface TikHupService {
@@ -27,4 +30,12 @@ public interface TikHupService {
*/ */
Object videoToCharacters2(List<String> fileLinkList); Object videoToCharacters2(List<String> fileLinkList);
/**
*
* @param promptType 提示词类型
* @param content 提示词内容
* @return deepseek改写后的内容
*/
Object deepseekAnalysis(String promptType,String content);
} }

View File

@@ -2,8 +2,10 @@ package cn.iocoder.yudao.module.ai.service.tikhup;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.ai.controller.admin.tikhup.vo.TikPromptVO;
import cn.iocoder.yudao.module.ai.controller.admin.tikhup.vo.TikTokenVO; import cn.iocoder.yudao.module.ai.controller.admin.tikhup.vo.TikTokenVO;
import cn.iocoder.yudao.module.ai.dal.mysql.tikhup.TikHupMapper; import cn.iocoder.yudao.module.ai.dal.mysql.tikhup.TikPromptMapper;
import cn.iocoder.yudao.module.ai.dal.mysql.tikhup.TikTokenMapper;
import com.alibaba.dashscope.audio.asr.transcription.Transcription; import com.alibaba.dashscope.audio.asr.transcription.Transcription;
import com.alibaba.dashscope.audio.asr.transcription.TranscriptionParam; import com.alibaba.dashscope.audio.asr.transcription.TranscriptionParam;
import com.alibaba.dashscope.audio.asr.transcription.TranscriptionQueryParam; import com.alibaba.dashscope.audio.asr.transcription.TranscriptionQueryParam;
@@ -25,7 +27,9 @@ import java.util.List;
@RequiredArgsConstructor @RequiredArgsConstructor
public class TikHupServiceImpl implements TikHupService{ public class TikHupServiceImpl implements TikHupService{
private final TikHupMapper tikHupMapper; private final TikTokenMapper tikTokenMapper;
private final TikPromptMapper tikPromptMapper;
@Override @Override
public Object fetch_user_post_videos(String type,String sec_user_id, int max_cursor, int count){ public Object fetch_user_post_videos(String type,String sec_user_id, int max_cursor, int count){
@@ -37,7 +41,7 @@ public class TikHupServiceImpl implements TikHupService{
} else if("tik-app".equals(type)){ } else if("tik-app".equals(type)){
url = "https://api.tikhub.io/api/v1/douyin/app/v3/fetch_user_post_videos"; url = "https://api.tikhub.io/api/v1/douyin/app/v3/fetch_user_post_videos";
} }
TikTokenVO tikTokenVO = tikHupMapper.getPlatformToken(url); TikTokenVO tikTokenVO = tikTokenMapper.getPlatformToken(url);
String Authorization = tikTokenVO.getPlatformToken(); String Authorization = tikTokenVO.getPlatformToken();
try{ try{
Unirest.setTimeouts(0, 0); Unirest.setTimeouts(0, 0);
@@ -57,6 +61,7 @@ public class TikHupServiceImpl implements TikHupService{
private final String appKey = "sldJ4XSpYp3rKALZ "; private final String appKey = "sldJ4XSpYp3rKALZ ";
private final String accessKeyId = "LTAI5tPV9Ag3csf41GZjaLTA"; private final String accessKeyId = "LTAI5tPV9Ag3csf41GZjaLTA";
private final String accessKeySecret = "kDqlGeJTKw6tJtFYiaY8vQTFuVIQDs"; private final String accessKeySecret = "kDqlGeJTKw6tJtFYiaY8vQTFuVIQDs";
private final String deepseekApiKey="sk-7f666f993b144d279ae375a015e4de56";
@Override @Override
public Object videoToCharacters(String fileLink){ public Object videoToCharacters(String fileLink){
@@ -104,4 +109,26 @@ public class TikHupServiceImpl implements TikHupService{
} }
public Object deepseekAnalysis(String promptType,String content){
Unirest.setTimeouts(0, 0);
try{
TikPromptVO tikPromptVO = tikPromptMapper.getTikPromptVO(promptType);
HttpResponse<String> response = Unirest.post("https://api.deepseek.com/chat/completions")
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.header("Authorization", "Bearer " + deepseekApiKey)
.body("{\n \"messages\": [\n {\n \"content\": \""+tikPromptVO.getPrompt()+"\n\n\n原文如下\n"+content+"\",\n \"role\": \"system\"\n },\n {\n \"content\": \"Hi\",\n \"role\": \"user\"\n }\n ],\n \"model\": \"deepseek-chat\",\n \"frequency_penalty\": 0,\n \"max_tokens\": 8000,\n \"presence_penalty\": 0,\n \"response_format\": {\n \"type\": \"text\"\n },\n \"stop\": null,\n \"stream\": false,\n \"stream_options\": null,\n \"temperature\": 1,\n \"top_p\": 1,\n \"tools\": null,\n \"tool_choice\": \"none\",\n \"logprobs\": false,\n \"top_logprobs\": null\n}")
.asString();
if(response.getStatus() == 200){
return response.getBody();
}
}catch (Exception e){
log.error(e.getMessage());
return CommonResult.error(500,"文案改写失败!");
}
return null;
}
} }

View File

@@ -33,11 +33,11 @@
</dependency> </dependency>
<!-- 会员中心。默认注释,保证编译速度 --> <!-- 会员中心。默认注释,保证编译速度 -->
<!-- <dependency>--> <dependency>
<!-- <groupId>cn.iocoder.boot</groupId>--> <groupId>cn.iocoder.boot</groupId>
<!-- <artifactId>yudao-module-member</artifactId>--> <artifactId>yudao-module-member</artifactId>
<!-- <version>${revision}</version>--> <version>${revision}</version>
<!-- </dependency>--> </dependency>
<!-- 数据报表。默认注释,保证编译速度 --> <!-- 数据报表。默认注释,保证编译速度 -->
<!-- <dependency>--> <!-- <dependency>-->