1201 lines
53 KiB
Java
1201 lines
53 KiB
Java
package cn.van.business.util;
|
||
|
||
|
||
import cn.van.business.model.jd.OrderRow;
|
||
import cn.van.business.repository.OrderRowRepository;
|
||
import com.alibaba.fastjson2.JSON;
|
||
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.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.*;
|
||
import com.jd.open.api.sdk.response.kplunion.*;
|
||
import lombok.AllArgsConstructor;
|
||
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.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.chatRoom_BY;
|
||
import static cn.van.business.util.WXUtil.super_admins;
|
||
|
||
/**
|
||
* @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 LPF_WZ_ID = "4101253066";
|
||
// 导购的
|
||
private static final String LPF_APP_KEY_DG = "faf410cb9587dc80dc7b31e321d7d322";
|
||
private static final String LPF_SECRET_KEY_DG = "a4fb15d7bedd4316b97b4e96e4effc1c";
|
||
private static final String LL_APP_KEY_DG = "9c2011409f0fc906b73432dd3687599d";
|
||
private static final String LL_SECRET_KEY_DG = "3ceddff403e544a8a2eacc727cf05dab";
|
||
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 long TIMEOUT_MINUTES = 1;
|
||
private static final String WENAN_FANAN = "提供方法自己下单\n" +
|
||
"全程都是自己的账号下单\n" +
|
||
"标价就是下单的到手价\n" +
|
||
"(不包含教程费)\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_ZCXS = "\n" +
|
||
"购买后,两小时内出库,物流会电话联系您,同时生成京东官方安装单。送装一体,无需担心。\n" +
|
||
"\n" +
|
||
"\n" +
|
||
"1:全新正品,原包装未拆封(京东商城代购,就近直发)\n" +
|
||
"2:可提供下单运单号与电子发票(发票在收到货后找我要)。\n" +
|
||
"3:收货时查看是否有质量或运损问题。可拍照让京东免费申请换新。\n" +
|
||
"4:下单后非质量问题不支持退款退货,强制退扣100元。\n" +
|
||
"5:价格有浮动,不支持补差价,谢谢理解。\n" +
|
||
"6:全国联保,全国统一安装标准。支持官方 400,服务号查询,假一赔十。\n";
|
||
private static final String FANAN_COMMON = "\n复制到薇,依次领券,到J东APP结算\n" +
|
||
"换新选生活电器-除螨仪,可免回收\n";
|
||
|
||
final WXUtil wxUtil;
|
||
private final StringRedisTemplate redisTemplate;
|
||
private final OrderRowRepository orderRowRepository;
|
||
private final OrderUtil orderUtil;
|
||
// 添加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(StringRedisTemplate redisTemplate, OrderRowRepository orderRowRepository, WXUtil wxUtil, OrderUtil orderUtil) {
|
||
this.redisTemplate = redisTemplate;
|
||
this.orderRowRepository = orderRowRepository;
|
||
this.wxUtil = wxUtil;
|
||
this.orderUtil = orderUtil;
|
||
}
|
||
|
||
private static 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 static 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 String convertAddress(String input) {
|
||
try {
|
||
// 正则表达式分解输入内容
|
||
Pattern pattern = Pattern.compile("^(.+?)\\[(\\d+)\\]\\s+(\\d+)\\s+(.+)\\[\\d+\\]$");
|
||
Matcher matcher = pattern.matcher(input);
|
||
|
||
if (matcher.matches()) {
|
||
// 提取各部分内容
|
||
String name = matcher.group(1);
|
||
String code = matcher.group(2);
|
||
String phone = matcher.group(3);
|
||
String address = matcher.group(4).trim();
|
||
|
||
// 构造新地址格式
|
||
return String.format("%s 13068923963 %s联系客户%s转%s",
|
||
name,
|
||
address,
|
||
phone,
|
||
code);
|
||
}
|
||
} catch (Exception ignored) {
|
||
return "地址格式不匹配";
|
||
}
|
||
return "地址格式不匹配";
|
||
}
|
||
|
||
public void sendOrderToWxByOrderDefault(String order, String fromWxid) {
|
||
logger.info("执行 sendOrderToWxByOrderDefault 方法,order: {}, fromWxid: {}", order, fromWxid);
|
||
|
||
if ("礼".equals(order)) {
|
||
// 设置状态为等待文案输入
|
||
String key = INTERACTION_STATE_PREFIX + fromWxid;
|
||
UserInteractionState state = loadOrCreateState(key);
|
||
state.setCurrentState(UserInteractionState.ProcessState.GIFT_MONEY_FLOW);
|
||
state.setCurrentStep(UserInteractionState.GiftMoneyStep.STEP_WAIT_FOR_CONTENT);
|
||
saveState(key, state);
|
||
|
||
// 提示用户输入文案
|
||
wxUtil.sendTextMessage(fromWxid, "请输入包含商品链接的文案:", 1, fromWxid, false);
|
||
} else {
|
||
handleUserInteraction(fromWxid, order);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
/**
|
||
* 礼金创建流程 - 循环处理商品
|
||
*/
|
||
private void processGiftMoneyFlow(String wxid, String message, UserInteractionState state) {
|
||
try {
|
||
// 提取文案中的 u.jd.com 链接
|
||
List<String> urls = extractUJDUrls(message);
|
||
if (urls.isEmpty()) {
|
||
wxUtil.sendTextMessage(wxid, "未找到有效的商品链接,请重新输入包含商品链接的文案。", 1, wxid, false);
|
||
return;
|
||
}
|
||
|
||
// 查询商品信息,筛选有效商品
|
||
List<GoodsQueryResult> validProducts = new ArrayList<>();
|
||
for (String url : urls) {
|
||
try {
|
||
GoodsQueryResult productInfo = queryProductInfoByUJDUrl(url);
|
||
if (productInfo != null && productInfo.getCode() == 200 && productInfo.getTotalCount() > 0) {
|
||
validProducts.add(productInfo);
|
||
}
|
||
} catch (Exception e) {
|
||
logger.error("查询商品信息失败:{}", url, e);
|
||
}
|
||
}
|
||
|
||
if (validProducts.isEmpty()) {
|
||
wxUtil.sendTextMessage(wxid, "未找到有效的商品信息,请检查链接是否正确。", 1, wxid, false);
|
||
return;
|
||
}
|
||
|
||
// 缓存商品信息
|
||
cacheMap.put("validProducts" + wxid, JSON.toJSONString(validProducts));
|
||
|
||
// 更新状态
|
||
state.setCurrentStep(UserInteractionState.GiftMoneyStep.STEP_AMOUNT);
|
||
state.getCollectedFields().clear();
|
||
state.getCollectedFields().put("productIndex", "0"); // 当前处理的商品索引
|
||
state.getCollectedFields().put("totalProducts", String.valueOf(validProducts.size())); // 总商品数
|
||
saveState(INTERACTION_STATE_PREFIX + wxid, state);
|
||
|
||
// 提示用户输入金额
|
||
wxUtil.sendTextMessage(wxid, "请输入开通金额(1-50元,支持小数点后两位):", 1, wxid, false);
|
||
|
||
} catch (Exception e) {
|
||
logger.error("处理礼金文案异常 - 用户: {}", wxid, e);
|
||
wxUtil.sendTextMessage(wxid, "处理请求时发生异常,请重试", 1, wxid, false);
|
||
resetState(wxid, state);
|
||
}
|
||
}
|
||
|
||
public void cjwlm(String message) {
|
||
logger.info("执行 cjwlm 方法,order: {}", message);
|
||
if (message.contains("https://u.jd")) {
|
||
wxUtil.sendTextMessage(chatRoom_BY, "[爱心]---采集开始---[爱心] \n " + message, 1, chatRoom_BY, true);
|
||
|
||
// 2. 生成推广内容
|
||
HashMap<String, List<String>> contentResult = generatePromotionContent(message);
|
||
|
||
// 3. 发送文本内容
|
||
for (String text : contentResult.get("text")) {
|
||
wxUtil.sendTextMessage(chatRoom_BY, text, 1, chatRoom_BY, true);
|
||
}
|
||
|
||
// 4. 发送图片(如果有)
|
||
List<String> images = contentResult.get("images");
|
||
if (images != null) {
|
||
try {
|
||
Thread.sleep(6000);
|
||
} catch (InterruptedException ignored) {
|
||
|
||
}
|
||
for (String imageUrl : images) {
|
||
if (imageUrl != null) {
|
||
wxUtil.sendImageMessage(chatRoom_BY, imageUrl);
|
||
}
|
||
}
|
||
}
|
||
wxUtil.sendTextMessage(chatRoom_BY, "[爱心]---采集完成---[爱心]", 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 buildStatsContent(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;
|
||
}
|
||
|
||
public void sendOrderToWxByOrderPDD(String order, String fromWxid) {
|
||
String convertAddress = convertAddress(order);
|
||
wxUtil.sendTextMessage(fromWxid, convertAddress, 0, fromWxid, false);
|
||
}
|
||
|
||
|
||
/**
|
||
* 接收京粉指令指令
|
||
*/
|
||
public void sendOrderToWxByOrderJD(String order, String fromWxid) {
|
||
|
||
int[] param = {-1};
|
||
WXUtil.SuperAdmin superAdmin = super_admins.get(fromWxid);
|
||
String unionId = superAdmin.getUnionId();
|
||
List<OrderRow> orderRows = orderRowRepository.findByValidCodeNotInOrderByOrderTimeDescAndUnionId(param, Long.valueOf(unionId));
|
||
/**
|
||
* 菜单:
|
||
* 今日统计
|
||
* 昨日统计
|
||
* 最近七天统计
|
||
* 最近一个月统计
|
||
* 今天订单
|
||
* 昨天订单
|
||
* */
|
||
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);
|
||
|
||
break;
|
||
case "测试指令": {
|
||
//test01();
|
||
break;
|
||
}
|
||
case "今日统计": {
|
||
// 订单总数,已付款,已取消,佣金总计
|
||
List<OrderRow> todayOrders = filterOrdersByDate(orderRows, 0);
|
||
OrderStats stats = calculateStats(todayOrders);
|
||
contents.add(buildStatsContent("今日统计", stats));
|
||
break;
|
||
}
|
||
case "昨日统计": {
|
||
List<OrderRow> yesterdayOrders = filterOrdersByDate(orderRows, 1);
|
||
OrderStats stats = calculateStats(yesterdayOrders);
|
||
contents.add(buildStatsContent("昨日统计", stats));
|
||
|
||
break;
|
||
}
|
||
case "三日统计": {
|
||
List<OrderRow> last3DaysOrders = filterOrdersByDate(orderRows, 3);
|
||
OrderStats stats = calculateStats(last3DaysOrders);
|
||
contents.add(buildStatsContent("三日统计", stats));
|
||
break;
|
||
}
|
||
case "七日统计": {
|
||
List<OrderRow> last7DaysOrders = filterOrdersByDate(orderRows, 7);
|
||
OrderStats stats = calculateStats(last7DaysOrders);
|
||
contents.add(buildStatsContent("七日统计", stats));
|
||
break;
|
||
}
|
||
case "一个月统计": {
|
||
List<OrderRow> last30DaysOrders = filterOrdersByDate(orderRows, 30);
|
||
OrderStats stats = calculateStats(last30DaysOrders);
|
||
contents.add(buildStatsContent("一个月统计", stats));
|
||
break;
|
||
}
|
||
case "两个月统计": {
|
||
List<OrderRow> last60DaysOrders = filterOrdersByDate(orderRows, 60);
|
||
OrderStats stats = calculateStats(last60DaysOrders);
|
||
contents.add(buildStatsContent("两个月统计", stats));
|
||
break;
|
||
}
|
||
case "三个月统计": {
|
||
List<OrderRow> last90DaysOrders = filterOrdersByDate(orderRows, 90);
|
||
OrderStats stats = calculateStats(last90DaysOrders);
|
||
contents.add(buildStatsContent("三个月统计", stats));
|
||
break;
|
||
}
|
||
case "这个月统计": {
|
||
// 计算出距离1号有几天
|
||
int days = LocalDate.now().getDayOfMonth();
|
||
List<OrderRow> thisMonthOrders = filterOrdersByDate(orderRows, days);
|
||
OrderStats stats = calculateStats(thisMonthOrders);
|
||
contents.add(buildStatsContent("这个月统计", stats));
|
||
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(buildStatsContent("上个月统计", stats));
|
||
break;
|
||
|
||
}
|
||
//总统计
|
||
|
||
case "总统计": {
|
||
OrderStats stats = calculateStats(orderRows);
|
||
contents.add(buildStatsContent("总统计", stats));
|
||
break;
|
||
}
|
||
|
||
|
||
case "今日订单": {
|
||
|
||
List<OrderRow> todayOrders = filterOrdersByDate(orderRows, 0);
|
||
// 订单总数,已付款,已取消,佣金总计
|
||
OrderStats stats = calculateStats(todayOrders);
|
||
contents.add(buildStatsContent("今日统计", 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(buildStatsContent("昨日统计", 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(buildStatsContent("七日统计", 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};
|
||
WXUtil.SuperAdmin superAdmin = super_admins.get(fromWxid);
|
||
String unionId = superAdmin.getUnionId();
|
||
List<OrderRow> orderRows = orderRowRepository.findByValidCodeNotInOrderByOrderTimeDescAndUnionId(param, Long.valueOf(unionId));
|
||
|
||
List<StringBuilder> contents = new ArrayList<>();
|
||
StringBuilder content = new StringBuilder();
|
||
if (order.startsWith("高级")) {
|
||
content = new StringBuilder();
|
||
order = order.replace("高级", "");
|
||
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())).collect(Collectors.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 (orderRow.getUnionId().equals(Long.parseLong(unionId))) {
|
||
content.append(orderUtil.getFormattedOrderInfo(orderRow, orderRow.getValidCode()));
|
||
} 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.findBySkuIdAndUnionId(validCodes, Long.parseLong(order), Long.parseLong(unionId));
|
||
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, orderRow.getValidCode()));
|
||
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;
|
||
}
|
||
//public UnionOpenGoodsBigfieldQueryResponse getUnionOpenGoodsBigfieldQueryResponse(){
|
||
// JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, APP_KEY, SECRET_KEY);
|
||
//
|
||
// UnionOpenGoodsBigfieldQueryRequest request=new UnionOpenGoodsBigfieldQueryRequest();
|
||
// BigFieldGoodsReq goodsReq=new BigFieldGoodsReq();
|
||
// goodsReq.setSkuIds();
|
||
// request.setGoodsReq(goodsReq);
|
||
// request.setVersion("1.0");
|
||
// UnionOpenGoodsBigfieldQueryResponse response= null;
|
||
// try {
|
||
// response = client.execute(request);
|
||
// } catch (Exception e) {
|
||
// throw new RuntimeException(e);
|
||
// }
|
||
// return response;
|
||
//}
|
||
|
||
/**
|
||
* 消毒柜部分的业务逻辑
|
||
*/
|
||
@Scheduled(fixedRate = 60000) // 每分钟执行一次
|
||
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) {
|
||
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:
|
||
processGiftMoneyFlow(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");
|
||
wxUtil.sendTextMessage(wxid, "请输入推广方案(包含商品链接):", 1, wxid, false);
|
||
logger.info("进入转链流程 - 用户: {}", wxid);
|
||
}
|
||
}
|
||
|
||
private void handlePromotionState(String wxid, String message, UserInteractionState state) {
|
||
if ("content".equals(state.getCurrentField())) {
|
||
processContentInput(wxid, message, state);
|
||
} else if ("confirm".equals(state.getCurrentField())) {
|
||
handleGiftMoneyConfirmation(wxid, message, state);
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 处理用户输入的推广方案内容
|
||
*
|
||
* @param wxid 用户微信ID
|
||
* @param message 用户输入的方案内容
|
||
* @param state 当前交互状态
|
||
*/
|
||
private void processContentInput(String wxid, String message, UserInteractionState state) {
|
||
try {
|
||
// 1. 清除旧缓存
|
||
cacheMap.remove("productData" + wxid);
|
||
cacheMap.remove("finalWenAn" + wxid);
|
||
|
||
// 2. 生成推广内容
|
||
HashMap<String, List<String>> contentResult = generatePromotionContent(message);
|
||
|
||
// 3. 发送文本内容
|
||
for (String text : contentResult.get("text")) {
|
||
wxUtil.sendTextMessage(wxid, text, 1, wxid, true);
|
||
}
|
||
|
||
// 4. 发送图片(如果有)
|
||
List<String> images = contentResult.get("images");
|
||
if (images != null) {
|
||
try {
|
||
Thread.sleep(1000);
|
||
} catch (InterruptedException ignored) {
|
||
|
||
}
|
||
for (String imageUrl : images) {
|
||
if (imageUrl != null) {
|
||
wxUtil.sendImageMessage(wxid, imageUrl);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 5. 缓存商品数据
|
||
if (!contentResult.get("data").isEmpty()) {
|
||
String productData = contentResult.get("data").get(0);
|
||
cacheMap.put("productData" + wxid, productData);
|
||
cacheMap.put("finalWenAn" + wxid, contentResult.get("finalWenAn").get(0));
|
||
|
||
// 6. 进入确认礼金步骤
|
||
state.setCurrentField("confirm");
|
||
try {
|
||
Thread.sleep(5000);
|
||
} catch (InterruptedException ignored) {
|
||
|
||
}
|
||
wxUtil.sendTextMessage(wxid,
|
||
"是否需要开通礼金?\n回复 1 - 是\n回复 2 - 否",
|
||
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 {
|
||
if ("1".equals(message)) {
|
||
// 用户选择开通礼金
|
||
state.setCurrentState(UserInteractionState.ProcessState.GIFT_MONEY_FLOW);
|
||
state.setCurrentStep(UserInteractionState.GiftMoneyStep.STEP_AMOUNT);
|
||
state.getCollectedFields().clear();
|
||
|
||
wxUtil.sendTextMessage(wxid,
|
||
"请输入开通金额(1-50元,支持小数点后两位):\n" +
|
||
"示例:20.50",
|
||
1, wxid, false);
|
||
|
||
} else if ("2".equals(message)) {
|
||
// 用户选择不开通礼金
|
||
String cachedData = cacheMap.get("productData" + wxid);
|
||
if (cachedData != null) {
|
||
JSONObject productInfo = JSON.parseObject(cachedData);
|
||
String skuUrl = productInfo.getString("materialUrl");
|
||
String transferUrl = transfer(skuUrl, null);
|
||
String finalWenAn = cacheMap.get("finalWenAn" + wxid);
|
||
|
||
if (finalWenAn != null) {
|
||
finalWenAn = finalWenAn.replace(productInfo.getString("url"), transferUrl);
|
||
wxUtil.sendTextMessage(wxid,
|
||
"不开礼金,只转链的方案:\n",
|
||
1, wxid, false);
|
||
wxUtil.sendTextMessage(wxid,
|
||
finalWenAn,
|
||
1, wxid, true);
|
||
} else {
|
||
wxUtil.sendTextMessage(wxid, "未找到商品信息,请重新开始流程", 1, wxid, false);
|
||
}
|
||
} else {
|
||
wxUtil.sendTextMessage(wxid, "未找到商品信息,请重新开始流程", 1, wxid, false);
|
||
}
|
||
|
||
// 清理状态和缓存
|
||
state.reset();
|
||
cacheMap.remove("productData" + wxid);
|
||
cacheMap.remove("finalWenAn" + wxid);
|
||
|
||
} else {
|
||
// 无效输入
|
||
wxUtil.sendTextMessage(wxid,
|
||
"请输入有效选项:\n" +
|
||
"回复 1 - 开通礼金\n" +
|
||
"回复 2 - 直接转链",
|
||
1, wxid, false);
|
||
}
|
||
} 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);
|
||
}
|
||
|
||
|
||
|
||
|
||
/**
|
||
* 生成转链和方案的方法
|
||
*
|
||
* @param message 方案内容,包含商品链接
|
||
* @return 处理后的方案,附带商品信息
|
||
*/
|
||
public HashMap<String, List<String>> generatePromotionContent(String message) {
|
||
HashMap<String, List<String>> finalMessage = 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> urls = extractUJDUrls(message);
|
||
if (urls.isEmpty()) {
|
||
textList.add("方案中未找到有效的商品链接,请检查格式是否正确。");
|
||
finalMessage.put("text", textList);
|
||
return finalMessage;
|
||
}
|
||
|
||
for (String url : urls) {
|
||
try {
|
||
// 查询商品信息
|
||
GoodsQueryResult productInfo = queryProductInfoByUJDUrl(url);
|
||
if (productInfo == null || productInfo.getCode() != 200) {
|
||
textList.add("链接查询失败:" + url);
|
||
continue;
|
||
}
|
||
|
||
long totalCount = productInfo.getTotalCount();
|
||
if (totalCount == 0) {
|
||
// 优惠券链接
|
||
textList.add("链接类型:优惠券\n" + url);
|
||
} else {
|
||
// 商品链接
|
||
JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(productInfo.getData()[0]));
|
||
jsonObject.put("url", url);
|
||
dataList.add(jsonObject.toJSONString());
|
||
|
||
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());
|
||
String skuName = productInfo.getData()[0].getSkuName();
|
||
String cleanedSkuName = skuName.replaceAll("国家", "").replaceAll("补贴", "").replaceAll("15%", "").replaceAll("20%", "");
|
||
itemMap.put("skuName", cleanedSkuName);
|
||
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()));
|
||
|
||
StringBuilder couponInfo = new StringBuilder();
|
||
couponInfo.append("店铺:").append(itemMap.get("shopName")).append("\n")
|
||
.append("标题:").append(cleanedSkuName).append("\n")
|
||
.append("自营 POP:").append(itemMap.get("owner").equals("g") ? " 自营 " : " POP ").append("\n")
|
||
.append("佣金:").append(itemMap.get("commission")).append("\n")
|
||
.append("佣金比例:").append(itemMap.get("commissionShare"));
|
||
|
||
textList.add(couponInfo.toString());
|
||
|
||
if (productInfo.getData()[0].getImageInfo() != null) {
|
||
for (UrlInfo image : productInfo.getData()[0].getImageInfo().getImageList()) {
|
||
imagesList.add(image.getUrl());
|
||
}
|
||
}
|
||
|
||
// 生成闲鱼的商品文案
|
||
DateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
|
||
textList.add("(教你买) " + cleanedSkuName + "\n" + WENAN_FANAN.replaceAll("更新", dateFormat.format(new Date()) + "更新"));
|
||
|
||
textList.add(cleanedSkuName + "\n" + WENAN_ZCXS);
|
||
}
|
||
} catch (Exception e) {
|
||
logger.error("处理商品链接时发生异常:{}", url, e);
|
||
textList.add("处理商品链接时发生异常:" + url);
|
||
}
|
||
}
|
||
|
||
// 生成最终文案
|
||
StringBuilder wenan = new StringBuilder(FANAN_COMMON);
|
||
for (String url : urls) {
|
||
wenan.append(message.replace(url, ""));
|
||
}
|
||
finalWenAn.add(wenan.toString());
|
||
|
||
finalMessage.put("text", textList);
|
||
finalMessage.put("images", imagesList);
|
||
finalMessage.put("data", dataList);
|
||
finalMessage.put("finalWenAn", finalWenAn);
|
||
|
||
return finalMessage;
|
||
}
|
||
|
||
/**
|
||
* 提取方案中的所有 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;
|
||
}
|
||
|
||
// 将状态存储迁移到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);
|
||
}
|
||
|
||
// 定义一个内部类来存储用户交互状态
|
||
@Getter
|
||
@Setter
|
||
static class UserInteractionState {
|
||
private GiftMoneyStep currentStep; // 新增当前步骤字段
|
||
private String lastInteractionTime;
|
||
private ProcessState currentState;
|
||
private Map<String, String> collectedFields; // 用于存储收集到的字段值
|
||
private String currentField; // 当前正在询问的字段
|
||
|
||
public UserInteractionState() {
|
||
this.currentStep = GiftMoneyStep.STEP_CONFIRM_GIFT; // 初始
|
||
this.lastInteractionTime = LocalDateTime.now().format(DATE_TIME_FORMATTER);
|
||
this.currentState = INIT;
|
||
this.collectedFields = new HashMap<>();
|
||
this.currentField = null;
|
||
|
||
}
|
||
|
||
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();
|
||
}
|
||
|
||
|
||
// 推荐使用枚举管理状态
|
||
public enum ProcessState {
|
||
INIT, GIFT_MONEY_FLOW, PRODUCT_PROMOTION
|
||
}
|
||
|
||
@Getter
|
||
public enum GiftMoneyStep {
|
||
STEP_WAIT_FOR_CONTENT(3),
|
||
STEP_CONFIRM_GIFT(0), // 显式指定序号
|
||
STEP_AMOUNT(1), STEP_QUANTITY(2);
|
||
private final int code;
|
||
|
||
GiftMoneyStep(int code) {
|
||
this.code = 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;
|
||
|
||
}
|
||
|
||
// 统计指标DTO
|
||
@Getter
|
||
@AllArgsConstructor
|
||
class OrderStats {
|
||
private long totalOrders; // 总订单数
|
||
private long validOrders; // 有效订单数(不含取消)
|
||
private long paidOrders; // 已付款订单
|
||
private double paidCommission; // 已付款佣金
|
||
private long pendingOrders; // 待付款订单
|
||
private double pendingCommission; // 待付款佣金
|
||
private long canceledOrders; // 已取消订单
|
||
private long completedOrders; // 已完成订单
|
||
private double completedCommission;// 已完成佣金
|
||
private long violations; // 违规订单数
|
||
private double violationCommission;// 违规佣金
|
||
}
|
||
}
|