This commit is contained in:
Leo
2025-11-10 21:21:01 +08:00
parent 185483dace
commit af68b529b0

View File

@@ -423,48 +423,48 @@ public class InstructionServiceImpl implements IInstructionService {
// ==================== 追加:按下单人分组统计 ====================
outputs.add("\n━━━━━━━ 按下单人统计 ━━━━━━━");
// 按下单人分组(过滤掉拍错退款)
Map<String, List<JDOrder>> byBuyer = filtered.stream()
.filter(o -> o.getStatus() == null || !"拍错退款".equals(o.getStatus()))
.filter(o -> o.getBuyer() != null && !o.getBuyer().isEmpty())
.collect(Collectors.groupingBy(JDOrder::getBuyer));
List<Map.Entry<String, List<JDOrder>>> buyerEntries = new ArrayList<>(byBuyer.entrySet());
buyerEntries.sort(Comparator.comparing(en -> en.getKey() == null ? "" : en.getKey()));
for (Map.Entry<String, List<JDOrder>> e : buyerEntries) {
String buyer = e.getKey() != null ? e.getKey() : "未提供";
List<JDOrder> orders = e.getValue();
Map<String, Long> byModel = orders.stream()
.collect(Collectors.groupingBy(JDOrder::getModelNumber, Collectors.counting()));
int totalCount = 0;
StringBuilder summary = new StringBuilder();
List<Map.Entry<String, Long>> modelEntries = new ArrayList<>(byModel.entrySet());
modelEntries.sort(Comparator.comparing(en -> en.getKey() == null ? "" : en.getKey()));
// 记录该下单人每个型号的最高单价订单
Map<String, JDOrder> buyerMaxPriceOrders = new HashMap<>();
Map<String, Integer> buyerModelCounts = new HashMap<>();
for (Map.Entry<String, Long> em : modelEntries) {
int c = em.getValue().intValue();
totalCount += c;
String model = em.getKey() != null ? em.getKey() : "未知";
summary.append("型号:").append(model).append(" 数量:").append(c).append("\n");
buyerModelCounts.put(model, c);
// 找到该型号价格最高的订单
for (JDOrder order : orders) {
if (model.equals(order.getModelNumber())) {
JDOrder currentMax = buyerMaxPriceOrders.get(model);
if (currentMax == null ||
(order.getPaymentAmount() != null &&
(currentMax.getPaymentAmount() == null ||
(currentMax.getPaymentAmount() == null ||
order.getPaymentAmount() > currentMax.getPaymentAmount()))) {
buyerMaxPriceOrders.put(model, order);
}
@@ -472,7 +472,7 @@ public class InstructionServiceImpl implements IInstructionService {
}
}
summary.append("总计:").append(totalCount).append("\n详情");
List<JDOrder> sorted = orders.stream()
.sorted(Comparator.comparing(JDOrder::getRemark, Comparator.nullsFirst(String::compareTo)))
.collect(Collectors.toList());
@@ -488,52 +488,52 @@ public class InstructionServiceImpl implements IInstructionService {
.append("地址:").append(o.getAddress() != null ? o.getAddress() : "未提供").append("\n")
.append("物流链接:\n").append(o.getLogisticsLink() != null ? o.getLogisticsLink() : "");
}
// 订单详情
StringBuilder infoSingle = new StringBuilder();
infoSingle.append("下单人:").append(buyer).append("\n").append(summary).append(detail).append("\n");
outputs.add(infoSingle.toString().trim());
// 算钱文本(使用佣金支付)
if (!buyerModelCounts.isEmpty()) {
StringBuilder priceText = new StringBuilder();
priceText.append("下单人:").append(buyer).append("\n");
// 先输出型号和数量
List<Map.Entry<String, Integer>> sortedEntries = buyerModelCounts.entrySet().stream()
.sorted(Comparator.comparing(Map.Entry::getKey))
.collect(Collectors.toList());
for (Map.Entry<String, Integer> entry : sortedEntries) {
String model = entry.getKey();
int count = entry.getValue();
priceText.append("型号:").append(model).append(" 数量:").append(count).append("\n");
}
priceText.append("\n");
// 输出金额计算(使用佣金支付字段)
double grandTotal = 0.0;
StringBuilder calculationLines = new StringBuilder();
List<Double> totalPricesList = new ArrayList<>();
for (Map.Entry<String, Integer> entry : sortedEntries) {
String model = entry.getKey();
int count = entry.getValue();
// 获取该型号最高价格的订单
JDOrder maxPriceOrder = buyerMaxPriceOrders.get(model);
double singlePayment = 0.0;
double singleRebate = 0.0;
if (maxPriceOrder != null) {
singlePayment = maxPriceOrder.getPaymentAmount() != null ? maxPriceOrder.getPaymentAmount() : 0.0;
singleRebate = maxPriceOrder.getRebateAmount() != null ? maxPriceOrder.getRebateAmount() : 0.0;
}
singlePayment = Math.round(singlePayment * 100.0) / 100.0;
singleRebate = Math.round(singleRebate * 100.0) / 100.0;
// 从Redis获取佣金支付- 支付给下单人的佣金
double commissionPay = 0.0;
try {
@@ -549,17 +549,17 @@ public class InstructionServiceImpl implements IInstructionService {
} catch (Exception ex) {
// 如果获取失败佣金为0
}
// 计算净价(单台价格 - 返现 + 佣金支付)
double netPrice = singlePayment - singleRebate + commissionPay;
netPrice = Math.round(netPrice * 100.0) / 100.0;
// 计算总价
double totalPrice = netPrice * count;
totalPrice = Math.round(totalPrice * 100.0) / 100.0;
totalPricesList.add(totalPrice);
grandTotal += totalPrice;
// 格式:单台价格-返现+佣金=净价×数量=总价
if (count == 1) {
calculationLines.append(String.format("%.2f-%.2f+%.2f=%.2f\n",
@@ -569,9 +569,9 @@ public class InstructionServiceImpl implements IInstructionService {
singlePayment, singleRebate, commissionPay, netPrice, count, totalPrice));
}
}
priceText.append(calculationLines);
// 输出汇总
if (sortedEntries.size() > 1) {
priceText.append("\n");
@@ -584,7 +584,7 @@ public class InstructionServiceImpl implements IInstructionService {
grandTotal = Math.round(grandTotal * 100.0) / 100.0;
priceText.append(String.format("=%.2f", grandTotal));
}
outputs.add(priceText.toString().trim());
}
}
@@ -606,12 +606,12 @@ public class InstructionServiceImpl implements IInstructionService {
static {
/*
13103996539
15514755615
17746927935
13140433517
17746921532
15514786055
13103996539
15514755615
17746927935
13140433517
17746921532
15514786055
*/
phoneWithTF.add("13103996539");
phoneWithTF.add("15514755615");
@@ -660,12 +660,12 @@ private String handleTF(String input) {
// if ("13243039070".equals(phone) || "17530176250".equals(phone)) {
// String today = new java.text.SimpleDateFormat("yyyy-MM-dd").format(new Date());
// String usedPhonesKey = "phone_used_today:" + today;
// if (stringRedisTemplate != null) {
// try {
// // 获取今天已经使用过的号码集合
// Set<String> usedPhonesToday = stringRedisTemplate.opsForSet().members(usedPhonesKey);
// // 找出今天还没使用过的号码
// List<String> availablePhones = new ArrayList<>();
// for (String p : phoneWithTF) {
@@ -673,7 +673,7 @@ private String handleTF(String input) {
// availablePhones.add(p);
// }
// }
// if (!availablePhones.isEmpty()) {
// // 随机选择一个今天还没用过的号码
// phone = availablePhones.get(new Random().nextInt(availablePhones.size()));
@@ -684,7 +684,7 @@ private String handleTF(String input) {
// // phoneWithTF 中的号码今天都用完了,使用备用号码(轮换使用)
// String backupUsedKey = "backup_phone_used_today:" + today;
// Set<String> backupUsedToday = stringRedisTemplate.opsForSet().members(backupUsedKey);
// // 检查两个备用号码哪个还没用过
// if (backupUsedToday == null || !backupUsedToday.contains("15639125541")) {
// phone = "15639125541";
@@ -700,7 +700,7 @@ private String handleTF(String input) {
// phone = "15639125541";
// }
// }
// // 记录今天使用的备用号码
// stringRedisTemplate.opsForSet().add(backupUsedKey, phone);
// stringRedisTemplate.expire(backupUsedKey, 1, TimeUnit.DAYS);
@@ -1026,7 +1026,7 @@ private String handleTF(String input) {
private String generateOrderText(String shengInput) {
return generateOrderText(shengInput, null);
}
private String generateOrderText(String shengInput, String providedThirdPartyOrderNo) {
String[] split = shengInput.split("\n");
// 第一行可能是 生 或 生{备注}
@@ -1117,7 +1117,7 @@ private String handleTF(String input) {
thirdPartyOrderNo = extractOrderNumber(fenxiao);
}
String thirdPartyOrderNoValue = (thirdPartyOrderNo != null && !thirdPartyOrderNo.isEmpty()) ? thirdPartyOrderNo : "";
for (int i = 0; i < total; i++) {
int currentCount = startCount + i;
String orderId = today + String.format("%03d", currentCount);
@@ -1236,7 +1236,7 @@ private String handleTF(String input) {
if (existingByOrderId != null) {
// 如果是更新同一条记录remark相同则不提示重复
if (!order.getRemark().equals(existingByOrderId.getRemark())) {
String warn = "[炸弹] [炸弹] [炸弹] 录单警告!!! \n订单号重复\n" +
String warn = "[炸弹] [炸弹] [炸弹] 录单警告!!! \n订单号重复\n" +
"订单号:" + order.getOrderId() + "\n" +
"已存在的订单:" + existingByOrderId.getRemark() + "\n" +
"下单时间:" + new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(existingByOrderId.getOrderTime());
@@ -1251,7 +1251,7 @@ private String handleTF(String input) {
if (existingByLogistics != null) {
// 如果是更新同一条记录remark相同则不提示重复
if (!order.getRemark().equals(existingByLogistics.getRemark())) {
String warn = "[炸弹] [炸弹] [炸弹] 录单警告!!! \n物流链接重复\n" +
String warn = "[炸弹] [炸弹] [炸弹] 录单警告!!! \n物流链接重复\n" +
"物流链接:" + order.getLogisticsLink() + "\n" +
"已存在的订单:" + existingByLogistics.getRemark() + "\n" +
"下单时间:" + new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(existingByLogistics.getOrderTime());
@@ -1315,7 +1315,7 @@ private String handleTF(String input) {
try {
if (tencentDocDelayedPushService != null) {
tencentDocDelayedPushService.triggerDelayedPush();
System.out.println("✓ H-TF订单已触发延迟推送 - 单号: " + order.getRemark() +
System.out.println("✓ H-TF订单已触发延迟推送 - 单号: " + order.getRemark() +
", 第三方单号: " + order.getThirdPartyOrderNo());
}
} catch (Exception e) {
@@ -1339,10 +1339,10 @@ private String handleTF(String input) {
*/
private String formatOrderForm(JDOrder order, String originalInput) {
StringBuilder sb = new StringBuilder();
// 从原始输入中提取完整的物流链接
String originalLogisticsLink = extractOriginalLogisticsLink(originalInput);
sb.append("单:\n");
sb.append(order.getRemark() != null ? order.getRemark() : "").append("\n");
sb.append("备注:").append(order.getStatus() != null ? order.getStatus() : "").append("\n");
@@ -1367,10 +1367,10 @@ private String handleTF(String input) {
sb.append(order.getBuyer() != null ? order.getBuyer() : "").append("\n");
sb.append("京粉实际价格:\n");
sb.append(order.getJingfenActualPrice() != null ? order.getJingfenActualPrice().toString() : "").append("\n");
return sb.toString();
}
/**
* 从原始输入中提取完整的物流链接文本(不做清理)
*/
@@ -1393,15 +1393,15 @@ private String handleTF(String input) {
private JDOrder parseOrderFromText(String input) {
// 不要替换所有空格,保留换行符用于正确解析
Map<String, String> fields = new HashMap<>();
// 先尝试新格式(带第三方单号和京粉实际价格)
extractField(input, fields, "单:", "备注:");
extractField(input, fields, "备注:", "分销标记:");
// 检查是否有"第三方单号:"字段,如果没有则是旧格式
boolean hasThirdPartyOrderNo = input.contains("第三方单号:");
boolean hasJingfenPrice = input.contains("京粉实际价格:");
if (hasThirdPartyOrderNo) {
// 新格式:有第三方单号字段
extractField(input, fields, "分销标记:", "第三方单号:");
@@ -1410,7 +1410,7 @@ private String handleTF(String input) {
// 旧格式:分销标记后面直接是型号
extractField(input, fields, "分销标记:", "型号:");
}
extractField(input, fields, "型号:", "链接:");
extractField(input, fields, "链接:", "下单付款:");
extractField(input, fields, "下单付款:", "后返金额:");
@@ -1418,7 +1418,7 @@ private String handleTF(String input) {
extractField(input, fields, "地址:", "物流链接:");
extractField(input, fields, "物流链接:", "订单号:");
extractField(input, fields, "订单号:", "下单人:");
// 京粉实际价格不从表单解析,而是从数据库查询获取
// 如果表单中有这个字段,也提取出来(但不会使用,仅用于兼容)
if (hasJingfenPrice) {
@@ -1427,7 +1427,7 @@ private String handleTF(String input) {
} else {
extractField(input, fields, "下单人:", "");
}
// 使用正则提取下单人(兼容新旧格式)
String nextField = hasJingfenPrice ? "京粉实际价格:" : "单:";
java.util.regex.Pattern buyerPattern = java.util.regex.Pattern.compile("下单人:\\s*(.*?)\\s*(?=" + nextField + "|\\Z)", java.util.regex.Pattern.DOTALL);
@@ -1443,7 +1443,7 @@ private String handleTF(String input) {
JDOrder order = new JDOrder();
order.setRemark(fields.getOrDefault("", null));
// 处理分销标记:如果提取的内容包含换行符,说明可能提取过度了,只取第一行
String distributionMark = fields.getOrDefault("分销标记", null);
if (distributionMark != null && distributionMark.contains("\n")) {
@@ -1464,7 +1464,7 @@ private String handleTF(String input) {
distributionMark = "H-TF";
}
order.setDistributionMark(distributionMark);
// 优先从字段中获取第三方单号,如果没有则从分销标记中提取
String thirdPartyOrderNo = fields.getOrDefault("第三方单号", null);
if (thirdPartyOrderNo != null) {
@@ -1681,7 +1681,7 @@ private String handleTF(String input) {
if (v == null) return "";
// 如果长度大于4进行截取
if (v.length() > 4) {
if (v.length() > 5) {
// 查找第一个左括号的位置
int leftParenIndex = v.indexOf('(');
if (leftParenIndex > 0) {
@@ -1730,7 +1730,7 @@ private String handleTF(String input) {
/**
* 异步写入订单到腾讯文档
* 当订单的分销标识是 H-TF 时,自动追加到腾讯文档表格
*
*
* @param order 订单对象
*/
private void asyncWriteToTencentDoc(JDOrder order) {
@@ -1739,7 +1739,7 @@ private String handleTF(String input) {
try {
// Redis key前缀用于获取文档配置
final String REDIS_KEY_PREFIX = "tencent:doc:auto:config:";
// 1. 从Token服务获取access-token会自动从Redis读取并刷新
String accessToken = null;
try {
@@ -1749,12 +1749,12 @@ private String handleTF(String input) {
System.err.println(" 提示:请先完成腾讯文档授权,访问 /jarvis/tendoc/authUrl 获取授权URL");
return;
}
// 2. 从Redis获取文档配置fileId、sheetId 和 startRow
String fileId = redisCache.getCacheObject(REDIS_KEY_PREFIX + "fileId");
String sheetId = redisCache.getCacheObject(REDIS_KEY_PREFIX + "sheetId");
Integer startRow = redisCache.getCacheObject(REDIS_KEY_PREFIX + "startRow");
// 如果Redis中没有则使用配置文件中的默认值
if (fileId == null || fileId.isEmpty()) {
fileId = tencentDocConfig.getFileId();
@@ -1765,34 +1765,34 @@ private String handleTF(String input) {
if (startRow == null) {
startRow = tencentDocConfig.getStartRow();
}
// 3. 验证配置是否完整
if (fileId == null || fileId.isEmpty() || sheetId == null || sheetId.isEmpty()) {
System.err.println("✗ H-TF订单文档配置不完整跳过自动写入。" +
"\n 提示:请通过 POST /jarvis/tencentDoc/config 接口配置文档ID和工作表ID" +
"\n fileId: " + (fileId != null && !fileId.isEmpty() ? fileId : "未配置") +
"\n fileId: " + (fileId != null && !fileId.isEmpty() ? fileId : "未配置") +
"\n sheetId: " + (sheetId != null && !sheetId.isEmpty() ? sheetId : "未配置") +
"\n startRow: " + (startRow != null ? startRow : "默认3"));
return;
}
// 4. 调用腾讯文档服务追加订单数据
com.alibaba.fastjson2.JSONObject result = tencentDocService.appendLogisticsToSheet(
accessToken, fileId, sheetId, startRow, order);
if (result != null) {
System.out.println("✓ H-TF订单已自动追加到腾讯文档" +
"\n 单号: " + order.getRemark() +
"\n 第三方单号: " + order.getThirdPartyOrderNo() +
"\n 单号: " + order.getRemark() +
"\n 第三方单号: " + order.getThirdPartyOrderNo() +
"\n 文档: " + fileId + "/" + sheetId);
} else {
System.err.println("✗ 订单追加到腾讯文档失败 - 单号: " + order.getRemark() +
System.err.println("✗ 订单追加到腾讯文档失败 - 单号: " + order.getRemark() +
"\n API返回null请检查access-token是否有效或文档是否存在");
}
} catch (Exception e) {
// 写入失败不影响录单结果,仅记录错误日志
System.err.println("✗ 异步写入腾讯文档失败 - 单号: " + order.getRemark() +
"\n 错误: " + e.getMessage() +
System.err.println("✗ 异步写入腾讯文档失败 - 单号: " + order.getRemark() +
"\n 错误: " + e.getMessage() +
"\n 提示:" +
"\n 1. 检查腾讯文档授权是否有效" +
"\n 2. 检查文档ID和工作表ID是否正确" +