From 5dcae40907a1c0ed29dc8506d351a83e5a95606d Mon Sep 17 00:00:00 2001 From: Van0313 <60689272+Van0313@users.noreply.github.com> Date: Mon, 28 Apr 2025 16:28:31 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=84=E8=AE=BA+ds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/cn/van/business/util/JDUtil.java | 66 +++++++++++++++---- .../business/util/ds/DeepSeekClientUtil.java | 63 ++++++++++++++++++ 2 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 src/main/java/cn/van/business/util/ds/DeepSeekClientUtil.java diff --git a/src/main/java/cn/van/business/util/JDUtil.java b/src/main/java/cn/van/business/util/JDUtil.java index 7b63090..d08aaf2 100644 --- a/src/main/java/cn/van/business/util/JDUtil.java +++ b/src/main/java/cn/van/business/util/JDUtil.java @@ -3,6 +3,7 @@ package cn.van.business.util; import cn.van.business.model.jd.OrderRow; import cn.van.business.repository.OrderRowRepository; +import cn.van.business.util.ds.DeepSeekClientUtil; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; @@ -30,6 +31,7 @@ import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.time.LocalDate; @@ -76,6 +78,8 @@ public class JDUtil { private static final String ACCESS_TOKEN = ""; private static final Logger logger = LoggerFactory.getLogger(JDUtil.class); private static final String INTERACTION_STATE_PREFIX = "interaction_state:"; + private static final String COMMENT_TEMPLATES = "comment_templates"; + private static final String COMMENT_TEMPLATES_DS = "我需要为我的商品模拟一些商品评论。你协助我生成评价内容,100字以内即可。我会为你提供其他真实用户的评论:"; private static final long TIMEOUT_MINUTES = 10; private static final String WENAN_FANAN_LQD = "提供方法自己下单\n" + "全程都是自己的账号下单\n" + "标价就是下单的到手价\n" + "本人有耐心,会一步一步提供教程\n" + "以后有什么质量问题都是用自己的账号走京东售后\n" + "\n" + "更新\n" + "\n" + "用你自己的账号下单\n" + "官方店铺 提供方法自己下单\n" + "——————————————————————————————————————————\n" + "同行可长久合作,可提供神级家电线报\n" + "\n" + "配合家电线报可以自己下单,不用找代购和代下,订单和利润都掌握在自己手中。\n" + "\n" + "一次入会永久使用,包含家电帮,雷神价,韭菜帮,河南&湖南帮,各种暗号帮后返等内部独家家电线报\n" + "\n" + "JD采销采购不定时发放独家优惠券\n" + "\n" + "基本上你能看到的京东家电低价都是从这些渠道里面出来。\n" + "\n" + "2025年家电项目新方向,配合家电线报下单,秒省1K+。"; private static final String WENAN_FANAN_HG = "手把手教你实现超值下单,无需依赖他人!全程使用个人专属账号操作,所见即所得,页面标价即为最终到手价。别担心操作难题,我会全程贴心指导,每一步都细致讲解,助你轻松下单。后续若出现任何质量问题,凭借个人账号就能直接对接JD官方售后,售后无忧。\n" + "更新\n" + "采用自主账号下单模式,官方店铺商品随心购,专业方法全程提供!\n" + "——————————————————————————————————————————\n" + "诚邀同行建立长期合作关系,海量独家家电优惠线报倾囊相授!\n" + "借助这些优质家电线报,无需寻求代购代下服务,自己就能轻松下单,订单信息与收益牢牢掌握在手中。\n" + "一次加入,终身受益!涵盖家电帮、雷神价、韭菜帮、河南 & 湖南帮等众多渠道,还有各类暗号帮后返等内部专属家电优惠信息一网打尽。\n" + "JD采销团队会不定时发放独家隐藏优惠券,市面上那些令人心动的JD家电低价好物,大多都源自这些渠道!\n" + "2025 年家电选购新趋势,依托线报下单,轻松省下千元开支,开启超值购物之旅!"; @@ -102,6 +106,7 @@ public class JDUtil { private final StringRedisTemplate redisTemplate; private final OrderRowRepository orderRowRepository; private final OrderUtil orderUtil; + private final DeepSeekClientUtil deepSeekClientUtil; // 添加ObjectMapper来序列化和反序列化UserInteractionState private final ObjectMapper objectMapper = new ObjectMapper(); private final ConcurrentHashMap userInteractionStates = new ConcurrentHashMap<>(); @@ -109,11 +114,12 @@ public class JDUtil { // 构造函数中注入StringRedisTemplate @Autowired - public JDUtil(StringRedisTemplate redisTemplate, OrderRowRepository orderRowRepository, WXUtil wxUtil, OrderUtil orderUtil) { + public JDUtil(StringRedisTemplate redisTemplate, OrderRowRepository orderRowRepository, WXUtil wxUtil, OrderUtil orderUtil, DeepSeekClientUtil deepSeekClientUtil) { this.redisTemplate = redisTemplate; this.orderRowRepository = orderRowRepository; this.wxUtil = wxUtil; this.orderUtil = orderUtil; + this.deepSeekClientUtil = deepSeekClientUtil; } private List filterOrdersByDate(List orderRows, int daysBack) { @@ -1615,7 +1621,6 @@ public class JDUtil { // 设置当前状态为生成评论流程 state.setCurrentState(UserInteractionState.ProcessState.COMMENT_GENERATION); state.setCurrentField("commentTypeSelection"); - // 提示用户选择评论类型 wxUtil.sendTextMessage(fromWxid, "请选择要生成的评论类型:\n回复 1 - 消毒柜评论\n回复 2 - 油烟机评论", 1, fromWxid, false); logger.info("进入生成评论流程 - 用户: {}", fromWxid); @@ -1637,7 +1642,16 @@ public class JDUtil { * 处理生成评论流程中的用户交互 */ private void handleCommentInteraction(String fromWxid, String message, UserInteractionState state) { - String key = INTERACTION_STATE_PREFIX + fromWxid; + String superOrder = ""; + try { + String[] split = message.split("/n"); + if (split.length != 0) { + message = split[0]; + for (int i = 1; i < split.length; i++) { + superOrder += split[i] + " "; + } + } + } String key = INTERACTION_STATE_PREFIX + fromWxid; logger.info("处理生成评论流程中的用户交互 - 用户: {}, 状态: {}", fromWxid, state); try { // 检查当前状态是否为生成评论流程 @@ -1648,10 +1662,16 @@ public class JDUtil { // 根据用户输入生成对应的评论 switch (message) { case "1": - generateComment(fromWxid, "消毒柜"); + generateComment(fromWxid, "1", superOrder); + break; + case "1+": + generateComment(fromWxid, "1+", superOrder); break; case "2": - generateComment(fromWxid, "油烟机"); + generateComment(fromWxid, "2", superOrder); + break; + case "2+": + generateComment(fromWxid, "2+", superOrder); break; default: wxUtil.sendTextMessage(fromWxid, "无效的选择,请回复 1 或 2", 1, fromWxid, false); @@ -1670,15 +1690,33 @@ public class JDUtil { /** * 生成评论内容 */ - private void generateComment(String fromWxid, String productType) { - // 这里可以调用缓存或AI生成文案,目前先返回固定模板 - String commentTemplate = "这是一条关于%s的评论:\n" + "1. 性能非常出色,使用体验极佳。\n" + "2. 安装方便,操作简单。\n" + "3. 售后服务到位,值得信赖。\n" + "感谢您的购买与支持!"; - - String comment = String.format(commentTemplate, productType); - wxUtil.sendTextMessage(fromWxid, comment, 1, fromWxid, false); - - // 重置状态 - resetState(fromWxid, loadOrCreateState(INTERACTION_STATE_PREFIX + fromWxid)); + private void generateComment(String fromWxid, String productType, String superOrder) { + if (productType.equals("1") || productType.equals("2")) { + // 这里可以调用缓存或AI生成文案,目前先返回固定模板 + //用redis的 list存放评论模板, + List commentTemplates = redisTemplate.opsForList().range(COMMENT_TEMPLATES + productType, 0, -1); + if (commentTemplates == null || commentTemplates.isEmpty()) { + wxUtil.sendTextMessage(fromWxid, "暂无评论模板,请先添加评论模板", 1, fromWxid, false); + return; + } + String comment = commentTemplates.get(new Random().nextInt(commentTemplates.size())); + String deepSeekResponse = "ds响应失败"; + try { + deepSeekResponse = deepSeekClientUtil.getDeepSeekResponse(comment); + } catch (IOException e) { + logger.error("生成评论异常 - 用户: {}, ", fromWxid, e); + } + wxUtil.sendTextMessage(fromWxid, deepSeekResponse, 1, fromWxid, false); + // 重置状态 + resetState(fromWxid, loadOrCreateState(INTERACTION_STATE_PREFIX + fromWxid)); + } else { + if (!superOrder.isEmpty()) { + superOrder = superOrder.replace("+", ""); + // 追加评论模板到redis + redisTemplate.opsForList().leftPush(COMMENT_TEMPLATES + productType, superOrder); + wxUtil.sendTextMessage(fromWxid, "评论模板添加成功", 1, fromWxid, false); + } + } } diff --git a/src/main/java/cn/van/business/util/ds/DeepSeekClientUtil.java b/src/main/java/cn/van/business/util/ds/DeepSeekClientUtil.java new file mode 100644 index 0000000..a70f3c7 --- /dev/null +++ b/src/main/java/cn/van/business/util/ds/DeepSeekClientUtil.java @@ -0,0 +1,63 @@ +package cn.van.business.util.ds; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.Method; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@Component +public class DeepSeekClientUtil { + private static final String DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"; // 确认 API 地址 + private static final String DEEPSEEK_API_KEY = "d99b8cc6b7414cc88a5d950a3ff7585e"; // 替换为你的密钥 + private static final ObjectMapper objectMapper = new ObjectMapper(); // Jackson JSON 解析器 + + /** + * 调用 DeepSeek API 并返回第一次回复的文本内容 + * + * @param inputText 用户输入的文本 + * @return API 返回的第一个回复内容 + * @throws IOException 如果网络请求或 JSON 解析失败 + * @throws IllegalArgumentException 如果输入为空或过长 + */ + public String getDeepSeekResponse(String inputText) throws IOException { + // 1. 输入校验 + if (inputText == null || inputText.trim().isEmpty()) { + throw new IllegalArgumentException("输入文本不能为空"); + } + if (inputText.length() > 10000) { // 可根据 API 限制调整 + throw new IllegalArgumentException("输入文本过长"); + } + + // 2. 构建请求体(使用 Jackson 生成 JSON) + Map requestBody = new HashMap<>(); + requestBody.put("model", "deepseek-chat"); + requestBody.put("messages", new Map[]{Map.of("role", "user", "content", inputText)}); + requestBody.put("temperature", 0.7); + + String jsonBody = objectMapper.writeValueAsString(requestBody); + + // 3. 使用 Hutool HTTP 发送请求 + HttpResponse response = HttpRequest.of(DEEPSEEK_API_URL).method(Method.POST).header("Content-Type", "application/json").header("Authorization", "Bearer " + DEEPSEEK_API_KEY).header("Accept", "application/json").body(jsonBody).execute(); + + // 4. 检查 HTTP 状态码 + if (response.getStatus() != 200) { + throw new IOException("API 调用失败,HTTP 状态码: " + response.getStatus()); + } + + // 5. 解析 JSON 响应(使用 Jackson) + JsonNode rootNode = objectMapper.readTree(response.body()); + JsonNode choices = rootNode.path("choices"); + if (choices.isEmpty()) { + throw new IOException("API 返回数据格式异常,未找到回复内容"); + } + + // 6. 提取第一个回复的 content + return choices.get(0).path("message").path("content").asText(); + } +}