Files
Jarvis_java/src/main/java/cn/van/business/util/JDUtil.java

1503 lines
74 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

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

package cn.van.business.util;
import cn.van.business.model.jd.OrderRow;
import cn.van.business.model.jd.ProductOrder;
import cn.van.business.repository.OrderRowRepository;
import cn.van.business.repository.ProductOrderRepository;
import com.alibaba.fastjson2.JSON;
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.CouponService.request.stop.StopGiftCouponReq;
import com.jd.open.api.sdk.domain.kplunion.GoodsService.request.query.BigFieldGoodsReq;
import com.jd.open.api.sdk.domain.kplunion.GoodsService.request.query.GoodsReq;
import com.jd.open.api.sdk.domain.kplunion.GoodsService.response.query.BigfieldQueryResult;
import com.jd.open.api.sdk.domain.kplunion.GoodsService.response.query.GoodsQueryResult;
import com.jd.open.api.sdk.domain.kplunion.OrderService.request.query.OrderRowReq;
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.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.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.*;
import static cn.van.business.util.JDUtil.UserInteractionState.ProductOrderStep.*;
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_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 final StringRedisTemplate redisTemplate;
private final OrderRowRepository orderRowRepository;
private final ProductOrderRepository productOrderRepository;
private final WXUtil wxUtil;
private final OrderUtil orderUtil;
// 添加ObjectMapper来序列化和反序列化UserInteractionState
private final ObjectMapper objectMapper = new ObjectMapper();
// 构造函数中注入StringRedisTemplate
@Autowired
public JDUtil(StringRedisTemplate redisTemplate, ProductOrderRepository productOrderRepository, OrderRowRepository orderRowRepository, WXUtil wxUtil, OrderUtil orderUtil) {
this.redisTemplate = redisTemplate;
this.orderRowRepository = orderRowRepository;
this.productOrderRepository = productOrderRepository;
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 void sendOrderToWxByOrderDefault(String order, String fromWxid) {
logger.info("执行 sendOrderToWxByOrderDefault 方法order: {}, fromWxid: {}", order, fromWxid);
handleUserInteraction(fromWxid, order);
// 具体逻辑
}
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 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);
//content = new StringBuilder();
//
//content.append("礼金\r");
//content.append("转链\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);
}
}
}
/**
* 接收京粉指令指令
* 高级菜单
*/
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);
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);
content = new StringBuilder();
contents.add(content);
}
// 转链
if (order.startsWith("转链")) {
content = new StringBuilder();
order = order.replace("转链", "");
String[] split = order.split("\r\n");
for (String s : split) {
content.append("https://item.jd.com/").append(s.trim()).append(".html").append("\r\n");
}
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);
}
}
}
/**
* 获取订单列表
*
* @param start 开始时间
* @param end 结束时间
* @return
* @throws Exception
*/
public UnionOpenOrderRowQueryResponse getUnionOpenOrderRowQueryResponse(LocalDateTime start, LocalDateTime end, Integer pageIndex, String appKey, String secretKey) throws Exception {
String startTime = start.format(DATE_TIME_FORMATTER);
String endTime = end.format(DATE_TIME_FORMATTER);
// 模拟 API 调用
//System.out.println("调用API - 从 " + startTime
// + " 到 " + endTime);
// 实际的 API 调用逻辑应在这里进行
JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, appKey, secretKey);
UnionOpenOrderRowQueryRequest request = new UnionOpenOrderRowQueryRequest();
OrderRowReq orderReq = new OrderRowReq();
orderReq.setPageIndex(pageIndex);
orderReq.setPageSize(200);
orderReq.setStartTime(startTime);
orderReq.setEndTime(endTime);
orderReq.setType(1);
request.setOrderReq(orderReq);
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));
return client.execute(request);
}
/**
* 接口描述:通过商品链接、领券链接、活动链接获取普通推广链接或优惠券二合一推广链接
* jd.union.open.promotion.bysubunionid.get
*/
String transfer(String url) {
JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, LPF_APP_KEY_DG, LPF_SECRET_KEY_DG);
UnionOpenPromotionBysubunionidGetRequest request = new UnionOpenPromotionBysubunionidGetRequest();
PromotionCodeReq promotionCodeReq = new PromotionCodeReq();
promotionCodeReq.setMaterialId(url);
promotionCodeReq.setSceneId(1);
request.setPromotionCodeReq(promotionCodeReq);
request.setVersion("1.0");
UnionOpenPromotionBysubunionidGetResponse response = null;
try {
response = client.execute(request);
} 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("200")) {
result = response.getGetResult().getData().getShortURL();
}
} else {
result = null;
}
return result;
}
/**
* 消毒柜部分的业务逻辑
*/
@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());
}
});
}
//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;
//}
private void handleUserInteraction(String fromWxid, String message) {
String key = INTERACTION_STATE_PREFIX + fromWxid;
String stateJson = redisTemplate.opsForValue().get(key);
UserInteractionState state;
if (stateJson == null) {
state = new UserInteractionState();
logger.debug("New interaction state created for user: {}", fromWxid);
} else {
try {
state = objectMapper.readValue(stateJson, UserInteractionState.class);
// 检查是否超时
LocalDateTime now = LocalDateTime.now();
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 user: {}", fromWxid);
state = new UserInteractionState();
}
} catch (Exception e) {
logger.error("Error parsing interaction state: {}", e.getMessage());
state = new UserInteractionState();
}
}
state.updateLastInteractionTime();
switch (state.getCurrentState()) {
case INIT:
if ("转链".equals(message)) {
state.setCurrentState(UserInteractionState.ProcessState.PRODUCT_PROMOTION);
state.setCurrentField("content");
wxUtil.sendTextMessage(fromWxid, "请输入推广文案(包含商品链接):", 1, fromWxid);
logger.info("进入转链流程 - 文案输入步骤");
} else if ("礼金".equals(message)) {
state.setCurrentState(GIFT_MONEY_FLOW);
state.setCurrentStep(STEP_PRODUCT_LINK);
wxUtil.sendTextMessage(fromWxid, "请输入商品链接:", 1, fromWxid);
logger.info("进入礼金开通流程 - 商品链接步骤");
} else if ("登记".equals(message)) {
state.setCurrentState(DISINFECTANT_CABINET);
state.setCurrentField("orderId");
wxUtil.sendTextMessage(fromWxid, "请输入订单号:", 1, fromWxid);
logger.debug("User {} entered DISINFECTANT_CABINET state", fromWxid);
}
break;
case PRODUCT_PROMOTION:
if ("content".equals(state.getCurrentField())) {
// 第一次输入文案
state.getCollectedFields().put("content", message);
state.setCurrentField("option");
wxUtil.sendTextMessage(fromWxid, "请选择操作:\n回复 1 - 需要图片和 SKU 名称\n回复 2 - 仅进行转链", 1, fromWxid);
logger.info("转链流程 - 等待用户选择操作");
} else if ("option".equals(state.getCurrentField())) {
// 第二次选择操作
if ("1".equals(message)) {
// 需要图片和 SKU 名称
String content = state.getCollectedFields().get("content");
String result = generatePromotionContent(content, true);
wxUtil.sendTextMessage(fromWxid, "处理结果:\n" + result, 1, fromWxid);
state.reset();
} else if ("2".equals(message)) {
// 仅进行转链
String content = state.getCollectedFields().get("content");
String result = generatePromotionContent(content, false);
wxUtil.sendTextMessage(fromWxid, "处理结果:\n" + result, 1, fromWxid);
state.reset();
} else {
wxUtil.sendTextMessage(fromWxid, "无效的选择,请重新输入:\n回复 1 - 需要图片和 SKU 名称\n回复 2 - 仅进行转链", 1, fromWxid);
}
}
break;
case GIFT_MONEY_FLOW:
handleGiftMoneyFlow(fromWxid, message, state);
break;
case PRODUCT_ORDER_REGISTRATION:
handleProductOrderRegistration(fromWxid, message, state);
break;
default:
wxUtil.sendTextMessage(fromWxid, "无效的状态,请重新开始对话", 1, fromWxid);
state.setCurrentState(INIT);
logger.debug("User {} reset to INIT state due to invalid state", fromWxid);
break;
}
try {
redisTemplate.opsForValue().set(key, objectMapper.writeValueAsString(state), TIMEOUT_MINUTES, TimeUnit.MINUTES);
logger.debug("Saved interaction state for user {}: {}", fromWxid, state);
} catch (Exception e) {
logger.error("Error saving interaction state: {}", e.getMessage());
}
}
// 新增礼金流程处理方法
private void handleGiftMoneyFlow(String fromWxid, String message, UserInteractionState state) {
if (state.getCurrentStep() == null) {
state.setCurrentStep(STEP_PRODUCT_LINK);
wxUtil.sendTextMessage(fromWxid, "流程异常,已重置。请输入商品链接:", 1, fromWxid);
return;
}
if ("礼金".equals(message)) {
state.reset();
logger.debug("用户 {} 重置礼金流程", fromWxid); // 新增
wxUtil.sendTextMessage(fromWxid, "流程已重置,请重新开始", 1, fromWxid);
return;
}
try {
String skuId = "";
double amount = 0.0;
String owner = "pop";
switch (state.getCurrentStep()) {
case STEP_PRODUCT_LINK:
skuId = parseSkuFromUrl(message);
logger.debug("用户 {} 输入商品链接:{}, 解析出SKU: {}", fromWxid, message, skuId); // 新增
if (skuId == null) {
wxUtil.sendTextMessage(fromWxid, "❌ 商品链接格式错误,请重新输入", 1, fromWxid);
return;
}
//BigfieldQueryResult queryResult = queryProductInfo(skuId);
//logger.debug("商品ID {} 查询结果:{}", skuId, queryResult); // 新增
//if (queryResult == null) {
// wxUtil.sendTextMessage(fromWxid, "⚠️ 商品信息查询失败,可能链接无效", 1, fromWxid);
// return;
//}
//BigFieldGoodsResp productInfo = queryResult.getData()[0];
//owner = productInfo.getOwner();
//state.getCollectedFields().put("skuId", skuId);
//state.getCollectedFields().put("productInfo", productInfo.getBaseBigFieldInfo().getWdis());
//state.getCollectedFields().put("owner", owner);
//
//logger.debug("商品信息已收集SKU={}, Owner={}, WDIS={}", skuId, owner, productInfo.getBaseBigFieldInfo().getWdis()); // 新增
String productInfo = skuId;
state.getCollectedFields().put("skuId", skuId);
state.setCurrentStep(STEP_AMOUNT);
String prompt = String.format("商品SKU\n %s\n 请输入开通金额(元):", productInfo);
wxUtil.sendTextMessage(fromWxid, prompt, 1, fromWxid);
break;
case STEP_AMOUNT:
logger.debug("用户 {} 输入金额:{}", fromWxid, message);
// 强制检查空值
if (message == null || message.trim().isEmpty()) {
wxUtil.sendTextMessage(fromWxid, "❌ 金额不能为空请输入数字100.00", 1, fromWxid);
return;
}
// 校验格式
if (!isValidAmount(message)) {
wxUtil.sendTextMessage(fromWxid, "❌ 金额格式错误请输入数字100.00", 1, fromWxid);
return;
}
// 转换并保留两位小数
amount = Double.parseDouble(message);
String formattedAmount = String.format("%.2f", amount);
state.getCollectedFields().put("amount", formattedAmount);
state.setCurrentStep(STEP_QUANTITY);
wxUtil.sendTextMessage(fromWxid, "请输入数量1-1000", 1, fromWxid);
break;
case STEP_QUANTITY:
logger.debug("用户 {} 输入数量:{}", fromWxid, message); // 新增
if (!isValidQuantity(message)) {
wxUtil.sendTextMessage(fromWxid, "❌ 数量格式错误,请输入整数", 1, fromWxid);
return;
}
int quantity = Integer.parseInt(message);
if (quantity < 1 || quantity > 1000) {
wxUtil.sendTextMessage(fromWxid, "❌ 数量需在1-1000之间", 1, fromWxid);
return;
}
logger.debug("礼金参数准备完成SKU={},金额={}元,数量={}Owner={}", state.getCollectedFields().get("skuId"), amount, quantity, state.getCollectedFields().get("owner")); // 新增
String giftKey = createGiftCoupon(state.getCollectedFields().get("skuId"),
Double.parseDouble(state.getCollectedFields().get("amount")),
quantity,
owner);
if (giftKey == null) {
logger.error("用户 {} 礼金创建失败SKU={}, 金额={}, 数量={}, Owner={}", fromWxid, skuId, amount, quantity, owner); // 新增
wxUtil.sendTextMessage(fromWxid, "❌ 礼金创建失败,请检查商品是否符合要求", 1, fromWxid);
state.reset();
return;
}
logger.info("用户 {} 礼金创建成功批次ID={}, 参数SKU={}, 金额={}元,数量={}, Owner={}", fromWxid, giftKey, skuId, amount, quantity, owner); // 新增关键成功日志
state.getCollectedFields().put("giftKey", giftKey);
break;
}
} catch (Exception e) {
logger.error("礼金流程异常,用户 {} 当前步骤:{}", fromWxid, state.getCurrentStep(), e); // 新增
wxUtil.sendTextMessage(fromWxid, "❌ 系统异常,请稍后重试", 1, fromWxid);
state.reset();
}
}
/**
* 生成转链和文案的方法
*
* @param message 文案内容,包含商品链接
* @return 处理后的文案,附带商品信息
*/
public String generatePromotionContent(String message, Boolean needImagesAndSkuName) {
// 提取文案中的所有 u.jd.com 链接
List<String> urls = extractUJDUrls(message);
if (urls.isEmpty()) {
return "文案中未找到有效的商品链接,请检查格式是否正确。\n" + message;
}
/**
* {
* "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 enrichedContent = new StringBuilder();
ArrayList<HashMap<String, String>> resultList = new ArrayList<>();
for (String url : urls) {
try {
// 查询商品信息
GoodsQueryResult productInfo = queryProductInfoByUJDUrl(url);
if (productInfo == null || productInfo.getCode() != 200) {
enrichedContent.append("商品链接查询失败:").append(url).append("\n");
continue;
}
long totalCount = productInfo.getTotalCount();
HashMap<String, String> itemMap = new HashMap<>();
itemMap.put("url", url);
if (totalCount == 0) {
itemMap.put("type", "coupon");
} else {
itemMap.put("type", "goods");
//itemMap.put("data", JSONObject.toJSONString(productInfo.getData()));
//"materialUrl": "jingfen.jd.com/detail/BsrqLq5CfIziE7BSl3ItPp8q_3DFaNVYJqfkRRLc7HR.html",
//"oriItemId": "BMrqLq5CfIz9X04KC3ItPp8q_3DFaNVYJqfkRRLc7HR",
//"owner": "g",
//"shopInfo": {
// "shopId": 1000001741,
// "shopLabel": "0",
// "shopLevel": 4.9,
// "shopName": "松下洗衣机京东自营旗舰店"
//},
//"skuName": "松下Panasonic白月光4.0Ultra 洗烘套装 10kg滚筒洗衣机+变频热泵烘干机 除毛升级2.0 水氧SPA护理 8532N+8532NR",
//"spuid": 100137629936,
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());
itemMap.put("spuid", String.valueOf(productInfo.getData()[0].getSpuid()));
}
resultList.add(itemMap);
} catch (Exception e) {
logger.error("处理商品链接时发生异常:{}", url, e);
enrichedContent.append("❌ 处理商品链接时发生异常:").append(url).append("\n");
}
}
if (resultList.isEmpty()){
enrichedContent.append("❌ 处理商品链接时发生异常:").append("\n");
return enrichedContent.toString();
}
if(!needImagesAndSkuName){
// 完成转链后替换链接为u.jd.com链接文案不修改就返回
for (HashMap<String, String> stringStringHashMap : resultList) {
String url = stringStringHashMap.get("url");
String transferUrl = transfer(url);
stringStringHashMap.put("transferUrl", transferUrl);
message = message.replace(url, transferUrl);
}
}
//enrichedContent.append("商品推广文案:\n\n").append(message).append("\n\n");
return enrichedContent.toString();
}
/**
* 提取文案中的所有 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_APP_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);
request.setGoodsReqDTO(goodsReq);
request.setVersion("1.0");
return client.execute(request);
}
public GoodsQueryResult queryProductInfoByUJDUrl(String skuId) throws Exception {
UnionOpenGoodsQueryResponse response = getUnionOpenGoodsQueryRequest(skuId);
if (response == null || response.getQueryResult() == null) {
return null;
}
GoodsQueryResult queryResult = response.getQueryResult();
if (queryResult.getCode() != 200) {
return null;
}
return queryResult;
}
// 在JDUtil类中新增方法实现商品详情查询接口
public UnionOpenGoodsBigfieldQueryResponse getUnionOpenGoodsBigfieldQueryResponse(String skuId) throws Exception {
JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, LPF_APP_KEY_WZ, LPF_APP_KEY_WZ);
UnionOpenGoodsBigfieldQueryRequest request = new UnionOpenGoodsBigfieldQueryRequest();
BigFieldGoodsReq goodsReq = new BigFieldGoodsReq();
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.setSceneId(2); // 设置场景ID
request.setGoodsReq(goodsReq);
request.setVersion("1.0");
request.setSignmethod("md5");
request.setTimestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
return client.execute(request);
}
// 修改parseSkuFromUrl方法提取SKU
private String parseSkuFromUrl(String url) {
Matcher m = Pattern.compile("item.jd.com/(\\d+).html").matcher(url);
return m.find() ? m.group(1) : null;
}
public BigfieldQueryResult queryProductInfo(String skuId) throws Exception {
UnionOpenGoodsBigfieldQueryResponse response = getUnionOpenGoodsBigfieldQueryResponse(skuId);
if (response == null || response.getQueryResult() == null) {
return null;
}
BigfieldQueryResult queryResult = response.getQueryResult();
if (queryResult.getCode() != 200) {
return null;
}
return queryResult;
}
// 新增礼金创建方法
public String createGiftCoupon(String skuId, double amount, int quantity, String owner) 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 : "自营";
JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, LPF_APP_KEY_WZ, LPF_APP_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"));
} else {
startTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH"));
endTime = LocalDateTime.now().plusDays(1).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH"));
}
couponReq.setReceiveStartTime(startTime);
couponReq.setReceiveEndTime(endTime);
couponReq.setEffectiveDays(7);
couponReq.setIsSpu(1);
couponReq.setExpireType(1);
couponReq.setShare(-1);
couponReq.setCouponTitle(skuId);
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 ("200".equals(response.getCode())) {
String giftKey = response.getGetResult().getData().getGiftCouponKey();
logger.debug("礼金创建成功批次ID={}, 返回数据:{}", giftKey, response.getGetResult().getData());
return giftKey;
} else {
logger.error("礼金创建失败:错误码={}, 错误信息={}", response.getCode(), response.getMsg());
}
return null;
}
// 修改activateGiftMoney方法调用真实接口
private boolean activateGiftMoney(String skuId, double amount, int quantity, String owner) {
try {
String giftKey = createGiftCoupon(skuId, amount, quantity, owner);
if (giftKey != null) {
// 可在此处保存礼金批次ID到用户状态
return true;
}
} catch (Exception e) {
logger.error("礼金创建失败", e);
}
return false;
}
// 新增礼金停止方法(可选)
public boolean stopGiftCoupon(String giftKey) throws Exception {
JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, LPF_APP_KEY_WZ, LPF_APP_KEY_WZ);
UnionOpenCouponGiftStopRequest request = new UnionOpenCouponGiftStopRequest();
StopGiftCouponReq couponReq = new StopGiftCouponReq();
couponReq.setGiftCouponKey(giftKey);
request.setCouponReq(couponReq);
request.setVersion("1.0");
request.setSignmethod("md5");
request.setTimestamp(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
UnionOpenCouponGiftStopResponse response = client.execute(request);
return "200".equals(response.getCode());
}
private 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) {
return input.matches("^\\d+$");
}
private void handleProductOrderRegistration(String fromWxid, String message, UserInteractionState state) {
switch (state.getCurrentProductOrderStep()) {
case STEP_ORDER_ID:
if (!message.matches("^\\d{10,20}$")) {
wxUtil.sendTextMessage(fromWxid, "⚠️ 订单号格式错误需10-20位数字", 1, fromWxid);
return;
}
state.getCollectedFields().put("orderId", message);
state.setCurrentProductOrderStep(STEP_PRODUCT_INFO);
wxUtil.sendTextMessage(fromWxid, "请输入商品信息(格式:商品名称-类型编号)\n类型对照1-家电 2-数码 3-服饰\n示例格力空调-1", 1, fromWxid);
break;
case STEP_PRODUCT_INFO:
String[] productInfo = message.split("-");
if (productInfo.length != 2 || !productInfo[1].matches("[1-3]")) {
wxUtil.sendTextMessage(fromWxid, "❌ 格式错误或类型编号无效", 1, fromWxid);
return;
}
state.getCollectedFields().put("skuName", productInfo[0]);
state.getCollectedFields().put("skuType", productInfo[1]);
state.setCurrentProductOrderStep(UserInteractionState.ProductOrderStep.STEP_RECIPIENT_INFO);
wxUtil.sendTextMessage(fromWxid, "请输入收件信息(格式:姓名-电话-地址)\n示例张三-13812345678-北京市朝阳区", 1, fromWxid);
break;
case STEP_RECIPIENT_INFO:
String[] recipientInfo = message.split("-");
if (recipientInfo.length < 3) {
wxUtil.sendTextMessage(fromWxid, "❌ 格式错误,请按示例格式输入", 1, fromWxid);
return;
}
state.getCollectedFields().put("recipientName", recipientInfo[0]);
state.getCollectedFields().put("recipientPhone", recipientInfo[1]);
state.getCollectedFields().put("recipientAddress", String.join("-", Arrays.copyOfRange(recipientInfo, 2, recipientInfo.length)));
// 生成确认信息
String confirmMsg = buildConfirmMessage(state);
wxUtil.sendTextMessage(fromWxid, confirmMsg, 1, fromWxid);
state.setCurrentProductOrderStep(STEP_REVIEW_CONFIRM);
break;
case STEP_REVIEW_CONFIRM:
if ("确认".equals(message)) {
boolean success = saveFullProductOrder(state, fromWxid);
wxUtil.sendTextMessage(fromWxid, success ? "✅ 订单登记成功!" : "❌ 保存失败,请联系管理员", 1, fromWxid);
state.reset();
} else {
state.setCurrentProductOrderStep(STEP_ORDER_ID);
wxUtil.sendTextMessage(fromWxid, "请重新输入订单号:", 1, fromWxid);
}
break;
}
}
private String buildConfirmMessage(UserInteractionState state) {
return "📋 请确认登记信息:\n" + "────────────────\n" + "▪ 订单号:" + state.getCollectedFields().get("orderId") + "\n" + "▪ 商品名称:" + state.getCollectedFields().get("skuName") + "\n" + "▪ 商品类型:" + getTypeDesc(state.getCollectedFields().get("skuType")) + "\n" + "▪ 收件人:" + state.getCollectedFields().get("recipientName") + "\n" + "▪ 联系方式:" + state.getCollectedFields().get("recipientPhone") + "\n" + "▪ 收货地址:" + state.getCollectedFields().get("recipientAddress") + "\n" + "────────────────\n" + "回复【确认】提交,其他内容重新开始";
}
private boolean saveFullProductOrder(UserInteractionState state, String fromWxid) {
try {
ProductOrder order = new ProductOrder();
order.setOrderId(state.getCollectedFields().get("orderId"));
order.setSkuName(state.getCollectedFields().get("skuName"));
order.setSkuType(Integer.valueOf(state.getCollectedFields().get("skuType")));
order.setRecipientName(state.getCollectedFields().get("recipientName"));
order.setRecipientPhone(state.getCollectedFields().get("recipientPhone"));
order.setRecipientAddress(state.getCollectedFields().get("recipientAddress"));
order.setOrderTime(new Date()); // 设置下单时间为当前时间
order.setWhoOrder(state.getCollectedFields().get("recipientName")); // 关联管理员信息
order.setIsReviewed(false); // 默认是否晒图登记
order.setIsCashbackReceived(false); // 默认是否返现到账
order.setFromWxid(fromWxid); // 设置当前交互的wxid
productOrderRepository.save(order);
logger.info("订单登记成功:{}", order);
return true;
} catch (Exception e) {
logger.error("订单保存异常:{}", e.getMessage());
return false;
}
}
private String getTypeDesc(String skuType) {
return switch (skuType) {
case "1" -> "家电";
case "2" -> "数码";
case "3" -> "服饰";
default -> "未知类型";
};
}
// 定义一个内部类来存储用户交互状态
@Getter
@Setter
static class UserInteractionState {
private GiftMoneyStep currentStep; // 新增当前步骤字段
private String lastInteractionTime;
private ProcessState currentState;
private Map<String, String> collectedFields; // 用于存储收集到的字段值
private String currentField; // 当前正在询问的字段
private ProductOrderStep currentProductOrderStep;
public UserInteractionState() {
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_PRODUCT_LINK; // 明确重置步骤
this.currentProductOrderStep = null;
updateLastInteractionTime();
}
// 推荐使用枚举管理状态
public enum ProcessState {
INIT, GIFT_MONEY_FLOW, DISINFECTANT_CABINET, PRODUCT_ORDER_REGISTRATION,PRODUCT_PROMOTION
}
public enum GiftMoneyStep {
STEP_PRODUCT_LINK, STEP_AMOUNT, STEP_QUANTITY
}
// 在UserInteractionState类中新增步骤枚举
public enum ProductOrderStep {
STEP_ORDER_ID, STEP_PRODUCT_INFO, STEP_RECIPIENT_INFO, STEP_REVIEW_CONFIRM, STEP_CASHBACK_TRACK
}
}
// 限流异常类(需自定义)
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;// 违规佣金
}
}