评论
This commit is contained in:
48
src/main/java/cn/van/business/model/pl/TaobaoComment.java
Normal file
48
src/main/java/cn/van/business/model/pl/TaobaoComment.java
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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<TaobaoComment, Integer> {
|
||||
|
||||
List<TaobaoComment> findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(String productId, Integer isUse);
|
||||
|
||||
List<TaobaoComment> findByProductIdAndPictureUrlsIsNotNull(String productId);
|
||||
}
|
||||
@@ -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<String, String> productTypeMap = new HashMap<>();
|
||||
private static HashMap<String, String> 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<String, String> getProductTypeMapForTB() {
|
||||
Map<Object, Object> rawMap = redisTemplate.opsForHash().entries(PRODUCT_TYPE_MAP_PREFIX_TB);
|
||||
|
||||
if (!rawMap.isEmpty()) {
|
||||
productTypeMapTB.clear();
|
||||
for (Map.Entry<Object, Object> 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,17 +1936,19 @@ public class JDUtil {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成评论内容
|
||||
/**
|
||||
* 根据商品类型生成评论内容,并优先使用京东评论,若无则使用淘宝评论
|
||||
*
|
||||
* @param fromWxid 微信用户ID
|
||||
* @param productType 商品类型(如:烟灶套餐、单烟机等)
|
||||
*/
|
||||
private synchronized void generateComment(String fromWxid, String productType) {
|
||||
|
||||
//wxUtil.sendTextMessage(fromWxid, "已接收到评论生成指令,等候过程请勿重复输入", 1, fromWxid, true);
|
||||
private synchronized void generateComment(String fromWxid, String productType) {
|
||||
int allCommentCount = 0;
|
||||
int usedCommentCount = 0;
|
||||
int canUseComentCount = 0;
|
||||
int addCommentCount = 0;
|
||||
// 获取产品ID
|
||||
boolean isTb = false;
|
||||
|
||||
getProductTypeMap();
|
||||
String product_id = productTypeMap.get(productType);
|
||||
if (product_id == null || product_id.isEmpty()) {
|
||||
@@ -1914,32 +1956,52 @@ public class JDUtil {
|
||||
return;
|
||||
}
|
||||
|
||||
// 从数据库获取可用评论
|
||||
// 获取本地可用的京东评论
|
||||
List<Comment> availableComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 1);
|
||||
List<Comment> 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 {
|
||||
wxUtil.sendTextMessage(fromWxid, "没有本地评论,调用外部接口抓取", 1, fromWxid, true);
|
||||
// 没有本地评论,调用外部接口抓取
|
||||
|
||||
/**
|
||||
* ✅ 新增逻辑:先尝试从淘宝获取评论,但前提是 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,继续使用京东评论流程");
|
||||
}
|
||||
|
||||
/**
|
||||
* 2️⃣ 然后尝试从京东外部接口获取新的评论
|
||||
*/
|
||||
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;
|
||||
@@ -1948,73 +2010,110 @@ public class JDUtil {
|
||||
}
|
||||
} 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);
|
||||
if (!usedComments.isEmpty()) {
|
||||
Collections.shuffle(usedComments);
|
||||
commentToUse = usedComments.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (commentToUse == null) {
|
||||
wxUtil.sendTextMessage(fromWxid, "本地和京东均无可用的评论数据,请检查sku", 1, fromWxid, false);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 🚀 如果京东本地和接口都没有,则再次尝试从淘宝评论获取(兜底逻辑)
|
||||
*/
|
||||
//if (commentToUse == null) {
|
||||
// wxUtil.sendTextMessage(fromWxid, "京东无可用评论,尝试从淘宝评论中获取", 1, fromWxid, false);
|
||||
// generateTaobaoComment(fromWxid, productType);
|
||||
// return;
|
||||
//}
|
||||
|
||||
/**
|
||||
* ✨ 发送最终评论内容及图片
|
||||
*/
|
||||
wxUtil.sendTextMessage(fromWxid, commentToUse.getCommentText(), 1, fromWxid, true);
|
||||
// 发送图片(如果有)
|
||||
|
||||
String pictureUrls = commentToUse.getPictureUrls();
|
||||
if (pictureUrls != null && !pictureUrls.isEmpty()) {
|
||||
// 使用 fastjson 将 JSON 字符串解析为 List<String>
|
||||
List<String> urlList = JSON.parseArray(pictureUrls, String.class);
|
||||
|
||||
for (String url : urlList) {
|
||||
wxUtil.sendImageMessage(fromWxid, url.trim()); // 假设 sendImageMessage 支持 URL
|
||||
wxUtil.sendImageMessage(fromWxid, url);
|
||||
}
|
||||
}
|
||||
// 调用 DeepSeek 生成新的评论内容
|
||||
StringBuilder deepSeekPrompt = new StringBuilder(COMMENT_TEMPLATES_DS + commentToUse.getCommentText());
|
||||
//String deepSeekResponse = "";
|
||||
String gptResponse = "";
|
||||
|
||||
try {
|
||||
//deepSeekResponse = deepSeekClientUtil.getDeepSeekResponse(deepSeekPrompt);
|
||||
List<Comment> 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);
|
||||
}
|
||||
// 发送生成的评论文本
|
||||
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<TaobaoComment> 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
|
||||
|
||||
Reference in New Issue
Block a user