1314 lines
58 KiB
Java
1314 lines
58 KiB
Java
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<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);
|
||
}
|
||
|
||
/**
|
||
* 将 响应参数转化为 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<OrderRow> 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<String, String, String> 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<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.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<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 "今日统计": {
|
||
content = new StringBuilder();
|
||
// 订单总数,已付款,已取消,佣金总计
|
||
List<OrderRow> todayOrders = filterOrdersByDate(orderRows, 0);
|
||
OrderStats stats = calculateStats(todayOrders);
|
||
contents.add(buildStatsContent("今日统计", stats));
|
||
contents.add(content);
|
||
break;
|
||
}
|
||
case "昨日统计": {
|
||
content = new StringBuilder();
|
||
List<OrderRow> yesterdayOrders = filterOrdersByDate(orderRows, 1);
|
||
OrderStats stats = calculateStats(yesterdayOrders);
|
||
contents.add(buildStatsContent("昨日统计", stats));
|
||
contents.add(content);
|
||
|
||
break;
|
||
}
|
||
case "三日统计": {
|
||
content = new StringBuilder();
|
||
List<OrderRow> last3DaysOrders = filterOrdersByDate(orderRows, 3);
|
||
OrderStats stats = calculateStats(last3DaysOrders);
|
||
contents.add(buildStatsContent("三日统计", stats));
|
||
|
||
contents.add(content);
|
||
break;
|
||
}
|
||
case "七日统计": {
|
||
content = new StringBuilder();
|
||
List<OrderRow> last7DaysOrders = filterOrdersByDate(orderRows, 7);
|
||
OrderStats stats = calculateStats(last7DaysOrders);
|
||
contents.add(buildStatsContent("七日统计", stats));
|
||
contents.add(content);
|
||
break;
|
||
}
|
||
case "一个月统计": {
|
||
content = new StringBuilder();
|
||
List<OrderRow> last30DaysOrders = filterOrdersByDate(orderRows, 30);
|
||
OrderStats stats = calculateStats(last30DaysOrders);
|
||
contents.add(buildStatsContent("一个月统计", stats));
|
||
contents.add(content);
|
||
break;
|
||
}
|
||
case "两个月统计": {
|
||
|
||
content = new StringBuilder();
|
||
List<OrderRow> last60DaysOrders = filterOrdersByDate(orderRows, 60);
|
||
OrderStats stats = calculateStats(last60DaysOrders);
|
||
contents.add(buildStatsContent("两个月统计", stats));
|
||
contents.add(content);
|
||
break;
|
||
}
|
||
case "三个月统计": {
|
||
|
||
content = new StringBuilder();
|
||
List<OrderRow> 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<OrderRow> 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<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));
|
||
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<OrderRow> 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<OrderRow> yesterdayOrders = filterOrdersByDate(orderRows, 1);
|
||
List<OrderRow> 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<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);
|
||
}
|
||
|
||
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<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) 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<String, String> 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<String, String> 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<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);
|
||
}
|
||
|
||
// 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;// 违规佣金
|
||
}
|
||
|
||
}
|