Files
Jarvis_java/src/main/java/cn/van/business/util/JDUtil.java
Van0313 d101ea3c6a 录单
2025-06-08 20:04:13 +08:00

2343 lines
117 KiB
Java
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cn.van.business.util;
import cn.hutool.http.HttpRequest;
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.repository.CommentRepository;
import cn.van.business.repository.JDOrderRepository;
import cn.van.business.repository.OrderRowRepository;
import cn.van.business.util.ds.DeepSeekClientUtil;
import cn.van.business.util.ds.GPTClientUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jd.open.api.sdk.DefaultJdClient;
import com.jd.open.api.sdk.JdClient;
import com.jd.open.api.sdk.domain.kplunion.CouponService.request.get.CreateGiftCouponReq;
import com.jd.open.api.sdk.domain.kplunion.GoodsService.request.query.GoodsReq;
import com.jd.open.api.sdk.domain.kplunion.GoodsService.response.query.GoodsQueryResult;
import com.jd.open.api.sdk.domain.kplunion.GoodsService.response.query.UrlInfo;
import com.jd.open.api.sdk.domain.kplunion.promotionbysubunioni.PromotionService.request.get.PromotionCodeReq;
import com.jd.open.api.sdk.request.kplunion.UnionOpenCouponGiftGetRequest;
import com.jd.open.api.sdk.request.kplunion.UnionOpenGoodsQueryRequest;
import com.jd.open.api.sdk.request.kplunion.UnionOpenPromotionBysubunionidGetRequest;
import com.jd.open.api.sdk.response.kplunion.UnionOpenCouponGiftGetResponse;
import com.jd.open.api.sdk.response.kplunion.UnionOpenGoodsQueryResponse;
import com.jd.open.api.sdk.response.kplunion.UnionOpenPromotionBysubunionidGetResponse;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static cn.van.business.util.JDUtil.UserInteractionState.GiftMoneyStep.*;
import static cn.van.business.util.JDUtil.UserInteractionState.ProcessState.INIT;
import static cn.van.business.util.WXUtil.*;
/**
* @author Leo
* @version 1.0
* @create 2024/11/5 17:40
* @description
*/
@Component
public class JDUtil {
static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/**
* 密钥配置
*/
// van论坛
private static final String LPF_APP_KEY_WZ = "98e21c89ae5610240ec3f5f575f86a59";
private static final String LPF_SECRET_KEY_WZ = "3dcb6b23a1104639ac433fd07adb6dfb";
private static final String SERVER_URL = "https://api.jd.com/routerjson";
//accessToken
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 PRODUCT_TYPE_MAP_PREFIX = "product_type_map";
private static HashMap<String, String> productTypeMap = new HashMap<>();
private static final String COMMENT_TEMPLATES_DS = "我需要为我的商品模拟一些商品评论。你协助我生成2条不同的评价内容京东商品评价的风格每条评价100字左右要基于原来的评论稍作修改不要更换产品类型只需要好评。不需要太浮夸也不要太像ai生成尽量模拟真实客户评价不要提到以旧换新和国家补贴只要回复我评论的内容就可以。这个是给你参考的其他真实用户的评论";
private static final long TIMEOUT_MINUTES = 5;
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 = "\n手把手教你实现超值下单无需依赖他人全程使用个人专属账号操作所见即所得页面标价即为最终到手价。\n别担心操作难题我会全程贴心指导每一步都细致讲解助你轻松下单。\n后续若出现任何质量问题凭借个人账号就能直接对接JD官方售后售后无忧。\n\n" + "更新\n" + "\n采用自主账号下单模式官方店铺商品随心购专业方法全程提供\n" + "————————————————————\n" + "诚邀同行建立长期合作关系,海量独家家电优惠线报倾囊相授!\n" + "借助这些优质家电线报,无需寻求代购代下服务,自己就能轻松下单,订单信息与收益牢牢掌握在手中。\n" + "一次加入,终身受益!涵盖家电帮、雷神价、韭菜帮、河南 & 湖南帮等众多渠道,还有各类暗号帮后返等内部专属家电优惠信息一网打尽。\n" + "JD采销团队会不定时发放独家隐藏优惠券市面上那些令人心动的JD家电低价好物大多都源自这些渠道\n" + "2025 年家电选购新趋势,依托线报下单,轻松省下千元开支,开启超值购物之旅!";
private static final String WENAN_ZCXS = """
购买后,两小时内出库,物流会电话联系您,同时生成京东官方安装单。送装一体,无需担心。
1:全新正品,原包装未拆封(京东商城代购,就近直发)
2:可提供下单运单号与电子发票(发票在收到货后找我要)。
3:收货时查看是否有质量或运损问题。可拍照让京东免费申请换新。
4:下单后非质量问题不支持退款退货强制退扣100元。
5:价格有浮动,不支持补差价,谢谢理解。
6:全国联保,全国统一安装标准。支持官方 400服务号查询假一赔十。
""";
private static final String WENAN_FANAN_BX = "本人提供免费指导下单服务,一台也是团购价,细心指导\n" + "\n" + "【质量】官旗下单,包正的\n" + "【物流】您自己账户可跟踪24小时发货\n" + "【售后】您自己账户直接联系,无忧售后\n" + "【安装】专业人员安装,全程无需您操心\n" + "【价格】标价就是到手价,骑共享单车去酒吧,该省省该花花\n" + "【服务】手把手教您下单,有问题随时咨询\n" + "【体验】所有服务都是官旗提供,价格有内部渠道优惠,同品质更优惠!\n" + "\n" + "信息更新日期:\n" + "\n" + "捡漏价格不定时有变动,优惠不等人,发「省份+型号」免费咨询当日最低价!";
private static final String FANAN_COMMON = "\n1 文案复制到微点击领券到J东APP结算\n" + "2 换新可直接代消单,不用提供回收\n " + "3 独家虹包 https://u.jd.com/raa0eI4 至高可领256188 \n";
/**
* 内部单号:
* 分销标记(标记用,勿改):
* 型号:
* <p>
* 价格:
* <p>
* 后返:
* <p>
* 地址:
* <p>
* 物流单号:
* <p>
* 订单号:
*/
private static final String WENAN_D = "单:\n" + "{单号} \n备注{单的备注}\n" + "分销标记:{分销标记}\n" + "型号:\n" + "{型号}" + "\n" + "链接:\n" + "{链接}" + "\n" + "下单付款:\n" + "\n" + "后返金额:\n" + "\n" + "地址:\n" + "{地址}" + "\n" + "物流链接:\n" + "\n" + "订单号:\n" + "\n" + "下单人:\n" + "\n";
final WXUtil wxUtil;
private final StringRedisTemplate redisTemplate;
private final OrderRowRepository orderRowRepository;
private final CommentRepository commentRepository;
private JDOrderRepository jdOrderRepository;
private final OrderUtil orderUtil;
private final DeepSeekClientUtil deepSeekClientUtil;
private final GPTClientUtil gptClientUtil;
// 添加ObjectMapper来序列化和反序列化UserInteractionState
private final ObjectMapper objectMapper = new ObjectMapper();
private final ConcurrentHashMap<String, UserInteractionState> userInteractionStates = new ConcurrentHashMap<>();
private HashMap<String, String> cacheMap = new HashMap<>();
// 构造函数中注入StringRedisTemplate
@Autowired
public JDUtil(JDOrderRepository jdOrderRepository, StringRedisTemplate redisTemplate, OrderRowRepository orderRowRepository, WXUtil wxUtil, OrderUtil orderUtil, DeepSeekClientUtil deepSeekClientUtil, CommentRepository commentRepository, GPTClientUtil gptClientUtil) {
this.jdOrderRepository = jdOrderRepository;
this.redisTemplate = redisTemplate;
this.orderRowRepository = orderRowRepository;
this.wxUtil = wxUtil;
this.orderUtil = orderUtil;
this.deepSeekClientUtil = deepSeekClientUtil;
this.commentRepository = commentRepository;
this.gptClientUtil = gptClientUtil;
}
private List<OrderRow> filterOrdersByDate(List<OrderRow> orderRows, int daysBack) {
LocalDate now = LocalDate.now();
return orderRows.stream().filter(order -> {
// 将 Date 转换为 LocalDate
LocalDate orderDate = order.getOrderTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
// 计算是否在给定的天数内
return !orderDate.isBefore(now.minusDays(daysBack)) && !orderDate.isAfter(now);
}).collect(Collectors.toList());
}
private Stream<OrderRow> getStreamForWeiGui(List<OrderRow> todayOrders) {
return todayOrders.stream().filter(orderRow -> orderRow.getValidCode() == 13 || orderRow.getValidCode() == 25 || orderRow.getValidCode() == 26 || orderRow.getValidCode() == 27 || orderRow.getValidCode() == 28 || orderRow.getValidCode() == 29);
}
public void sendOrderToWxByOrderDefault(String order, String fromWxid) {
logger.info("执行 sendOrderToWxByOrderDefault 方法order: {}, fromWxid: {}", order, fromWxid);
handleUserInteraction(fromWxid, order);
// 具体逻辑
}
public void xb(String message, String fromWxid) {
logger.info("执行 线报处理 方法order: {}", message);
if (message.contains("https://u.jd")) {
String chatRoomRemark = chatRoom_xb.get(fromWxid);
// 生成日期
String date = DateUtil.format(new Date(), "HH:mm:ss yyyy-MM-dd");
wxUtil.sendTextMessage(chatRoom_BY, chatRoomRemark + " " + date + "\n\n " + message, 1, chatRoom_BY, true);
// 2. 生成推广内容
HashMap<String, List<String>> contentResult = generatePromotionContent(message, true);
// 3. 发送文本内容
for (String text : contentResult.get("text")) {
wxUtil.sendTextMessage(chatRoom_BY, text, 1, chatRoom_BY, true);
}
}
// 具体逻辑
}
private OrderStats calculateStats(List<OrderRow> orders) {
long paid = orders.stream().filter(o -> o.getValidCode() == 16).count();
long pending = orders.stream().filter(o -> o.getValidCode() == 15).count();
long canceled = orders.stream().filter(o -> o.getValidCode() != 16 && o.getValidCode() != 17).count();
long completed = orders.stream().filter(o -> o.getValidCode() == 17).count();
return new OrderStats(orders.size(), orders.size() - canceled, paid, orders.stream().filter(o -> o.getValidCode() == 16).mapToDouble(OrderRow::getEstimateFee).sum(), pending, orders.stream().filter(o -> o.getValidCode() == 15).mapToDouble(OrderRow::getEstimateFee).sum(), canceled, completed, orders.stream().filter(o -> o.getValidCode() == 17).mapToDouble(OrderRow::getEstimateFee).sum(), getStreamForWeiGui(orders).count(), getStreamForWeiGui(orders).mapToDouble(o -> o.getEstimateCosPrice() * o.getCommissionRate() * 0.01).sum());
}
private StringBuilder buildStatsContentAll(String title, OrderStats stats) {
StringBuilder content = new StringBuilder();
content//[爱心][Wow][Packet][Party][Broken][心碎][亲亲][色]
.append("* ").append(title).append(" *\n").append("━━━━━━━━━━━━\n").append("[爱心] 订单总数:").append(stats.getTotalOrders()).append("\n") // [文件]
.append("[Party] 有效订单:").append(stats.getValidOrders()).append("\n") // [OK]
.append("[心碎]已取消:").append(stats.getCanceledOrders()).append("\n") // [禁止]
.append("────────────\n").append("[爱心] 已付款:").append(stats.getPaidOrders()).append("\n") // [钱袋]
.append("[Packet] 已付款佣金:").append(String.format("%.2f", stats.getPaidCommission())).append("\n") // [钞票]
.append("────────────\n").append("[Wow] 待付款:").append(stats.getPendingOrders()).append("\n") // [时钟]
.append("[Packet] 待付款佣金:").append(String.format("%.2f", stats.getPendingCommission())).append("\n") // [钱]
.append("────────────\n").append("[亲亲] 已完成:").append(stats.getCompletedOrders()).append("\n") // [旗帜]
.append("[Packet] 已完成佣金:").append(String.format("%.2f", stats.getCompletedCommission())).append("\n") // [信用卡]
.append("────────────\n").append("[Emm] 违规订单:").append(stats.getViolations()).append("\n") // [警告]
.append("[Broken] 违规佣金:").append(String.format("%.2f", stats.getViolationCommission())).append("\n") // [炸弹]
.append("━━━━━━━━━━━━");
return content;
}
private StringBuilder buildStatsContentMin(String title, OrderStats stats) {
StringBuilder content = new StringBuilder();
content//[爱心][Wow][Packet][Party][Broken][心碎][亲亲][色]
.append(title).append("\n")
//.append("[爱心] 订单总数:").append(stats.getTotalOrders()).append("\n") // [文件]
.append("[Party] 有效订单:").append(stats.getValidOrders()).append("\n") // [OK]
//.append("[心碎]已取消:").append(stats.getCanceledOrders()).append("\n") // [禁止]
.append("[爱心] 已付款:").append(stats.getPaidOrders())
.append(" >>> ").append(String.format("%.2f", stats.getPaidCommission())).append("\n") // [钞票]
.append("[亲亲] 已完成:").append(stats.getCompletedOrders())
.append(" >>> ").append(String.format("%.2f", stats.getCompletedCommission())).append("\n") // [信用卡]
.append("[Emm] 违规单:").append(stats.getViolations())
.append(" >>> ").append(String.format("%.2f", stats.getViolationCommission())).append("\n") // [炸弹]
.append("━━━━━━━━━━━━\n");
return content;
}
/**
* 接收京粉指令指令
*/
public void sendOrderToWxByOrderJD(String order, String fromWxid) {
int[] param = {-1};
List<WXUtil.SuperAdmin> superAdmins = getSuperAdmins(fromWxid);
List<Long> unionIds = new ArrayList<>();
for (WXUtil.SuperAdmin superAdmin : superAdmins) {
String unionId = superAdmin.getUnionId();
unionIds.add(Long.valueOf(unionId));
}
List<OrderRow> orderRows = orderRowRepository.findByValidCodeNotInAndUnionIdIn(param, unionIds);
/**
* 菜单:
* 今日统计
* 昨日统计
* 最近七天统计
* 最近一个月统计
* 今天订单
* 昨天订单
* */
List<StringBuilder> contents = new ArrayList<>();
StringBuilder content = new StringBuilder();
switch (order) {
case "菜单":
content.append("菜单:京+命令 \n 如: 京今日统计\r");
content.append("今日统计\r");
content.append("昨天统计\r");
content.append("七日统计\r");
content.append("一个月统计\r");
content.append("两个月统计\r");
content.append("三个月统计\r");
content.append("总统计\r\n");
content.append("这个月统计\r");
content.append("上个月统计\r\n");
content.append("今日订单\r");
content.append("昨日订单\r");
content.append("七日订单\r");
content.append("刷新7天\r");
contents.add(content);
content = new StringBuilder();
content.append("高级菜单:京+高级+命令 \n 如: 京高级违规30\r");
content.append("京高级违规+整数(不传数字为365天)\r");
content.append("京高级SKU+sku\r");
content.append("京高级搜索+搜索标题(精准查询订单号+精准查询sku+模糊查询收件人+模糊查询地址)只返回最近100条\r");
contents.add(content);
//content = new StringBuilder();
//
//content.append("礼金\r");
//content.append("转链\r");
//
//contents.add(content);
break;
case "今日统计": {
// 订单总数,已付款,已取消,佣金总计
List<OrderRow> todayOrders = filterOrdersByDate(orderRows, 0);
OrderStats stats = calculateStats(todayOrders);
contents.add(buildStatsContentAll("今日统计", stats));
Map<Long, OrderStats> statsByUnionId = OrderStatsUtil.groupByUnionIdAndCalculateStats(todayOrders);
StringBuilder stringBuilder = new StringBuilder().append("详:\n");
if (statsByUnionId != null) {
for (Map.Entry<Long, OrderStats> entry : statsByUnionId.entrySet()) {
OrderStats statsDetail = entry.getValue();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(entry.getKey()));
stringBuilder.append(buildStatsContentMin("粉:" + remarkFromJdid, statsDetail));
}
}
contents.add(stringBuilder);
break;
}
case "昨日统计": {
List<OrderRow> yesterdayOrders = filterOrdersByDate(orderRows, 1);
OrderStats stats = calculateStats(yesterdayOrders);
contents.add(buildStatsContentAll("昨日统计", stats));
Map<Long, OrderStats> statsByUnionId = OrderStatsUtil.groupByUnionIdAndCalculateStats(yesterdayOrders);
StringBuilder stringBuilder = new StringBuilder().append("详:\n");
if (statsByUnionId != null) {
for (Map.Entry<Long, OrderStats> entry : statsByUnionId.entrySet()) {
OrderStats statsDetail = entry.getValue();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(entry.getKey()));
stringBuilder.append(buildStatsContentMin("粉:" + remarkFromJdid, statsDetail));
}
}
contents.add(stringBuilder);
break;
}
case "三日统计": {
List<OrderRow> last3DaysOrders = filterOrdersByDate(orderRows, 3);
OrderStats stats = calculateStats(last3DaysOrders);
contents.add(buildStatsContentAll("三日统计", stats));
Map<Long, OrderStats> statsByUnionId = OrderStatsUtil.groupByUnionIdAndCalculateStats(last3DaysOrders);
StringBuilder stringBuilder = new StringBuilder().append("详:\n");
if (statsByUnionId != null) {
for (Map.Entry<Long, OrderStats> entry : statsByUnionId.entrySet()) {
OrderStats statsDetail = entry.getValue();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(entry.getKey()));
stringBuilder.append(buildStatsContentMin("粉:" + remarkFromJdid, statsDetail));
}
}
contents.add(stringBuilder);
break;
}
case "七日统计": {
List<OrderRow> last7DaysOrders = filterOrdersByDate(orderRows, 7);
OrderStats stats = calculateStats(last7DaysOrders);
contents.add(buildStatsContentAll("七日统计", stats));
Map<Long, OrderStats> statsByUnionId = OrderStatsUtil.groupByUnionIdAndCalculateStats(last7DaysOrders);
StringBuilder stringBuilder = new StringBuilder().append("详:\n");
if (statsByUnionId != null) {
for (Map.Entry<Long, OrderStats> entry : statsByUnionId.entrySet()) {
OrderStats statsDetail = entry.getValue();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(entry.getKey()));
stringBuilder.append(buildStatsContentMin("粉:" + remarkFromJdid, statsDetail));
}
}
contents.add(stringBuilder);
break;
}
case "一个月统计": {
List<OrderRow> last30DaysOrders = filterOrdersByDate(orderRows, 30);
OrderStats stats = calculateStats(last30DaysOrders);
contents.add(buildStatsContentAll("一个月统计", stats));
Map<Long, OrderStats> statsByUnionId = OrderStatsUtil.groupByUnionIdAndCalculateStats(last30DaysOrders);
StringBuilder stringBuilder = new StringBuilder().append("详:\n");
if (statsByUnionId != null) {
for (Map.Entry<Long, OrderStats> entry : statsByUnionId.entrySet()) {
OrderStats statsDetail = entry.getValue();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(entry.getKey()));
stringBuilder.append(buildStatsContentMin("粉:" + remarkFromJdid, statsDetail));
}
}
contents.add(stringBuilder);
break;
}
case "两个月统计": {
List<OrderRow> last60DaysOrders = filterOrdersByDate(orderRows, 60);
OrderStats stats = calculateStats(last60DaysOrders);
contents.add(buildStatsContentAll("两个月统计", stats));
Map<Long, OrderStats> statsByUnionId = OrderStatsUtil.groupByUnionIdAndCalculateStats(last60DaysOrders);
StringBuilder stringBuilder = new StringBuilder().append("详:\n");
if (statsByUnionId != null) {
for (Map.Entry<Long, OrderStats> entry : statsByUnionId.entrySet()) {
OrderStats statsDetail = entry.getValue();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(entry.getKey()));
stringBuilder.append(buildStatsContentMin("粉:" + remarkFromJdid, statsDetail));
}
}
contents.add(stringBuilder);
break;
}
case "三个月统计": {
List<OrderRow> last90DaysOrders = filterOrdersByDate(orderRows, 90);
OrderStats stats = calculateStats(last90DaysOrders);
contents.add(buildStatsContentAll("三个月统计", stats));
Map<Long, OrderStats> statsByUnionId = OrderStatsUtil.groupByUnionIdAndCalculateStats(last90DaysOrders);
StringBuilder stringBuilder = new StringBuilder().append("详:\n");
if (statsByUnionId != null) {
for (Map.Entry<Long, OrderStats> entry : statsByUnionId.entrySet()) {
OrderStats statsDetail = entry.getValue();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(entry.getKey()));
stringBuilder.append(buildStatsContentMin("粉:" + remarkFromJdid, statsDetail));
}
}
contents.add(stringBuilder);
break;
}
case "这个月统计": {
// 计算出距离1号有几天
int days = LocalDate.now().getDayOfMonth();
List<OrderRow> thisMonthOrders = filterOrdersByDate(orderRows, days);
OrderStats stats = calculateStats(thisMonthOrders);
contents.add(buildStatsContentAll("这个月统计", stats));
Map<Long, OrderStats> statsByUnionId = OrderStatsUtil.groupByUnionIdAndCalculateStats(thisMonthOrders);
StringBuilder stringBuilder = new StringBuilder().append("详:\n");
if (statsByUnionId != null) {
for (Map.Entry<Long, OrderStats> entry : statsByUnionId.entrySet()) {
OrderStats statsDetail = entry.getValue();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(entry.getKey()));
stringBuilder.append(buildStatsContentMin("粉:" + remarkFromJdid, statsDetail));
}
}
contents.add(stringBuilder);
break;
}
case "上个月统计": {
LocalDate lastMonth = LocalDate.now().minusMonths(1);
int days = LocalDate.now().getDayOfMonth();
List<OrderRow> lastMonthOrders = filterOrdersByDate(orderRows, lastMonth.lengthOfMonth() + days);
List<OrderRow> thisMonthOrders = filterOrdersByDate(orderRows, days);
lastMonthOrders = lastMonthOrders.stream().filter(orderRow -> !thisMonthOrders.contains(orderRow)).collect(Collectors.toList());
OrderStats stats = calculateStats(lastMonthOrders);
contents.add(buildStatsContentAll("上个月统计", stats));
Map<Long, OrderStats> statsByUnionId = OrderStatsUtil.groupByUnionIdAndCalculateStats(lastMonthOrders);
StringBuilder stringBuilder = new StringBuilder().append("详:\n");
if (statsByUnionId != null) {
for (Map.Entry<Long, OrderStats> entry : statsByUnionId.entrySet()) {
OrderStats statsDetail = entry.getValue();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(entry.getKey()));
stringBuilder.append(buildStatsContentMin("粉:" + remarkFromJdid, statsDetail));
}
}
contents.add(stringBuilder);
break;
}
//总统计
case "总统计": {
OrderStats stats = calculateStats(orderRows);
contents.add(buildStatsContentAll("总统计", stats));
Map<Long, OrderStats> statsByUnionId = OrderStatsUtil.groupByUnionIdAndCalculateStats(orderRows);
StringBuilder stringBuilder = new StringBuilder().append("详:\n");
if (statsByUnionId != null) {
for (Map.Entry<Long, OrderStats> entry : statsByUnionId.entrySet()) {
OrderStats statsDetail = entry.getValue();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(entry.getKey()));
stringBuilder.append(buildStatsContentMin("粉:" + remarkFromJdid, statsDetail));
}
}
contents.add(stringBuilder);
break;
}
case "今日订单": {
List<OrderRow> todayOrders = filterOrdersByDate(orderRows, 0);
// 订单总数,已付款,已取消,佣金总计
OrderStats stats = calculateStats(todayOrders);
contents.add(buildStatsContentAll("今日统计", stats));
if (!todayOrders.isEmpty()) {
orderUtil.orderToWxBatch(todayOrders);
}
break;
}
case "昨日订单": {
content = new StringBuilder();
List<OrderRow> yesterdayOrders = filterOrdersByDate(orderRows, 1);
List<OrderRow> todayOrders = filterOrdersByDate(orderRows, 0);
logger.info("原始订单数量:{}", orderRows.size());
logger.info("昨日过滤后数量:{}", yesterdayOrders.size());
yesterdayOrders.removeAll(todayOrders);
logger.info("今日过滤后数量:{}", todayOrders.size());
logger.info("最终昨日订单数量:{}", yesterdayOrders.size());
OrderStats stats = calculateStats(yesterdayOrders);
contents.add(buildStatsContentAll("昨日统计", stats));
if (!yesterdayOrders.isEmpty()) {
orderUtil.orderToWxBatch(yesterdayOrders);
}
break;
}
case "七日订单": {
List<OrderRow> last7DaysOrders = filterOrdersByDate(orderRows, 1);
List<OrderRow> todayOrders = filterOrdersByDate(orderRows, 0);
last7DaysOrders.removeAll(todayOrders);
OrderStats stats = calculateStats(last7DaysOrders);
contents.add(buildStatsContentAll("七日统计", stats));
if (!last7DaysOrders.isEmpty()) {
orderUtil.orderToWxBatch(last7DaysOrders);
}
break;
}
default:
sendOrderToWxByOrderJDAdvanced(order, fromWxid);
}
if (!contents.isEmpty()) {
for (StringBuilder stringBuilder : contents) {
wxUtil.sendTextMessage(fromWxid, stringBuilder.toString(), 1, fromWxid, false);
}
}
}
/**
* 接收京粉指令指令
* 高级菜单
*/
public void sendOrderToWxByOrderJDAdvanced(String order, String fromWxid) {
int[] param = {-1};
List<WXUtil.SuperAdmin> superAdmins = getSuperAdmins(fromWxid);
List<Long> unionIds = new ArrayList<>();
for (WXUtil.SuperAdmin superAdmin : superAdmins) {
String unionId = superAdmin.getUnionId();
unionIds.add(Long.valueOf(unionId));
}
List<OrderRow> orderRows = orderRowRepository.findByValidCodeNotInAndUnionIdIn(param, unionIds);
List<StringBuilder> contents = new ArrayList<>();
StringBuilder content = new StringBuilder();
if (order.startsWith("高级")) {
content = new StringBuilder();
order = order.replace("高级", "");
if (order.startsWith("价保")) {
String search;
if (order.length() > 2) {
search = order.substring(2);
} else {
search = null;
}
// 获取过去30天内的订单
List<OrderRow> last30DaysOrders = filterOrdersByDate(orderRows, 30);
// 过滤出 proPriceAmount 大于 0 的订单
List<OrderRow> priceProtectedOrders = last30DaysOrders.stream().filter(orderRow -> {
if (search != null) {
return orderRow.getProPriceAmount() > 0 && orderRow.getSkuName().contains(search);
}
return false;
}).collect(Collectors.toList());
if (!priceProtectedOrders.isEmpty()) {
//调用 orderUtil.orderToWx(orderRow, true);
//priceProtectedOrders.forEach(orderRow -> orderUtil.orderToWx(orderRow, true, true));
// 可选:发送具体的订单信息给用户
orderUtil.orderToWxBatchForJB(priceProtectedOrders);
} else {
wxUtil.sendTextMessage(fromWxid, "没有找到符合条件的价保订单。", 1, fromWxid, false);
}
}
if (order.startsWith("违规")) {
String days = order.replace("违规", "");
Integer daysInt = 365;
if (Util.isNotEmpty(days)) {
daysInt = Integer.parseInt(days);
}
List<OrderRow> filterOrdersByDays = filterOrdersByDate(orderRows, daysInt);
content.append("违规排行:");
content.append(daysInt).append("").append("\r\n");
Map<String, Long> skuIdViolationCountMap = filterOrdersByDays.stream().filter(orderRow -> orderRow.getValidCode() == 27 || orderRow.getValidCode() == 28).filter(orderRow -> orderRow.getSkuName() != null).collect(Collectors.groupingBy(OrderRow::getSkuName, // ✅ 拼接SKU
Collectors.counting()));
Map<String, List<OrderInfo>> orderInfoMap = filterOrdersByDays.stream().filter(orderRow -> orderRow.getValidCode() == 27 || orderRow.getValidCode() == 28).filter(orderRow -> orderRow.getSkuName() != null).map(orderRow -> {
OrderInfo info = new OrderInfo();
info.setSkuName(orderRow.getSkuName());
info.setOrderId(orderRow.getOrderId());
info.setOrderDate(orderRow.getOrderTime());
return info;
}).collect(Collectors.groupingBy(OrderInfo::getSkuName));
List<Map.Entry<String, Long>> sortedViolationCounts = skuIdViolationCountMap.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).toList();
Integer num = 0;
for (Map.Entry<String, Long> entry : sortedViolationCounts) {
num++;
String skuName = entry.getKey();
Long count = entry.getValue();
// 修改后直接使用已包含SKU信息的key
content.append("\n").append(num).append(",商品:").append(entry.getKey()) // 这里已包含SKU信息
.append("\r\r").append(" 违规次数:").append(count).append("\r");
List<OrderInfo> infos = orderInfoMap.get(skuName);
if (infos != null) {
for (OrderInfo info : infos) {
content.append("\n 订单:").append(info.getOrderId()).append("\r 下单:").append(info.getOrderDate()).append("\r");
}
}
}
contents.add(content);
}
// 订单查询
if (order.startsWith("搜索")) {
order = order.replace("搜索", "");
content = new StringBuilder();
// 精准查询订单号+精准查询sku+模糊查询收件人+模糊查询地址
content.append("精准查询订单号:\r");
List<OrderRow> orderRowList = orderRowRepository.findByOrderId(Long.parseLong(order));
if (!orderRowList.isEmpty()) {
OrderRow orderRow = orderRowList.get(0);
if (unionIds.contains(orderRow.getUnionId())) {
content.append(orderUtil.getFormattedOrderInfo(orderRow));
} else {
content.append("订单不属于你,无法查询\r");
}
} else {
content.append("订单不存在\r");
}
contents.add(content);
content = new StringBuilder();
// 不统计已取消的订单
content.append("精准查询sku不统计已取消的订单:\r");
int[] validCodes = {-1, 3};
List<OrderRow> bySkuIdAndUnionId = orderRowRepository.findBySkuIdAndUnionIdIn(validCodes, Long.parseLong(order), unionIds);
int size = bySkuIdAndUnionId.size();
content.append("查询到").append(size).append("条订单\r");
// 切割成20条20条返回前100条
for (int i = 0; i < size; i += 20) {
List<OrderRow> subList = bySkuIdAndUnionId.subList(i, Math.min(i + 20, size));
content.append("").append(i / 20 + 1).append("页:\r");
for (OrderRow orderRow : subList) {
content.append(orderUtil.getFormattedOrderInfo(orderRow));
contents.add(content);
content = new StringBuilder();
}
}
content = new StringBuilder();
content.append("模糊查询收件人+模糊查询地址:\r");
//List<OrderRow> orderRowList = orderRowRepository
content.append("暂不支持");
contents.add(content);
}
if (order.startsWith("SKU")) {
content = new StringBuilder();
order = order.replace("SKU", "");
String[] split = order.split("\r\n");
content.append("电脑端").append("\r\n");
for (String s : split) {
content.append("https://item.jd.com/").append(s.trim()).append(".html").append("\r\n");
}
wxUtil.sendTextMessage(fromWxid, content.toString(), 1, fromWxid, false);
content = new StringBuilder();
content.append("手机端").append("\r\n");
for (String s : split) {
content.append("https://item.m.jd.com/product/").append(s.trim()).append(".html").append("\r\n");
}
wxUtil.sendTextMessage(fromWxid, content.toString(), 1, fromWxid, false);
content = new StringBuilder();
contents.add(content);
}
// 转链
if (order.startsWith("转链")) {
content = new StringBuilder();
order = order.replace("转链", "");
String jsonString;
try {
GoodsQueryResult goodsQueryResult = queryProductInfoByUJDUrl(order);
jsonString = JSON.toJSONString(goodsQueryResult);
content.append(jsonString);
} catch (Exception e) {
content.append(e);
throw new RuntimeException(e);
}
contents.add(content);
}
} else {
try {
sendOrderToWxByOrderJD("菜单", fromWxid);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
if (!contents.isEmpty()) {
for (StringBuilder stringBuilder : contents) {
wxUtil.sendTextMessage(fromWxid, stringBuilder.toString(), 1, fromWxid, false);
}
}
}
/**
* 接口描述:通过商品链接、领券链接、活动链接获取普通推广链接或优惠券二合一推广链接
* jd.union.open.promotion.bysubunionid.get
*/
String transfer(String url, String giftCouponKey) {
JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, LPF_APP_KEY_WZ, LPF_SECRET_KEY_WZ);
UnionOpenPromotionBysubunionidGetRequest request = new UnionOpenPromotionBysubunionidGetRequest();
PromotionCodeReq promotionCodeReq = new PromotionCodeReq();
promotionCodeReq.setSceneId(1);
promotionCodeReq.setMaterialId(url);
if (giftCouponKey != null) {
promotionCodeReq.setGiftCouponKey(giftCouponKey);
}
request.setPromotionCodeReq(promotionCodeReq);
request.setVersion("1.0");
//logger.info("transfer - request {}", JSON.toJSONString(request));
UnionOpenPromotionBysubunionidGetResponse response;
try {
response = client.execute(request);
//logger.info("transfer - response {}", JSON.toJSONString(response));
} catch (Exception e) {
throw new RuntimeException(e);
}
/**
* {
* "jd_union_open_promotion_bysubunionid_get_responce": {
* "getResult": {
* "code": "200",
* "data": {
* "clickURL": "https://union-click.jd.com/jdc?e=XXXXXX p=XXXXXXXXXXX",
* "weChatShortLink": "#小程序://京小街/****",
* "jShortCommand": "短口令",
* "shortURL": "https://u.jd.com/XXXXX",
* "jCommand": "6.0复制整段话 http://JhT7V5wlKygHDK京口令内容#J6UFE5iMn***"
* },
* "message": "success"
* }
* }
* }
* */
String result = "";
if (Util.isNotEmpty(response)) {
if (response.getCode().equals("0") && response.getGetResult().getCode() == 200) {
result = response.getGetResult().getData().getShortURL();
}
} else {
result = null;
}
return result;
}
/**
* 消毒柜部分的业务逻辑
*/
@Scheduled(fixedRate = 1000) // 每10分钟执行一次
public void cleanUpTimeoutStates() {
LocalDateTime now = LocalDateTime.now();
redisTemplate.keys(INTERACTION_STATE_PREFIX + "*").forEach(key -> {
String stateJson = redisTemplate.opsForValue().get(key);
try {
UserInteractionState state = objectMapper.readValue(stateJson, UserInteractionState.class);
LocalDateTime lastInteractionTime = LocalDateTime.parse(state.getLastInteractionTime(), DATE_TIME_FORMATTER);
if (ChronoUnit.MINUTES.between(lastInteractionTime, now) > TIMEOUT_MINUTES) {
String wxid = key.replace(INTERACTION_STATE_PREFIX, "");
wxUtil.sendTextMessage(wxid, TIMEOUT_MINUTES + " 分钟未操作,已退出会话", 1, wxid, false);
redisTemplate.delete(key);
logger.debug("Deleted timeout state for key: {}", key);
}
} catch (Exception e) {
logger.error("Error parsing interaction state: {}", e.getMessage());
}
});
}
private void handleUserInteraction(String fromWxid, String message) {
String key = INTERACTION_STATE_PREFIX + fromWxid;
UserInteractionState state = loadOrCreateState(key);
try {
switch (state.getCurrentState()) {
case INIT:
handleInitState(fromWxid, message, state);
break;
case PRODUCT_PROMOTION:
handlePromotionState(fromWxid, message, state);
break;
case GIFT_MONEY_FLOW:
handleGiftMoneyFlow(fromWxid, message, state);
break;
default:
resetState(fromWxid, state);
wxUtil.sendTextMessage(fromWxid, "流程异常,请重新开始", 1, fromWxid, false);
}
} catch (Exception e) {
logger.error("流程处理异常 - 用户: {}, 状态: {}", fromWxid, state, e);
resetState(fromWxid, state);
wxUtil.sendTextMessage(fromWxid, "处理异常,请重新开始", 1, fromWxid, false);
} finally {
saveState(key, state);
}
userInteractionStates.put(key, state);
logger.debug("Updated interaction state for user {}: {}", fromWxid, JSON.toJSONString(state));
}
private UserInteractionState loadOrCreateState(String key) {
UserInteractionState state = loadState(key);
if (state == null) {
state = new UserInteractionState();
logger.debug("创建新交互状态: {}", key);
} else if (isStateExpired(state)) {
redisTemplate.delete(key);
state = new UserInteractionState();
logger.debug("状态已过期,重置: {}", key);
}
return state;
}
private boolean isStateExpired(UserInteractionState state) {
LocalDateTime lastTime = LocalDateTime.parse(state.getLastInteractionTime(), DATE_TIME_FORMATTER);
return ChronoUnit.MINUTES.between(lastTime, LocalDateTime.now()) > TIMEOUT_MINUTES;
}
private void handleInitState(String wxid, String message, UserInteractionState state) {
if ("".equals(message)) {
state.setCurrentState(UserInteractionState.ProcessState.PRODUCT_PROMOTION);
state.setCurrentField("content—withOutPic");
wxUtil.sendTextMessage(wxid, "请输入推广方案 \n " + "(指令: 转 不返回图文 \n 文案 带图文 ", 1, wxid, false);
logger.info("进入转链流程 - 用户: {}", wxid);
} else if ("文案".equals(message)) {
state.setCurrentState(UserInteractionState.ProcessState.PRODUCT_PROMOTION);
state.setCurrentField("content—withPic");
wxUtil.sendTextMessage(wxid, "请输入推广方案 \n " + "(指令: 转 不返回图文 \n 文案 带图文 ", 1, wxid, false);
logger.info("进入转链流程 - 用户: {}", wxid);
}
}
private void handlePromotionState(String wxid, String message, UserInteractionState state) {
if ("content—withOutPic".equals(state.getCurrentField())) {
processContentInput(wxid, message, state, false);
} else if ("content—withPic".equals(state.getCurrentField())) {
processContentInput(wxid, message, state, true);
} else if ("confirm".equals(state.getCurrentField())) {
handleGiftMoneyConfirmation(wxid, message, state);
}
}
private void handleGiftMoneyFlow(String wxid, String message, UserInteractionState state) {
if (!state.validateStep(state.getCurrentStep())) {
wxUtil.sendTextMessage(wxid, "流程顺序异常,请重新开始", 1, wxid, false);
return;
}
switch (state.getCurrentStep()) {
case STEP_AMOUNT:
processAmountInput(wxid, message, state);
break;
case STEP_QUANTITY:
processQuantityInput(wxid, message, state);
break;
default:
state.setCurrentStep(STEP_AMOUNT);
wxUtil.sendTextMessage(wxid, "[Packet] 请输入金额1-50元", 1, wxid, false);
}
}
private void processAmountInput(String wxid, String message, UserInteractionState state) {
if (!isValidAmount(message)) {
wxUtil.sendTextMessage(wxid, "金额格式错误请输入1-50元", 1, wxid, false);
return;
}
state.getCollectedFields().put("amount", message);
state.setCurrentStep(STEP_QUANTITY);
wxUtil.sendTextMessage(wxid, "请输入数量1-100", 1, wxid, false);
}
private void processQuantityInput(String wxid, String message, UserInteractionState state) {
if (!isValidQuantity(message)) {
wxUtil.sendTextMessage(wxid, "数量格式错误请输入1-100的整数", 1, wxid, false);
return;
}
// 从缓存获取商品数据
String productDataJson = cacheMap.get("productData" + wxid);
String finalWenAn = cacheMap.get("finalWenAn" + wxid);
if (productDataJson == null || finalWenAn == null) {
logger.error("数据丢失 - productData: {}, finalWenAn: {}", productDataJson != null, finalWenAn != null);
wxUtil.sendTextMessage(wxid, "数据丢失,请重新开始流程", 1, wxid, false);
resetState(wxid, state);
return;
}
try {
// 解析商品数据
/**
* // 取出来的每一个String都是JSONObject
* List<String> data = contentResult.get("data");
* JSONArray jsonObjectArr = new JSONArray(data);
*
*
* cacheMap.put("productData" + wxid, jsonObjectArr.toJSONString());*/
List<String> productList = JSON.parseArray(productDataJson, String.class);
double amount = Double.parseDouble(state.getCollectedFields().get("amount"));
int quantity = Integer.parseInt(message);
StringBuilder result = new StringBuilder();
String updatedContent = finalWenAn;
for (String productJSONString : productList) {
// 序列化好了的
JSONObject product = JSON.parseObject(productJSONString);
String skuId = product.getString("materialUrl");
String owner = product.getString("owner");
String skuName = product.getString("skuName");
String originalUrl = product.getString("url");
String giftKey = createGiftCoupon(skuId, amount, quantity, owner, skuName);
if (giftKey == null) {
result.append(" ").append(skuName).append(" 礼金创建失败\n");
continue;
}
String transferUrl = transfer(skuId, giftKey);
if (transferUrl != null) {
updatedContent = updatedContent.replace(originalUrl, transferUrl);
result.append(" ").append(skuName).append(" 礼金创建成功\n");
} else {
result.append(" ").append(skuName).append(" 转链失败\n");
}
}
wxUtil.sendTextMessage(wxid, result.toString(), 1, wxid, false);
wxUtil.sendTextMessage(wxid, updatedContent, 1, wxid, true);
} catch (Exception e) {
logger.error("礼金处理异常", e);
wxUtil.sendTextMessage(wxid, "处理异常:" + e.getMessage(), 1, wxid, false);
} finally {
resetState(wxid, state);
cacheMap.remove("productData" + wxid);
cacheMap.remove("finalWenAn" + wxid);
}
}
/**
* 处理用户输入的推广方案内容
*
* @param wxid 用户微信ID
* @param message 用户输入的方案内容
* @param state 当前交互状态
* @param b
*/
private void processContentInput(String wxid, String message, UserInteractionState state, boolean withPic) {
try {
// 1. 清除旧缓存
cacheMap.remove("productData" + wxid);
cacheMap.remove("finalWenAn" + wxid);
// 2. 生成推广内容
HashMap<String, List<String>> contentResult = generatePromotionContent(message, false);
if (withPic) {
// 3. 发送文本内容
for (String text : contentResult.get("text")) {
wxUtil.sendTextMessage(wxid, text, 1, wxid, true);
}
// 4. 发送图片(如果有)
List<String> images = contentResult.get("images");
if (images != null) {
for (String imageUrl : images) {
if (imageUrl != null) {
wxUtil.sendImageMessage(wxid, imageUrl);
}
}
}
} else {
// 3. 发送文本内容
for (String text : contentResult.get("text")) {
if (text.contains("链接类型")) {
wxUtil.sendTextMessage(wxid, text, 1, wxid, true);
}
}
}
// 5. 缓存商品数据
if (!contentResult.get("data").isEmpty()) {
// 取出来的每一个String都是JSONObject
List<String> data = contentResult.get("data");
JSONArray jsonObjectArr = new JSONArray(data);
cacheMap.put("productData" + wxid, jsonObjectArr.toJSONString());
cacheMap.put("finalWenAn" + wxid, contentResult.get("finalWenAn").get(0));
state.setCurrentField("confirm");
wxUtil.sendTextMessage(wxid, "检测到" + jsonObjectArr.size() + "个商品\n" + "是否需要开通礼金?\n回复 1 - 是\n回复 任何其他消息退出", 1, wxid, false);
} else {
wxUtil.sendTextMessage(wxid, "未获取到商品数据,请检查链接格式", 1, wxid, false);
state.reset();
}
} catch (Exception e) {
logger.error("处理推广内容异常 - 用户: {}", wxid, e);
wxUtil.sendTextMessage(wxid, "处理内容时发生异常,请重试", 1, wxid, false);
state.reset();
}
}
/**
* 处理用户对礼金开通的确认
*
* @param wxid 用户微信ID
* @param message 用户回复1或2
* @param state 当前交互状态
*/
private void handleGiftMoneyConfirmation(String wxid, String message, UserInteractionState state) {
try {
String cachedData = cacheMap.get("productData" + wxid);
JSONArray jsonObjectArr = JSON.parseArray(cachedData);
if ("1".equals(message)) {
// 用户选择开通礼金
state.setCurrentState(UserInteractionState.ProcessState.GIFT_MONEY_FLOW);
state.setCurrentStep(STEP_AMOUNT);
state.getCollectedFields().clear();
wxUtil.sendTextMessage(wxid, "当前选择" + jsonObjectArr.size() + "个商品\n" + "请输入开通金额1-50元支持小数点后两位\n" + "示例20.50", 1, wxid, false);
} else {
// 用户选择不开通礼金
if (cachedData != null) {
try {
// 清理缓存和状态
resetState(wxid, state);
cacheMap.remove("productData" + wxid);
cacheMap.remove("finalWenAn" + wxid);
wxUtil.sendTextMessage(wxid, "已清除商品信息缓存,可输入新的文案", 1, wxid, false);
} catch (Exception e) {
logger.error("处理不开礼金转链失败 - 用户: {}", wxid, e);
wxUtil.sendTextMessage(wxid, "处理失败:" + e.getMessage(), 1, wxid, false);
resetState(wxid, state);
}
}
}
} catch (Exception e) {
logger.error("处理礼金确认异常 - 用户: {}", wxid, e);
wxUtil.sendTextMessage(wxid, "处理请求时发生异常,请重试", 1, wxid, false);
state.reset();
}
}
private void resetState(String wxid, UserInteractionState state) {
state.reset();
saveState(INTERACTION_STATE_PREFIX + wxid, state);
}
public static List<String> extractPrices(String text) {
List<String> prices = new ArrayList<>();
if (text.contains("🔥")) {
// 处理包含 🔥 的文本
Pattern pattern = Pattern.compile("\\d+(\\.\\d+)?💰");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
String priceStr = matcher.group().replace("💰", "");
prices.add(priceStr);
}
} else {
// 处理不包含 🔥 的文本
Pattern pattern = Pattern.compile("💰(\\d+)");
Matcher matcher = pattern.matcher(text);
while (matcher.find()) {
prices.add(matcher.group(1));
}
}
return prices;
}
/**
* 生成转链和方案的方法
*
* @param message 方案内容,包含商品链接
* @return 处理后的方案,附带商品信息
*/
public synchronized HashMap<String, List<String>> generatePromotionContent(String message, Boolean isCj) {
HashMap<String, List<String>> finallyMessage = new HashMap<>();
List<String> textList = new ArrayList<>();
List<String> imagesList = new ArrayList<>();
List<String> dataList = new ArrayList<>();
// 最终的方案
List<String> finalWenAn = new ArrayList<>();
// 提取方案中的所有 u.jd.com 链接
List<String> urlList = new ArrayList<>();
List<String> priceList = new ArrayList<>();
// 提取方案中的所有 u.jd.com 链接
List<String> urls = extractUJDUrls(message);
if (urls.isEmpty()) {
textList.add("方案中未找到有效的商品链接,请检查格式是否正确。");
finallyMessage.put("text", textList);
return finallyMessage;
}
/**
* {
* "jd_union_open_goods_query_responce": {
* "code": "0",
* "queryResult": {
* "code": 200,
* "data": [
* {
* "brandCode": "16407",
* "brandName": "松下Panasonic",
* "categoryInfo": {
* "cid1": 737,
* "cid1Name": "家用电器",
* "cid2": 794,
* "cid2Name": "大 家 电",
* "cid3": 880,
* "cid3Name": "洗衣机"
* },
* "comments": 10000,
* "commissionInfo": {
* "commission": 599.95,
* "commissionShare": 5,
* "couponCommission": 592.95,
* "endTime": 1743609599000,
* "isLock": 1,
* "plusCommissionShare": 5,
* "startTime": 1742486400000
* },
* "couponInfo": {
* "couponList": [
* {
* "bindType": 1,
* "couponStatus": 0,
* "couponStyle": 0,
* "discount": 100,
* "getEndTime": 1746028799000,
* "getStartTime": 1743436800000,
* "isBest": 1,
* "isInputCoupon": 1,
* "link": "https://coupon.m.jd.com/coupons/show.action?linkKey=AAROH_xIpeffAs_-naABEFoeVJzBK6Q6mfry4nz4ylRYHXsp_NnipDSrReNwGZGTxXhj94SGi9SkzR_7xaWEXLpO2boqow",
* "platformType": 0,
* "quota": 5000,
* "useEndTime": 1746028799000,
* "useStartTime": 1743436800000
* },
* {
* "bindType": 1,
* "couponStatus": 0,
* "couponStyle": 0,
* "discount": 50,
* "getEndTime": 1746028799000,
* "getStartTime": 1743436800000,
* "hotValue": 0,
* "isBest": 0,
* "isInputCoupon": 0,
* "link": "https://coupon.m.jd.com/coupons/show.action?linkKey=AAROH_xIpeffAs_-naABEFoervD9YaNNXlsj6OBbZoWSFI1sZXw31PSjK04AH6tFP_Iu0YJlePWBT6sZfNvp14W0QK2K6A",
* "platformType": 0,
* "quota": 5000,
* "useEndTime": 1746028799000,
* "useStartTime": 1743436800000
* },
* {
* "bindType": 1,
* "couponStatus": 0,
* "couponStyle": 0,
* "discount": 40,
* "getEndTime": 1746028799000,
* "getStartTime": 1743436800000,
* "hotValue": 0,
* "isBest": 0,
* "isInputCoupon": 0,
* "link": "https://coupon.m.jd.com/coupons/show.action?linkKey=AAROH_xIpeffAs_-naABEFoeqQdmhZbv1TiAn0QqI5gnT4g_zAgV5887llN8j6TatV-zpDfReipMr-hKkwQasE9NNUV2uQ",
* "platformType": 0,
* "quota": 500,
* "useEndTime": 1746028799000,
* "useStartTime": 1743436800000
* },
* {
* "bindType": 1,
* "couponStatus": 0,
* "couponStyle": 0,
* "discount": 20,
* "getEndTime": 1746028799000,
* "getStartTime": 1743436800000,
* "hotValue": 0,
* "isBest": 0,
* "isInputCoupon": 0,
* "link": "https://coupon.m.jd.com/coupons/show.action?linkKey=AAROH_xIpeffAs_-naABEFoepukjRCuLpbwceODRbBw5HpNLRTVe5Olp99d34Izdo0s9lDtTIdyYZ-uJRCEXnc5N3OZ5LA",
* "platformType": 0,
* "quota": 2000,
* "useEndTime": 1746028799000,
* "useStartTime": 1743436800000
* },
* {
* "bindType": 1,
* "couponStatus": 0,
* "couponStyle": 0,
* "discount": 10,
* "getEndTime": 1746028799000,
* "getStartTime": 1743436800000,
* "hotValue": 0,
* "isBest": 0,
* "isInputCoupon": 0,
* "link": "https://coupon.m.jd.com/coupons/show.action?linkKey=AAROH_xIpeffAs_-naABEFoeKIIbuvk-VsfJj_m1o3PRHqRaEBIbXHwooEMMt3z44T3cNQTkQsS8TjGnEeIz1obKp_t_mQ",
* "platformType": 0,
* "quota": 1000,
* "useEndTime": 1746028799000,
* "useStartTime": 1743436800000
* }
* ]
* },
* "deliveryType": 1,
* "eliteType": [],
* "forbidTypes": [
* 0
* ],
* "goodCommentsShare": 99,
* "imageInfo": {
* "imageList": [
* {
* "url": "https://img14.360buyimg.com/pop/jfs/t1/273873/17/14072/113544/67ebd215Feef3f56b/fc5156bf59a25ba3.jpg"
* },
* {
* "url": "https://img14.360buyimg.com/pop/jfs/t1/273802/16/14273/84419/67ebd224F72dbcc8d/8ffe6f99aeeeb8fd.jpg"
* },
* {
* "url": "https://img14.360buyimg.com/pop/jfs/t1/278931/40/12819/81341/67ea0227F41ffc604/ab1c6727e5d4f224.jpg"
* },
* {
* "url": "https://img14.360buyimg.com/pop/jfs/t1/276989/12/13440/79310/67ea0226F68c2ed40/8acdeda05aa3596b.jpg"
* },
* {
* "url": "https://img14.360buyimg.com/pop/jfs/t1/284503/25/12384/59498/67ea0225F8be60c83/b29ea34abc64346e.jpg"
* },
* {
* "url": "https://img14.360buyimg.com/pop/jfs/t1/284667/29/12481/56118/67ea0225F97d9c729/f4c81d77064957bd.jpg"
* },
* {
* "url": "https://img14.360buyimg.com/pop/jfs/t1/270959/35/13852/50552/67ea0224F911b8f00/248f9a7751549db7.jpg"
* },
* {
* "url": "https://img14.360buyimg.com/pop/jfs/t1/274981/3/13326/48019/67ea0224Fe64bca69/b062218fc8db9b29.jpg"
* },
* {
* "url": "https://img14.360buyimg.com/pop/jfs/t1/283691/1/12478/82267/67ea0223Fd4ecb0f5/2c32de327d8aa440.jpg"
* },
* {
* "url": "https://img14.360buyimg.com/pop/jfs/t1/281457/31/12476/94335/67ea0222Fde76593a/cdddcd59b0b20c9a.jpg"
* }
* ]
* },
* "inOrderComm30Days": 633170.8,
* "inOrderCount30Days": 3000,
* "inOrderCount30DaysSku": 100,
* "isHot": 1,
* "isJdSale": 1,
* "isOversea": 0,
* "itemId": "BsrqLq5CfIziE7BSl3ItPp8q_3DFaNVYJqfkRRLc7HR",
* "jxFlags": [],
* "materialUrl": "jingfen.jd.com/detail/BsrqLq5CfIziE7BSl3ItPp8q_3DFaNVYJqfkRRLc7HR.html",
* "oriItemId": "BMrqLq5CfIz9X04KC3ItPp8q_3DFaNVYJqfkRRLc7HR",
* "owner": "g",
* "pinGouInfo": {},
* "pingGouInfo": {},
* "priceInfo": {
* "lowestCouponPrice": 11899,
* "lowestPrice": 11999,
* "lowestPriceType": 1,
* "price": 11999
* },
* "purchasePriceInfo": {
* "basisPriceType": 1,
* "code": 200,
* "couponList": [
* {
* "bindType": 1,
* "couponStatus": 0,
* "couponStyle": 0,
* "discount": 100,
* "isBest": 0,
* "link": "https://coupon.m.jd.com/coupons/show.action?linkKey=AAROH_xIpeffAs_-naABEFoeVJzBK6Q6mfry4nz4ylRYHXsp_NnipDSrReNwGZGTxXhj94SGi9SkzR_7xaWEXLpO2boqow",
* "platformType": 0,
* "quota": 5000
* },
* {
* "bindType": 1,
* "couponStatus": 0,
* "couponStyle": 0,
* "discount": 40,
* "isBest": 0,
* "link": "https://coupon.m.jd.com/coupons/show.action?linkKey=AAROH_xIpeffAs_-naABEFoeqQdmhZbv1TiAn0QqI5gnT4g_zAgV5887llN8j6TatV-zpDfReipMr-hKkwQasE9NNUV2uQ",
* "platformType": 0,
* "quota": 500
* }
* ],
* "message": "success",
* "purchaseNum": 1,
* "purchasePrice": 11859,
* "thresholdPrice": 11999
* },
* "shopInfo": {
* "shopId": 1000001741,
* "shopLabel": "0",
* "shopLevel": 4.9,
* "shopName": "松下洗衣机京东自营旗舰店"
* },
* "skuName": "松下Panasonic白月光4.0Ultra 洗烘套装 10kg滚筒洗衣机+变频热泵烘干机 除毛升级2.0 水氧SPA护理 8532N+8532NR",
* "skuTagList": [
* {
* "index": 1,
* "name": "自营",
* "type": 11
* },
* {
* "index": 1,
* "name": "plus",
* "type": 6
* },
* {
* "index": 3,
* "name": "7天无理由退货",
* "type": 12
* }
* ],
* "spuid": 100137629936,
* "videoInfo": {}
* }
* ],
* "message": "success",
* "requestId": "o_0b721560_m8yp5pqj_88394507",
* "totalCount": 1
* }
* }
* }
*
* */
// 如果需要图片和SKU名称则代表要把图片下载发过去还有对应的skuName
StringBuilder couponInfo = null;
ArrayList<HashMap<String, String>> resultList = new ArrayList<>();
String format = null;
DateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
if (!isCj) {
try {
priceList = extractPrices(message);
finallyMessage.put("priceList", priceList);
} catch (Exception ignored) {
}
}
for (String url : urls) {
try {
// 重置。不然会一直追加文本内容
couponInfo = new StringBuilder();
// 新建格式好日期
format = dateFormat.format(new Date());
// 查询商品信息
GoodsQueryResult productInfo = queryProductInfoByUJDUrl(url);
if (productInfo == null || productInfo.getCode() != 200) {
couponInfo.append("链接查询失败:").append(url).append("\n");
continue;
}
long totalCount = productInfo.getTotalCount();
if (totalCount == 0) {
//couponInfo.append("链接类型:优惠券\n\n");
} else {
couponInfo.append(url).append("\n");
urlList.add(url);
couponInfo.append("链接类型:商品\n\n");
JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(productInfo.getData()[0]));
jsonObject.put("url", url);
dataList.add(jsonObject.toString());
HashMap<String, String> itemMap = new HashMap<>();
itemMap.put("url", url);
itemMap.put("materialUrl", productInfo.getData()[0].getMaterialUrl());
itemMap.put("oriItemId", productInfo.getData()[0].getOriItemId());
itemMap.put("owner", productInfo.getData()[0].getOwner());
itemMap.put("shopId", String.valueOf(productInfo.getData()[0].getShopInfo().getShopId()));
itemMap.put("shopName", productInfo.getData()[0].getShopInfo().getShopName());
itemMap.put("skuName", productInfo.getData()[0].getSkuName());
String replaceAll = itemMap.get("skuName").replaceAll("以旧|政府|换新|领取|国家|补贴|15%|20%|国补|立减|【|】", "");
itemMap.put("spuid", String.valueOf(productInfo.getData()[0].getSpuid()));
itemMap.put("commission", String.valueOf(productInfo.getData()[0].getCommissionInfo().getCommission()));
itemMap.put("commissionShare", String.valueOf(productInfo.getData()[0].getCommissionInfo().getCommissionShare()));
//for (HashMap.Entry<String, String> entry : itemMap.entrySet()) {
//couponInfo.append(" ").append(entry.getKey()).append(": ").append(entry.getValue()).append("\n");
//}
couponInfo.append("店铺: ").append(itemMap.get("shopName")).append("\n").append("标题: ").append(replaceAll).append("\n").append("自营 POP ").append(itemMap.get("owner").equals("g") ? " 自营 " : " POP ").append("\n").append("佣金比例: ").append(itemMap.get("commissionShare")).append("\n").append("佣金: ").append(itemMap.get("commission")).append("\n");
//StringBuilder images = new StringBuilder();
if (productInfo.getData()[0].getImageInfo() != null) {
//images.append(" ").append("图片信息:\n");
//int index = 1;
if (!isCj) {
for (UrlInfo image : productInfo.getData()[0].getImageInfo().getImageList()) {
//images.append("图片 ").append(index++).append("\n").append(image.getUrl()).append("\n");
imagesList.add(image.getUrl());
}
}
}
//textList.add(String.valueOf(images));
resultList.add(itemMap);
String title = "";
if (!isCj) {
try {
if (!message.equals(url)) {
String[] lines = message.split("\\r?\\n");
if (lines.length > 0) {
title = lines[0];
// 有的换行了
if (lines[1].length() > 3 && !lines[1].contains("u.jd")) {
title = title + lines[1];
}
logger.info("文案首行 {}", title);
title = title.replaceAll("@|所有人", "");
}
}
} catch (Exception e) {
logger.error("文案首行异常", e);
}
/*直接生成闲鱼的商品文案*/
StringBuilder sb1 = new StringBuilder();
sb1.append("(标价到手) ").append(title).append(replaceAll).append("\n").append(WENAN_FANAN_LQD.replaceAll("更新", format + "更新"));
//textList.add("闲鱼方案的文案:\n");
textList.add(String.valueOf(sb1));
StringBuilder sb2 = new StringBuilder();
sb2.append("(一键代下) ").append(title).append(replaceAll).append("\n").append(WENAN_ZCXS);
//textList.add("闲鱼正常销售:\n");
textList.add(String.valueOf(sb2));
StringBuilder sb3 = new StringBuilder();
sb3.append("(标价到手) ").append(title).append(replaceAll).append("\n").append(WENAN_FANAN_HG.replaceAll("更新", format + "更新"));
textList.add(String.valueOf(sb3));
StringBuilder sb4 = new StringBuilder();
sb4.append("【教你下单】 ").append(title).append(replaceAll).append("\n").append(WENAN_FANAN_BX.replaceAll("信息更新日期:", "信息更新日期:" + format));
textList.add(String.valueOf(sb4));
}
}
textList.add(String.valueOf(couponInfo));
finallyMessage.put("data", dataList);
finallyMessage.put("urlList", urlList);
} catch (Exception e) {
logger.error("处理商品链接时发生异常:{}", url, e);
couponInfo.append(" 处理商品链接时发生异常:").append(url).append("\n");
textList.add(String.valueOf(couponInfo));
finallyMessage.put("text", textList);
}
}
/**
* 因为在这里转链返回,没有什么意义,后面走转链不转链,会重新发方案
* */
StringBuilder wenan = new StringBuilder();
if (isCj) {
// 完成转链后替换链接为u.jd.com链接
for (HashMap<String, String> stringStringHashMap : resultList) {
String transferUrl = transfer(stringStringHashMap.get("materialUrl"), null);
wenan = new StringBuilder(message.replace(stringStringHashMap.get("url"), transferUrl));
}
} else {
wenan = new StringBuilder().append(format).append(FANAN_COMMON).append(message);
}
finalWenAn.add(String.valueOf(wenan));
finallyMessage.put("text", textList);
finallyMessage.put("images", imagesList);
finallyMessage.put("finalWenAn", finalWenAn);
return finallyMessage;
}
/**
* 提取方案中的所有 u.jd.com 链接
*
* @param message 方案内容
* @return 包含所有 u.jd.com 链接的列表
*/
private List<String> extractUJDUrls(String message) {
List<String> urls = new ArrayList<>();
Pattern pattern = Pattern.compile("https://u\\.jd\\.com/\\S+");
Matcher matcher = pattern.matcher(message);
while (matcher.find()) {
urls.add(matcher.group());
}
return urls;
}
/**
* 通过这个可以将u.jd.*** 查询到对应的商品信息 和 商品图片,甚至可以查询到是不是自营的商品
*/
public UnionOpenGoodsQueryResponse getUnionOpenGoodsQueryRequest(String uJDUrl) throws Exception {
JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, LPF_APP_KEY_WZ, LPF_SECRET_KEY_WZ);
UnionOpenGoodsQueryRequest request = new UnionOpenGoodsQueryRequest();
GoodsReq goodsReq = new GoodsReq();
//goodsReq.setSkuIds(List.of(Long.parseLong(skuId)).toArray(new Long[0])); // 传入skuId集合
/**
* 查询域集合不填写则查询全部目目前支持categoryInfo类目信息,imageInfo图片信息,
* baseBigFieldInfo基础大字段信息,bookBigFieldInfo图书大字段信息,
* videoBigFieldInfo影音大字段信息,detailImages商详图
* */
//goodsReq.setFields(new String[]{"categoryInfo", "imageInfo", "baseBigFieldInfo"}); // 设置需要查询的字段
goodsReq.setKeyword(uJDUrl);
goodsReq.setSceneId(1);
request.setGoodsReqDTO(goodsReq);
request.setVersion("1.0");
request.setSignmethod("md5");
// 时间戳格式为yyyy-MM-dd HH:mm:ss时区为GMT+8。API服务端允许客户端请求最大时间误差为10分钟
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
request.setTimestamp(simpleDateFormat.format(date));
//logger.debug("getUnionOpenGoodsQueryRequest 查询商品信息请求:{}", JSON.toJSONString(request));
UnionOpenGoodsQueryResponse execute = client.execute(request);
//logger.debug("getUnionOpenGoodsQueryRequest 查询商品信息响应:{}", JSON.toJSONString(execute));
return execute;
}
public GoodsQueryResult queryProductInfoByUJDUrl(String uJDUrl) throws Exception {
UnionOpenGoodsQueryResponse response = getUnionOpenGoodsQueryRequest(uJDUrl);
if (response == null || response.getQueryResult() == null) {
return null;
}
GoodsQueryResult queryResult = response.getQueryResult();
if (queryResult.getCode() != 200) {
return null;
}
return queryResult;
}
// 新增礼金创建方法
public String createGiftCoupon(String skuId, double amount, int quantity, String owner, String skuName) throws Exception {
logger.debug("准备创建礼金SKU={}, 金额={}元,数量={}, Owner={}", skuId, amount, quantity, owner);
// 参数校验
if (skuId == null || amount <= 0 || quantity <= 0) {
logger.error("礼金创建失败参数错误SKU={}, 金额={}元,数量={}", skuId, amount, quantity);
return null;
}
// 设置默认值
owner = (owner != null) ? owner : "g";
JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, LPF_APP_KEY_WZ, LPF_SECRET_KEY_WZ);
UnionOpenCouponGiftGetRequest request = new UnionOpenCouponGiftGetRequest();
CreateGiftCouponReq couponReq = new CreateGiftCouponReq();
couponReq.setSkuMaterialId(skuId); // 使用SKU或链接
couponReq.setDiscount(amount);
couponReq.setAmount(quantity);
// 自营的只能设置一天pop的只能设置7天默认为自营
String startTime;
String endTime;
if ("pop".equals(owner)) {
startTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH"));
endTime = LocalDateTime.now().plusDays(6).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH"));
couponReq.setEffectiveDays(7);
} else {
startTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH"));
endTime = LocalDateTime.now().plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH"));
couponReq.setEffectiveDays(1);
}
couponReq.setReceiveStartTime(startTime);
couponReq.setReceiveEndTime(endTime);
couponReq.setIsSpu(1);
couponReq.setExpireType(1);
couponReq.setShare(-1);
if (skuName.length() >= 25) {
skuName = skuName.substring(0, 25);
}
// 新建格式日期
DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
skuName = skuName + " " + dateFormat.format(new Date());
couponReq.setCouponTitle(skuName);
couponReq.setContentMatch(1);
request.setCouponReq(couponReq);
request.setVersion("1.0");
logger.debug("请求参数:{}", JSON.toJSONString(request));
UnionOpenCouponGiftGetResponse response = client.execute(request);
logger.debug("API响应{}", JSON.toJSONString(response));
if ("0".equals(response.getCode()) && response.getGetResult().getCode() == 200) {
String giftKey = response.getGetResult().getData().getGiftCouponKey();
logger.debug("礼金创建成功批次ID={}, 返回数据:{}", giftKey, response.getGetResult().getData());
return giftKey;
} else {
logger.error("礼金创建失败:错误码={}, 错误信息={}", response.getCode(), response.getMsg());
}
return null;
}
boolean isValidAmount(String input) {
// 新增:直接处理 null 或空字符串
if (input == null || input.trim().isEmpty()) {
return false;
}
try {
double amount = Double.parseDouble(input);
return amount >= 1 && amount <= 50;
} catch (NumberFormatException e) {
return false;
}
}
private boolean isValidQuantity(String input) {
if (input == null || input.trim().isEmpty()) {
logger.error("数量为空或无效: {}", input);
return false;
}
try {
int quantity = Integer.parseInt(input);
if (quantity < 1 || quantity > 100) {
logger.error("数量超出范围: {}", quantity);
return false;
}
return true;
} catch (NumberFormatException e) {
logger.error("数量格式错误: {}", input, e);
return false;
}
}
// 将状态存储迁移到Redis
public void saveState(String wxid, UserInteractionState state) {
String key = INTERACTION_STATE_PREFIX + wxid;
redisTemplate.opsForValue().set(key, JSON.toJSONString(state), 10, TimeUnit.MINUTES);
}
public UserInteractionState loadState(String wxid) {
String json = redisTemplate.opsForValue().get(INTERACTION_STATE_PREFIX + wxid);
return JSON.parseObject(json, UserInteractionState.class);
}
public void sendOrderToWxByOrderD(String order, String fromWxid) {
if (order.equals("-1")) {
String today = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd "));
String redisKey = "order_count:" + today;
int count;
try {
// 从 Redis 获取当前日期的订单计数器
String s = redisTemplate.opsForValue().get(redisKey);
count = s != null ? Integer.parseInt(s) : 0;
if (count > 0) {
redisTemplate.opsForValue().set(redisKey, String.valueOf(count - 1), 1, TimeUnit.DAYS);
}
wxUtil.sendTextMessage(fromWxid, "count" + (count-1), 1, fromWxid, false);
} catch (Exception e) {
logger.error("Redis操作失败: {}", e.getMessage(), e);
}
return;
}
if (order == null || order.trim().isEmpty()) {
wxUtil.sendTextMessage(fromWxid, "输入格式为:\n 单\n分销标记\n型号\n转链链接\n数量\n地址", 1, fromWxid, false);
return;
}
String[] split = order.split("\n");
if (split.length != 6) {
wxUtil.sendTextMessage(fromWxid, "输入格式为:\n 单\n分销标记\n型号\n转链链接\n数量\n地址", 1, fromWxid, false);
return;
}
String remark = "";
if (!split[0].trim().replace("", "").isEmpty()) {
remark = split[0].trim().replace("{单}", "").replace("\n", "");
}
// 今天的日期
String today = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd "));
String redisKey = "order_count:" + today;
Integer count;
try {
// 从 Redis 获取当前日期的订单计数器
String s = redisTemplate.opsForValue().get(redisKey);
count = s != null ? Integer.parseInt(s) : 0; // 初始为 0后面再 ++
logger.info("sendOrderToWxByOrderD.count beforeFor {}", count);
Integer num = 1;
try {
num = Integer.valueOf(split[4].trim());
} catch (NumberFormatException e) {
logger.error("sendOrderToWxByOrderD 订单数量格式错误,请输入正确的数字", e);
wxUtil.sendTextMessage(fromWxid, "订单数量格式错误,请输入正确的数字", 1, fromWxid, false);
return;
}
// 先一次性增加计数器,并设置到 Redis原子性操作
int startCount = count + 1;
int endCount = count + num;
// 更新 Redis 的计数器为最终值
redisTemplate.opsForValue().set(redisKey, String.valueOf(endCount), 1, TimeUnit.DAYS);
// 批量生成订单号并发送
for (int i = 0; i < num; i++) {
int currentCount = startCount + i;
String orderID = today + String.format("%03d", currentCount); // 格式化为三位数
// 每次使用原始模板进行替换,避免污染
String currentTemp = WENAN_D.replace("{单的备注}", remark).replace("{单号}", orderID).replace("{分销标记}", split[1]).replace("{链接}", split[3]).replace("{地址}", split[5]).replace("{型号}", split[2]).replaceAll("[|]", "");
// 发送订单信息到微信
wxUtil.sendTextMessage(fromWxid, currentTemp, 1, fromWxid, true);
}
} catch (Exception e) {
logger.error("生成订单号时发生异常 - 用户: {}, 日期: {}", fromWxid, today, e);
wxUtil.sendTextMessage(fromWxid, "生成订单号时发生异常,请重试", 1, fromWxid, false);
}
}
public void sendOrderToWxByOrderP(String order, String fromWxid) {
logger.info("sendOrderToWxByOrderP.order {}", order);
getProductTypeMap();
// 检查是否命中“评”指令
if (!productTypeMap.containsKey(order.trim())) {
if (order.startsWith("添加型号")) {
String replace = order.replace("添加型号-", "");
String[] split = replace.split("-");
if (split.length == 2) {
addProductTypeMap(split[0], split[1]);
wxUtil.sendTextMessage(fromWxid, "添加型号成功", 1, fromWxid, false);
} else {
wxUtil.sendTextMessage(fromWxid, "添加型号格式错误,请输入正确的格式:添加型号-型号名称-型号SKU", 1, fromWxid, false);
}
return;
} else if (order.startsWith("删除型号")) {
String replace = order.replace("删除型号-", "");
String[] split = replace.split("-");
if (split.length == 1) {
delProductTypeMap(split[0]);
wxUtil.sendTextMessage(fromWxid, "删除型号成功", 1, fromWxid, false);
} else {
wxUtil.sendTextMessage(fromWxid, "删除型号格式错误,请输入正确的格式:删除型号-型号名称", 1, fromWxid, false);
}
}
try {
StringBuilder productTypeStr = new StringBuilder();
for (Map.Entry<String, String> entry : productTypeMap.entrySet()) {
String k = entry.getKey();
//String value = entry.getValue();
productTypeStr.append(k).append("\n");
}
wxUtil.sendTextMessage(fromWxid, "请选择要生成的评论类型\n(烟灶套餐 晒图 单烟机型号,新型号联系管理员添加(970用C61的图)\n\n" + productTypeStr, 1, fromWxid, true);
logger.info("进入生成评论流程 - 用户: {}", fromWxid);
} catch (Exception e) {
logger.error("生成评论流程初始化异常 - 用户: {}", fromWxid, e);
wxUtil.sendTextMessage(fromWxid, "处理异常,请重新开始", 1, fromWxid, false);
}
} else {
handleCommentInteraction(fromWxid, order);
}
}
//getProductTypeMap 从redis中获取PRODUCT_TYPE_MAP_PREFIX
public HashMap<String, String> getProductTypeMap() {
Map<Object, Object> rawMap = redisTemplate.opsForHash().entries(PRODUCT_TYPE_MAP_PREFIX);
if (!rawMap.isEmpty()) {
productTypeMap.clear();
for (Map.Entry<Object, Object> entry : rawMap.entrySet()) {
productTypeMap.put(entry.getKey().toString(), entry.getValue().toString());
}
// 排序
productTypeMap = productTypeMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
return productTypeMap;
} else {
logger.warn("Redis 中未找到键为 {} 的 Hash 数据", PRODUCT_TYPE_MAP_PREFIX);
}
return null;
}
public void addProductTypeMap(String key, String value) {
redisTemplate.opsForHash().put(PRODUCT_TYPE_MAP_PREFIX, key, value);
}
public void delProductTypeMap(String key) {
redisTemplate.opsForHash().delete(PRODUCT_TYPE_MAP_PREFIX, key);
}
/**
* 处理生成评论流程中的用户交互
*/
private void handleCommentInteraction(String fromWxid, String message) {
logger.info("handleCommentInteraction 处理生成评论流程中的用户交互 - 用户: {},message: {}", fromWxid, message);
try {
getProductTypeMap();
if (productTypeMap.containsKey(message)) {
generateComment(fromWxid, message);
} else {
wxUtil.sendTextMessage(fromWxid, "无效的选择", 1, fromWxid, false);
}
} catch (Exception e) {
logger.error("生成评论流程处理异常 - 用户: {}", fromWxid, e);
wxUtil.sendTextMessage(fromWxid, "处理异常,请重新开始", 1, fromWxid, false);
}
}
/**
* 生成评论内容
*/
private synchronized void generateComment(String fromWxid, String productType) {
//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;
}
// 从数据库获取可用评论
List<Comment> availableComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 1);
List<Comment> usedComments = commentRepository.findByProductIdAndIsUseNotAndPictureUrlsIsNotNull(product_id, 0);
canUseComentCount = availableComments.size();
usedCommentCount = usedComments.size();
Comment commentToUse = null;
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(60000).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);
return;
}
} catch (Exception e) {
logger.error("调用外部接口获取评论失败", e);
wxUtil.sendTextMessage(fromWxid, "调用外部接口获取评论失败", 1, fromWxid, false);
return;
}
}
if (commentToUse == null) {
wxUtil.sendTextMessage(fromWxid, "没有找到可用的评论数据", 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);
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 = "";
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());
} catch (Exception e) {
logger.error("生成评论异常 - 用户: {}", fromWxid, e);
wxUtil.sendTextMessage(fromWxid, "AI 评论生成失败", 1, fromWxid, false);
}
// 发送生成的评论文本
wxUtil.sendTextMessage(fromWxid, gptResponse, 1, fromWxid, true);
wxUtil.sendTextMessage(fromWxid, "评论统计:\n" + "型号 " + productType + "\n" + "新增:" + addCommentCount + "\n" + "已使用:" + usedCommentCount + "\n" + "可用:" + canUseComentCount + "\n" + "总数:" + allCommentCount, 1, fromWxid, true);
// 更新评论状态为已使用
commentToUse.setIsUse(1);
commentRepository.save(commentToUse);
}
// 定义一个内部类来存储用户交互状态
@Getter
@Setter
static class UserInteractionState {
private List<String> selectedSkuIds; // 新增字段存储多个商品SKU ID
private List<String> selectedSkuNames; // 新增字段,存储多个商品名称
private GiftMoneyStep currentStep; // 新增当前步骤字段
private String lastInteractionTime;
private ProcessState currentState;
private Map<String, String> collectedFields; // 用于存储收集到的字段值
private String currentField; // 当前正在询问的字段
public UserInteractionState() {
this.currentStep = STEP_CONFIRM_GIFT; // 初始
this.lastInteractionTime = LocalDateTime.now().format(DATE_TIME_FORMATTER);
this.currentState = INIT;
this.collectedFields = new HashMap<>();
this.currentField = null;
this.selectedSkuIds = new ArrayList<>();
this.selectedSkuNames = new ArrayList<>();
}
public void updateLastInteractionTime() {
this.lastInteractionTime = LocalDateTime.now().format(DATE_TIME_FORMATTER);
}
public void reset() {
this.currentState = INIT;
this.collectedFields.clear();
this.currentStep = STEP_CONFIRM_GIFT; // 明确重置步骤
updateLastInteractionTime();
}
// 在UserInteractionState类中增加状态校验方法
public boolean validateStep(GiftMoneyStep expectedStep) {
return this.currentStep != null && this.currentStep.getCode() == expectedStep.getCode();
}
// 推荐使用枚举管理状态
public enum ProcessState {
INIT, GIFT_MONEY_FLOW, PRODUCT_PROMOTION, COMMENT_GENERATION
}
public enum GiftMoneyStep {
STEP_CONFIRM_GIFT(0), // 显式指定序号
STEP_AMOUNT(1), STEP_QUANTITY(2);
private final int code;
GiftMoneyStep(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}
}
// 限流异常类(需自定义)
public static class RateLimitExceededException extends RuntimeException {
public RateLimitExceededException(String message) {
super(message);
}
}
@Setter
@Getter
public static class OrderInfo {
private String skuName;
private Long count;
private Long orderId;
private Date orderDate;
}
public LocalDate getDateFromLD(String dateStr) {
// 定义支持的日期格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
// 解析日期字符串为 LocalDate 对象
LocalDate date = LocalDate.parse(dateStr, formatter);
// 成功解析后,可执行后续操作(例如业务逻辑)
logger.info("成功解析日期: {}", date);
return date;
}
public static LocalDateTime getEffectiveToday() {
LocalDateTime now = LocalDateTime.now();
if (now.getHour() < 8) {
return now.minusDays(1);
}
return now;
}
/**
* 解析用户输入中的日期部分
*
* @param input 用户输入内容
* @return 解析出的 LocalDate 对象,如果没有带日期则返回 null
*/
private LocalDate parseUserDateIfPresent(String input) {
if (input.startsWith("慢单|录单") && !input.startsWith("")) {
String dateStr = input.replaceAll("慢单|录单", "").trim();
if (!dateStr.isEmpty()) {
return getDateFromLD(dateStr);
}
}
return null; // 没有带日期或格式错误,返回 null
}
public void LD(String input, String fromWxid) {
// 1⃣ 调用封装好的方法提取日期
LocalDate userDate = parseUserDateIfPresent(input);
// 2⃣ 获取有效“今天”
LocalDateTime today = getEffectiveToday();
if (userDate != null) {
today = userDate.atStartOfDay(); // ⬅️ 用户指定了日期
}
// 3⃣ 构造查询时间范围
LocalDateTime startOfDay = today.with(LocalTime.MIN);
LocalDateTime endOfDay = today.with(LocalTime.MAX);
Date startDate = Date.from(startOfDay.atZone(ZoneId.systemDefault()).toInstant());
Date endDate = Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
if (input.startsWith("慢单")) {
List<JDOrder> todayOrders = jdOrderRepository.findByOrderTimeBetween(startDate, endDate);
if (todayOrders.isEmpty()) {
wxUtil.sendTextMessage(fromWxid, "今天没有订单。", 1, fromWxid, false);
return;
}
// 按 distributionMark 分组
Map<String, List<JDOrder>> groupedByDistributionMark = todayOrders.stream().collect(Collectors.groupingBy(JDOrder::getDistributionMark, Collectors.toList()));
for (Map.Entry<String, List<JDOrder>> entry : groupedByDistributionMark.entrySet()) {
String distributionMark = entry.getKey() != null ? entry.getKey() : "未提供";
List<JDOrder> orders = entry.getValue();
// 总统计信息
StringBuilder summaryBuilder = new StringBuilder();
int totalCount = 0;
// 按 modelNumber 再次分组
Map<String, List<JDOrder>> groupedByModel = orders.stream().collect(Collectors.groupingBy(JDOrder::getModelNumber));
for (Map.Entry<String, List<JDOrder>> modelEntry : groupedByModel.entrySet()) {
String modelNumber = modelEntry.getKey();
List<JDOrder> modelOrders = modelEntry.getValue();
int count = modelOrders.size();
totalCount += count;
summaryBuilder.append("型号:").append(modelNumber != null ? modelNumber : "未知").append(" 数量:").append(count).append("\n");
}
// 添加合计
summaryBuilder.append("总计:").append(totalCount).append("\n详情");
// 发送总统计消息
//wxUtil.sendTextMessage(fromWxid, "分销标记:" + distributionMark + "\n" + summaryBuilder, 1, fromWxid, true);
List<JDOrder> sortedOrders = orders.stream().sorted(Comparator.comparing(JDOrder::getRemark, Comparator.nullsFirst(String::compareTo))).toList();
// 发送详细订单列表
StringBuilder detailBuilder = new StringBuilder();
for (JDOrder order : sortedOrders) {
detailBuilder.append("单:").append(order.getRemark() != null ? order.getRemark() : "未提供")
.append("\n型号").append(order.getModelNumber() != null ? order.getModelNumber() : "未提供")
.append("\n下单付款").append(order.getPaymentAmount() != null ? String.format("%.2f", order.getPaymentAmount()) : "0.00").append("")
.append("\n后返金额").append(order.getRebateAmount() != null ? String.format("%.2f", order.getRebateAmount()) : "0.00").append("")
.append("\n地址").append(order.getAddress() != null ? order.getAddress() : "未提供")
.append("\n物流链接\n").append(order.getLogisticsLink() != null ? order.getLogisticsLink() : "")
.append("\n────────────\n");
}
// 发送详细订单消息
wxUtil.sendTextMessage(fromWxid, "分销标记:" + distributionMark + "\n" + summaryBuilder + "\n────────────\n" + detailBuilder, 1, fromWxid, true);
}
} else if (input.startsWith("录单")) {
List<JDOrder> todayOrders = jdOrderRepository.findByOrderTimeBetween(startDate, endDate);
if (todayOrders.isEmpty()) {
wxUtil.sendTextMessage(fromWxid, "今天没有订单。", 1, fromWxid, false);
return;
}
//排序
List<JDOrder> sortedOrders = todayOrders.stream().sorted(Comparator.comparing(JDOrder::getRemark, Comparator.nullsFirst(String::compareTo))).toList();
// 按每份20单切割并发送
int batchSize = 20;
int totalOrderCount = sortedOrders.size();
for (int i = 0; i < totalOrderCount; i += batchSize) {
int endIndex = Math.min(i + batchSize, totalOrderCount);
List<JDOrder> batchOrders = sortedOrders.subList(i, endIndex);
StringBuilder batchSb = new StringBuilder();
for (JDOrder jdOrder : batchOrders) {
String distributionMark = jdOrder.getDistributionMark();
String distributionMark2 = "";
if (distributionMark != null) {
if (distributionMark.startsWith("H")) {
distributionMark2 = "鸿";
} else if (distributionMark.startsWith("F")) {
distributionMark2 = "";
}
}
batchSb.append(jdOrder.getRemark()).append("\t").append(jdOrder.getOrderId()).append("\t").append(DateUtil.dateToStr(jdOrder.getOrderTime(), "yyyy-MM-dd")).append("\t").append(jdOrder.getModelNumber()).append("\t").append(jdOrder.getAddress()).append("\t").append(jdOrder.getLogisticsLink()).append("\t\t").append(jdOrder.getBuyer()).append("\t").append(jdOrder.getPaymentAmount()).append("\t").append(jdOrder.getRebateAmount()).append("\t").append(distributionMark2).append("\n");
}
// 发送每批次的订单信息
wxUtil.sendTextMessage(fromWxid, batchSb.toString(), 1, null, true);
}
} else if (input.startsWith("")) {
// 生成当前日期
JDOrder jdOrder = parseOrderFromText(input.trim().replace("", ""));
if (jdOrder.getOrderId() == null) {
return;
}
if (Util.isAnyEmpty(jdOrder.getOrderId(), jdOrder.getBuyer(), jdOrder.getOrderTime(), jdOrder.getPaymentAmount(), jdOrder.getRebateAmount(), jdOrder.getAddress(), jdOrder.getLogisticsLink(), jdOrder.getModelNumber(), jdOrder.getLink(), jdOrder.getOrderId(), jdOrder.getBuyer())) {
return;
}
JDOrder byRemark = jdOrderRepository.findByRemark(jdOrder.getRemark());
String info;
if (byRemark != null) {
logger.info("订单已存在:{}", byRemark);
info = "更新成功";
jdOrder.setId(byRemark.getId());
} else {
info = "新增成功";
logger.info("订单不存在,新增:{}", jdOrder);
}
jdOrderRepository.save(jdOrder);
StringBuilder sb = new StringBuilder();
// 单号 下单日期 型号 内部订单号 地址 物流 外派给谁 后返金额 谁的单 下单价格
String distributionMark = jdOrder.getDistributionMark();
String distributionMark2 = "";
if (distributionMark != null) {
if (distributionMark.startsWith("H")) {
distributionMark2 = "鸿";
} else if (distributionMark.startsWith("F")) {
distributionMark2 = "";
}
}
sb.append(jdOrder.getRemark()).append("\t").append(jdOrder.getOrderId()).append("\t").append(DateUtil.dateToStr(jdOrder.getOrderTime(), "yyyy-MM-dd")).append("\t").append(jdOrder.getModelNumber()).append("\t").append(jdOrder.getAddress()).append("\t").append(jdOrder.getLogisticsLink()).append("\t\t").append(jdOrder.getBuyer()).append("\t").append(jdOrder.getPaymentAmount()).append("\t").append(jdOrder.getRebateAmount()).append("\t").append(distributionMark2);
logger.info("订单信息:{}", sb);
if (fromWxid.isEmpty()) {
return;
}
wxUtil.sendTextMessage(fromWxid, info, 1, null, true);
wxUtil.sendTextMessage(fromWxid, sb.toString(), 1, null, true);
} else if (input.startsWith("TF")) {
/*
ZQD130F-EB130 1 张林 17530176250 湖北省 武汉市 东西湖区 径河街道 径河街道临空港小区二期 8栋2单元2204联系15783450649转6316
切割出
ZQD130F-EB130
1
张林 17530176250 湖北省 武汉市 东西湖区 径河街道 径河街道临空港小区二期 8栋2单元2204联系15783450649转6316
* */
String[] parts = input.replace("TF", "").split("\t"); // 使用制表符分割
if (parts.length >= 3) {
String modelNumber = parts[0].replace("\n", ""); // 型号
String quantityStr = parts[1]; // 数量
StringBuilder address = new StringBuilder();
// 使用正则表达式提取中文字符
Pattern pattern = Pattern.compile("[\\u4E00-\\u9FA5]+");
Matcher matcher = pattern.matcher(parts[2]);
if (matcher.find()) {
address = new StringBuilder(matcher.group());
}
for (int i = 3; i < parts.length; i++) {
address.append(parts[i]);
}
StringBuilder order = new StringBuilder();
order.append("").append("\n").append("H-TF").append("\n").append(modelNumber).append("\n").append(" ").append("\n").append(quantityStr).append("\n").append(address);
sendOrderToWxByOrderD(order.toString(), fromWxid);
}
}
}
public JDOrder parseOrderFromText(String input) {
// 清理多余的空白字符
input = input.replaceAll("\\s+", " ").trim();
Map<String, String> fields = new HashMap<>();
// 定义正则表达式提取各个字段
extractField(input, fields, "单:", "备注:");
extractField(input, fields, "分销标记:", "型号:");
extractField(input, fields, "型号:", "链接:");
extractField(input, fields, "链接:", "下单付款:");
extractField(input, fields, "下单付款:", "后返金额:");
extractField(input, fields, "后返金额:", "地址:");
extractField(input, fields, "地址:", "物流链接:");
extractField(input, fields, "物流链接:", "订单号:");
extractField(input, fields, "订单号:", "下单人:");
// 手动提取“下单人”
Pattern buyerPattern = Pattern.compile("下单人:\\s*(.*?)\\s*(?=单:|\\Z)", Pattern.DOTALL);
Matcher buyerMatcher = buyerPattern.matcher(input);
if (buyerMatcher.find()) {
fields.put("下单人", buyerMatcher.group(1).trim());
}
// 构建 JDOrder 对象
JDOrder order = new JDOrder();
order.setRemark(fields.getOrDefault("", null));
order.setDistributionMark(fields.getOrDefault("分销标记", null));
order.setModelNumber(fields.getOrDefault("型号", null));
order.setLink(fields.getOrDefault("链接", null));
try {
order.setPaymentAmount(Double.parseDouble(fields.getOrDefault("下单付款", "0")));
} catch (NumberFormatException ignored) {
}
try {
order.setRebateAmount(Double.parseDouble(fields.getOrDefault("后返金额", "0")));
} catch (NumberFormatException ignored) {
}
order.setAddress(fields.getOrDefault("地址", null));
order.setLogisticsLink(extractFirstUrl(fields.getOrDefault("物流链接", "")));
order.setOrderId(fields.getOrDefault("订单号", null));
order.setBuyer(fields.getOrDefault("下单人", null));
// 设置下单时间,格式为 yyyy-MM-dd HH:mm:ss
try {
String dateStr = fields.getOrDefault("", "").split(" ")[0];
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
order.setOrderTime(sdf.parse(dateStr));
} catch (Exception e) {
order.setOrderTime(new Date());
}
return order;
}
// 提取字段的方法
private void extractField(String input, Map<String, String> map, String startKeyword, String endKeyword) {
Pattern pattern = Pattern.compile(startKeyword + "\\s*(.*?)\\s*(?=" + endKeyword + "|\\Z)", Pattern.DOTALL);
Matcher matcher = pattern.matcher(input);
if (matcher.find()) {
String value = matcher.group(1).trim().replaceAll("^" + startKeyword, "").trim();
map.put(startKeyword.replace("", ""), value);
}
}
// 提取 URL 的辅助方法
private String extractFirstUrl(String text) {
if (text == null || text.isEmpty()) return null;
Pattern urlPattern = Pattern.compile("(https?://[^\\s]+)");
Matcher matcher = urlPattern.matcher(text);
return matcher.find() ? matcher.group(0) : null;
}
}