Files
Jarvis_java/src/main/java/cn/van/business/util/OrderUtil.java
雷欧(林平凡) e216f2f686 价保
2025-05-29 17:13:59 +08:00

369 lines
18 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

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

package cn.van.business.util;
import cn.van.business.enums.ValidCodeConverter;
import cn.van.business.model.jd.OrderRow;
import cn.van.business.repository.OrderRowRepository;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static cn.van.business.util.WXUtil.*;
/**
* @author Leo
* @version 1.0
* @create 2024/11/29 11:43
* @description
*/
@Service
public class OrderUtil {
private static final Logger logger = LoggerFactory.getLogger(OrderUtil.class);
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private OrderRowRepository orderRowRepository;
@Autowired
private WXUtil wxUtil;
//标记唯一订单行:订单+sku维度的唯一标识
private static final String ORDER_ROW_KEY = "jd:order:row:";
private static final String ORDER_ROW_JB_KEY = "jd:order:row:jb:";
/**
* 手动调用 将订单发送到微信
*/
@Async("threadPoolTaskExecutor")
public void orderToWx(OrderRow orderRow, Boolean isAutoFlush, Boolean isAutoFlushJB) {
try {
// 获取订单当前状态
Integer newValidCode = orderRow.getValidCode();
String oldValidCode = getFromRedis(ORDER_ROW_KEY + orderRow.getId());
// 检查Redis中是否有旧的状态码没有的话赋予默认值
Integer lastValidCode = oldValidCode != null ? Integer.parseInt(oldValidCode) : -100;
// 先更新 Redis 状态,防止消息发送失败导致重复触发
redisTemplate.opsForValue().set(ORDER_ROW_KEY + orderRow.getId(), String.valueOf(orderRow.getValidCode()));
// 订单变化:当 isAutoFlush == false 或状态确实有变化时,进行消息发送
if (!isAutoFlush || !lastValidCode.equals(newValidCode)) {
String content = getFormattedOrderInfo(orderRow);
String wxId = getWxidFromJdid(orderRow.getUnionId().toString());
if (Util.isNotEmpty(wxId)) {
wxUtil.sendTextMessage(wxId, content, 1, wxId, true);
if (newValidCode != 17) {
// 发送今日统计信息
sendDailyStats(wxId);
}
}
}
// 处理价保逻辑
BigDecimal newProPriceAmount = Optional.ofNullable(orderRow.getProPriceAmount()).map(BigDecimal::new).orElse(BigDecimal.ZERO);
// 获取Redis中的旧价保金额和通知状态
String redisKey = ORDER_ROW_JB_KEY + orderRow.getId();
String redisValue = getFromRedis(redisKey);
// 解析Redis值格式为 "金额:通知状态",例如 "100.00:true"
String[] parts = redisValue != null ? redisValue.split(":") : new String[0];
BigDecimal oldProPriceAmount = parts.length > 0 ? new BigDecimal(parts[0]) : BigDecimal.ZERO;
boolean hasNotified = parts.length > 1 && Boolean.parseBoolean(parts[1]);
// 判断是否需要发送通知:金额不为零且金额变化了或者未发送过通知
boolean shouldNotify = newProPriceAmount.compareTo(BigDecimal.ZERO) > 0 && (newProPriceAmount.compareTo(oldProPriceAmount) != 0 || !hasNotified);
if (isAutoFlushJB) {
shouldNotify = true;
}
if (shouldNotify) {
String wxId = getWxidFromJdid(orderRow.getUnionId().toString());
if (Util.isNotEmpty(wxId)) {
String content = getFormattedOrderInfoForJB(orderRow);
String alertMsg = "[爱心] 价保了 " + newProPriceAmount + " [爱心] \n" + content;
try {
// 先发送通知
wxUtil.sendTextMessage(wxId, alertMsg, 1, wxId, true);
// 通知成功后更新Redis格式为 "金额:true"
if (!isAutoFlushJB) {
String newRedisValue = newProPriceAmount + ":true";
redisTemplate.opsForValue().set(redisKey, newRedisValue);
}
} catch (Exception e) {
logger.error("发送价保通知失败: {}", e.getMessage(), e);
// 通知失败不更新Redis下次继续尝试
}
}
}
} catch (Exception e) {
logger.error("处理订单微信通知失败: {}", e.getMessage(), e);
// 可加入重试机制或上报监控
}
}
private void sendDailyStats(String wxId) {
List<WXUtil.SuperAdmin> superAdmins = getSuperAdmins(wxId);
if (superAdmins.isEmpty()) return;
List<Long> unionIds = superAdmins.stream().map(admin -> Long.valueOf(admin.getUnionId())).collect(Collectors.toList());
List<OrderRow> orderRows = orderRowRepository.findByValidCodeNotInAndUnionIdIn(new int[]{-1}, unionIds);
List<OrderRow> todayOrders = filterOrdersByDate(orderRows, 0);
if (todayOrders.isEmpty()) return;
// 按照 unionId 分组统计
Map<String, List<OrderRow>> grouped = todayOrders.stream().collect(Collectors.groupingBy(o -> o.getUnionId().toString()));
StringBuilder resultContent = new StringBuilder();
for (Map.Entry<String, List<OrderRow>> entry : grouped.entrySet()) {
String unionId = entry.getKey();
OrderStats stats = calculateStats(entry.getValue());
resultContent.append(buildStatsContent("京粉 : " + getRemarkFromJdid(unionId), stats));
}
OrderStats totalStats = calculateStats(todayOrders);
String totalMsg = buildStatsContent("今日总统计 : ", totalStats) + " \n详:\n━━━━━━━━━━━━\n" + resultContent;
wxUtil.sendTextMessage(wxId, totalMsg, 1, wxId, true);
}
private String getFromRedis(String key) {
try {
return redisTemplate.opsForValue().get(key);
} catch (Exception e) {
logger.warn("Redis get 失败 key={}", key, e);
return null;
}
}
/**
* 手动调用 将订单发送到微信 批量
*/
@Async("threadPoolTaskExecutor")
public void orderToWxBatch(List<OrderRow> orderRowList) {
if (!orderRowList.isEmpty()) {
int i = 1;
String wxId = getWxidFromJdid(orderRowList.get(0).getUnionId().toString());
StringBuilder content = new StringBuilder();
content.append("批量订单:\n\r ").append("").append(orderRowList.size()).append("\r");
List<OrderRow> filterList = orderRowList.stream().filter(orderRow -> orderRow.getValidCode() != 2 && orderRow.getValidCode() != 3).toList();
content.append("移除 拆单或者取消 的订单, 共 ").append(filterList.size()).append("单: \n\r");
for (OrderRow orderRow : filterList) {
content.append("\r\n");
content.append(i++).append("");
content.append(getFormattedOrderInfoBatch(orderRow));
}
if (Util.isNotEmpty(wxId)) {
wxUtil.sendTextMessage(wxId, content.toString(), 1, wxId, false);
}
}
}
/**
* 将数据库订单转化成微信所需要文本
*/
public String getFormattedOrderInfo(OrderRow orderRow) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ValidCodeConverter converter = new ValidCodeConverter();
Long unionId = orderRow.getUnionId();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(unionId));
StringBuilder orderInfo = new StringBuilder().append(" ").append(getEmjoy(orderRow.getValidCode())).append(" ").append(converter.getCodeDescription(orderRow.getValidCode())).append("\r");
//if (oldValidCode != -100 && !oldValidCode.equals(orderRow.getValidCode())) {
// orderInfo.insert(0, "从 " + getEmjoy(oldValidCode) + " "
// + converter.getCodeDescription(oldValidCode) + "\r变成 "
// + getEmjoy(orderRow.getValidCode()) + " "
// + converter.getCodeDescription(orderRow.getValidCode()) + "\r\n");
//}
orderInfo
//+ "订单+sku" + orderRow.getId() + "\r"
.append("京粉:").append(remarkFromJdid).append("\r").append("订单:").append(orderRow.getOrderId()).append(" (").append(orderRow.getPlus() == 1 ? "plus" : "非plus").append(")\r").append("名称:").append(orderRow.getSkuName()).append("\r").append("\r")
//+ "商品单价:" + orderRow.getPrice() + "\r"
//+ "商品数量:" + orderRow.getSkuNum() + "\r"
//+ "商品总价:" + (orderRow.getPrice() * orderRow.getSkuNum()) + "\r"
.append("计佣金额:").append(orderRow.getEstimateCosPrice()).append("\r")
//+ "金额:" + orderRow.getActualCosPrice() + "\r"
.append("比例:").append(orderRow.getCommissionRate()).append("\r").append("[Packet] 佣金:").append(orderRow.getEstimateFee()).append("\r").append("下单:").append(formatter.format(orderRow.getOrderTime())).append("\r").append("完成:").append(orderRow.getFinishTime() != null ? formatter.format(orderRow.getFinishTime()) : "未完成");
return orderInfo.toString();
}
// 价保
public String getFormattedOrderInfoForJB(OrderRow orderRow) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ValidCodeConverter converter = new ValidCodeConverter();
Long unionId = orderRow.getUnionId();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(unionId));
StringBuilder orderInfo = new StringBuilder().append(" ").append(getEmjoy(orderRow.getValidCode())).append(" ").append(converter.getCodeDescription(orderRow.getValidCode())).append("\r");
orderInfo
//+ "订单+sku" + orderRow.getId() + "\r"
.append("京粉:").append(remarkFromJdid).append("\r").append("订单:").append(orderRow.getOrderId()).append(" (").append(orderRow.getPlus() == 1 ? "plus" : "非plus").append(")\r").append("名称:").append(orderRow.getSkuName()).append("\r").append("\r").append("下单:").append(formatter.format(orderRow.getOrderTime())).append("\r").append("完成:").append(orderRow.getFinishTime() != null ? formatter.format(orderRow.getFinishTime()) : "未完成");
return orderInfo.toString();
}
/**
* 手动调用 将订单发送到微信 批量
*/
@Async("threadPoolTaskExecutor")
public void orderToWxBatchForJB(List<OrderRow> orderRowList) {
if (!orderRowList.isEmpty()) {
int i = 1;
String wxId = getWxidFromJdid(orderRowList.get(0).getUnionId().toString());
StringBuilder content = new StringBuilder();
content.append("批量订单:\n\r ").append("").append(orderRowList.size()).append("\r");
List<OrderRow> filterList = orderRowList.stream().filter(orderRow -> orderRow.getValidCode() != 2 && orderRow.getValidCode() != 3).toList();
content.append("移除 拆单或者取消 的订单, 共 ").append(filterList.size()).append("单: \n\r");
for (OrderRow orderRow : filterList) {
content.append("\r\n");
content.append(i++).append("");
content.append(getFormattedOrderInfoBatchForJB(orderRow));
}
if (Util.isNotEmpty(wxId)) {
wxUtil.sendTextMessage(wxId, content.toString(), 1, wxId, false);
}
}
}
public String getEmjoy(Integer status) {
return switch (status) {
//[爱心]已付款
case 16 -> "[爱心]";
//[Wow] 待付款
case 15 -> "[Wow]";
//[心碎]已取消
case 2, 3 -> "[心碎]";
//[亲亲] 已完成
case 17 -> "[亲亲]";
//[Broken] 违规
case 27, 28 -> "[Broken]";
default -> "";
};
}
/**
* 将数据库订单转化成微信所需要文本 简洁版
*/
public String getFormattedOrderInfoBatch(OrderRow orderRow) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ValidCodeConverter converter = new ValidCodeConverter();
Long unionId = orderRow.getUnionId();
String remarkFromJdid = getRemarkFromJdid(String.valueOf(unionId));
return "订单:" + orderRow.getOrderId() + " (" + (orderRow.getPlus() == 1 ? "plus" : "非plus") + ")\r" + "走的京粉:" + remarkFromJdid + "\r"
+ "状态:" + (converter.getCodeDescription(orderRow.getValidCode())) + "\r"
+ "标题:" + orderRow.getSkuName() + "\r\n" + "\r\n"
//+ "商品单价:" + orderRow.getPrice() + "\r"
//+ "商品数量:" + orderRow.getSkuNum() + "\r"
//+ "商品总价:" + (orderRow.getPrice() * orderRow.getSkuNum()) + "\r"
+ "计佣金额:" + orderRow.getEstimateCosPrice() + "\r"
//+ "金额:" + orderRow.getActualCosPrice() + "\r"
//+ "比例:" + orderRow.getCommissionRate() + "\r"
+ "佣金:" + orderRow.getEstimateFee() + "\r\n"
+ "下单:" + formatter.format(orderRow.getOrderTime()) + "\r" + "完成:" + (orderRow.getFinishTime() != null ? formatter.format(orderRow.getFinishTime()) : "未完成") + "\r";
}
/**
* 将数据库订单转化成微信所需要文本 简洁版
*/
public String getFormattedOrderInfoBatchForJB(OrderRow orderRow) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
StringBuilder orderInfo = new StringBuilder().append("\n").append("价保了:").append(orderRow.getProPriceAmount()).append("\n");
orderInfo.append("订单:").append(orderRow.getOrderId()).append(" (").append(orderRow.getPlus() == 1 ? "plus" : "非plus").append(")\r").append("名称:").append(orderRow.getSkuName()).append("\r").append("\r").append("下单:").append(formatter.format(orderRow.getOrderTime())).append("\r").append("完成:").append(orderRow.getFinishTime() != null ? formatter.format(orderRow.getFinishTime()) : "未完成");
return orderInfo.toString();
}
/**
* JDUtil拷贝的方法避免循环注入
*/
private List<OrderRow> filterOrdersByDate(List<OrderRow> orderRows, int daysBack) {
LocalDate now = LocalDate.now();
return orderRows.stream().filter(order -> {
// 将 Date 转换为 LocalDate
LocalDate orderDate = order.getOrderTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
// 计算是否在给定的天数内
return !orderDate.isBefore(now.minusDays(daysBack)) && !orderDate.isAfter(now);
}).collect(Collectors.toList());
}
private 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 String buildStatsContent(String title, OrderStats stats) {
StringBuilder content = new StringBuilder();
content//[爱心][Wow][Packet][Party][Broken][心碎][亲亲][色]
.append(title).append(" \n").append("[爱心] 订单总数:").append(stats.getTotalOrders()).append("\n") // [文件]
.append("[Party] 有效订单:").append(stats.getValidOrders()).append("\n") // [OK]
.append("[心碎]已取消:").append(stats.getCanceledOrders()).append("\n") // [禁止]
.append("[爱心]已付款:").append(stats.getPaidOrders()).append("\n") // [钱袋]
.append("[Packet] 已付款佣金:").append(String.format("%.2f", stats.getPaidCommission())).append("\n") // [钞票]
.append("[Wow] 待付款:").append(stats.getPendingOrders()).append("\n") // [时钟]
.append("[Packet] 待付款佣金:").append(String.format("%.2f", stats.getPendingCommission())).append("\n").append("━━━━━━━━━━━━\n");
return content.toString();
}
private Stream<OrderRow> getStreamForWeiGui(List<OrderRow> todayOrders) {
return todayOrders.stream().filter(orderRow -> orderRow.getValidCode() == 13 || orderRow.getValidCode() == 25 || orderRow.getValidCode() == 26 || orderRow.getValidCode() == 27 || orderRow.getValidCode() == 28 || orderRow.getValidCode() == 29);
}
// 统计指标DTO
@Getter
@AllArgsConstructor
static 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;// 违规佣金
}
}