From 47a77eff474f3e142d7f208a7c23fd702c2fc745 Mon Sep 17 00:00:00 2001 From: Van0313 <60689272+Van0313@users.noreply.github.com> Date: Sun, 6 Jul 2025 11:25:56 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=84=E8=AE=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../van/business/model/pl/TaobaoComment.java | 48 +++ .../repository/TaobaoCommentRepository.java | 21 ++ .../java/cn/van/business/util/JDUtil.java | 299 ++++++++++++------ 3 files changed, 268 insertions(+), 100 deletions(-) create mode 100644 src/main/java/cn/van/business/model/pl/TaobaoComment.java create mode 100644 src/main/java/cn/van/business/repository/TaobaoCommentRepository.java diff --git a/src/main/java/cn/van/business/model/pl/TaobaoComment.java b/src/main/java/cn/van/business/model/pl/TaobaoComment.java new file mode 100644 index 0000000..f926f9e --- /dev/null +++ b/src/main/java/cn/van/business/model/pl/TaobaoComment.java @@ -0,0 +1,48 @@ +package cn.van.business.model.pl; + +import jakarta.persistence.*; +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * @author Van + * @version 1.0 + * @create 2025/7/6 + * @description 淘宝评论实体类 + */ +@Entity +@Table(name = "taobao_comments") +@Data +public class TaobaoComment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Integer id; + + @Column(name = "product_id", nullable = false, length = 255) + private String productId; + + @Column(name = "user_name", length = 255) + private String userName; + + @Lob + @Column(name = "comment_text") + private String commentText; + + @Column(name = "comment_id", length = 255, unique = false) + private String commentId; + + @Lob + @Column(name = "picture_urls") + private String pictureUrls; + + @Column(name = "created_at") + private LocalDateTime createdAt; + + @Column(name = "comment_date", length = 255) + private String commentDate; + + @Column(name = "is_use", columnDefinition = "int default 0") + private Integer isUse; +} diff --git a/src/main/java/cn/van/business/repository/TaobaoCommentRepository.java b/src/main/java/cn/van/business/repository/TaobaoCommentRepository.java new file mode 100644 index 0000000..e5a0d5b --- /dev/null +++ b/src/main/java/cn/van/business/repository/TaobaoCommentRepository.java @@ -0,0 +1,21 @@ +package cn.van.business.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import cn.van.business.model.pl.TaobaoComment; + +import java.util.List; + +/** + * @author Van + * @version 1.0 + * @create 2025/7/6 + * @description 淘宝评论数据访问层接口 + */ +@Repository +public interface TaobaoCommentRepository extends JpaRepository { + + List findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(String productId, Integer isUse); + + List findByProductIdAndPictureUrlsIsNotNull(String productId); +} diff --git a/src/main/java/cn/van/business/util/JDUtil.java b/src/main/java/cn/van/business/util/JDUtil.java index 9916a59..e6e1f95 100644 --- a/src/main/java/cn/van/business/util/JDUtil.java +++ b/src/main/java/cn/van/business/util/JDUtil.java @@ -6,9 +6,11 @@ import cn.hutool.http.HttpResponse; import cn.van.business.model.jd.JDOrder; import cn.van.business.model.jd.OrderRow; import cn.van.business.model.pl.Comment; +import cn.van.business.model.pl.TaobaoComment; import cn.van.business.repository.CommentRepository; import cn.van.business.repository.JDOrderRepository; import cn.van.business.repository.OrderRowRepository; +import cn.van.business.repository.TaobaoCommentRepository; import cn.van.business.util.ds.DeepSeekClientUtil; import cn.van.business.util.ds.GPTClientUtil; import com.alibaba.fastjson2.JSON; @@ -81,7 +83,10 @@ public class JDUtil { private static final Logger logger = LoggerFactory.getLogger(JDUtil.class); private static final String INTERACTION_STATE_PREFIX = "interaction_state:"; private static final String PRODUCT_TYPE_MAP_PREFIX = "product_type_map"; + private static final String PRODUCT_TYPE_MAP_PREFIX_TB = "product_type_map_tb"; private static HashMap productTypeMap = new HashMap<>(); + private static HashMap productTypeMapTB = new HashMap<>(); + private static final String COMMENT_TEMPLATES_DS = "我需要为我的商品模拟一些商品评论。你协助我生成2条不同的评价内容,京东商品评价的风格,每条评价100字左右,要基于原来的评论稍作修改,不要更换产品类型,只需要好评。不需要太浮夸,也不要太像ai生成,尽量模拟真实客户评价,不要提到以旧换新和国家补贴,只要回复我评论的内容就可以。这个是给你参考的其他真实用户的评论:"; private static final long TIMEOUT_MINUTES = 30; 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+。"; @@ -122,6 +127,7 @@ public class JDUtil { private final OrderRowRepository orderRowRepository; private final CommentRepository commentRepository; private JDOrderRepository jdOrderRepository; + private final TaobaoCommentRepository taobaoCommentRepository; private final OrderUtil orderUtil; private final DeepSeekClientUtil deepSeekClientUtil; @@ -136,11 +142,12 @@ public class JDUtil { // 构造函数中注入StringRedisTemplate @Autowired - public JDUtil(JDOrderRepository jdOrderRepository, StringRedisTemplate redisTemplate, OrderRowRepository orderRowRepository, WXUtil wxUtil, OrderUtil orderUtil, DeepSeekClientUtil deepSeekClientUtil, CommentRepository commentRepository, GPTClientUtil gptClientUtil) { + public JDUtil(JDOrderRepository jdOrderRepository, StringRedisTemplate redisTemplate, OrderRowRepository orderRowRepository, WXUtil wxUtil, TaobaoCommentRepository taobaoCommentRepository, OrderUtil orderUtil, DeepSeekClientUtil deepSeekClientUtil, CommentRepository commentRepository, GPTClientUtil gptClientUtil) { this.jdOrderRepository = jdOrderRepository; this.redisTemplate = redisTemplate; this.orderRowRepository = orderRowRepository; this.wxUtil = wxUtil; + this.taobaoCommentRepository = taobaoCommentRepository; this.orderUtil = orderUtil; this.deepSeekClientUtil = deepSeekClientUtil; this.commentRepository = commentRepository; @@ -1874,6 +1881,39 @@ public class JDUtil { public void delProductTypeMap(String key) { redisTemplate.opsForHash().delete(PRODUCT_TYPE_MAP_PREFIX, key); } + public HashMap getProductTypeMapForTB() { + Map rawMap = redisTemplate.opsForHash().entries(PRODUCT_TYPE_MAP_PREFIX_TB); + + if (!rawMap.isEmpty()) { + productTypeMapTB.clear(); + for (Map.Entry entry : rawMap.entrySet()) { + productTypeMapTB.put(entry.getKey().toString(), entry.getValue().toString()); + } + // 排序 + productTypeMapTB = productTypeMapTB.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); + return productTypeMapTB; + } else { + logger.warn("Redis 中未找到键为 {} 的 Hash 数据", PRODUCT_TYPE_MAP_PREFIX_TB); + } + return null; + } +/** + * 根据原始的 product_id 查询淘宝映射后的 ID + * + * @param rawProductId 原始 product_id + * @return 映射后的 ID,如果不存在则返回原值 + */ +public static String getMappedProductId(String rawProductId) { + // 确保 map 已加载(可选:调用一次 getProductTypeMapForTB()) + if (productTypeMapTB == null || productTypeMapTB.isEmpty()) { + // 可以在这里自动加载(需传入 RedisTemplate 或通过 Spring 注入) + // 示例:JDUtil.getProductTypeMapForTB(); + return rawProductId; + } + + // 查找映射值 + return productTypeMapTB.getOrDefault(rawProductId, rawProductId); +} /** @@ -1896,125 +1936,184 @@ public class JDUtil { } - /** - * 生成评论内容 - */ - private synchronized void generateComment(String fromWxid, String productType) { +/** + * 根据商品类型生成评论内容,并优先使用京东评论,若无则使用淘宝评论 + * + * @param fromWxid 微信用户ID + * @param productType 商品类型(如:烟灶套餐、单烟机等) + */ +private synchronized void generateComment(String fromWxid, String productType) { + int allCommentCount = 0; + int usedCommentCount = 0; + int canUseComentCount = 0; + int addCommentCount = 0; + boolean isTb = false; - //wxUtil.sendTextMessage(fromWxid, "已接收到评论生成指令,等候过程请勿重复输入", 1, fromWxid, true); - int allCommentCount = 0; - int usedCommentCount = 0; - int canUseComentCount = 0; - int addCommentCount = 0; - // 获取产品ID - getProductTypeMap(); - String product_id = productTypeMap.get(productType); - if (product_id == null || product_id.isEmpty()) { - wxUtil.sendTextMessage(fromWxid, "缺失对应的SKUID", 1, fromWxid, false); - return; + getProductTypeMap(); + String product_id = productTypeMap.get(productType); + if (product_id == null || product_id.isEmpty()) { + wxUtil.sendTextMessage(fromWxid, "缺失对应的SKUID", 1, fromWxid, false); + return; + } + + // 获取本地可用的京东评论 + List availableComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 1); + List usedComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 0); + + canUseComentCount = availableComments.size(); + usedCommentCount = usedComments.size(); + allCommentCount = canUseComentCount + usedCommentCount; + + Comment commentToUse = null; + + // 1️⃣ 先尝试使用本地可用的京东评论 + if (!availableComments.isEmpty()) { + Collections.shuffle(availableComments); + commentToUse = availableComments.get(0); + } else { + + /** + * ✅ 新增逻辑:先尝试从淘宝获取评论,但前提是 productTypeMapTB 存在对应映射 + */ + getProductTypeMapForTB(); // 加载淘宝映射表 + String taobaoProductId = productTypeMapTB.getOrDefault(product_id, null); + + if (taobaoProductId != null && !taobaoProductId.isEmpty()) { + logger.info("发现淘宝映射ID,尝试从淘宝获取评论"); + wxUtil.sendTextMessage(fromWxid, "本地无未用评论,已发现淘宝映射ID,从淘宝获取评论", 1, null, true); + Comment taobaoComment = generateTaobaoComment(fromWxid, productType); + if (taobaoComment != null){ + commentToUse = taobaoComment; + isTb = true; + } + } else { + logger.info("未找到淘宝映射ID,继续使用京东评论流程"); } - // 从数据库获取可用评论 - List availableComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 1); - List usedComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 0); - canUseComentCount = availableComments.size(); - usedCommentCount = usedComments.size(); + /** + * 2️⃣ 然后尝试从京东外部接口获取新的评论 + */ + try { + String fetchUrl = "http://192.168.8.6:5000/fetch_comments?product_id=" + product_id; + HttpResponse response = HttpRequest.post(fetchUrl).timeout(1000 * 60).execute(); + logger.info("fetchUrl: {}", fetchUrl); - Comment commentToUse = null; + if (response.getStatus() == 200) { + wxUtil.sendTextMessage(fromWxid, "已获取新的评论,请稍等", 1, fromWxid, true); - if (!availableComments.isEmpty()) { - // 随机选取一个未使用的评论 - Collections.shuffle(availableComments); - commentToUse = availableComments.get(0); - } else { - wxUtil.sendTextMessage(fromWxid, "没有本地评论,调用外部接口抓取", 1, fromWxid, true); - // 没有本地评论,调用外部接口抓取 - try { - String fetchUrl = "http://192.168.8.6:5000/fetch_comments?product_id=" + product_id; - // 用hutool发起post请求 - HttpResponse response = HttpRequest.post(fetchUrl).timeout(1000 * 60).execute(); - - logger.info("fetchUrl: {}", fetchUrl); -// code = 200 表示成功,-200 表示失败 - if (response.getStatus() == 200) { - wxUtil.sendTextMessage(fromWxid, "已获取新的评论,请稍等", 1, fromWxid, true); - // ✅ 关键修改:重新从数据库中查询,而不是使用内存中的 fetchedComments - availableComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 1); - if (!availableComments.isEmpty()) { - addCommentCount = availableComments.size() - canUseComentCount; - Collections.shuffle(availableComments); - commentToUse = availableComments.get(0); - } - } else if (response.getStatus() == -200) { - wxUtil.sendTextMessage(fromWxid, "请求响应:暂无新的评论。为您随机选取使用过的评论", 1, fromWxid, false); - // 随机选取一个使用过的评论 + availableComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 1); + if (!availableComments.isEmpty()) { + addCommentCount = availableComments.size() - canUseComentCount; + Collections.shuffle(availableComments); + commentToUse = availableComments.get(0); + } + } else if (response.getStatus() == -200) { + wxUtil.sendTextMessage(fromWxid, "请求响应:暂无新的评论。为您随机选取使用过的评论", 1, fromWxid, false); + if (!usedComments.isEmpty()) { Collections.shuffle(usedComments); commentToUse = usedComments.get(0); } - } catch (Exception e) { - logger.error("调用外部接口获取评论失败", e); - wxUtil.sendTextMessage(fromWxid, "请求响应:响应超时(60s)。为您随机选取使用过的评论", 1, fromWxid, false); + } + + } catch (Exception e) { + logger.error("调用外部接口获取评论失败", e); + wxUtil.sendTextMessage(fromWxid, "请求响应:响应超时(60s)。为您随机选取使用过的评论", 1, fromWxid, false); + if (!usedComments.isEmpty()) { + Collections.shuffle(usedComments); commentToUse = usedComments.get(0); } } + } - if (commentToUse == null) { - wxUtil.sendTextMessage(fromWxid, "本地和京东均无可用的评论数据,请检查sku", 1, fromWxid, false); - return; - } - wxUtil.sendTextMessage(fromWxid, commentToUse.getCommentText(), 1, fromWxid, true); - // 发送图片(如果有) - String pictureUrls = commentToUse.getPictureUrls(); - if (pictureUrls != null && !pictureUrls.isEmpty()) { - // 使用 fastjson 将 JSON 字符串解析为 List - List urlList = JSON.parseArray(pictureUrls, String.class); + /** + * 🚀 如果京东本地和接口都没有,则再次尝试从淘宝评论获取(兜底逻辑) + */ + //if (commentToUse == null) { + // wxUtil.sendTextMessage(fromWxid, "京东无可用评论,尝试从淘宝评论中获取", 1, fromWxid, false); + // generateTaobaoComment(fromWxid, productType); + // return; + //} - for (String url : urlList) { - wxUtil.sendImageMessage(fromWxid, url.trim()); // 假设 sendImageMessage 支持 URL - } - } - // 调用 DeepSeek 生成新的评论内容 - StringBuilder deepSeekPrompt = new StringBuilder(COMMENT_TEMPLATES_DS + commentToUse.getCommentText()); - //String deepSeekResponse = ""; - String gptResponse = ""; + /** + * ✨ 发送最终评论内容及图片 + */ + wxUtil.sendTextMessage(fromWxid, commentToUse.getCommentText(), 1, fromWxid, true); - try { - //deepSeekResponse = deepSeekClientUtil.getDeepSeekResponse(deepSeekPrompt); - List comments = commentRepository.findByProductIdAndPictureUrlsIsNotNull(product_id); - allCommentCount = comments.size(); - // 随机截取至多10个 - comments = comments.subList(0, Math.min(10, comments.size())); - for (Comment comment : comments) { - String commentText = comment.getCommentText(); - if (commentText != null && !commentText.isEmpty()) { - deepSeekPrompt.append("\n").append(commentText); - } - } - gptResponse = gptClientUtil.getGPTResponse(deepSeekPrompt.toString()); - logger.info("gptResponse: {}", gptResponse); - } catch (Exception e) { - logger.error("生成评论异常 - 用户: {}", fromWxid, e); - wxUtil.sendTextMessage(fromWxid, "AI 评论生成失败", 1, fromWxid, false); + String pictureUrls = commentToUse.getPictureUrls(); + if (pictureUrls != null && !pictureUrls.isEmpty()) { + List urlList = JSON.parseArray(pictureUrls, String.class); + for (String url : urlList) { + wxUtil.sendImageMessage(fromWxid, url); } - // 发送生成的评论文本 - if (gptResponse != null && !gptResponse.isEmpty()) { - try { - String[] split = gptResponse.split("\n"); - for (String s : split) { - s = s.replaceAll("1.|2.", ""); - wxUtil.sendTextMessage(fromWxid, s, 1, fromWxid, true); - } - } catch (Exception ignored) { - } - } - wxUtil.sendTextMessage(fromWxid, "评论统计:\n" + "型号 " + productType + "\n" + "新增:" + addCommentCount + "\n" + "已使用:" + usedCommentCount + "\n" + "可用:" + canUseComentCount + "\n" + "总数:" + allCommentCount, 1, fromWxid, true); - // 更新评论状态为已使用 + } + + /** + * 📝 更新评论状态为已使用(仅限数据库中的评论) + */ + if (commentToUse.getId() != null && !isTb) { commentToUse.setIsUse(1); commentRepository.save(commentToUse); - } + /** + * 📊 发送统计信息 + */ + wxUtil.sendTextMessage(fromWxid, + "评论统计:\n" + + "型号 " + productType + "\n" + + "新增:" + addCommentCount + "\n" + + "已使用:" + usedCommentCount + "\n" + + "可用:" + canUseComentCount + "\n" + + "总数:" + allCommentCount, 1, fromWxid, true); +} + + +private Comment generateTaobaoComment(String fromWxid, String productType) { + getProductTypeMap(); // 加载京东的 productTypeMap + getProductTypeMapForTB(); // 加载淘宝的 productTypeMapTB + + String product_id = productTypeMap.get(productType); // 先查京东SKU + + if (product_id == null) { + wxUtil.sendTextMessage(fromWxid, "未找到对应的商品ID", 1, fromWxid, false); + return null; + } + + // ✅ 在这里进行淘宝的 product_id 映射转换 + String taobaoProductId = productTypeMapTB.getOrDefault(product_id, product_id); + + // 然后使用 taobaoProductId 去查询 TaobaoComment + List taobaoComments = taobaoCommentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(taobaoProductId, 1); + + if (!taobaoComments.isEmpty()) { + Collections.shuffle(taobaoComments); + TaobaoComment selected = taobaoComments.get(0); + // 将淘宝评论转换为京东评论返回 + Comment comment = new Comment(); + comment.setCommentText(selected.getCommentText()); + String pictureUrls = selected.getPictureUrls(); + if (pictureUrls != null){ + pictureUrls = pictureUrls.replace("//img.", "https://img."); + } + comment.setPictureUrls(pictureUrls); + comment.setCommentId(selected.getCommentId()); + comment.setProductId(product_id); + comment.setUserName(selected.getUserName()); + comment.setCreatedAt(selected.getCreatedAt()); + // 保存淘宝评论为已使用 + selected.setIsUse(1); + taobaoCommentRepository.save(selected); + + // 返回京东评论 + return comment; + + } else { + return null; + } +} + + // 定义一个内部类来存储用户交互状态 @Getter