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 cn.van.business.util.jdReq.*; import com.alibaba.fastjson2.util.DateUtils; 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.OrderService.request.query.OrderRowReq; import com.jd.open.api.sdk.domain.kplunion.OrderService.response.query.OrderRowResp; import com.jd.open.api.sdk.domain.kplunion.promotionbysubunioni.PromotionService.request.get.PromotionCodeReq; import com.jd.open.api.sdk.request.kplunion.UnionOpenOrderRowQueryRequest; import com.jd.open.api.sdk.request.kplunion.UnionOpenPromotionBysubunionidGetRequest; import com.jd.open.api.sdk.response.kplunion.UnionOpenOrderRowQueryResponse; import com.jd.open.api.sdk.response.kplunion.UnionOpenPromotionBysubunionidGetResponse; 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.beans.factory.annotation.Value; import org.springframework.data.redis.core.HashOperations; 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.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 { /** * 密钥配置 */ // 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"; /** * 实际业务处理 */ // 标记是否拉取过小时的订单,空订单会set 一个 tag,避免重复拉取 private static final String JD_REFRESH_TAG = "jd:refresh:tag:"; 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 DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 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(); // Getter 方法,方便外部访问 @Getter @Value("${isRunning.wx}") private String isRunning_wx; @Getter @Value("${isRunning.jd}") private String isRunning_jd; // 构造函数中注入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 filterOrdersByDate(List 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 getStreamForWeiGui(List todayOrders) { return todayOrders.stream().filter(orderRow -> orderRow.getValidCode() == 13 || orderRow.getValidCode() == 25 || orderRow.getValidCode() == 26 || orderRow.getValidCode() == 27 || orderRow.getValidCode() == 28 || orderRow.getValidCode() == 29); } /** * 将 响应参数转化为 OrderRow,并返回 */ private OrderRow createOrderRow(OrderRowResp orderRowResp) { OrderRow orderRow = new OrderRow(); orderRow.setOrderId(orderRowResp.getOrderId()); orderRow.setSkuId(orderRowResp.getSkuId()); orderRow.setSkuName(orderRowResp.getSkuName()); orderRow.setItemId(orderRowResp.getItemId()); orderRow.setSkuNum(orderRowResp.getSkuNum()); orderRow.setPrice(orderRowResp.getPrice()); orderRow.setActualCosPrice(orderRowResp.getActualCosPrice()); orderRow.setActualFee(orderRowResp.getActualFee()); orderRow.setEstimateCosPrice(orderRowResp.getEstimateCosPrice()); orderRow.setEstimateFee(orderRowResp.getEstimateFee()); orderRow.setSubSideRate(orderRowResp.getSubSideRate()); orderRow.setSubsidyRate(orderRowResp.getSubsidyRate()); orderRow.setCommissionRate(orderRowResp.getCommissionRate()); orderRow.setFinalRate(orderRowResp.getFinalRate()); orderRow.setOrderTime(DateUtils.parseDate(orderRowResp.getOrderTime())); orderRow.setFinishTime(DateUtils.parseDate(orderRowResp.getFinishTime())); orderRow.setOrderTag(orderRowResp.getOrderTag()); orderRow.setOrderEmt(orderRowResp.getOrderEmt()); orderRow.setUnionId(orderRowResp.getUnionId()); orderRow.setUnionRole(orderRowResp.getUnionRole()); orderRow.setUnionAlias(orderRowResp.getUnionAlias()); orderRow.setUnionTag(orderRowResp.getUnionTag()); orderRow.setTraceType(orderRowResp.getTraceType()); orderRow.setValidCode(orderRowResp.getValidCode()); orderRow.setPayMonth(orderRowResp.getPayMonth()); orderRow.setSiteId(orderRowResp.getSiteId()); orderRow.setParentId(orderRowResp.getParentId()); //GoodsInfo goodsInfo = orderRowResp.getGoodsInfo(); //GoodsInfoVO goodsInfoVO = new GoodsInfoVO(); //goodsInfoVO.setShopId(String.valueOf(goodsInfo.getShopId())); //goodsInfoVO.setShopName(goodsInfo.getShopName()); //goodsInfoVO.setOwner(goodsInfo.getOwner()); //goodsInfoVO.setProductId(String.valueOf(goodsInfo.getProductId())); //goodsInfoVO.setImageUrl(goodsInfo.getImageUrl()); //orderRow.setGoodsInfo(goodsInfoVO); orderRow.setCallerItemId(orderRowResp.getCallerItemId()); orderRow.setPid(orderRowResp.getPid()); orderRow.setCid1(orderRowResp.getCid1()); orderRow.setCid2(orderRowResp.getCid2()); orderRow.setCid3(orderRowResp.getCid3()); orderRow.setChannelId(orderRowResp.getChannelId()); orderRow.setProPriceAmount(orderRowResp.getProPriceAmount()); orderRow.setSkuFrozenNum(orderRowResp.getSkuFrozenNum()); orderRow.setSkuReturnNum(orderRowResp.getSkuReturnNum()); orderRow.setSkuTag(orderRowResp.getSkuTag()); orderRow.setPositionId(orderRowResp.getPositionId()); orderRow.setPopId(orderRowResp.getPopId()); orderRow.setRid(orderRowResp.getRid()); orderRow.setPlus(orderRowResp.getPlus()); orderRow.setCpActId(orderRowResp.getCpActId()); orderRow.setGiftCouponKey(orderRowResp.getGiftCouponKey()); orderRow.setModifyTime(new Date()); orderRow.setSign(orderRowResp.getSign()); orderRow.setBalanceExt(orderRowResp.getBalanceExt()); orderRow.setExpressStatus(orderRowResp.getExpressStatus()); orderRow.setExt1(orderRowResp.getExt1()); orderRow.setSubUnionId(orderRowResp.getSubUnionId()); orderRow.setGiftCouponOcsAmount(orderRowResp.getGiftCouponOcsAmount()); orderRow.setTraceType(orderRowResp.getTraceType()); orderRow.setExpressStatus(orderRowResp.getExpressStatus()); orderRow.setTraceType(orderRowResp.getTraceType()); orderRow.setId(orderRowResp.getId()); orderRow.setValidCode(orderRowResp.getValidCode()); orderRow.setExpressStatus(orderRowResp.getExpressStatus()); orderRow.setTraceType(orderRowResp.getTraceType()); return orderRow; } public int fetchOrders(OrderFetchStrategy strategy, String appKey, String secretKey) { TimeRange range = strategy.calculateRange(LocalDateTime.now()); int count = 0; // 复用原有的抓取逻辑 LocalDateTime current = range.getStart(); while (!current.isAfter(range.getEnd())) { // 调用分页抓取API... Integer pageIndex = 1; boolean hasMore = true; while (hasMore) { try { // 30-60 天 ,非实时,非分钟 UnionOpenOrderRowQueryResponse response = fetchOrdersForDateTime(current, false, pageIndex, false, appKey, secretKey); if (response != null && response.getQueryResult() != null) { if (response.getQueryResult().getCode() == 200) { OrderRowResp[] orderRowResps = response.getQueryResult().getData(); if (orderRowResps != null) { for (OrderRowResp orderRowResp : orderRowResps) { if (orderRowResp != null) { // Check each orderRowResp is not null OrderRow orderRow = createOrderRow(orderRowResp); if (orderRow != null) { // Ensure orderRow is not null after creation orderRowRepository.save(orderRow); count++; } } } } hasMore = Boolean.TRUE.equals(response.getQueryResult().getHasMore()); } else { hasMore = false; } } else { hasMore = false; } } catch (Exception e) { hasMore = false; // Optionally break out of the while loop if required } if (hasMore) pageIndex++; } current = current.plusHours(1); } return count; } /** * 实时刷新最近10分钟的订单(Resilience4j限流集成) */ @Scheduled(cron = "0 * * * * ?") public void fetchLatestOrder() { if (isRunning_jd.equals("true")) { LocalDateTime now = LocalDateTime.now(); LocalDateTime startTime = now.minusMinutes(10).withSecond(0).withNano(0); super_admins.values().parallelStream().forEach(admin -> { if (Util.isAnyEmpty(admin.getAppKey(), admin.getSecretKey())) return; try { UnionOpenOrderRowQueryResponse response = fetchOrdersForDateTime(startTime, true, 1, true, admin.getAppKey(), admin.getSecretKey()); if (isValidResponse(response)) { processOrderResponse(response, admin); } } catch (RateLimitExceededException e) { logger.warn("[限流] {} 请求频率受限", admin.getAppKey().substring(18)); } catch (Exception e) { logger.error("{} 订单抓取异常: {}", admin.getAppKey().substring(18), e.getMessage()); } }); } } // 响应校验方法 private boolean isValidResponse(UnionOpenOrderRowQueryResponse response) { return response != null && response.getQueryResult() != null && response.getQueryResult().getCode() == 200 && response.getQueryResult().getData() != null; } // 订单处理方法 private void processOrderResponse(UnionOpenOrderRowQueryResponse response, WXUtil.SuperAdmin admin) { Arrays.stream(response.getQueryResult().getData()).parallel().map(this::createOrderRow).forEach(orderRowRepository::save); } /** * 扫描订单发送到微信 * 每分钟的30秒执行一次 */ @Scheduled(cron = "3 * * * * ?") public void sendOrderToWx() { if (isRunning_wx.equals("true")) { //long start = System.currentTimeMillis(); int[] validCodes = {-1}; // 只要三个月的,更多的也刷新不出来的 Date threeMonthsAgo = Date.from(LocalDateTime.now().minusMonths(3).atZone(ZoneId.systemDefault()).toInstant()); List orderRows = orderRowRepository.findByValidCodeNotInAndOrderTimeGreaterThanOrderByOrderTimeDesc(validCodes, threeMonthsAgo); for (OrderRow orderRow : orderRows) { orderUtil.orderToWx(orderRow, true); } //logger.info("扫描订单发送到微信耗时:{} ms, 订单数:{} ", System.currentTimeMillis() - start, orderRows.size()); } } /** * 一天拉取三次 30天到60天前的订单 */ @Scheduled(cron = "0 0 */4 * * ?") public void fetchHistoricalOrders3090() { if (isRunning_jd.equals("true")) { try { OrderFetchStrategy strategy = new Days3090Strategy(); for (WXUtil.SuperAdmin admin : super_admins.values()) { try { int count = fetchOrders(strategy, admin.getAppKey(), admin.getSecretKey()); logger.info("账号{} 3090订单拉取完成,新增{}条", admin.getAppKey().substring(18), count); } catch (Exception e) { logger.error("账号 {} 拉取异常: {}", admin.getAppKey().substring(18), e.getMessage()); } } } catch (Exception ex) { logger.error("策略执行异常", ex); } } } /** * 一天拉取6次 14天到30天前的订单 */ @Scheduled(cron = "0 0 * * * ?") public void fetchHistoricalOrders1430() { if (isRunning_jd.equals("true")) { try { OrderFetchStrategy strategy = new Days1430Strategy(); // 需补充Days1430Strategy实现 for (WXUtil.SuperAdmin admin : super_admins.values()) { try { int count = fetchOrders(strategy, admin.getAppKey(), admin.getSecretKey()); logger.info("账号{} 1430订单拉取完成,新增{}条", admin.getAppKey().substring(18), count); } catch (Exception e) { logger.error("账号 {} 拉取异常: {}", admin.getAppKey().substring(18), e.getMessage()); } } } catch (Exception ex) { logger.error("1430策略执行异常", ex); } } } /** * 每10分钟拉取07-14天的订单 */ @Scheduled(cron = "0 0 * * * ?") public void fetchHistoricalOrders0714() { if (isRunning_jd.equals("true")) { try { OrderFetchStrategy strategy = new Days0714Strategy(); super_admins.values().parallelStream().forEach(admin -> { if (Util.isAnyEmpty(admin.getAppKey(), admin.getSecretKey())) return; try { int count = fetchOrders(strategy, admin.getAppKey(), admin.getSecretKey()); logger.info("账号{} 0714订单拉取完成,新增{}条", admin.getAppKey().substring(18), count); } catch (Exception e) { logger.error("账号 {} 0714拉取异常: {}", admin.getAppKey().substring(18), e.getMessage()); } }); } catch (Exception ex) { logger.error("0714策略执行异常", ex); } } } @Scheduled(cron = "0 */5 * * * ?") public void fetchHistoricalOrders0007() { if (isRunning_jd.equals("true")) { try { OrderFetchStrategy strategy = new Days0007Strategy(); super_admins.values().parallelStream().forEach(admin -> { if (Util.isAnyEmpty(admin.getAppKey(), admin.getSecretKey())) return; try { int count = fetchOrders(strategy, admin.getAppKey(), admin.getSecretKey()); logger.info("账号{} 0007订单拉取完成,新增{}条", admin.getAppKey().substring(18), count); } catch (RateLimitExceededException e) { logger.warn("[限流] {} 0007请求受限", admin.getAppKey().substring(18)); } catch (Exception e) { logger.error("账号{}0007拉取异常: {}", admin.getAppKey().substring(18), e.getMessage()); } }); } catch (Exception ex) { logger.error("0007策略执行异常", ex); } } } /** * 根据指定的日期时间拉取订单 * * @param startTime 开始时间 * isRealTime 是否是实时订单 是的话不会判断是否拉取过 * page 分页页码 * isMinutes 是否是分钟级订单 分钟的每次加10分钟,小时每小时加1小时 */ public UnionOpenOrderRowQueryResponse fetchOrdersForDateTime(LocalDateTime startTime, boolean isRealTime, Integer page, boolean isMinutes, String appKey, String secretKey) { LocalDateTime endTime = isMinutes ? startTime.plusMinutes(10) : startTime.plusHours(1); String hourMinuteTag = isRealTime ? "minute" : "hour"; //String oldTimeTag = JD_REFRESH_TAG + startTime.format(DATE_TIME_FORMATTER); String newTimeTag = JD_REFRESH_TAG + appKey + ":" + startTime.format(DATE_TIME_FORMATTER); HashOperations hashOps = redisTemplate.opsForHash(); // 检查这个小时或分钟是否已经被处理过 if (hashOps.hasKey(newTimeTag, hourMinuteTag)) { if (!isMinutes) { return null; } } // 调用 API 以拉取订单 try { UnionOpenOrderRowQueryResponse unionOpenOrderRowQueryResponse = getUnionOpenOrderRowQueryResponse(startTime, endTime, page, appKey, secretKey); // 历史的订单才进行标记为已拉取,小时分钟的都进行拉取并且标记 if (!isRealTime) { // 只有没有订单的才进行标记为已拉取 if (unionOpenOrderRowQueryResponse != null && unionOpenOrderRowQueryResponse.getQueryResult() != null && unionOpenOrderRowQueryResponse.getQueryResult().getData() == null) { hashOps.put(newTimeTag, hourMinuteTag, "done"); } } // 打印方法调用和开始结束时间 if (isRealTime && (LocalDateTime.now().getMinute() % 10 == 0)) { logger.debug(" {} --- 拉取订单, 分钟还是秒 {} , 开始时间:{} --- 结束时间:{}", appKey.substring(appKey.length() - 4), hourMinuteTag, startTime.format(DATE_TIME_FORMATTER), endTime.format(DATE_TIME_FORMATTER)); } return unionOpenOrderRowQueryResponse; } catch (Exception e) { return null; } } public void sendOrderToWxByOrderDefault(String order, String fromWxid) { logger.info("执行 sendOrderToWxByOrderDefault 方法,order: {}, fromWxid: {}", order, fromWxid); handleUserInteraction(fromWxid, order); // 具体逻辑 } private OrderStats calculateStats(List 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.append(title).append(":\n").append("订单总数:").append(stats.getTotalOrders()).append("\r").append("订单总数(不含取消):").append(stats.getValidOrders()).append("\r\n").append("已付款:").append(stats.getPaidOrders()).append("\r").append("已付款佣金:").append(stats.getPaidCommission()).append("\r\n").append("待付款:").append(stats.getPendingOrders()).append("\r") // 修正了原代码中的Stream未终止问题 .append("待付款佣金:").append(stats.getPendingCommission()).append("\r\n").append("已取消:").append(stats.getCanceledOrders()).append("\r").append("已完成:").append(stats.getCompletedOrders()).append("\r").append("已完成佣金:").append(stats.getCompletedCommission()).append("\r").append("违规:").append(stats.getViolations()).append("\r").append("违规佣金:").append(stats.getViolationCommission()); return content; } /** * 接收京粉指令指令 */ public void sendOrderToWxByOrderJD(String order, String fromWxid) { int[] param = {-1}; WXUtil.SuperAdmin superAdmin = super_admins.get(fromWxid); String unionId = superAdmin.getUnionId(); List orderRows = orderRowRepository.findByValidCodeNotInOrderByOrderTimeDescAndUnionId(param, Long.valueOf(unionId)); /** * 菜单: * 今日统计 * 昨日统计 * 最近七天统计 * 最近一个月统计 * 今天订单 * 昨天订单 * */ List 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 "今日统计": { content = new StringBuilder(); // 订单总数,已付款,已取消,佣金总计 List todayOrders = filterOrdersByDate(orderRows, 0); OrderStats stats = calculateStats(todayOrders); contents.add(buildStatsContent("今日统计", stats)); contents.add(content); break; } case "昨日统计": { content = new StringBuilder(); List yesterdayOrders = filterOrdersByDate(orderRows, 1); OrderStats stats = calculateStats(yesterdayOrders); contents.add(buildStatsContent("昨日统计", stats)); contents.add(content); break; } case "三日统计": { content = new StringBuilder(); List last3DaysOrders = filterOrdersByDate(orderRows, 3); OrderStats stats = calculateStats(last3DaysOrders); contents.add(buildStatsContent("三日统计", stats)); contents.add(content); break; } case "七日统计": { content = new StringBuilder(); List last7DaysOrders = filterOrdersByDate(orderRows, 7); OrderStats stats = calculateStats(last7DaysOrders); contents.add(buildStatsContent("七日统计", stats)); contents.add(content); break; } case "一个月统计": { content = new StringBuilder(); List last30DaysOrders = filterOrdersByDate(orderRows, 30); OrderStats stats = calculateStats(last30DaysOrders); contents.add(buildStatsContent("一个月统计", stats)); contents.add(content); break; } case "两个月统计": { content = new StringBuilder(); List last60DaysOrders = filterOrdersByDate(orderRows, 60); OrderStats stats = calculateStats(last60DaysOrders); contents.add(buildStatsContent("两个月统计", stats)); contents.add(content); break; } case "三个月统计": { content = new StringBuilder(); List last90DaysOrders = filterOrdersByDate(orderRows, 90); OrderStats stats = calculateStats(last90DaysOrders); contents.add(buildStatsContent("三个月统计", stats)); contents.add(content); break; } case "这个月统计": { content = new StringBuilder(); // 计算出距离1号有几天 int days = LocalDate.now().getDayOfMonth(); List thisMonthOrders = filterOrdersByDate(orderRows, days); OrderStats stats = calculateStats(thisMonthOrders); contents.add(buildStatsContent("这个月统计", stats)); contents.add(content); break; } case "上个月统计": { content = new StringBuilder(); LocalDate lastMonth = LocalDate.now().minusMonths(1); int days = LocalDate.now().getDayOfMonth(); List lastMonthOrders = filterOrdersByDate(orderRows, lastMonth.lengthOfMonth() + days); List thisMonthOrders = filterOrdersByDate(orderRows, days); lastMonthOrders = lastMonthOrders.stream().filter(orderRow -> !thisMonthOrders.contains(orderRow)).collect(Collectors.toList()); OrderStats stats = calculateStats(lastMonthOrders); contents.add(buildStatsContent("上个月统计", stats)); contents.add(content); break; } //总统计 case "总统计": { content = new StringBuilder(); OrderStats stats = calculateStats(orderRows); contents.add(buildStatsContent("总统计", stats)); contents.add(content); break; } case "今日订单": { content = new StringBuilder(); List todayOrders = filterOrdersByDate(orderRows, 0); // 订单总数,已付款,已取消,佣金总计 OrderStats stats = calculateStats(todayOrders); contents.add(buildStatsContent("今日统计", stats)); if (!todayOrders.isEmpty()) { orderUtil.orderToWxBatch(todayOrders); } contents.add(content); break; } case "昨日订单": { content = new StringBuilder(); List yesterdayOrders = filterOrdersByDate(orderRows, 1); List todayOrders = filterOrdersByDate(orderRows, 0); yesterdayOrders.removeAll(todayOrders); OrderStats stats = calculateStats(yesterdayOrders); contents.add(buildStatsContent("昨日统计", stats)); if (!yesterdayOrders.isEmpty()) { orderUtil.orderToWxBatch(yesterdayOrders); } contents.add(content); break; } case "七日订单": { content = new StringBuilder(); List last7DaysOrders = filterOrdersByDate(orderRows, 1); List todayOrders = filterOrdersByDate(orderRows, 0); last7DaysOrders.removeAll(todayOrders); OrderStats stats = calculateStats(last7DaysOrders); contents.add(buildStatsContent("七日统计", stats)); if (!last7DaysOrders.isEmpty()) { orderUtil.orderToWxBatch(last7DaysOrders); } contents.add(content); break; } case "刷新7天": { content = new StringBuilder(); long start = System.currentTimeMillis(); int count = 0; LocalDateTime startDate = LocalDateTime.now().minusDays(7).withMinute(0).withSecond(0).withNano(0); LocalDateTime lastHour = LocalDateTime.now().minusHours(1).withMinute(0).withSecond(0).withNano(0); String appKey = superAdmin.getAppKey(); String secretKey = superAdmin.getSecretKey(); if (Util.isAnyEmpty(appKey, secretKey)) { return; } while (!startDate.isEqual(lastHour)) { startDate = startDate.plusHours(1); UnionOpenOrderRowQueryResponse response = fetchOrdersForDateTime(startDate, false, 1, false, appKey, secretKey); if (response != null) { int code = response.getQueryResult().getCode(); if (code == 200) { if (response.getQueryResult().getCode() == 200) { OrderRowResp[] orderRowResps = response.getQueryResult().getData(); if (orderRowResps == null) { continue; } for (OrderRowResp orderRowResp : orderRowResps) { // 固化到数据库 OrderRow orderRow = createOrderRow(orderRowResp); // 订单号不存在就保存,存在就更新订单状态 orderRowRepository.save(orderRow); count++; } } } } } content.append("刷新7天成功,耗时").append((System.currentTimeMillis() - start) / 1000).append("秒\r").append("刷新订单数:").append(count); contents.add(content); 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 orderRows = orderRowRepository.findByValidCodeNotInOrderByOrderTimeDescAndUnionId(param, Long.valueOf(unionId)); List 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 filterOrdersByDays = filterOrdersByDate(orderRows, daysInt); content.append("违规排行:"); content.append(daysInt).append("天").append("\r\n"); Map 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> 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> sortedViolationCounts = skuIdViolationCountMap.entrySet().stream().sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())).collect(Collectors.toList()); Integer num = 0; for (Map.Entry 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 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 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 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 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 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) throws Exception { JdClient client = new DefaultJdClient(SERVER_URL, ACCESS_TOKEN, LPF_APP_KEY_DG, LPF_SECRET_KEY_DG); UnionOpenPromotionBysubunionidGetRequest request = new UnionOpenPromotionBysubunionidGetRequest(); PromotionCodeReq promotionCodeReq = new PromotionCodeReq(); request.setPromotionCodeReq(promotionCodeReq); request.setVersion("1.0"); UnionOpenPromotionBysubunionidGetResponse response = client.execute(request); /** * { * "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; //} public 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(GIFT_MONEY_FLOW); state.setCurrentStep(STEP_PRODUCT_LINK); wxUtil.sendTextMessage(fromWxid, "请输入商品链接:", 1, fromWxid); logger.info("进入礼金开通流程 - 商品链接步骤"); } 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 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 ("礼金".equals(message)) { state.reset(); // 重置流程 wxUtil.sendTextMessage(fromWxid, "流程已重置,请重新开始", 1, fromWxid); return; } try { switch (state.getCurrentStep()) { case STEP_PRODUCT_LINK: // 解析商品链接获取SKU String skuId = parseSkuFromUrl(message); Map productInfo = queryProductInfo(skuId); state.getCollectedFields().put("skuId", skuId); state.getCollectedFields().put("productInfo", productInfo.get("name") + "\n价格:" + productInfo.get("price")); state.setCurrentStep(STEP_AMOUNT); wxUtil.sendTextMessage(fromWxid, "商品信息:\n" + productInfo.get("name") + "\n当前价格:" + productInfo.get("price") + "\n请输入开通金额(元):", 1, fromWxid); break; case STEP_AMOUNT: if (!isValidAmount(message)) { wxUtil.sendTextMessage(fromWxid, "金额格式错误,请重新输入", 1, fromWxid); return; } state.getCollectedFields().put("amount", message); state.setCurrentStep(STEP_QUANTITY); wxUtil.sendTextMessage(fromWxid, "请输入开通数量:", 1, fromWxid); break; case STEP_QUANTITY: if (!isValidQuantity(message)) { wxUtil.sendTextMessage(fromWxid, "数量格式错误,请重新输入", 1, fromWxid); return; } state.getCollectedFields().put("quantity", message); // 调用开通接口 boolean result = activateGiftMoney(state.getCollectedFields().get("skuId"), Double.parseDouble(state.getCollectedFields().get("amount")), Integer.parseInt(message)); String response = result ? "开通成功!" : "开通失败,请稍后重试"; wxUtil.sendTextMessage(fromWxid, response, 1, fromWxid); state.reset(); // 完成流程 break; } } catch (Exception e) { logger.error("礼金流程处理异常", e); wxUtil.sendTextMessage(fromWxid, "系统异常,流程已终止", 1, fromWxid); state.reset(); } } private String parseSkuFromUrl(String url) { // 实现从URL中解析SKU的逻辑 return "123456"; // 示例返回值 } private boolean isValidAmount(String input) { return input.matches("^\\d+(\\.\\d{1,2})?$"); } private Map queryProductInfo(String skuId) { // 调用京东商品查询API(需要实现) return Map.of("name", "示例商品", "price", "299.00"); } private boolean isValidQuantity(String input) { return input.matches("^\\d+$"); } private boolean activateGiftMoney(String skuId, double amount, int quantity) { // 实现实际的开通接口调用 return true; } 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 { // 推荐使用枚举管理状态 public enum ProcessState { INIT, GIFT_MONEY_FLOW, DISINFECTANT_CABINET,PRODUCT_ORDER_REGISTRATION } 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 } private GiftMoneyStep currentStep; // 新增当前步骤字段 private String lastInteractionTime; private ProcessState currentState; private Map 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); } // UserInteractionState类缺少reset方法 public void reset() { this.currentState = INIT; this.collectedFields.clear(); this.currentStep = null; updateLastInteractionTime(); } } // 限流异常类(需自定义) 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;// 违规佣金 } }