This commit is contained in:
Van0313
2025-07-06 11:25:56 +08:00
parent cdaff20462
commit 47a77eff47
3 changed files with 268 additions and 100 deletions

View 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;
}

View File

@@ -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);
}

View File

@@ -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,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<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 {
/**
* ✅ 新增逻辑:先尝试从淘宝获取评论,但前提是 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<Comment> availableComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 1);
List<Comment> 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<String>
List<String> 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<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);
String pictureUrls = commentToUse.getPictureUrls();
if (pictureUrls != null && !pictureUrls.isEmpty()) {
List<String> 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<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