2380 lines
119 KiB
Java
2380 lines
119 KiB
Java
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.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");
|
||
}
|
||
String transferUrl = transfer(skuId, giftKey);
|
||
if (transferUrl != null) {
|
||
updatedContent = updatedContent.replace(originalUrl, transferUrl);
|
||
result.append(" ").append(skuName).append(" 礼金创建成功\n");
|
||
if (giftKey == null) {
|
||
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) {
|
||
logger.info("开始解析日期 {}", 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("录单")) && !input.startsWith("单")) {
|
||
logger.info("开始解析用户输入的日期 {}", input);
|
||
String dateStr = input.replaceAll("慢单|录单", "").trim();
|
||
logger.info("解析出的日期 {}", dateStr);
|
||
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())) {
|
||
// 警告缺少的字段getOrderId
|
||
StringBuilder sb = new StringBuilder();
|
||
if (Util.isEmpty(jdOrder.getOrderId())) {
|
||
sb.append(" 单号");
|
||
}
|
||
if (Util.isEmpty(jdOrder.getBuyer())) {
|
||
sb.append(" 下单人");
|
||
}
|
||
if (Util.isEmpty(jdOrder.getPaymentAmount())) {
|
||
sb.append(" 下单价格");
|
||
}
|
||
if (Util.isEmpty(jdOrder.getRebateAmount())) {
|
||
sb.append(" 后返金额");
|
||
}
|
||
if (Util.isEmpty(jdOrder.getLogisticsLink())) {
|
||
sb.append(" 物流链接");
|
||
}
|
||
if (Util.isEmpty(jdOrder.getAddress())) {
|
||
sb.append(" 收货地址");
|
||
}
|
||
if (Util.isEmpty(jdOrder.getModelNumber())) {
|
||
sb.append(" 型号");
|
||
}
|
||
for (int i = 0; i < 3; i++) {
|
||
wxUtil.sendTextMessage(fromWxid, "[Broken] [Broken] [Broken] 录单警告!!! 缺少 " + sb, 1, fromWxid, false);
|
||
}
|
||
return;
|
||
}
|
||
List<JDOrder> byAddress = jdOrderRepository.findByAddressOrderByOrderTimeDesc(jdOrder.getAddress());
|
||
if (!byAddress.isEmpty()) {
|
||
int count = byAddress.size();
|
||
StringBuilder sb = new StringBuilder();
|
||
sb.append(DateUtil.dateToStr(byAddress.get(0).getOrderTime(), "yyyy-MM-dd HH:mm:ss"));
|
||
wxUtil.sendTextMessage(fromWxid, "[Broken] [Broken] [Broken] 录单警告!!! 收货地址重复,请确认 !!! \n 此地址共" + count + "个订单,最近的订单时间:" + sb, 1, fromWxid, false);
|
||
}
|
||
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;
|
||
}
|
||
}
|