diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/InstructionServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/InstructionServiceImpl.java index 9c092ed..c2445df 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/InstructionServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/InstructionServiceImpl.java @@ -658,17 +658,6 @@ public class InstructionServiceImpl implements IInstructionService { } if (input.startsWith("单")) { String primary = handleDanWriteDb(input, forceGenerate, isFromConsole); - String norm = input.trim().replace("元", ""); - if (isNewOrderFormInput(norm)) { - JDOrder parsedForSummary = parseOrderFromText(norm); - String compact = buildNewFormDanCompactSummary(parsedForSummary); - if (compact != null) { - List two = new ArrayList<>(2); - two.add(primary); - two.add(compact); - return two; - } - } return Collections.singletonList(primary); } return Collections.singletonList(helpText()); @@ -1312,32 +1301,6 @@ public class InstructionServiceImpl implements IInstructionService { || originalInput.contains("备注(下单号码有变动/没法带分机号的写这里):"); } - /** - * 新模板录单时追加的精简文案:型号、地址、物流(物流为主链接,与解析入库一致)。 - */ - private String buildNewFormDanCompactSummary(JDOrder order) { - if (order == null) { - return null; - } - String model = order.getModelNumber(); - String address = order.getAddress(); - String logistics = order.getLogisticsLink(); - if (isEmpty(model) && isEmpty(address) && isEmpty(logistics)) { - return null; - } - StringBuilder sb = new StringBuilder(); - if (!isEmpty(model)) { - sb.append("型号:").append(model.trim()).append('\n'); - } - if (!isEmpty(address)) { - sb.append("地址:").append(normalizeWhitespace(address.trim())).append('\n'); - } - if (!isEmpty(logistics)) { - sb.append("物流:").append(logistics.trim()); - } - return sb.toString().trim(); - } - // ===== "单 …" 写库 ===== private String handleDanWriteDb(String input) { return handleDanWriteDb(input, false, false); @@ -1476,10 +1439,58 @@ public class InstructionServiceImpl implements IInstructionService { } } - // 返回完整的表单格式,使用原始输入保留完整物流链接 + if (isNewOrderFormInput(originalInput)) { + return buildDanRecordSuccessMessage(order, originalInput); + } return formatOrderForm(order, originalInput); } + /** + * 新模板录单成功:简短提示 + 必要字段(企微/控制台共用)。 + */ + private String buildDanRecordSuccessMessage(JDOrder order, String originalInput) { + StringBuilder sb = new StringBuilder(); + sb.append("录单成功\n"); + sb.append("单号:").append(nvl(order.getRemark())).append("\n"); + sb.append("订单号:").append(nvl(order.getOrderId())).append("\n"); + if (!isEmpty(order.getThirdPartyOrderNo())) { + sb.append("第三方单号:").append(order.getThirdPartyOrderNo().trim()).append("\n"); + } + sb.append("分销标记:").append(nvl(order.getDistributionMark())).append("\n"); + sb.append("型号:").append(nvl(order.getModelNumber())).append("\n"); + sb.append("下单人:").append(nvl(order.getBuyer())).append("\n"); + sb.append("付款:").append(order.getPaymentAmount() != null ? order.getPaymentAmount().toString() : "").append("\n"); + sb.append("后返:").append(order.getRebateAmount() != null ? order.getRebateAmount().toString() : "").append("\n"); + String logistics = extractOriginalLogisticsLinkNew(originalInput); + if (logistics == null && order.getLogisticsLink() != null) { + logistics = order.getLogisticsLink(); + } + if (!isEmpty(logistics)) { + sb.append("物流:").append(shortenLogisticsLineForReply(logistics)).append("\n"); + } + if (order.getJingfenActualPrice() != null) { + sb.append("京粉实际价:").append(order.getJingfenActualPrice()); + } + return sb.toString().trim(); + } + + /** 物流块多行时只保留主链接或首行,避免企微回显过长 */ + private String shortenLogisticsLineForReply(String logisticsBlock) { + if (logisticsBlock == null) { + return ""; + } + Matcher m = Pattern.compile("https?://3\\.cn/[A-Za-z0-9\\-]+").matcher(logisticsBlock); + if (m.find()) { + return m.group(); + } + String[] lines = logisticsBlock.split("\\r?\\n"); + String first = lines.length > 0 ? lines[0].trim() : logisticsBlock.trim(); + if (first.length() > 220) { + return first.substring(0, 217) + "..."; + } + return first; + } + private boolean isEmpty(String s) { return s == null || s.isEmpty(); } diff --git a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/WeComInboundServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/WeComInboundServiceImpl.java index 6daaa78..651de82 100644 --- a/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/WeComInboundServiceImpl.java +++ b/ruoyi-system/src/main/java/com/ruoyi/jarvis/service/impl/WeComInboundServiceImpl.java @@ -19,7 +19,8 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * LinPingFan:全部指令;其他人员:须在超级管理员中识别为本人(wxid=企微 UserID,**或** 企微 UserID 出现在 touser 逗号分隔列表中),且仅「京*」指令 + 京东分享物流链接流程。 + * LinPingFan:全部指令;其他人员:须在超级管理员中识别为本人(wxid=企微 UserID,**或** 企微 UserID 出现在 touser 逗号分隔列表中),且仅「京*」指令 + 京东分享物流链接流程; + * 例外:以「单」或「开始」开头且含「分销标记」的录单正文优先于物流(不进入 3.cn 多轮、不占用物流监听)。 * 多轮会话使用 Redis({@link WeComChatSession},键 interaction_state:wecom:{FromUserName}),与旧版「开通礼金」interaction_state 思路一致。 */ @Service @@ -55,6 +56,8 @@ public class WeComInboundServiceImpl implements IWeComInboundService { return "无权限:请在后台「超级管理员」将您的企微 UserID 填到 wxid,或加入该行的 touser(逗号分隔)"; } + final boolean danRecordPriority = isDanRecordPriorityOverLogistics(content); + WeComChatSession session = weComChatSessionService.get(from); if (session != null && !session.matchLogisticsWaitRemark()) { weComChatSessionService.delete(from); @@ -67,14 +70,16 @@ public class WeComInboundServiceImpl implements IWeComInboundService { weComChatSessionService.delete(from); return "已取消物流链接录入"; } - if (isJdShareLogisticsMessage(content)) { + if (danRecordPriority) { + weComChatSessionService.delete(from); + log.info("企微用户 {} 在录单优先下退出物流备注会话", from); + } else if (isJdShareLogisticsMessage(content)) { String url = extractJd3cnUrl(content); if (url != null) { weComChatSessionService.put(from, WeComChatSession.startLogisticsWaitRemark(url)); return "收到物流链接 " + url + " ,请输入备注信息"; } - } - if (t.startsWith("京")) { + } else if (t.startsWith("京")) { weComChatSessionService.delete(from); log.info("企微用户 {} 在京指令下中断物流备注会话", from); } else { @@ -87,7 +92,7 @@ public class WeComInboundServiceImpl implements IWeComInboundService { } } - if (isJdShareLogisticsMessage(content)) { + if (!danRecordPriority && isJdShareLogisticsMessage(content)) { String url = extractJd3cnUrl(content); if (url != null) { weComChatSessionService.put(from, WeComChatSession.startLogisticsWaitRemark(url)); @@ -98,8 +103,8 @@ public class WeComInboundServiceImpl implements IWeComInboundService { if (!isSuper) { String cmd = content.trim(); - if (!cmd.startsWith("京")) { - return "当前账号仅支持:以「京」开头的指令,或发送含 3.cn 京东物流分享链接"; + if (!cmd.startsWith("京") && !danRecordPriority) { + return "当前账号仅支持:以「京」开头的指令,或发送含 3.cn 京东物流分享链接,或以「单/开始」开头且含「分销标记」的录单"; } } @@ -114,6 +119,25 @@ public class WeComInboundServiceImpl implements IWeComInboundService { return reply; } + /** + * 录单正文(指令层走「单…」写库)优先于物流:与 {@link InstructionServiceImpl} 新模板一致。 + * 典型:以「单:」开头、含「分销标记」「下单链接(必须用这个)」等;同条含 3.cn 也先录单、不进物流多轮。 + */ + private static boolean isDanRecordPriorityOverLogistics(String text) { + if (!StringUtils.hasText(text)) { + return false; + } + String t = text.trim().replaceFirst("^\uFEFF", ""); + if (!t.contains("分销标记")) { + return false; + } + if (t.startsWith("单") || t.startsWith("开始")) { + return true; + } + return t.contains("单:") + && (t.contains("下单链接(必须用这个)") || t.contains("—————————") || t.contains("下单地址(注意带分机)")); + } + private static boolean isJdShareLogisticsMessage(String text) { if (!StringUtils.hasText(text)) { return false;