diff --git a/pom.xml b/pom.xml index 5df1abae8d..bc99634106 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ yudao-module-system yudao-module-infra - + yudao-module-member diff --git a/yudao-module-ai/pom.xml b/yudao-module-ai/pom.xml index 8773e02a9a..4f545f9a1a 100644 --- a/yudao-module-ai/pom.xml +++ b/yudao-module-ai/pom.xml @@ -149,6 +149,43 @@ ${alibaba-ai.version} + + com.alibaba + dashscope-sdk-java + 2.21.11 + + + com.github.victools + jsonschema-generator + + + + com.squareup.okhttp3 + okhttp + + + com.squareup.okio + okio + + + + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + com.squareup.okio + okio + 3.8.0 + + + + + org.jetbrains.kotlin + kotlin-stdlib + 1.9.20 + org.springaicommunity diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/tikhup/TikHupController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/tikhup/TikHupController.java index 7e7a0114a9..50eceff67d 100644 --- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/tikhup/TikHupController.java +++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/tikhup/TikHupController.java @@ -1,10 +1,23 @@ 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 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.tags.Tag; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; +import reactor.core.scheduler.Schedulers; import java.util.List; import java.util.Map; @@ -13,10 +26,13 @@ import java.util.Map; @RestController @RequestMapping("/ai/tikHup") @RequiredArgsConstructor +@Slf4j public class TikHupController { private final TikHupService tikHupService; + private final TikPromptMapper tikPromptMapper; + @GetMapping("/fetch_user_post_videos") @Operation( summary = "获取用户主页作品数据", @@ -44,4 +60,68 @@ public class TikHupController { 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 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 callWorkflow(@RequestBody Map 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 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()); + } + } + } diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/tikhup/vo/TikPromptVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/tikhup/vo/TikPromptVO.java new file mode 100644 index 0000000000..c0b8a4b01b --- /dev/null +++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/tikhup/vo/TikPromptVO.java @@ -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; + +} diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/tikhup/vo/TikTokenVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/tikhup/vo/TikTokenVO.java index cf8db27f28..6913526c49 100644 --- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/tikhup/vo/TikTokenVO.java +++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/tikhup/vo/TikTokenVO.java @@ -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.TableName; -import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; -@Schema(description = "TikToken - TikToken管理 Request VO") @TableName("tik_token") @Data 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") private String platformUrl; - @Schema( - description = "TikHup平台访问Token,用于API认证", - requiredMode = Schema.RequiredMode.REQUIRED, - example = "y15aJOgphU+dik4QGaCW3CL2bR7eQ6ZA2Tz3tUe8c6ycoW27OX+zRTO6Ow==", - title = "平台访问Token" - ) @TableField(value = "platform_token") private String platformToken; diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/tikhup/TikPromptMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/tikhup/TikPromptMapper.java new file mode 100644 index 0000000000..7a05739326 --- /dev/null +++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/tikhup/TikPromptMapper.java @@ -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 { + + default TikPromptVO getTikPromptVO(String promptType) { + return selectOne("prompt_type",promptType); + } + + default List getTikPromptVOList() { + return selectList(); + } + +} diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/tikhup/TikHupMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/tikhup/TikTokenMapper.java similarity index 84% rename from yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/tikhup/TikHupMapper.java rename to yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/tikhup/TikTokenMapper.java index 442e848a85..689b00424e 100644 --- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/tikhup/TikHupMapper.java +++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/tikhup/TikTokenMapper.java @@ -5,7 +5,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.tikhup.vo.TikTokenVO; import org.apache.ibatis.annotations.Mapper; @Mapper -public interface TikHupMapper extends BaseMapperX { +public interface TikTokenMapper extends BaseMapperX { default TikTokenVO getPlatformToken(String platformUrl) { return selectOne("platform_url",platformUrl); diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/tikhup/TikHupService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/tikhup/TikHupService.java index 2801f03209..a932b48ab5 100644 --- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/tikhup/TikHupService.java +++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/tikhup/TikHupService.java @@ -1,6 +1,9 @@ package cn.iocoder.yudao.module.ai.service.tikhup; +import org.springframework.web.bind.annotation.RequestBody; + import java.util.List; +import java.util.Map; public interface TikHupService { @@ -27,4 +30,12 @@ public interface TikHupService { */ Object videoToCharacters2(List fileLinkList); + /** + * + * @param promptType 提示词类型 + * @param content 提示词内容 + * @return deepseek改写后的内容 + */ + Object deepseekAnalysis(String promptType,String content); + } diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/tikhup/TikHupServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/tikhup/TikHupServiceImpl.java index e4e1e0fcd6..2e586a273f 100644 --- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/tikhup/TikHupServiceImpl.java +++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/tikhup/TikHupServiceImpl.java @@ -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.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.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.TranscriptionParam; import com.alibaba.dashscope.audio.asr.transcription.TranscriptionQueryParam; @@ -25,7 +27,9 @@ import java.util.List; @RequiredArgsConstructor public class TikHupServiceImpl implements TikHupService{ - private final TikHupMapper tikHupMapper; + private final TikTokenMapper tikTokenMapper; + + private final TikPromptMapper tikPromptMapper; @Override 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)){ 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(); try{ Unirest.setTimeouts(0, 0); @@ -57,6 +61,7 @@ public class TikHupServiceImpl implements TikHupService{ private final String appKey = "sldJ4XSpYp3rKALZ "; private final String accessKeyId = "LTAI5tPV9Ag3csf41GZjaLTA"; private final String accessKeySecret = "kDqlGeJTKw6tJtFYiaY8vQTFuVIQDs"; + private final String deepseekApiKey="sk-7f666f993b144d279ae375a015e4de56"; @Override 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 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; + + } + } diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml index b4c9bb04ac..d4f7b570ab 100644 --- a/yudao-server/pom.xml +++ b/yudao-server/pom.xml @@ -33,11 +33,11 @@ - - - - - + + cn.iocoder.boot + yudao-module-member + ${revision} +