package com.ruoyi.jarvis.service.impl;

import com.ruoyi.jarvis.domain.OrderRows;
import com.ruoyi.jarvis.domain.JDOrder;
import com.ruoyi.jarvis.domain.SuperAdmin;
import com.ruoyi.jarvis.domain.WeComShareLinkLogisticsJob;
import com.ruoyi.jarvis.enums.ValidCodeConverter;
import com.ruoyi.jarvis.service.IInstructionService;
import com.ruoyi.jarvis.service.IOrderRowsService;
import com.ruoyi.jarvis.service.IJDOrderService;
import com.ruoyi.jarvis.service.IJDOrderModelShopService;
import com.ruoyi.jarvis.service.IProductJdConfigService;
import com.ruoyi.jarvis.service.IPhoneReplaceConfigService;
import com.ruoyi.jarvis.service.SuperAdminService;
import com.ruoyi.jarvis.service.IWeComShareLinkLogisticsJobService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.TimeUnit;

/**
 * 将 jd 项目中 JDUtil 的指令能力，抽取为无微信依赖的本地执行版。
 * 仅实现最常用统计/订单查询与转链占位能力，按需扩展。
 */
@Service
public class InstructionServiceImpl implements IInstructionService {

    @Resource
    private IOrderRowsService orderRowsService;
    @Resource
    private IJDOrderService jdOrderService;
    @Resource
    private IJDOrderModelShopService jdOrderModelShopService;
    @Resource
    private SuperAdminService superAdminService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private IProductJdConfigService productJdConfigService;
    @Resource
    private IPhoneReplaceConfigService phoneReplaceConfigService;
    @Resource
    private com.ruoyi.jarvis.service.ITencentDocService tencentDocService;
    @Resource
    private com.ruoyi.jarvis.service.ITencentDocTokenService tencentDocTokenService;
    @Resource
    private IWeComShareLinkLogisticsJobService weComShareLinkLogisticsJobService;
    @Resource
    private com.ruoyi.jarvis.config.TencentDocConfig tencentDocConfig;
    @Resource
    private com.ruoyi.common.core.redis.RedisCache redisCache;
    @Autowired(required = false)
    private com.ruoyi.jarvis.service.ITencentDocDelayedPushService tencentDocDelayedPushService;

    /** 与 {@link com.ruoyi.jarvis.service.impl.WeComInboundServiceImpl#WE_COM_SUPER_USER_ID} 一致：该账号在京统计中视为全局视角 */
    private static final String WE_COM_SUPER_GLOBAL_STATS_USER = "LinPingFan";

    /** 与企微物流分享链解析一致 */
    private static final Pattern JD_3CN = Pattern.compile("https://3\\.cn/[A-Za-z0-9\\-]+");

    private static final int EXTERNAL_LOGISTICS_LIST_DAYS = 60;
    private static final int EXTERNAL_LOGISTICS_LIST_LIMIT = 35;

    // 录单模板（与 jd/JDUtil 中 WENAN_D 保持一致）
    private static final String WENAN_D = "单：\n" + "{单号} \n" + "分销标记：{分销标记}\n" + "第三方单号：{第三方单号}\n" + "—————————\n" + "下单链接（必须用这个）：\n" + "{链接}\n" + "下单地址（注意带分机）：\n" + "{地址}\n" + "—————————\n" + "型号：{型号}\n" + "\n" + "下单人（需填）：\n" + "\n" + "下单付款（注意核对）：\n" + "\n" + "后返金额（注意核对）：\n" + "\n" + "订单号（需填）：\n" + "\n" + "物流链接（需填）：\n" + "\n" + "备注（下单号码有变动/没法带分机号的写这里）：\n" + "{单的备注}\n" + "—————————\n" + "京粉实际价格：不用填";

    @Override
    public List<String> execute(String command) {
        return execute(command, false);
    }

    @Override
    public List<String> execute(String command, boolean forceGenerate) {
        return execute(command, forceGenerate, false);
    }

    @Override
    public List<String> execute(String command, boolean forceGenerate, boolean isFromConsole) {
        return execute(command, forceGenerate, isFromConsole, null);
    }

    @Override
    public List<String> execute(String command, boolean forceGenerate, boolean isFromConsole, String wecomUserId) {
        // 存储接收的消息到Redis队列
        storeMessageToRedis("instruction:request", command);

        // 检测价保/赔付消息，自动标记后返到账
        if (command != null && !command.trim().isEmpty()) {
            handleCompensationMessage(command);
        }

        List<String> result;

        if (command == null || command.trim().isEmpty()) {
            result = Collections.singletonList("「指令」\n\n请发送具体指令内容；不确定可发「京菜单」查看列表。");
        } else {
            String input = command.trim();

            // 一级命令分流：京系（统计/订单）、录单/慢单、转链/礼金…
            if (input.startsWith("京") || menuKeywords().contains(input)) {
                result = Collections.singletonList(handleJingFen(input.replaceFirst("^京", ""), wecomUserId));
            }
            // TF/H/生/拼多多 生成类指令
            else if (input.startsWith("TF")) {
                result = handleTF(input, forceGenerate);
            } else if (input.startsWith("H")) {
                result = Collections.singletonList(handleH(input, forceGenerate));
            } else if (input.startsWith("W")) {
                result = Collections.singletonList(handleW(input, forceGenerate));
            } else if (input.startsWith("生")) {
                result = Collections.singletonList(handleSheng(input, forceGenerate));
            } else if (isPDDWCommand(input)) {
                result = Collections.singletonList(handlePDDW(input, forceGenerate));
            } else if (input.equals("拼多多") || input.startsWith("拼多多\n") || input.startsWith("拼多多\r\n") || isPDDFormat(input)) {
                result = Collections.singletonList(handlePDD(input, forceGenerate));
            } else if (input.startsWith("录单") || input.startsWith("慢单") || input.startsWith("慢搜") || input.startsWith("慢查") || input.startsWith("单")) {
                result = handleRecordLikeMulti(input, forceGenerate, isFromConsole);
            } else if (input.startsWith("转链") || input.startsWith("文案") || input.startsWith("转")) {
                result = Collections.singletonList(handleTransferLike(input));
            } else {
                result = Collections.singletonList(helpText());
            }
        }

        // 存储返回的消息到Redis队列
        String response = result != null ? String.join("\n", result) : "";
        storeMessageToRedis("instruction:response", response);

        return result;
    }

    /**
     * 「京」统计/订单所用京粉订单数据源：企微成员按超级管理员绑定 unionId；全局视角排除「不参与订单统计」联盟。
     */
    private static final class JingStatsScope {
        private final List<OrderRows> rows;
        /** 可为 null：禁止访问统计（不参与统计等） */
        private final String denyMessage;
        /** 回复前缀，标明当前统计归属 */
        private final String replyPrefix;

        private JingStatsScope(List<OrderRows> rows, String denyMessage, String replyPrefix) {
            this.rows = rows != null ? rows : Collections.emptyList();
            this.denyMessage = denyMessage;
            this.replyPrefix = replyPrefix != null ? replyPrefix : "";
        }

        static JingStatsScope denied(String msg) {
            return new JingStatsScope(Collections.emptyList(), msg, "");
        }

        static JingStatsScope ok(List<OrderRows> rows, String replyPrefix) {
            return new JingStatsScope(rows, null, replyPrefix);
        }
    }

    private List<Long> buildExcludeUnionIdsForStats() {
        List<Long> excludeUnionIds = new ArrayList<>();
        List<SuperAdmin> superAdminList = superAdminService.selectSuperAdminList(null);
        if (superAdminList != null) {
            for (SuperAdmin admin : superAdminList) {
                if (admin.getIsCount() != null && admin.getIsCount() == 0 && admin.getUnionId() != null && !admin.getUnionId().trim().isEmpty()) {
                    try {
                        excludeUnionIds.add(Long.parseLong(admin.getUnionId().trim()));
                    } catch (NumberFormatException ignored) {
                    }
                }
            }
        }
        return excludeUnionIds;
    }

    private JingStatsScope resolveJingStatsScope(String wecomUserId) {
        if (wecomUserId == null || wecomUserId.trim().isEmpty()
                || WE_COM_SUPER_GLOBAL_STATS_USER.equals(wecomUserId.trim())) {
            List<OrderRows> all = orderRowsService.selectOrderRowsListWithFilter(new OrderRows(), null, null, buildExcludeUnionIdsForStats());
            return JingStatsScope.ok(all, "");
        }
        String wxUser = wecomUserId.trim();
        SuperAdmin sa = superAdminService.selectSuperAdminByWecomUserId(wxUser);
        if (sa == null) {
            return JingStatsScope.ok(Collections.emptyList(), "");
        }
        if (sa.getIsCount() != null && sa.getIsCount() == 0) {
            return JingStatsScope.denied("「京统计」\n\n当前企微账号在后台「超级管理员」中标记为不参与订单统计，无法使用统计与订单类京指令。\n如需开通请联系管理员。");
        }
        if (sa.getUnionId() == null || sa.getUnionId().trim().isEmpty()) {
            return JingStatsScope.denied("「京统计」\n\n当前企微账号未绑定联盟ID，无法匹配京粉订单。\n请在后台「超级管理员」中维护该账号对应行的联盟ID。");
        }
        try {
            long uid = Long.parseLong(sa.getUnionId().trim());
            OrderRows probe = new OrderRows();
            probe.setUnionId(uid);
            List<OrderRows> scoped = orderRowsService.selectOrderRowsListWithFilter(probe, null, null, Collections.emptyList());
            String namePart = sa.getName() != null && !sa.getName().trim().isEmpty() ? sa.getName().trim() : "未命名";
            String prefix = "【联盟 " + sa.getUnionId().trim() + " · " + namePart + "】\n";
            return JingStatsScope.ok(scoped, prefix);
        } catch (NumberFormatException e) {
            return JingStatsScope.denied("「京统计」\n\n联盟ID格式不正确，请联系管理员检查「超级管理员」配置。");
        }
    }

    /**
     * 将消息存储到Redis队列，最多保留100条
     * @param key Redis键
     * @param message 消息内容
     */
    private void storeMessageToRedis(String key, String message) {
        if (stringRedisTemplate == null || message == null) {
            return;
        }
        try {
            // 添加时间戳
            String timestampedMessage = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " | " + message;
            // 从左边推入消息（新消息在前）
            stringRedisTemplate.opsForList().leftPush(key, timestampedMessage);
            // 保持列表最多100条，删除最早的消息
            stringRedisTemplate.opsForList().trim(key, 0, 999);
        } catch (Exception e) {
            // 记录异常但不影响主流程
            System.err.println("存储消息到Redis失败: " + e.getMessage());
        }
    }

    @Override
    public List<String> getHistory(String type, Integer limit) {
        return getHistory(type, limit, null);
    }

    @Override
    public List<String> getHistory(String type, Integer limit, String keyword) {
        if (stringRedisTemplate == null) {
            return Collections.emptyList();
        }

        try {
            String key;
            if ("request".equalsIgnoreCase(type)) {
                key = "instruction:request";
            } else if ("response".equalsIgnoreCase(type)) {
                key = "instruction:response";
            } else {
                return Collections.emptyList();
            }

            int maxReturn = (limit != null && limit > 0) ? Math.min(limit, 1000) : 200;
            boolean hasKeyword = keyword != null && !keyword.trim().isEmpty();
            String kwLower = hasKeyword ? keyword.trim().toLowerCase() : null;

            List<String> messages;
            if (hasKeyword) {
                // 搜索模式：取全部数据后在内存中按关键词过滤
                messages = stringRedisTemplate.opsForList().range(key, 0, -1);
                if (messages == null) messages = Collections.emptyList();
                messages = messages.stream()
                        .filter(msg -> msg != null && msg.toLowerCase().contains(kwLower))
                        .limit(maxReturn)
                        .collect(Collectors.toList());
            } else {
                // 普通模式：只取最近 N 条
                messages = stringRedisTemplate.opsForList().range(key, 0, maxReturn - 1);
            }

            return messages != null ? messages : Collections.emptyList();
        } catch (Exception e) {
            System.err.println("获取历史消息失败: " + e.getMessage());
            return Collections.emptyList();
        }
    }

    private Set<String> menuKeywords() {
        return new HashSet<>(Arrays.asList("菜单", "今日统计", "昨日统计", "三日统计", "七日统计", "一个月统计", "两个月统计", "三个月统计", "这个月统计", "上个月统计", "今日订单", "昨日订单", "七日订单", "总统计"));
    }

    private String handleJingFen(String cmd, String wecomUserId) {
        String action = cmd.trim();
        if (action.isEmpty() || action.equals("菜单")) {
            return jingMenu();
        }

        if (action.startsWith("外物列表")) {
            String kw = action.substring("外物列表".length()).trim();
            return textExternalShareLinkLogisticsList(kw);
        }
        if (action.startsWith("外物删")) {
            String rest = action.substring("外物删".length()).trim();
            return textExternalShareLinkLogisticsDelete(rest);
        }

        JingStatsScope scope = resolveJingStatsScope(wecomUserId);
        if (scope.denyMessage != null) {
            return scope.denyMessage;
        }
        List<OrderRows> all = scope.rows;
        String header = scope.replyPrefix;

        switch (action) {
            case "今日统计":
                return header + statsText(filterByDays(all, 0), "今日统计");
            case "昨日统计":
                return header + statsText(filterYesterday(all), "昨日统计");
            case "三日统计":
                return header + statsText(filterByRange(all, 3), "三日统计");
            case "七日统计":
                return header + statsText(filterByRange(all, 7), "七日统计");
            case "一个月统计":
                return header + statsText(filterByRange(all, 30), "一个月统计");
            case "两个月统计":
                return header + statsText(filterByRange(all, 60), "两个月统计");
            case "三个月统计":
                return header + statsText(filterByRange(all, 90), "三个月统计");
            case "这个月统计":
                return header + statsText(filterThisMonth(all), "这个月统计");
            case "上个月统计":
                return header + statsText(filterLastMonth(all), "上个月统计");
            case "总统计":
                return header + statsText(all, "总统计");
            case "今日订单":
                return header + listOrders(filterByDays(all, 0), "今日订单");
            case "昨日订单":
                return header + listOrders(filterYesterday(all), "昨日订单");
            case "七日订单":
                return header + listOrders(filterByRange(all, 7), "七日订单");
            default:
                // 高级命令：违规N、SKU、搜索、JF… 此处按需扩展
                if (action.startsWith("高级")) {
                    return "高级指令暂未开放";
                }
                return jingMenu();
        }
    }

    private List<String> handleRecordLikeMulti(String input) {
        return handleRecordLikeMulti(input, false, false);
    }

    private List<String> handleRecordLikeMulti(String input, boolean forceGenerate) {
        return handleRecordLikeMulti(input, forceGenerate, false);
    }

    private List<String> handleRecordLikeMulti(String input, boolean forceGenerate, boolean isFromConsole) {
        // 仅实现“慢搜/慢查 关键词”与“录单/慢单 日期范围”的只读输出，避免侵入写库
        if (input.startsWith("慢搜") || input.startsWith("慢查")) {
            String kw = input.replaceFirst("^慢搜|^慢查", "").trim();
            if (kw.isEmpty()) return Collections.singletonList("请输入搜索关键词");
            List<JDOrder> list = jdOrderService.selectJDOrderList(new JDOrder());
            if (list == null) list = Collections.emptyList();
            // 统一截取分销标记
            list.forEach(order -> order.setDistributionMark(truncateDistributionMark(order.getDistributionMark())));
            String low = kw.toLowerCase(Locale.ROOT);
            List<JDOrder> matched = list.stream().filter(o -> contains(o.getRemark(), low) || contains(o.getOrderId(), low) || contains(o.getModelNumber(), low) || contains(o.getModelShop(), low) || contains(orderFullModel(o), low) || contains(o.getAddress(), low) || contains(o.getBuyer(), low)).limit(50).collect(Collectors.toList());
            if (matched.isEmpty()) return Collections.singletonList("未找到匹配的订单");
            StringBuilder sb = new StringBuilder();
            int i = 0;
            for (JDOrder o : matched) {
                i++;
                sb.append(i).append(",   单：").append(nvl(o.getRemark())).append("\n分销标记：").append(nvl(o.getDistributionMark())).append("\n型号：").append(nvl(orderFullModel(o))).append("\n链接：").append(nvl(o.getLink())).append("\n下单付款：").append(nvl(o.getPaymentAmount())).append("\n后返金额：").append(nvl(o.getRebateAmount())).append("\n地址：").append(nvl(o.getAddress())).append("\n物流链接：").append(nvl(o.getLogisticsLink())).append("\n订单号：").append(nvl(o.getOrderId())).append("\n下单人：").append(nvl(o.getBuyer())).append("\n下单时间：").append(nvl(o.getOrderTime())).append("\n备注：").append(nvl(o.getStatus())).append("\n━━━━━━━━━━━━\n");
            }
            return Collections.singletonList(sb.toString());
        }
        if (input.startsWith("录单")) {
            // 录单导出样式（tab）
            List<LocalDate> range = parseDateRange(input.replaceFirst("^录单", "").trim());
            if (range == null) {
                LocalDate today = effectiveToday().toLocalDate();
                range = Arrays.asList(today, today);
            }
            Date start = Date.from(range.get(0).atStartOfDay(ZoneId.systemDefault()).toInstant());
            Date end = Date.from(range.get(1).atTime(LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant());
            List<JDOrder> list = jdOrderService.selectJDOrderList(new JDOrder());
            if (list == null) list = Collections.emptyList();
            // 统一截取分销标记
            list.forEach(order -> order.setDistributionMark(truncateDistributionMark(order.getDistributionMark())));
            // 过滤条件：时间范围 + 参与统计标记（isCountEnabled为null或1表示参与统计）
            List<JDOrder> filtered = list.stream().filter(o -> o.getOrderTime() != null && !o.getOrderTime().before(start) && !o.getOrderTime().after(end) && (o.getIsCountEnabled() == null || o.getIsCountEnabled() == 1)).collect(Collectors.toList());
            if (filtered.isEmpty()) return Collections.singletonList("今天没有订单。");
            // 按 remark 排序
            List<JDOrder> sorted = filtered.stream().sorted(Comparator.comparing(JDOrder::getRemark, Comparator.nullsFirst(String::compareTo))).collect(Collectors.toList());
            StringBuilder sb = new StringBuilder();
            for (JDOrder o : sorted) {
                sb.append(nvl(o.getRemark())).append('\t').append(nvl(o.getOrderId())).append('\t').append(fmt(o.getOrderTime()).split(" ")[0]).append('\t').append(nvl(orderFullModel(o))).append('\t').append(nvl(o.getAddress())).append('\t').append(nvl(o.getLogisticsLink())).append('\t').append('\t').append(nvl(o.getBuyer())).append('\t').append(nvl(o.getPaymentAmount())).append('\t').append(nvl(o.getRebateAmount())).append('\t').append(mapDistribution(o.getDistributionMark())).append('\t').append(nvl(o.getStatus())).append("\n");
            }
            return Collections.singletonList(sb.toString());
        }
        if (input.startsWith("慢单")) {
            // 慢单：分销标记分组 -> 型号统计 + 详情 + 算钱文本
            List<LocalDate> range = parseDateRange(input.replaceFirst("^慢单", "").trim());
            if (range == null) {
                LocalDate today = effectiveToday().toLocalDate();
                range = Arrays.asList(today, today);
            }
            Date start = Date.from(range.get(0).atStartOfDay(ZoneId.systemDefault()).toInstant());
            Date end = Date.from(range.get(1).atTime(LocalTime.MAX).atZone(ZoneId.systemDefault()).toInstant());
            List<JDOrder> list = jdOrderService.selectJDOrderList(new JDOrder());
            if (list == null) list = Collections.emptyList();
            // 统一截取分销标记
            list.forEach(order -> order.setDistributionMark(truncateDistributionMark(order.getDistributionMark())));
            // 过滤条件：时间范围 + 参与统计标记（isCountEnabled为null或1表示参与统计）
            List<JDOrder> filtered = list.stream().filter(o -> o.getOrderTime() != null && !o.getOrderTime().before(start) && !o.getOrderTime().after(end) && (o.getIsCountEnabled() == null || o.getIsCountEnabled() == 1)).collect(Collectors.toList());
            if (filtered.isEmpty()) {
                LocalDate queryDate = range.get(0);
                String dateStr = queryDate.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                return Collections.singletonList("日期：" + dateStr + "\n" + dateStr + "没有订单。");
            }

            Map<String, List<JDOrder>> byDM = filtered.stream().filter(o -> o.getStatus() == null || !"拍错退款".equals(o.getStatus())).collect(Collectors.groupingBy(JDOrder::getDistributionMark));
            List<String> outputs = new ArrayList<>();
            List<Map.Entry<String, List<JDOrder>>> dmEntries = new ArrayList<>(byDM.entrySet());
            dmEntries.sort(Comparator.comparing(en -> en.getKey() == null ? "" : en.getKey()));

            for (Map.Entry<String, List<JDOrder>> e : dmEntries) {
                String dm = e.getKey() != null ? e.getKey() : "未提供";
                List<JDOrder> orders = e.getValue();

                Map<String, Long> byModel = orders.stream().collect(Collectors.groupingBy(this::orderFullModel, 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> groupMaxPriceOrders = new HashMap<>();
                Map<String, Integer> groupModelCounts = 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");

                    // 记录该分组的型号数量
                    groupModelCounts.put(model, c);

                    // 找到该型号价格最高的订单
                    for (JDOrder order : orders) {
                        if (model.equals(orderFullModel(order))) {
                            JDOrder currentMax = groupMaxPriceOrders.get(model);
                            if (currentMax == null ||
                                (order.getPaymentAmount() != null &&
                                 (currentMax.getPaymentAmount() == null || order.getPaymentAmount() > currentMax.getPaymentAmount()))) {
                                groupMaxPriceOrders.put(model, order);
                            }
                        }
                    }
                }
                summary.append("总计：").append(totalCount).append("\n详情：");

                List<JDOrder> sorted = orders.stream().sorted(Comparator.comparing(JDOrder::getRemark, Comparator.nullsFirst(String::compareTo))).collect(Collectors.toList());
                StringBuilder detail = new StringBuilder();
                int idx = 0;
                for (JDOrder o : sorted) {
                    if ("拍错退款".equals(o.getStatus())) continue;
                    idx++;
                    detail.append("\n").append(idx).append("  ────────────\n")
                        .append("单：").append(o.getRemark() != null ? o.getRemark() : "未提供").append("\n")
                        .append("备注：").append(o.getStatus() != null ? o.getStatus() : " ").append("\n")
                        .append("第三方单号：").append(o.getThirdPartyOrderNo() != null && !o.getThirdPartyOrderNo().isEmpty() ? o.getThirdPartyOrderNo() : "无").append("\n")
                        .append("型号：").append(o.getModelNumber() != null || o.getModelShop() != null ? orderFullModel(o) : "未提供").append("\n")
                        .append("地址：").append(o.getAddress() != null ? o.getAddress() : "未提供").append("\n")
                        .append("物流链接：\n").append(o.getLogisticsLink() != null ? o.getLogisticsLink() : "无");
                }
                // 拆分为多条消息 - 订单详情
                StringBuilder infoSingle = new StringBuilder();
                infoSingle.append("分销标记：").append(dm).append("\n").append(summary).append(detail).append("\n");
                outputs.add(infoSingle.toString().trim());

                // 为该分组添加算钱文本
                if (!groupModelCounts.isEmpty()) {
                    StringBuilder priceText = new StringBuilder();
                    priceText.append("分销标记：").append(dm).append("\n");

                    // 先输出型号和数量
                    List<Map.Entry<String, Integer>> sortedEntries = groupModelCounts.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 = groupMaxPriceOrders.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;
                        }

                        // 四舍五入到2位小数
                        singlePayment = Math.round(singlePayment * 100.0) / 100.0;
                        singleRebate = Math.round(singleRebate * 100.0) / 100.0;

                        // 从Redis获取佣金（收取）
                        double commission = 0.0;
                        try {
                            com.ruoyi.jarvis.domain.ProductJdConfig config = productJdConfigService.selectProductJdConfigByModel(model);
                            if (config != null) {
                                // 优先使用新的佣金（收取）字段，如果没有则使用旧的commission字段
                                if (config.getCommissionReceive() != null) {
                                    commission = config.getCommissionReceive().doubleValue();
                                } else if (config.getCommission() != null) {
                                    commission = config.getCommission().doubleValue();
                                }
                            }
                        } catch (Exception ex) {
                            // 如果获取失败，佣金为0
                        }

                        // 计算净价（单台价格 - 返现 + 佣金）
                        double netPrice = singlePayment - singleRebate + commission;
                        // 四舍五入到2位小数
                        netPrice = Math.round(netPrice * 100.0) / 100.0;

                        // 计算总价
                        double totalPrice = netPrice * count;
                        // 四舍五入到2位小数
                        totalPrice = Math.round(totalPrice * 100.0) / 100.0;
                        totalPricesList.add(totalPrice);
                        grandTotal += totalPrice;

                        // 格式：单台价格-返现+佣金=净价×数量=总价
                        if (count == 1) {
                            // 数量为1时，不显示×1
                            calculationLines.append(String.format("%.2f-%.2f+%.2f=%.2f\n",
                                singlePayment, singleRebate, commission, totalPrice));
                        } else {
                            calculationLines.append(String.format("%.2f-%.2f+%.2f=%.2f×%d=%.2f\n",
                                singlePayment, singleRebate, commission, netPrice, count, totalPrice));
                        }
                    }

                    priceText.append(calculationLines);

                    // 输出汇总（各型号总价相加）
                    if (sortedEntries.size() > 1) {
                        priceText.append("\n");
                        // 构建汇总公式
                        for (int i = 0; i < totalPricesList.size(); i++) {
                            if (i > 0) {
                                priceText.append("+");
                            }
                            priceText.append(String.format("%.2f", totalPricesList.get(i)));
                        }
                        // 四舍五入汇总到2位小数
                        grandTotal = Math.round(grandTotal * 100.0) / 100.0;
                        priceText.append(String.format("=%.2f", grandTotal));
                    }

                    outputs.add(priceText.toString().trim());
                }
            }

            // ==================== 追加：按下单人分组统计 ====================
            outputs.add("\n━━━━━━━ 按下单人统计 ━━━━━━━");

            // 按下单人「前缀」分组：取第一个 "-" 之前为同组（如 凡-林、凡-淑玲 → 凡；陈文慧-林、陈文慧-666 → 陈文慧）；无 "-" 则按完整下单人
            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(o -> buyerGroupKey(o.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(this::orderFullModel, 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(orderFullModel(order))) {
                            JDOrder currentMax = buyerMaxPriceOrders.get(model);
                            if (currentMax == null ||
                                (order.getPaymentAmount() != null &&
                                 (currentMax.getPaymentAmount() == null ||
                                  order.getPaymentAmount() > currentMax.getPaymentAmount()))) {
                                buyerMaxPriceOrders.put(model, order);
                            }
                        }
                    }
                }
                summary.append("总计：").append(totalCount).append("\n详情：");

                List<JDOrder> sorted = orders.stream()
                    .sorted(Comparator.comparing(JDOrder::getRemark, Comparator.nullsFirst(String::compareTo)))
                    .collect(Collectors.toList());
                StringBuilder detail = new StringBuilder();
                int idx = 0;
                for (JDOrder o : sorted) {
                    if ("拍错退款".equals(o.getStatus())) continue;
                    idx++;
                    detail.append("\n").append(idx).append("  ────────────\n")
                        .append("单：").append(o.getRemark() != null ? o.getRemark() : "未提供").append("\n")
                        .append("备注：").append(o.getStatus() != null ? o.getStatus() : " ").append("\n")
                        .append("第三方单号：").append(o.getThirdPartyOrderNo() != null && !o.getThirdPartyOrderNo().isEmpty() ? o.getThirdPartyOrderNo() : "无").append("\n")
                        .append("型号：").append(o.getModelNumber() != null || o.getModelShop() != null ? orderFullModel(o) : "未提供").append("\n")
                        .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 {
                            com.ruoyi.jarvis.domain.ProductJdConfig config = productJdConfigService.selectProductJdConfigByModel(model);
                            if (config != null) {
                                // 优先使用新的佣金（支付）字段，如果没有则使用旧的commission字段
                                if (config.getCommissionPay() != null) {
                                    commissionPay = config.getCommissionPay().doubleValue();
                                } else if (config.getCommission() != null) {
                                    commissionPay = config.getCommission().doubleValue();
                                }
                            }
                        } 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",
                                singlePayment, singleRebate, commissionPay, totalPrice));
                        } else {
                            calculationLines.append(String.format("%.2f-%.2f+%.2f=%.2f×%d=%.2f\n",
                                singlePayment, singleRebate, commissionPay, netPrice, count, totalPrice));
                        }
                    }

                    priceText.append(calculationLines);

                    // 输出汇总
                    if (sortedEntries.size() > 1) {
                        priceText.append("\n");
                        for (int i = 0; i < totalPricesList.size(); i++) {
                            if (i > 0) {
                                priceText.append("+");
                            }
                            priceText.append(String.format("%.2f", totalPricesList.get(i)));
                        }
                        grandTotal = Math.round(grandTotal * 100.0) / 100.0;
                        priceText.append(String.format("=%.2f", grandTotal));
                    }

                    outputs.add(priceText.toString().trim());
                }
            }
            // ==================== 按下单人统计结束 ====================

            // 在返回数据前，在每个元素的头部添加日期信息
            if (!outputs.isEmpty()) {
                LocalDate queryDate = range.get(0);
                String dateStr = queryDate.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                String dateHeader = "日期：" + dateStr + "\n";
                // 在每个原有元素的头部追加日期信息
                for (int i = 0; i < outputs.size(); i++) {
                    outputs.set(i, dateHeader + outputs.get(i));
                }
            }

            return outputs.isEmpty() ? Collections.singletonList("无数据") : outputs;
        }
        if (input.startsWith("单")) {
            String primary = handleDanWriteDb(input, forceGenerate, isFromConsole);
            String norm = input.trim().replace("元", "");
            if (isNewTemplateDanWriteSuccess(primary) && isNewOrderFormInput(norm)) {
                JDOrder parsed = parseOrderFromText(norm);
                if (isDistributionMarkF(parsed.getDistributionMark())) {
                    String compact = buildNewFormDanCompactSummary(parsed, norm);
                    if (compact != null && !compact.isEmpty()) {
                        List<String> two = new ArrayList<>(2);
                        two.add(primary);
                        two.add(compact);
                        return two;
                    }
                }
            }
            return Collections.singletonList(primary);
        }
        return Collections.singletonList(helpText());
    }

    // ===== TF/H/生 处理 =====
    // 产品京东配置已迁移到Redis，通过productJdConfigService获取
    // 手机号替换配置已迁移到Redis，通过phoneReplaceConfigService获取

    private List<String> handleTF(String input) {
        return handleTF(input, false);
    }

    private List<String> handleTF(String input, boolean forceGenerate) {
    String body = input.replaceFirst("^TF\\s*", "");
    body = body.replaceAll("[啊阿]", "");
    String[] lines = body.split("\\r?\\n+");
    List<String> outputs = new ArrayList<>();
    for (String line : lines) {
        if (line == null) continue;
        line = line.trim();
        if (line.isEmpty()) continue;
        String[] parts = line.split("\t"); // 使用制表符分割每一行
        /**
         * 原来的
         * ZQD180F-EB200	1	石**[7796]	13243039070	上海市 上海市 普陀区 水泉南路58弄3-12-502[阿15783543077转7796]
         * 改成
         * 10.10 	腾锋	JY202510093195	ZQD180F-EB200	1	石**[7796]	13243039070	上海市 上海市 普陀区 水泉南路58弄3-12-502[阿15783543077转7796] 	1650	有货		2
         * 型号，地址，电话的处理逻辑不变，将10.10 	腾锋	JY202510093195 设置成分销标识这个字段，比如之前是H-TF，现在改成H-TF(10.10 	腾锋	JY202510093195)
         * */
        if (parts.length >= 7) {  // 至少需要分销信息(3) + 型号(1) + 数量(1) + 姓名(1) + 电话(1) = 7个字段
            // 提取第二个字段，判断是"腾锋"还是"昭迎"
            String typeField = parts.length > 1 ? parts[1].trim() : "";
            String replaceType = null;
            if ("腾锋".equals(typeField)) {
                replaceType = "腾锋";
            } else if ("昭迎".equals(typeField)) {
                replaceType = "昭迎";
            }
            
            // 处理分销标记: 只保留H-TF，不包含其他内容（第三方单号已单独存储）
            String fenxiaoInfo = "H-TF";
            // 提取第三方单号（第3个字段，格式如：JY202511051374），用于单独存储，不添加到分销标记中
            String thirdPartyOrderNo = parts.length > 2 ? parts[2].trim() : "";

            // 提取型号（第4个字段）
            String modelNumber = parts[3].replace("\\n", "");

            // 提取数量（第5个字段）
            String quantityStr = parts[4];

            // 提取姓名部分（第6个字段）
            String namePart = parts[5];

            // 提取电话（第7个字段）
            String phone = parts[6];
            String originalPhone = phone;
            
            // 如果识别到"腾锋"或"昭迎"，则进行手机号替换
            if (replaceType != null && phoneReplaceConfigService != null) {
                List<String> phoneList = phoneReplaceConfigService.getPhoneList(replaceType);
                if (phoneList != null && !phoneList.isEmpty()) {
                    // 检查当前手机号是否在需要替换的列表中
                    boolean needReplace = false;
                    if ("腾锋".equals(replaceType)) {
                        // 腾锋：如果手机号是13243039070，则需要替换
                        needReplace = "13243039070".equals(phone);
                    } else if ("昭迎".equals(replaceType)) {
                        // 昭迎：如果手机号是17703916233或17530176250，则需要替换
                        needReplace = "17703916233".equals(phone) || "17530176250".equals(phone);
                    }
                    
                    if (needReplace) {
                        phone = phoneReplaceConfigService.getNextPhone(replaceType, originalPhone);
                    }
                }
            }

            // 构建地址：姓名+电话+地址信息
            StringBuilder address = new StringBuilder();

            // 使用正则表达式提取中文字符（常用于姓名开头）
            Pattern pattern = Pattern.compile("[\\u4E00-\\u9FA5]+");
            Matcher matcher = pattern.matcher(namePart);
            if (matcher.find()) {
                address = new StringBuilder(matcher.group());
            }
            address.append(phone);

            // 添加地址信息（从第8个字段开始的所有剩余内容）
            for (int i = 7; i < parts.length; i++) {
                if (!parts[i].trim().isEmpty()) {
                    // 过滤掉纯数字的价格信息
                    if (!parts[i].matches("^\\d+$") && !parts[i].matches("^[有无缺]货$")) {
                        address.append(parts[i]);
                    }
                }
            }

            String jf = productJdConfigService.getJdUrlByProductModel(jdOrderModelShopService.lookupModelBase(modelNumber));

            StringBuilder order = new StringBuilder();
            order.append("生"+phone).append("\n").append(fenxiaoInfo).append("\n").append(modelNumber).append("\n").append(jf).append("\n").append(quantityStr).append("\n").append(address);
            // 传递第三方单号给 generateOrderText，以便在生成订单文本时正确提取
            outputs.add(generateOrderText(order.toString(), thirdPartyOrderNo, forceGenerate));
        } else {
            outputs.add("TF 指令格式：TF\t分销信息\t分销信息\t分销信息\t型号\t数量\t姓名\t电话\t地址 ；也支持多行，每行一条数据");
        }
    }
    return outputs;
}



    private String handleH(String input) {
        return handleH(input, false);
    }

    private String handleH(String input, boolean forceGenerate) {
        return handleHLike(input, "H", "H", forceGenerate);
    }

    private String handleW(String input) {
        return handleW(input, false);
    }

    private String handleW(String input, boolean forceGenerate) {
        return handleHLike(input, "W", "W", forceGenerate);
    }

    private String handleHLike(String input, String commandKeyword, String distributionMark) {
        return handleHLike(input, commandKeyword, distributionMark, false);
    }

    private String handleHLike(String input, String commandKeyword, String distributionMark, boolean forceGenerate) {
        String cleaned = input.replaceFirst("^" + Pattern.quote(commandKeyword), "");
        if (cleaned.startsWith("\n")) {
            cleaned = cleaned.substring(1);
        }
        String[] lines = cleaned.split("\n");
        if (lines.length < 3) {
            return commandKeyword + " 指令需三行：姓名[后缀]\n电话\n地址 … [后缀] 型号";
        }

        String nameLine = lines[0].trim();
        String phone = lines[1].trim();
        String addressLine = lines[2].trim();

        String name = extractChinese(nameLine);
        String suffix = extractBetweenBrackets(nameLine);

        String rawModelToken = extractLastNonChineseToken(addressLine);
        String modelNumber = sanitizeModel(rawModelToken);
        String lookupModel = jdOrderModelShopService.lookupModelBase(modelNumber);
        String cleanedAddress = addressLine.replaceAll("\\[.*?]", "").replace(rawModelToken, "").replaceAll("\\s+", " ").trim();
        String fullAddress = cleanedAddress + " 安装派送联系" + phone + (suffix.isEmpty() ? "" : "转" + suffix);

        String jfLink = productJdConfigService.getJdUrlByProductModel(lookupModel);

        StringBuilder sheng = new StringBuilder();
        sheng.append("生\n").append(distributionMark).append("\n").append(modelNumber).append("\n").append(jfLink != null ? jfLink : "").append("\n")
                .append("1\n").append(name).append(fullAddress);
        return generateOrderText(sheng.toString(), null, forceGenerate);
    }

    private String handleSheng(String input) {
        return handleSheng(input, false);
    }

    private String handleSheng(String input, boolean forceGenerate) {
        // 输入格式：
        // 生\n分销标记\n型号\n转链链接\n数量\n地址
        String[] split = input.split("\n");
        if (split.length != 6) {
            return "生 指令格式：\n生\n分销标记\n型号\n转链链接\n数量\n地址";
        }
        split[1] = split[1] != null ? split[1].trim() : "";
        split[2] = sanitizeModel(split[2]);

        String distributionMark = split[1];
        String link = sanitizeLink(split[3]);
        if ((link == null || link.isEmpty()) && shouldAutoFillLink(distributionMark)) {
            String fetched = productJdConfigService.getJdUrlByProductModel(jdOrderModelShopService.lookupModelBase(split[2]));
            if (fetched != null && !fetched.isEmpty()) {
                link = fetched;
            }
        }
        split[3] = link;
        split[5] = sanitizeAddress(split[5]);
        return generateOrderText(String.join("\n", split), null, forceGenerate);
    }

    /**
     * 判断是否为拼多多格式
     * 拼多多格式特征：第一行包含🕒符号，或者行数>=4
     */
    private boolean isPDDFormat(String input) {
        if (input == null || input.trim().isEmpty()) {
            return false;
        }
        String[] lines = input.split("\r?\n");
        if (lines.length >= 4) {
            // 检查第一行是否包含🕒符号
            if (lines[0].contains("🕒")) {
                return true;
            }
            // 或者检查是否有4行数据且第二行看起来像电话号码
            if (lines.length >= 4 && lines[1].trim().matches("^1[3-9]\\d{9}$")) {
                return true;
            }
        }
        return false;
    }

    private boolean isPDDWCommand(String input) {
        if (input == null) {
            return false;
        }
        String trimmed = input.trim();
        if (trimmed.isEmpty()) {
            return false;
        }
        return trimmed.equals("拼多多 W") || trimmed.equals("拼多多W")
                || trimmed.startsWith("拼多多 W") || trimmed.startsWith("拼多多W");
    }

    private String handlePDD(String input) {
        return handlePDD(input, false);
    }

    private String handlePDD(String input, boolean forceGenerate) {
        return handlePDDWithMark(input, "拼多多", "PDD", forceGenerate);
    }

    private String handlePDDW(String input) {
        return handlePDDW(input, false);
    }

    private String handlePDDW(String input, boolean forceGenerate) {
        return handlePDDWithMark(input, "拼多多 W", "PDD-W", forceGenerate);
    }

    private String handlePDDWithMark(String input, String commandKeyword, String distributionMark) {
        return handlePDDWithMark(input, commandKeyword, distributionMark, false);
    }

    private String handlePDDWithMark(String input, String commandKeyword, String distributionMark, boolean forceGenerate) {
        // 拼多多新格式：
        // <指令>251102-457567158704072
        // 赵政委[6947]
        // 17283248756
        // 安徽省 宿州市 埇桥区 映月湾东区3栋103[6947]
        // eb150
        //
        // 旧格式兼容：
        // 🕒 林[1518]
        // 17284023487
        // 广西壮族自治区 柳州市 柳北区 绿城·杨柳郡柳园7栋[1518]
        // eb150

        if (input == null || input.trim().isEmpty()) {
            return buildPddHelpMessage(commandKeyword);
        }

        String trimmedOriginal = input.trim();
        if (trimmedOriginal.equals(commandKeyword)) {
            return buildPddHelpMessage(commandKeyword);
        }

        String normalizedInput = normalizePddInput(input, commandKeyword);
        String[] lines = normalizedInput.split("\r?\n");
        String thirdPartyOrderNo = null;
        int nameLineIndex = 0;
        boolean isNewFormat = false;

        if (lines.length > 0) {
            String firstLine = lines[0].trim();

            // 格式1：拼多多数字-数字（一行格式，如：拼多多251102-457567158704072）
            Pattern orderNoPattern = Pattern.compile("拼多多(\\d+-\\d+)");
            Matcher matcher = orderNoPattern.matcher(firstLine);
            if (matcher.find()) {
                thirdPartyOrderNo = matcher.group(1); // 提取订单号部分（不含"拼多多"）
                isNewFormat = true;
                nameLineIndex = 1; // 新格式下，姓名在第二行
            }
            // 格式2：第一行是"拼多多"，第二行是订单号（多行格式）
            else if (firstLine.equals("拼多多") && lines.length > 1) {
                String secondLine = lines.length > 1 ? lines[1].trim() : "";
                // 检查第二行是否是订单号格式（数字-数字）
                Pattern orderNoPattern2 = Pattern.compile("^(\\d+-\\d+)$");
                Matcher matcher2 = orderNoPattern2.matcher(secondLine);
                if (matcher2.find()) {
                    thirdPartyOrderNo = matcher2.group(1); // 提取订单号
                    isNewFormat = true;
                    nameLineIndex = 2; // 多行格式下，姓名在第三行
                } else {
                    // 旧格式：移除"拼多多"前缀（如果有）
                    String data = normalizedInput.replaceFirst("^拼多多\\s*[\r\n]+", "").trim();
                    lines = data.split("\r?\n");
                    nameLineIndex = 0;
                }
            } else {
                // 旧格式：移除"拼多多"前缀（如果有）
                String data = normalizedInput.replaceFirst("^拼多多\\s*[\r\n]+", "").trim();
                lines = data.split("\r?\n");
                nameLineIndex = 0;
            }
        }

        // 检查行数
        // 如果第三方单号在第二行（多行格式），需要6行；如果在第一行（一行格式），需要5行
        int requiredLines = isNewFormat ? (nameLineIndex == 2 ? 6 : 5) : 4;
        if (lines.length < requiredLines) {
            String commandLabel = commandKeyword.trim();
            String formatDesc = isNewFormat ?
                (nameLineIndex == 2 ?
                    "\n1. " + commandLabel + "\n2. 订单号\n3. 姓名[编号]\n4. 电话\n5. 地址[编号]\n6. 型号" :
                    "\n1. " + commandLabel + " 订单号\n2. 姓名[编号]\n3. 电话\n4. 地址[编号]\n5. 型号") :
                "\n1. 🕒 姓名[编号]\n2. 电话\n3. 地址[编号]\n4. 型号";
            return commandLabel + " 格式错误，需要" + requiredLines + "行数据：" + formatDesc;
        }

        // 解析姓名行
        String nameLine = lines[nameLineIndex].trim();
        // 移除emoji和特殊字符，提取中文姓名
        String name = extractChinese(nameLine);
        // 如果提取失败，尝试直接从第一行提取（去除emoji、符号和括号内容后剩余的中文）
        if (name.isEmpty()) {
            // 移除所有emoji和特殊符号，只保留中文
            String cleaned = nameLine.replaceAll("[🕒\\[\\]]", "").replaceAll("\\[.*?\\]", "").trim();
            // 只保留中文字符
            name = extractChinese(cleaned);
        }
        // 提取编号（如果有）
        String suffix = extractBetweenBrackets(nameLine);

        // 解析电话行
        int phoneLineIndex = nameLineIndex + 1;
        String phone = lines[phoneLineIndex].trim();

        // 解析地址行
        int addressLineIndex = nameLineIndex + 2;
        String addressLine = lines[addressLineIndex].trim();
        // 移除编号括号和其中的内容
        String address = addressLine.replaceAll("\\[.*?]", "").trim();

        // 解析型号行
        int modelLineIndex = nameLineIndex + 3;
        String modelNumber = lines[modelLineIndex].trim();
        // 清洗型号
        modelNumber = sanitizeModel(modelNumber);

        // 构建完整地址：姓名 + 固定号码(13068923963) + 地址 + 原始电话 + 转 + 编号
        // 格式：赵政委 13068923963 安徽省 宿州市 埇桥区 映月湾东区3栋103 17283248756转6947
        StringBuilder fullAddress = new StringBuilder();
        // 确保姓名不为空，如果仍为空则使用默认值（但这种情况应该不会发生）
        if (!name.isEmpty()) {
            fullAddress.append(name);
        } else {
            // 如果姓名提取失败，记录日志（生产环境可以移除）
            System.err.println("警告：拼多多格式姓名提取失败，姓名行内容：" + nameLine);
        }
        // 固定的号码
        fullAddress.append(" 13068923963 ");
        // 地址
        fullAddress.append(address);
        // 原始电话和编号
        fullAddress.append(" 送货打").append(phone);
        if (!suffix.isEmpty()) {
            fullAddress.append("转").append(suffix);
        }

        // 获取转链链接
        String jf = productJdConfigService.getJdUrlByProductModel(jdOrderModelShopService.lookupModelBase(modelNumber));

        // 构建"生"指令格式
        StringBuilder sheng = new StringBuilder();
        sheng.append("生\n")
            .append(distributionMark)  // 分销标记
            .append("\n")
            .append(modelNumber)
            .append("\n")
            .append(jf != null ? jf : "")  // 转链链接
            .append("\n")
            .append("1")  // 数量默认为1
            .append("\n")
            .append(fullAddress.toString());

        // 传递第三方单号给 generateOrderText
        return generateOrderText(sheng.toString(), thirdPartyOrderNo, forceGenerate);
    }

    private boolean shouldAutoFillLink(String distributionMark) {
        if (distributionMark == null || distributionMark.trim().isEmpty()) {
            return false;
        }
        String trimmed = distributionMark.trim();
        char first = trimmed.charAt(0);
        return first == 'F' || first == 'f' || first == '凡'
            || first == 'W' || first == 'w' || first == '纹'
            || first == 'H' || first == 'h' || first == '鸿';
    }

    private String buildPddHelpMessage(String commandKeyword) {
        String commandLabel = commandKeyword.trim();
        String examplePrefix = commandLabel;
        return "请粘贴" + commandLabel + "格式数据：\n" +
            "新格式（推荐）：\n" +
            examplePrefix + "251102-457567158704072\n" +
            "赵政委[6947]\n" +
            "17283248756\n" +
            "安徽省 宿州市 埇桥区 映月湾东区3栋103[6947]\n" +
            "eb150\n\n" +
            "旧格式（兼容）：\n" +
            "🕒 林[1518]\n" +
            "17284023487\n" +
            "广西壮族自治区 柳州市 柳北区 绿城·杨柳郡柳园7栋[1518]\n" +
            "eb150";
    }

    private String normalizePddInput(String input, String commandKeyword) {
        if (input == null) {
            return "";
        }
        String normalized = input;
        String trimmedKeyword = commandKeyword.trim();
        if ("拼多多 W".equals(trimmedKeyword)) {
            normalized = normalized.replaceFirst("^\\s*拼多多\\s*[Ww]", "拼多多");
        }
        return normalized;
    }

    private String generateOrderText(String shengInput) {
        return generateOrderText(shengInput, null, false);
    }

    private String generateOrderText(String shengInput, String providedThirdPartyOrderNo) {
        return generateOrderText(shengInput, providedThirdPartyOrderNo, false);
    }

    private String generateOrderText(String shengInput, String providedThirdPartyOrderNo, boolean forceGenerate) {
        String[] split = shengInput.split("\n");
        // 第一行可能是 生 或 生{备注}
        String head = split[0].trim();
        String remark = head.replace("{生}", "").replace("生", "").replace("\n", "").trim();
        String fenxiao = split[1].trim();
        String model = split[2].trim();
        String link = split[3].trim();
        int num = 1;
        try {
            num = Integer.parseInt(split[4].trim());
        } catch (Exception ignore) {
        }
        String address = split[5].trim();

        // 第一重判断：从分销标记中提取订单编号进行去重（如 JY202510093195）
        // 如果提供了第三方单号，也用于去重判断
        String orderNumberForDedup = providedThirdPartyOrderNo;
        if (orderNumberForDedup == null || orderNumberForDedup.isEmpty()) {
            orderNumberForDedup = extractOrderNumber(fenxiao);
        }
        if (stringRedisTemplate != null && orderNumberForDedup != null && !orderNumberForDedup.isEmpty()) {
            String orderNumberKey = "order_number:" + orderNumberForDedup;
            String existedOrderNumber = stringRedisTemplate.opsForValue().get(orderNumberKey);
            if (existedOrderNumber != null) {
                // 如果强制生成，跳过订单编号重复检查
                if (!forceGenerate) {
                    // 返回特殊错误码，前端会识别并弹出验证码
                    return "ERROR_CODE:ORDER_NUMBER_DUPLICATE\n此订单编号(" + orderNumberForDedup + ")已经存在，请勿重复生成订单";
                }
                // forceGenerate为true时，跳过订单编号重复检查，继续执行
            }
            // 只有在不强制生成或订单编号不存在时才设置Redis（强制生成时也更新Redis记录）
            stringRedisTemplate.opsForValue().set(orderNumberKey, orderNumberForDedup, 1, TimeUnit.DAYS);
        }

        String today = new java.text.SimpleDateFormat("yyyy-MM-dd ").format(new Date());
        String todayNoSpace = today.trim();
        String keyWithSpace = "order_count:" + today;      // 带空格
        String keyNoSpace = "order_count:" + todayNoSpace; // 不带空格
        String redisKey = keyNoSpace;
        if (stringRedisTemplate != null) {
            try {
                Boolean noSpaceExists = stringRedisTemplate.hasKey(keyNoSpace);
                Boolean spaceExists = stringRedisTemplate.hasKey(keyWithSpace);
                if (Boolean.TRUE.equals(noSpaceExists)) {
                    redisKey = keyNoSpace;
                } else if (Boolean.TRUE.equals(spaceExists)) {
                    redisKey = keyWithSpace;
                } else {
                    redisKey = keyNoSpace; // 默认使用不带空格
                }
            } catch (Exception ignore) {
                redisKey = keyNoSpace;
            }
        }
        StringBuilder out = new StringBuilder();
        int total = Math.max(1, num);

        // 第二重：同日 + 型号 + 地址 槽位。重复提交视为「更新」——复用当日已分配序号，不递增全局 order_count；强制生成则始终新占号并覆盖槽位。
        String slotDigest = digestShengSlot(model, address);
        String slotKey = "sheng_slot:" + todayNoSpace + ":" + slotDigest;
        Integer reusedCounter = null;
        if (stringRedisTemplate != null && !forceGenerate && total == 1) {
            String slotVal = stringRedisTemplate.opsForValue().get(slotKey);
            if (slotVal != null && !slotVal.isEmpty()) {
                try {
                    int c = Integer.parseInt(slotVal.trim());
                    if (c >= 1) {
                        reusedCounter = c;
                    }
                } catch (NumberFormatException ignore) {
                    reusedCounter = null;
                }
            }
        }

        int startCount = 1;
        if (reusedCounter != null) {
            startCount = reusedCounter;
            if (stringRedisTemplate != null) {
                stringRedisTemplate.opsForValue().set(slotKey, String.valueOf(reusedCounter), 1, TimeUnit.DAYS);
            }
        } else if (stringRedisTemplate != null) {
            try {
                String s = stringRedisTemplate.opsForValue().get(redisKey);
                int count = s != null ? Integer.parseInt(s) : 0;
                startCount = count + 1;
                int endCount = count + total;
                stringRedisTemplate.opsForValue().set(redisKey, String.valueOf(endCount), 30, TimeUnit.DAYS);
                if (total == 1) {
                    stringRedisTemplate.opsForValue().set(slotKey, String.valueOf(startCount), 1, TimeUnit.DAYS);
                }
            } catch (Exception ignore) {
            }
        }

        // 优先使用提供的第三方单号，如果没有则从分销标记中提取
        String thirdPartyOrderNo = providedThirdPartyOrderNo;
        if (thirdPartyOrderNo == null || thirdPartyOrderNo.isEmpty()) {
            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);
            String current = WENAN_D.replace("{单号}", orderId).replace("{单的备注}", remark).replace("{分销标记}", fenxiao).replace("{第三方单号}", thirdPartyOrderNoValue).replace("{链接}", link).replace("{地址}", address).replace("{型号}", model).replaceAll("[|]", "");
            out.append(current);
            if (i < total - 1) out.append("\n");
        }
        return out.toString();
    }

    /**
     * 同日「生」指令槽位键摘要：规范化后的 型号 + 地址，避免仅按地址误伤「同址不同型号」。
     */
    private String digestShengSlot(String model, String address) {
        String m = normalizeWhitespace(model == null ? "" : model);
        String a = normalizeWhitespace(address == null ? "" : address);
        String raw = m + "\u0001" + a;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] d = md.digest(raw.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder(32);
            for (byte b : d) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();
        } catch (Exception e) {
            return Integer.toHexString(Objects.hash(m, a));
        }
    }

    /**
     * 从分销标记中提取订单编号
     * 例如：从 "H-TF(10.10 	腾锋	JY202510093195)" 中提取 "JY202510093195"
     * 例如：从 "PDD(251102-457567158704072)" 中提取 "251102-457567158704072"
     */
    private String extractOrderNumber(String fenxiao) {
        if (fenxiao == null || fenxiao.isEmpty()) {
            return null;
        }
        // 优先匹配 JY 开头后跟数字的模式（TF订单）
        Pattern jyPattern = Pattern.compile("JY\\d+");
        Matcher jyMatcher = jyPattern.matcher(fenxiao);
        if (jyMatcher.find()) {
            return jyMatcher.group();
        }
        // 匹配拼多多订单号格式：数字-数字（在括号内）
        Pattern pddPattern = Pattern.compile("\\(([\\d-]+)\\)");
        Matcher pddMatcher = pddPattern.matcher(fenxiao);
        if (pddMatcher.find()) {
            String orderNo = pddMatcher.group(1);
            // 验证格式是否为数字-数字（拼多多格式）
            if (orderNo.matches("\\d+-\\d+")) {
                return orderNo;
            }
        }
        return null;
    }

    private String extractChinese(String s) {
        Matcher m = Pattern.compile("[\\u4E00-\\u9FA5]+").matcher(s);
        return m.find() ? m.group() : "";
    }

    private String extractBetweenBrackets(String s) {
        int a = s.indexOf('[');
        int b = s.indexOf(']');
        if (a != -1 && b != -1 && b > a) return s.substring(a + 1, b);
        return "";
    }

    private String extractLastNonChineseToken(String s) {
        String[] tokens = s.split("\\s+");
        String result = "";
        for (String t : tokens) {
            if (!t.matches(".*[\\u4E00-\\u9FA5].*")) result = t;
        }
        return result;
    }

    // ===== 文本清洗 =====
    private String sanitizeModel(String s) {
        if (s == null) return "";
        s = s.replace("\\n", "");
        s = removeNoiseSymbols(s);
        return normalizeWhitespace(s);
    }

    private String sanitizeAddress(String s) {
        if (s == null) return "";
        s = s.replaceAll("\\[.*?]", "");
        s = removeNoiseSymbols(s);
        return normalizeWhitespace(s);
    }

    private String sanitizeLink(String s) {
        if (s == null) return "";
        return normalizeWhitespace(s.trim());
    }

    private String removeNoiseSymbols(String s) {
        // 移除常见噪声：**、*、|（含全角）、零宽字符等
        s = s.replaceAll("\\*+", "");
        s = s.replace("|", "").replace("｜", "");
        s = s.replace("\u200B", "").replace("\uFEFF", "");
        return s;
    }

    private String normalizeWhitespace(String s) {
        return s.replaceAll("[\\t ]+", " ").replaceAll("\n{3,}", "\n\n").trim();
    }

    /** 是否与录单表单新模板一致（与 {@link #formatOrderForm} 中判断一致） */
    private boolean isNewOrderFormInput(String originalInput) {
        if (originalInput == null) {
            return false;
        }
        return originalInput.contains("—————————")
            || originalInput.contains("下单链接（必须用这个）：")
            || originalInput.contains("下单地址（注意带分机）：")
            || originalInput.contains("下单人（需填）：")
            || originalInput.contains("下单付款（注意核对）：")
            || originalInput.contains("后返金额（注意核对）：")
            || originalInput.contains("订单号（需填）：")
            || originalInput.contains("物流链接（需填）：")
            || originalInput.contains("备注（下单号码有变动/没法带分机号的写这里）：");
    }

    /**
     * 分销标记 F 录单成功后第二条：型号、地址、物流（物流与成功回显一致压缩 3.cn）。
     */
    private String buildNewFormDanCompactSummary(JDOrder order, String originalInput) {
        if (order == null) {
            return null;
        }
        String model = jdOrderModelShopService.fullModel(order);
        String address = order.getAddress();
        String logistics = extractOriginalLogisticsLinkNew(originalInput);
        if (logistics == null && order.getLogisticsLink() != null) {
            logistics = order.getLogisticsLink();
        }
        if (isEmpty(model) && isEmpty(address) && isEmpty(logistics)) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        sb.append("「发货摘要」\n");
        sb.append("————————————\n");
        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(shortenLogisticsLineForReply(logistics));
        }
        return sb.toString().trim();
    }

    private static boolean isDistributionMarkF(String mark) {
        if (mark == null) {
            return false;
        }
        String t = mark.trim();
        if (t.isEmpty()) {
            return false;
        }
        if ("F".equalsIgnoreCase(t)) {
            return true;
        }
        return t.length() >= 2 && t.regionMatches(true, 0, "F-", 0, 2);
    }

    private static boolean isNewTemplateDanWriteSuccess(String primary) {
        if (primary == null || primary.isEmpty()) {
            return false;
        }
        if (primary.contains("[炸弹]") || primary.contains("录单警告")) {
            return false;
        }
        return primary.contains("「录单」已成功");
    }

    // ===== "单 …" 写库 =====
    private String handleDanWriteDb(String input) {
        return handleDanWriteDb(input, false, false);
    }

    private String handleDanWriteDb(String input, boolean forceGenerate) {
        return handleDanWriteDb(input, forceGenerate, false);
    }

    private String handleDanWriteDb(String input, boolean forceGenerate, boolean isFromConsole) {
        // 保存原始输入，用于返回前端时保留完整物流链接
        String originalInput = input.trim().replace("元", "");
        // 与 JDUtil.parseOrderFromText 一致的模板字段
        JDOrder order = parseOrderFromText(originalInput);
        jdOrderModelShopService.normalizeOrder(order);

        // 字段校验并返回缺失项（根据新模板格式，单号存储在remark字段中）
        StringBuilder missing = new StringBuilder();
        if (isEmpty(order.getRemark())) missing.append("单号\n");
        if (isEmpty(order.getBuyer())) missing.append("下单人\n");
        if (order.getPaymentAmount() == null || order.getPaymentAmount() == 0) missing.append("下单价格\n");
        if (order.getRebateAmount() == null) missing.append("后返金额\n");
        if (isEmpty(order.getLogisticsLink())) missing.append("物流链接\n");
        if (isEmpty(order.getAddress())) missing.append("收货地址\n");
        if (isEmpty(order.getModelNumber())) missing.append("型号\n");
        if (missing.length() > 0) {
            String warn = "[炸弹] [炸弹] [炸弹] 录单警告!!! \n缺少表单字段 \n" + missing;
            return warn;
        }

        // 订单号重复校验（控制台入口跳过此校验，允许管理员更新单号）
        if (!isFromConsole && !isEmpty(order.getOrderId())) {
            JDOrder existingByOrderId = jdOrderService.selectJDOrderByOrderId(order.getOrderId());
            if (existingByOrderId != null) {
                // 如果是更新同一条记录（remark相同），则不提示重复
                if (!order.getRemark().equals(existingByOrderId.getRemark())) {
                    String warn = "[炸弹] [炸弹] [炸弹] 录单警告!!! \n订单号重复！\n" +
                                  "订单号：" + order.getOrderId() + "\n" +
                                  "已存在的订单：" + existingByOrderId.getRemark() + "\n" +
                                  "下单时间：" + new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(existingByOrderId.getOrderTime());
                    return warn;
                }
            }
        }

        // 物流链接重复校验（控制台入口跳过此校验，允许管理员更新物流链接）
        if (!isFromConsole && !isEmpty(order.getLogisticsLink())) {
            JDOrder existingByLogistics = jdOrderService.selectJDOrderByLogisticsLink(order.getLogisticsLink());
            if (existingByLogistics != null) {
                // 如果是更新同一条记录（remark相同），则不提示重复
                if (!order.getRemark().equals(existingByLogistics.getRemark())) {
                    String warn = "[炸弹] [炸弹] [炸弹] 录单警告!!! \n物流链接重复！\n" +
                                  "物流链接：" + order.getLogisticsLink() + "\n" +
                                  "已存在的订单：" + existingByLogistics.getRemark() + "\n" +
                                  "下单时间：" + new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(existingByLogistics.getOrderTime());
                    return warn;
                }
            }
        }

        // 地址重复提示（不阻断写入，与 JDUtil 提示一致）
        List<JDOrder> byAddress = jdOrderService.selectJDOrderListByAddress(order.getAddress());
        if (byAddress != null && !byAddress.isEmpty()) {
            java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String latest = sdf.format(byAddress.get(0).getOrderTime());
            // 仅提示，不阻断写入
        }

        // 根据订单号查询order_rows，获取京粉实际价格
        OrderRows orderRow = null;
        if (!isEmpty(order.getOrderId())) {
            try {
                orderRow = orderRowsService.selectOrderRowsByOrderId(order.getOrderId());
                if (orderRow != null && orderRow.getEstimateCosPrice() != null) {
                    order.setJingfenActualPrice(orderRow.getEstimateCosPrice());
                }
            } catch (Exception e) {
                // 查询失败不影响主流程，继续执行
            }
        }

        // 验证1：如果查询不到对应订单，提示用户（控制台入口跳过此校验）
        if (!isFromConsole && (isEmpty(order.getOrderId()) || orderRow == null)) {
            String warn = "[炸弹] [炸弹] [炸弹] 录单警告!!! \n查询不到对应订单（使用红包 或者 转链），请五分钟后再试，或重新下单";
            return warn;
        }

        // 验证2：如果京粉实际价格不为空，检查与付款金额的差值是否大于等于200
        if (order.getJingfenActualPrice() != null && order.getPaymentAmount() != null) {
            double diff = Math.abs(order.getPaymentAmount() - order.getJingfenActualPrice());
            // 如果差额大于等于200，则提示警告
            if (diff >= 200.0) {
                String warn = "[炸弹] [炸弹] [炸弹] 录单警告!!! \n付款金额和实际金额相差" + String.format("%.2f", diff) + "，请联系管理员录单\n" +
                              "付款金额：" + order.getPaymentAmount() + "\n" +
                              "京粉实际价格：" + order.getJingfenActualPrice() + "\n" +
                              "差额：" + String.format("%.2f", diff);
                return warn;
            }
        }

        // 判断新增/更新：所有入口都优先根据订单号更新（用于刷新数据）
        JDOrder exists = null;

        // 如果订单号不为空，优先根据订单号查找已存在的订单（用于刷新数据）
        if (!isEmpty(order.getOrderId())) {
            exists = jdOrderService.selectJDOrderByOrderId(order.getOrderId());
            if (exists != null) {
                // 根据订单号找到已存在的订单，更新该订单（刷新数据）
                order.setId(exists.getId());
                jdOrderService.updateJDOrder(order);
            }
        }

        // 如果没有通过订单号找到，则按 remark 判断新增/更新
        if (exists == null) {
            exists = jdOrderService.selectJDOrderByRemark(order.getRemark());
            if (exists != null) {
                order.setId(exists.getId());
                jdOrderService.updateJDOrder(order);
            } else {
                jdOrderService.insertJDOrder(order);
            }
        }

        // H-TF订单触发延迟推送机制
        // 录单后，重置10分钟倒计时，10分钟内无新录单则自动推送
        if ("H-TF".equals(order.getDistributionMark())) {
            try {
                if (tencentDocDelayedPushService != null) {
                    tencentDocDelayedPushService.triggerDelayedPush();
                    System.out.println("✓ H-TF订单已触发延迟推送 - 单号: " + order.getRemark() +
                        ", 第三方单号: " + order.getThirdPartyOrderNo());
                }
            } catch (Exception e) {
                // 触发延迟推送失败不影响录单结果
                System.err.println("✗ 触发延迟推送失败 - 单号: " + order.getRemark() + ", 错误: " + e.getMessage());
            }
        }

        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("————————————\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(jdOrderModelShopService.fullModel(order))).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 String orderFullModel(JDOrder order) {
        return order == null ? "" : jdOrderModelShopService.fullModel(order);
    }

    private boolean isEmpty(String s) {
        return s == null || s.isEmpty();
    }

    /**
     * 格式化订单为完整表单格式
     * @param order 订单对象
     * @param originalInput 原始输入文本，用于提取完整的物流链接
     */
    private String formatOrderForm(JDOrder order, String originalInput) {
        boolean isNewFormat = isNewOrderFormInput(originalInput);

        if (isNewFormat) {
            return formatOrderFormNew(order, originalInput);
        } else {
            return formatOrderFormOld(order, originalInput);
        }
    }
    
    /**
     * 格式化订单为新模板格式
     */
    private String formatOrderFormNew(JDOrder order, String originalInput) {
        StringBuilder sb = new StringBuilder();

        // 从原始输入中提取完整的物流链接
        String originalLogisticsLink = extractOriginalLogisticsLinkNew(originalInput);

        sb.append("单：\n");
        sb.append(order.getRemark() != null ? order.getRemark() : "").append(" \n");
        sb.append("分销标记：").append(order.getDistributionMark() != null ? order.getDistributionMark() : "").append("\n");
        sb.append("第三方单号：").append(order.getThirdPartyOrderNo() != null ? order.getThirdPartyOrderNo() : "").append("\n");
        sb.append("—————————\n");
        sb.append("下单链接（必须用这个）：\n");
        sb.append(order.getLink() != null ? order.getLink() : "").append("\n");
        sb.append("下单地址（注意带分机）：\n");
        sb.append(order.getAddress() != null ? order.getAddress() : "").append("\n");
        sb.append("—————————\n");
        sb.append("型号：").append(orderFullModel(order)).append("\n");
        sb.append("\n");
        sb.append("下单人（需填）：\n");
        sb.append(order.getBuyer() != null ? order.getBuyer() : "").append("\n");
        sb.append("下单付款（注意核对）：\n");
        sb.append(order.getPaymentAmount() != null ? order.getPaymentAmount().toString() : "").append("\n");
        sb.append("后返金额（注意核对）：\n");
        sb.append(order.getRebateAmount() != null ? order.getRebateAmount().toString() : "").append("\n");
        sb.append("订单号（需填）：\n");
        sb.append(order.getOrderId() != null ? order.getOrderId() : "").append("\n");
        sb.append("物流链接（需填）：\n");
        // 使用原始输入的完整物流链接，如果没有则使用数据库中的值
        sb.append(originalLogisticsLink != null ? originalLogisticsLink : (order.getLogisticsLink() != null ? order.getLogisticsLink() : "")).append("\n");
        sb.append("备注（下单号码有变动/没法带分机号的写这里）：\n");
        sb.append(order.getStatus() != null ? order.getStatus() : "").append("\n");
        sb.append("—————————\n");
        sb.append("京粉实际价格：");
        // 如果有实际价格则显示，否则显示"不用填"
        if (order.getJingfenActualPrice() != null) {
            sb.append(order.getJingfenActualPrice().toString());
        } else {
            sb.append("不用填");
        }

        return sb.toString();
    }
    
    /**
     * 格式化订单为旧模板格式（保持向后兼容）
     */
    private String formatOrderFormOld(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");
        sb.append("分销标记：").append(order.getDistributionMark() != null ? order.getDistributionMark() : "").append("\n");
        sb.append("第三方单号：").append(order.getThirdPartyOrderNo() != null ? order.getThirdPartyOrderNo() : "").append("\n");
        sb.append("型号：\n");
        sb.append(orderFullModel(order)).append("\n");
        sb.append("链接：\n");
        sb.append(order.getLink() != null ? order.getLink() : "").append("\n");
        sb.append("下单付款：\n");
        sb.append(order.getPaymentAmount() != null ? order.getPaymentAmount().toString() : "").append("\n");
        sb.append("后返金额：\n");
        sb.append(order.getRebateAmount() != null ? order.getRebateAmount().toString() : "").append("\n");
        sb.append("地址：\n");
        sb.append(order.getAddress() != null ? order.getAddress() : "").append("\n");
        sb.append("物流链接：\n");
        // 使用原始输入的完整物流链接，如果没有则使用数据库中的值
        sb.append(originalLogisticsLink != null ? originalLogisticsLink : (order.getLogisticsLink() != null ? order.getLogisticsLink() : "")).append("\n");
        sb.append("订单号：\n");
        sb.append(order.getOrderId() != null ? order.getOrderId() : "").append("\n");
        sb.append("下单人：\n");
        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();
    }
    
    /**
     * 从原始输入中提取完整的物流链接文本（新格式）
     */
    private String extractOriginalLogisticsLinkNew(String input) {
        if (input == null || input.isEmpty()) {
            return null;
        }
        // 提取"物流链接（需填）："到"备注（下单号码有变动/没法带分机号的写这里）："之间的完整内容
        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("物流链接（需填）：\\s*(.*?)\\s*(?=备注（下单号码有变动/没法带分机号的写这里）：|\\Z)", java.util.regex.Pattern.DOTALL);
        java.util.regex.Matcher matcher = pattern.matcher(input);
        if (matcher.find()) {
            String logisticsLink = matcher.group(1).trim();
            // 如果为空，返回null
            return logisticsLink.isEmpty() ? null : logisticsLink;
        }
        return null;
    }

    /**
     * 从原始输入中提取完整的物流链接文本（不做清理）
     */
    private String extractOriginalLogisticsLink(String input) {
        if (input == null || input.isEmpty()) {
            return null;
        }
        // 提取"物流链接："到"订单号："之间的完整内容
        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("物流链接：\\s*(.*?)\\s*(?=订单号：|\\Z)", java.util.regex.Pattern.DOTALL);
        java.util.regex.Matcher matcher = pattern.matcher(input);
        if (matcher.find()) {
            String logisticsLink = matcher.group(1).trim();
            // 如果为空，返回null
            return logisticsLink.isEmpty() ? null : logisticsLink;
        }
        return null;
    }

    // 参照 JDUtil.parseOrderFromText
    private JDOrder parseOrderFromText(String input) {
        // 检测是否是新格式（包含分隔线"—————————"或新格式字段标签）
        boolean isNewFormat = input.contains("—————————") || 
                              input.contains("下单链接（必须用这个）：") || 
                              input.contains("下单地址（注意带分机）：") ||
                              input.contains("下单人（需填）：") ||
                              input.contains("下单付款（注意核对）：") ||
                              input.contains("后返金额（注意核对）：") ||
                              input.contains("订单号（需填）：") ||
                              input.contains("物流链接（需填）：") ||
                              input.contains("备注（下单号码有变动/没法带分机号的写这里）：");
        
        System.out.println("=== 开始解析订单文本 ===");
        System.out.println("是否为新格式: " + isNewFormat);
        System.out.println("输入文本长度: " + (input != null ? input.length() : 0));
        
        Map<String, String> fields = new HashMap<>();

        if (isNewFormat) {
            // 新格式：使用新的解析方法（支持分隔线）
            System.out.println("使用新格式解析方法");
            parseNewFormat(input, fields);
        } else {
            // 旧格式：保持原有逻辑不变
            System.out.println("使用旧格式解析方法");
            parseOldFormat(input, fields);
        }
        
        System.out.println("解析到的字段: " + fields);

        // 构建 JDOrder 对象
        JDOrder order = new JDOrder();
        // 单号（格式：2025-12-13 006）
        String orderNo = fields.getOrDefault("单", "").trim();
        order.setRemark(orderNo);
        System.out.println("单号(remark): " + orderNo);

        // 处理分销标记：如果提取的内容包含换行符，说明可能提取过度了，只取第一行
        String distributionMark = fields.getOrDefault("分销标记", null);
        if (distributionMark != null && distributionMark.contains("\n")) {
            // 如果包含换行符，只取第一行，并清理可能的额外内容
            String firstLine = distributionMark.split("\n")[0].trim();
            // 检查第一行是否包含其他字段的关键词（如"型号："），如果有则截断
            if (firstLine.contains("型号：") || firstLine.contains("链接：")) {
                int index = firstLine.indexOf("型号：");
                if (index < 0) index = firstLine.indexOf("链接：");
                if (index > 0) {
                    firstLine = firstLine.substring(0, index).trim();
                }
            }
            distributionMark = firstLine;
        }
        // 如果分销标记是H-TF格式（包含括号），只保留H-TF
        if (distributionMark != null && distributionMark.startsWith("H-TF(")) {
            distributionMark = "H-TF";
        }
        order.setDistributionMark(distributionMark);

        // 优先从字段中获取第三方单号，如果没有则从分销标记中提取
        String thirdPartyOrderNo = fields.getOrDefault("第三方单号", null);
        if (thirdPartyOrderNo != null) {
            // 清理换行符和空白字符
            thirdPartyOrderNo = thirdPartyOrderNo.replaceAll("\\s+", "").trim();
        }
        if (thirdPartyOrderNo == null || thirdPartyOrderNo.isEmpty()) {
            if (distributionMark != null && !distributionMark.isEmpty()) {
                thirdPartyOrderNo = extractOrderNumber(distributionMark);
            }
        }
        // 保存第三方单号到订单对象
        order.setThirdPartyOrderNo(thirdPartyOrderNo != null && !thirdPartyOrderNo.trim().isEmpty() ? thirdPartyOrderNo.trim() : null);
        order.setModelNumber(fields.getOrDefault("型号", null));
        System.out.println("型号: " + order.getModelNumber());
        
        // 下单链接（新格式）或链接（旧格式）
        String link = fields.getOrDefault("下单链接（必须用这个）", null);
        if (link == null) {
            link = fields.getOrDefault("链接", null);
        }
        order.setLink(link);
        System.out.println("链接: " + link);
        
        // 备注字段
        order.setStatus(fields.getOrDefault("备注", null));
        System.out.println("备注: " + order.getStatus());
        
        // 下单付款（新格式）或下单付款（旧格式）
        String paymentStr = fields.getOrDefault("下单付款（注意核对）", null);
        if (paymentStr == null) {
            paymentStr = fields.getOrDefault("下单付款", "0");
        }
        try {
            paymentStr = paymentStr.trim();
            if (!paymentStr.isEmpty() && !paymentStr.equals("0")) {
                order.setPaymentAmount(Double.parseDouble(paymentStr));
            }
        } catch (Exception e) {
            System.out.println("解析下单付款失败: " + paymentStr + ", error: " + e.getMessage());
        }
        System.out.println("下单付款: " + order.getPaymentAmount());
        
        // 后返金额（新格式）或后返金额（旧格式）
        String rebateStr = fields.getOrDefault("后返金额（注意核对）", null);
        if (rebateStr == null) {
            rebateStr = fields.getOrDefault("后返金额", "0");
        }
        try {
            rebateStr = rebateStr.trim();
            if (!rebateStr.isEmpty() && !rebateStr.equals("0")) {
                order.setRebateAmount(Double.parseDouble(rebateStr));
            }
        } catch (Exception e) {
            System.out.println("解析后返金额失败: " + rebateStr + ", error: " + e.getMessage());
        }
        System.out.println("后返金额: " + order.getRebateAmount());
        
        // 下单地址（新格式）或地址（旧格式）
        String address = fields.getOrDefault("下单地址（注意带分机）", null);
        if (address == null) {
            address = fields.getOrDefault("地址", null);
        }
        order.setAddress(address);
        System.out.println("地址: " + (address != null && address.length() > 50 ? address.substring(0, 50) + "..." : address));
        
        // 物流链接（新格式）或物流链接（旧格式）
        String logisticsLink = fields.getOrDefault("物流链接（需填）", null);
        if (logisticsLink == null) {
            logisticsLink = fields.getOrDefault("物流链接", "");
        }
        order.setLogisticsLink(extractFirstUrl(logisticsLink));
        System.out.println("物流链接: " + order.getLogisticsLink());
        
        // 订单号（新格式）或订单号（旧格式）
        String orderId = fields.getOrDefault("订单号（需填）", null);
        if (orderId == null) {
            orderId = fields.getOrDefault("订单号", null);
        }
        order.setOrderId(orderId);
        System.out.println("订单号: " + orderId);
        
        // 下单人（新格式）或下单人（旧格式）
        String buyer = fields.getOrDefault("下单人（需填）", null);
        if (buyer == null) {
            buyer = fields.getOrDefault("下单人", null);
        }
        order.setBuyer(buyer);
        System.out.println("下单人: " + buyer);
        // 京粉实际价格不从表单解析，而是从数据库order_rows表中查询获取
        // 默认参与统计
        order.setIsCountEnabled(1);
        
        // 设置下单时间
        try {
            if (!orderNo.isEmpty()) {
                String dateStr = orderNo.split("\\s+")[0]; // 从"2025-12-13 006"中提取日期部分
                java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd");
                order.setOrderTime(sdf.parse(dateStr));
                System.out.println("下单时间: " + order.getOrderTime());
            } else {
                order.setOrderTime(new Date());
                System.out.println("下单时间(默认): " + order.getOrderTime());
            }
        } catch (Exception e) {
            order.setOrderTime(new Date());
            System.out.println("解析下单时间失败: " + e.getMessage());
        }
        
        System.out.println("=== 解析完成 ===");
        return order;
    }
    
    // 解析新格式（带分隔线）
    private void parseNewFormat(String input, Map<String, String> fields) {
        extractFieldWithSeparator(input, fields, "单：", "分销标记：");
        extractFieldWithSeparator(input, fields, "分销标记：", "第三方单号：");
        extractFieldWithSeparator(input, fields, "第三方单号：", "下单链接（必须用这个）：");
        extractFieldWithSeparator(input, fields, "下单链接（必须用这个）：", "下单地址（注意带分机）：");
        extractFieldWithSeparator(input, fields, "下单地址（注意带分机）：", "型号：");
        extractFieldWithSeparator(input, fields, "型号：", "下单人（需填）：");
        extractFieldWithSeparator(input, fields, "下单人（需填）：", "下单付款（注意核对）：");
        extractFieldWithSeparator(input, fields, "下单付款（注意核对）：", "后返金额（注意核对）：");
        extractFieldWithSeparator(input, fields, "后返金额（注意核对）：", "订单号（需填）：");
        extractFieldWithSeparator(input, fields, "订单号（需填）：", "物流链接（需填）：");
        extractFieldWithSeparator(input, fields, "物流链接（需填）：", "备注（下单号码有变动/没法带分机号的写这里）：");
        
        // 手动提取"备注"字段（最后一个字段，可能后面有分隔线或"京粉实际价格"）
        java.util.regex.Pattern remarkPattern = java.util.regex.Pattern.compile("备注（下单号码有变动/没法带分机号的写这里）：[\\s\\n]*([\\s\\S]*?)[\\s\\n]*(?=—————————|京粉实际价格|\\Z)", java.util.regex.Pattern.DOTALL);
        java.util.regex.Matcher remarkMatcher = remarkPattern.matcher(input);
        if (remarkMatcher.find()) {
            String remarkValue = remarkMatcher.group(1);
            if (remarkValue != null) {
                remarkValue = remarkValue.trim();
                // 去掉可能的分隔线
                remarkValue = remarkValue.replaceAll("^—————————[\\s\\n]*", "").trim();
                if (!remarkValue.isEmpty()) {
                    fields.put("备注", remarkValue);
                }
            }
        }
    }
    
    // 解析旧格式（保持原有逻辑不变）
    private void parseOldFormat(String input, Map<String, String> fields) {
        extractField(input, fields, "单：", "备注：");
        extractField(input, fields, "备注：", "分销标记：");

        // 检查是否有"第三方单号："字段
        boolean hasThirdPartyOrderNo = input.contains("第三方单号：");
        boolean hasJingfenPrice = input.contains("京粉实际价格：");

        if (hasThirdPartyOrderNo) {
            extractField(input, fields, "分销标记：", "第三方单号：");
            extractField(input, fields, "第三方单号：", "型号：");
        } else {
            extractField(input, fields, "分销标记：", "型号：");
        }

        extractField(input, fields, "型号：", "链接：");
        extractField(input, fields, "链接：", "下单付款：");
        extractField(input, fields, "下单付款：", "后返金额：");
        extractField(input, fields, "后返金额：", "地址：");
        extractField(input, fields, "地址：", "物流链接：");
        extractField(input, fields, "物流链接：", "订单号：");
        extractField(input, fields, "订单号：", "下单人：");

        if (hasJingfenPrice) {
            extractField(input, fields, "下单人：", "京粉实际价格：");
            extractField(input, fields, "京粉实际价格：", "");
        } 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);
        java.util.regex.Matcher buyerMatcher = buyerPattern.matcher(input);
        if (buyerMatcher.find()) {
            String buyer = buyerMatcher.group(1).trim();
            if (buyer.contains("\n")) {
                buyer = buyer.split("\n")[0].trim();
            }
            fields.put("下单人", buyer);
        }
    }
    
    // 提取字段的方法（支持分隔线，用于新格式）
    private void extractFieldWithSeparator(String input, Map<String, String> map, String startKeyword, String endKeyword) {
        try {
            String escapedStart = java.util.regex.Pattern.quote(startKeyword);
            String escapedEnd = java.util.regex.Pattern.quote(endKeyword);
            
            // 匹配模式：startKeyword + 可选的空白和换行 + 字段值（非贪婪，支持多行） + 后面是换行+endKeyword或换行+分隔线或结束
            String patternStr = escapedStart + "[\\s\\n]*([\\s\\S]*?)(?=\\s*\\n?\\s*" + escapedEnd + "|\\s*\\n?\\s*—————————|\\Z)";
            java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(patternStr, java.util.regex.Pattern.DOTALL);
            java.util.regex.Matcher matcher = pattern.matcher(input);

            if (matcher.find()) {
                String value = matcher.group(1);
                if (value != null) {
                    value = value.trim();
                    // 去掉可能的分隔线（在值的前后）
                    value = value.replaceAll("^—————————[\\s\\n]*", "").replaceAll("[\\s\\n]*—————————$", "").trim();
                    // 去掉字段标签本身（如果被匹配到了）
                    value = value.replaceAll("^" + escapedStart, "").trim();
                    // 保存字段值，使用去掉冒号的key
                    String key = startKeyword.replace("：", "").trim();
                    if (!value.isEmpty()) {
                        map.put(key, value);
                        System.out.println("  ✓ 提取字段成功: " + key + " = " + (value.length() > 50 ? value.substring(0, 50) + "..." : value));
                    } else {
                        System.out.println("  ✗ 字段值为空: " + key);
                    }
                } else {
                    System.out.println("  ✗ 未找到字段值: " + startKeyword);
                }
            } else {
                System.out.println("  ✗ 正则未匹配: " + startKeyword + " -> " + endKeyword);
            }
        } catch (Exception e) {
            System.out.println("  ✗ 提取字段异常: " + startKeyword + " -> " + endKeyword + ", error: " + e.getMessage());
            e.printStackTrace();
        }
    }

    private void extractField(String input, Map<String, String> map, String startKeyword, String endKeyword) {
        // 如果结束关键词为空，匹配到字符串末尾
        String endPattern = endKeyword.isEmpty() ? "\\Z" : endKeyword;
        java.util.regex.Pattern pattern = java.util.regex.Pattern.compile(startKeyword + "\\s*(.*?)\\s*(?=" + endPattern + ")", java.util.regex.Pattern.DOTALL);
        java.util.regex.Matcher matcher = pattern.matcher(input);
        if (matcher.find()) {
            String value = matcher.group(1).trim();
            // 移除可能的前缀（如果value以startKeyword开头）
            if (value.startsWith(startKeyword)) {
                value = value.substring(startKeyword.length()).trim();
            }
            // 如果值包含换行符，且结束关键词存在，需要确保只取到第一个换行符或结束关键词之前
            if (!endKeyword.isEmpty() && value.contains("\n")) {
                // 检查是否包含结束关键词
                int endIndex = value.indexOf(endKeyword);
                if (endIndex >= 0) {
                    value = value.substring(0, endIndex).trim();
                } else {
                    // 如果找不到结束关键词，取第一个换行符之前的内容
                    value = value.split("\n")[0].trim();
                }
            }
            map.put(startKeyword.replace("：", ""), value);
        }
    }

    private String extractFirstUrl(String text) {
        if (text == null || text.isEmpty()) return null;
        java.util.regex.Pattern urlPattern = java.util.regex.Pattern.compile("(https?://[^\\s]+)");
        java.util.regex.Matcher matcher = urlPattern.matcher(text);
        return matcher.find() ? matcher.group(0) : null;
    }

    private String handleTransferLike(String input) {
        // 前端仍复用已有 /jarvis/jdorder/generatePromotionContent 接口，这里仅提供一个回显占位
        return "指令入口：请在\"一键转链\"页面使用(输入文案/链接后点生成)";
    }

    // ===== 统计与列表渲染 =====
    private String statsText(List<OrderRows> rows, String title) {
        long total = rows.size();
        long paid = rows.stream().filter(o -> Integer.valueOf(16).equals(o.getValidCode())).count();
        long pending = rows.stream().filter(o -> Integer.valueOf(15).equals(o.getValidCode())).count();
        long completed = rows.stream().filter(o -> Integer.valueOf(17).equals(o.getValidCode())).count();
        long canceled = rows.stream().filter(o -> o.getValidCode() != null && o.getValidCode() != 16 && o.getValidCode() != 17).count();
        long violation = rows.stream().filter(o -> ValidCodeConverter.isIllegal(o.getValidCode())).count();
        double paidCommission = rows.stream().filter(o -> Integer.valueOf(16).equals(o.getValidCode()) && o.getEstimateFee() != null).mapToDouble(OrderRows::getEstimateFee).sum();
        double pendingCommission = rows.stream().filter(o -> Integer.valueOf(15).equals(o.getValidCode()) && o.getEstimateFee() != null).mapToDouble(OrderRows::getEstimateFee).sum();
        double completedCommission = rows.stream().filter(o -> Integer.valueOf(17).equals(o.getValidCode()) && o.getEstimateFee() != null).mapToDouble(OrderRows::getEstimateFee).sum();
        double violationCommission = rows.stream().filter(o -> ValidCodeConverter.isIllegal(o.getValidCode()) && o.getEstimateCosPrice() != null && o.getCommissionRate() != null).mapToDouble(o -> o.getEstimateCosPrice() * o.getCommissionRate() * 0.01).sum();

        StringBuilder sb = new StringBuilder();
        sb.append("* ").append(title).append(" *\n").append("━━━━━━━━━━━━\n").append("订单总数：").append(total).append("\n").append("有效订单：").append(total - canceled).append("\n").append("已取消：").append(canceled).append("\n").append("────────────\n").append("已付款：").append(paid).append("\n").append("已付款佣金：").append(fmt(paidCommission)).append("\n").append("────────────\n").append("待付款：").append(pending).append("\n").append("待付款佣金：").append(fmt(pendingCommission)).append("\n").append("────────────\n").append("已完成：").append(completed).append("\n").append("已完成佣金：").append(fmt(completedCommission)).append("\n").append("────────────\n").append("违规订单：").append(violation).append("\n").append("违规佣金：").append(fmt(violationCommission)).append("\n").append("━━━━━━━━━━━━");
        return sb.toString();
    }

    private String listOrders(List<OrderRows> rows, String title) {
        if (rows.isEmpty()) return title + "：无";
        StringBuilder sb = new StringBuilder();
        sb.append(title).append("（").append(rows.size()).append("条）\n");
        int i = 0;
        for (OrderRows r : rows) {
            i++;
            sb.append(i).append(". ").append(Optional.ofNullable(r.getSkuName()).orElse("-")).append(" | 订单号:").append(Optional.ofNullable(r.getOrderId()).map(String::valueOf).orElse("-")).append(" | 时间:").append(fmt(r.getOrderTime())).append(" | 有效:").append(r.getValidCode() == null ? "-" : r.getValidCode()).append("\n");
            if (i >= 100) break; // 最多100条
        }
        return sb.toString();
    }

    // ===== 过滤器 =====
    private List<OrderRows> filterByDays(List<OrderRows> list, int daysBack) {
        LocalDate now = LocalDate.now();
        return list.stream().filter(o -> {
            Date d = o.getOrderTime();
            if (d == null) return false;
            LocalDate od = d.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            if (daysBack == 0) return od.equals(now);
            return !od.isBefore(now.minusDays(daysBack)) && !od.isAfter(now);
        }).collect(Collectors.toList());
    }

    private List<OrderRows> filterByRange(List<OrderRows> list, int days) {
        return filterByDays(list, days);
    }

    private List<OrderRows> filterYesterday(List<OrderRows> list) {
        LocalDate y = LocalDate.now().minusDays(1);
        return list.stream().filter(o -> {
            Date d = o.getOrderTime();
            if (d == null) return false;
            LocalDate od = d.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            return od.equals(y);
        }).collect(Collectors.toList());
    }

    private List<OrderRows> filterThisMonth(List<OrderRows> list) {
        LocalDate now = LocalDate.now();
        return list.stream().filter(o -> {
            Date d = o.getOrderTime();
            if (d == null) return false;
            LocalDate od = d.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            return od.getYear() == now.getYear() && od.getMonth() == now.getMonth();
        }).collect(Collectors.toList());
    }

    private List<OrderRows> filterLastMonth(List<OrderRows> list) {
        LocalDate now = LocalDate.now().minusMonths(1);
        return list.stream().filter(o -> {
            Date d = o.getOrderTime();
            if (d == null) return false;
            LocalDate od = d.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            return od.getYear() == now.getYear() && od.getMonth() == now.getMonth();
        }).collect(Collectors.toList());
    }

    /**
     * 企微「外部分享链物流」登记查询：用于发现同一备注、不同短链等重复登记。
     */
    private String textExternalShareLinkLogisticsList(String remarkKeyword) {
        if (weComShareLinkLogisticsJobService == null) {
            return "「外物列表」\n\n服务未就绪。";
        }
        List<WeComShareLinkLogisticsJob> rows = weComShareLinkLogisticsJobService.selectRecentForInstruction(
            remarkKeyword, EXTERNAL_LOGISTICS_LIST_DAYS, EXTERNAL_LOGISTICS_LIST_LIMIT);
        if (rows == null || rows.isEmpty()) {
            return "「外物列表」\n\n近 " + EXTERNAL_LOGISTICS_LIST_DAYS + " 天内无匹配记录。"
                + (remarkKeyword.isEmpty() ? "" : "\n关键词：「" + remarkKeyword + "」");
        }
        Map<String, Long> remarkCount = rows.stream()
            .map(j -> j.getUserRemark() != null ? j.getUserRemark().trim() : "")
            .collect(Collectors.groupingBy(s -> s, Collectors.counting()));
        StringBuilder sb = new StringBuilder();
        sb.append("「外物列表」近").append(EXTERNAL_LOGISTICS_LIST_DAYS).append("天，最多")
            .append(EXTERNAL_LOGISTICS_LIST_LIMIT).append("条");
        if (!remarkKeyword.isEmpty()) {
            sb.append("，关键词「").append(remarkKeyword).append("」");
        }
        sb.append("\n\n");
        int i = 0;
        for (WeComShareLinkLogisticsJob j : rows) {
            i++;
            String rk = j.getJobKey() != null ? j.getJobKey() : "";
            String st = j.getStatus() != null ? j.getStatus() : "";
            String rm = j.getUserRemark() != null ? j.getUserRemark().trim() : "";
            if (rm.length() > 80) {
                rm = rm.substring(0, 80) + "…";
            }
            String url = j.getTrackingUrl() != null ? j.getTrackingUrl().trim() : "";
            long dup = remarkCount.getOrDefault(j.getUserRemark() != null ? j.getUserRemark().trim() : "", 0L);
            sb.append(i).append(". ").append(st);
            if (dup > 1) {
                sb.append(" ·本批同备注").append(dup).append("条");
            }
            sb.append("\nkey=").append(rk);
            sb.append("\n备注：").append(rm.isEmpty() ? "（空）" : rm);
            sb.append("\n链：").append(url);
            if (j.getCreateTime() != null) {
                sb.append("\n时：").append(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm")
                    .format(j.getCreateTime()));
            }
            sb.append("\n——————\n");
        }
        sb.append("删：京外物删 key\n或：京外物删 后接换行写备注，最后一行写含 3.cn 的链接。");
        return sb.toString();
    }

    /**
     * 删除外部分享链物流任务行（与后台物理删除一致；Redis 中未消费项会因库无行而跳过）。
     */
    private String textExternalShareLinkLogisticsDelete(String rest) {
        if (weComShareLinkLogisticsJobService == null) {
            return "「外物删」\n\n服务未就绪。";
        }
        if (rest == null || rest.isEmpty()) {
            return "「外物删」\n\n用法：\n1) 京外物删 <jobKey>（见「京外物列表」中的 key=）\n"
                + "2) 京外物删\n<备注全文>\nhttps://3.cn/…";
        }
        String oneLine = rest.replace("\r\n", "\n").trim();
        if (!oneLine.contains("\n") && looksLikeShareLinkJobKey(oneLine)) {
            WeComShareLinkLogisticsJob existed = weComShareLinkLogisticsJobService.selectByJobKey(oneLine);
            int n = weComShareLinkLogisticsJobService.deleteByJobKey(oneLine);
            if (n <= 0) {
                return "「外物删」\n\n未删除任何行（jobKey 可能不存在）：" + oneLine;
            }
            String hint = existed != null
                ? "\n备注：" + shortenForReply(existed.getUserRemark(), 120)
                    + "\n链：" + nvl(existed.getTrackingUrl())
                : "";
            return "「外物删」\n\n已删除 " + n + " 条。" + hint;
        }
        ParsedRemarkAndUrl parsed = parseRemarkAnd3cnFromDeletePayload(rest);
        if (parsed == null) {
            return "「外物删」\n\n未能解析备注与 3.cn 链接。请多行发送：倒数第一个含 3.cn 的为链接，其上一行起为备注；"
                + "或单行：京外物删 <jobKey>";
        }
        int n = weComShareLinkLogisticsJobService.deleteByRemarkAndTrackingUrl(parsed.remark, parsed.trackingUrl);
        if (n <= 0) {
            return "「外物删」\n\n未找到完全匹配的行（备注与短链需与登记一致，含空格请对齐）。\n备注："
                + shortenForReply(parsed.remark, 200) + "\n链：" + parsed.trackingUrl;
        }
        return "「外物删」\n\n已按备注+链接删除 " + n + " 条。";
    }

    private static boolean looksLikeShareLinkJobKey(String s) {
        if (s == null) {
            return false;
        }
        String t = s.trim();
        return t.matches("^[a-fA-F0-9]{32}$") || t.matches("^tracebf\\d+$");
    }

    private static String shortenForReply(String s, int maxChars) {
        if (s == null) {
            return "";
        }
        String t = s.trim();
        if (t.length() <= maxChars) {
            return t;
        }
        return t.substring(0, maxChars) + "…";
    }

    private static final class ParsedRemarkAndUrl {
        final String remark;
        final String trackingUrl;

        ParsedRemarkAndUrl(String remark, String trackingUrl) {
            this.remark = remark;
            this.trackingUrl = trackingUrl;
        }
    }

    private static ParsedRemarkAndUrl parseRemarkAnd3cnFromDeletePayload(String rest) {
        if (rest == null) {
            return null;
        }
        String normalized = rest.replace("\r\n", "\n").trim();
        String[] lines = normalized.split("\n");
        if (lines.length == 1) {
            ParsedRemarkAndUrl one = tryParseRemarkAndUrlSingleLine(lines[0]);
            if (one != null) {
                return one;
            }
        }
        int urlIndex = -1;
        String canonicalUrl = null;
        for (int i = lines.length - 1; i >= 0; i--) {
            String u = extractJd3cnForInstruction(lines[i]);
            if (u != null) {
                urlIndex = i;
                canonicalUrl = u;
                break;
            }
        }
        if (canonicalUrl == null || urlIndex < 0) {
            return null;
        }
        StringBuilder rem = new StringBuilder();
        for (int i = 0; i < urlIndex; i++) {
            if (i > 0) {
                rem.append('\n');
            }
            rem.append(lines[i].trim());
        }
        String remark = rem.toString().trim();
        return new ParsedRemarkAndUrl(remark, canonicalUrl);
    }

    /** 单行「…备注… https://3.cn/…」 */
    private static ParsedRemarkAndUrl tryParseRemarkAndUrlSingleLine(String line) {
        if (line == null || line.isEmpty()) {
            return null;
        }
        Matcher m = JD_3CN.matcher(line);
        if (m.find()) {
            String url = m.group();
            String rem = line.substring(0, m.start()).trim();
            return new ParsedRemarkAndUrl(rem, url);
        }
        Matcher m2 = Pattern.compile("http://3\\.cn/[A-Za-z0-9\\-]+").matcher(line);
        if (m2.find()) {
            String url = m2.group().replace("http://", "https://");
            String rem = line.substring(0, m2.start()).trim();
            return new ParsedRemarkAndUrl(rem, url);
        }
        return null;
    }

    private static String extractJd3cnForInstruction(String text) {
        if (text == null) {
            return null;
        }
        Matcher m = JD_3CN.matcher(text);
        if (m.find()) {
            return m.group();
        }
        Matcher m2 = Pattern.compile("http://3\\.cn/[A-Za-z0-9\\-]+").matcher(text);
        if (m2.find()) {
            return m2.group().replace("http://", "https://");
        }
        return null;
    }

    // ===== 工具 =====
    private String jingMenu() {
        return "「京粉 · 菜单」\n\n"
                + "企微/机器人前请加「京」，例如：京今日统计\n"
                + "说明：企微内统计仅含当前账号在「超级管理员」绑定的联盟ID；标记为不参与订单统计的联盟不会在全局汇总中出现（与后台京粉订单列表统计一致）。\n\n"
                + "—— 统计 ——\n"
                + "今日统计、昨日统计、三日统计、七日统计\n"
                + "一个月统计、两个月统计、三个月统计\n"
                + "这个月统计、上个月统计、总统计\n\n"
                + "—— 订单 ——\n"
                + "今日订单、昨日订单、七日订单\n\n"
                + "—— 外部分享链物流 ——\n"
                + "京外物列表 [关键词]、京外物删（见列表说明）\n\n"
                + "发「京」单独或「京菜单」可再次打开本列表。";
    }

    private String helpText() {
        return "「指令帮助」\n\n"
                + "常用示例：\n"
                + "· 京菜单 — 查看统计与订单类命令\n"
                + "· 京今日统计 / 京昨日统计 / 京七日统计 …\n"
                + "· 京今日订单 / 京昨日订单 / 京七日订单\n"
                + "· 慢搜 关键词、慢查 关键词（录单库模糊查询）\n"
                + "· 京外物列表 / 京外物删 — 企微 3.cn 分享链登记查询与删除\n"
                + "· 录单20250101-20250107 或 录单昨日|三日|七日（导出）\n\n"
                + "说明：转链、礼金等请使用系统内「一键转链」页面。";
    }

    private LocalDateTime effectiveToday() {
        LocalDateTime now = LocalDateTime.now();
        if (now.getHour() < 8) return now.minusDays(1);
        return now;
    }

    private List<LocalDate> parseDateRange(String s) {
        if (s == null || s.isEmpty()) return null;
        if ("昨日".equals(s)) {
            LocalDate y = LocalDate.now().minusDays(1);
            return Arrays.asList(y, y);
        }
        if ("三日".equals(s)) {
            LocalDate end = LocalDate.now().minusDays(1);
            LocalDate start = LocalDate.now().minusDays(3);
            return Arrays.asList(start, end);
        }
        if ("七日".equals(s)) {
            LocalDate end = LocalDate.now().minusDays(1);
            LocalDate start = LocalDate.now().minusDays(7);
            return Arrays.asList(start, end);
        }
        Pattern p = Pattern.compile("(\\d{8})(?:-(\\d{8}))?");
        Matcher m = p.matcher(s);
        if (m.find()) {
            LocalDate d1 = parseYmd(m.group(1));
            LocalDate d2 = m.group(2) != null ? parseYmd(m.group(2)) : d1;
            return Arrays.asList(d1, d2);
        }
        return null;
    }

    private LocalDate parseYmd(String ymd) {
        int y = Integer.parseInt(ymd.substring(0, 4));
        int mo = Integer.parseInt(ymd.substring(4, 6));
        int d = Integer.parseInt(ymd.substring(6, 8));
        return LocalDate.of(y, mo, d);
    }

    /**
     * 截取分销标记，如果长度大于4则进行截取
     * @param v 原始分销标记
     * @return 截取后的分销标记
     */
    private String truncateDistributionMark(String v) {
        if (v == null) return "";

        // 如果长度大于4，进行截取
        if (v.length() > 5) {
            // 查找第一个左括号的位置
            int leftParenIndex = v.indexOf('(');
            if (leftParenIndex > 0) {
                // 截取到左括号之前的部分
                return v.substring(0, leftParenIndex);
            } else {
                // 如果没有括号，直接截取前4个字符
                return v.substring(0, 4);
            }
        }

        return v;
    }

    private String mapDistribution(String v) {
        if (v == null) return "";

        String processedV = truncateDistributionMark(v);

        if (processedV.startsWith("H")) return "鸿";
        if (processedV.startsWith("F")) return "凡";
        return "";
    }

    /**
     * 慢单「按下单人统计」分组键：第一个 "-" 前的前缀视为同一对象；无 "-" 时用整段下单人。
     */
    private static String buyerGroupKey(String buyer) {
        if (buyer == null) {
            return "";
        }
        String t = buyer.trim();
        if (t.isEmpty()) {
            return "";
        }
        int idx = t.indexOf('-');
        if (idx > 0) {
            String prefix = t.substring(0, idx).trim();
            return prefix.isEmpty() ? t : prefix;
        }
        return t;
    }

    private boolean contains(Object field, String kwLower) {
        if (field == null) return false;
        return String.valueOf(field).toLowerCase(Locale.ROOT).contains(kwLower);
    }

    private String nvl(Object v) {
        return v == null ? "" : String.valueOf(v);
    }

    private String fmt(Object v) {
        if (v == null) return "";
        if (v instanceof Number) {
            return String.format(Locale.ROOT, "%.2f", ((Number) v).doubleValue());
        }
        if (v instanceof Date) {
            java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            return sdf.format((Date) v);
        }
        return String.valueOf(v);
    }

    /**
     * 异步写入订单到腾讯文档
     * 当订单的分销标识是 H-TF 时，自动追加到腾讯文档表格
     *
     * @param order 订单对象
     */
    private void asyncWriteToTencentDoc(JDOrder order) {
        // 使用独立线程异步执行，避免阻塞录单流程
        new Thread(() -> {
            try {
                // Redis key前缀（用于获取文档配置）
                final String REDIS_KEY_PREFIX = "tencent:doc:auto:config:";

                // 1. 从Token服务获取access-token（会自动从Redis读取并刷新）
                String accessToken = null;
                try {
                    accessToken = tencentDocTokenService.getValidAccessToken();
                } catch (Exception e) {
                    System.err.println("✗ 无法获取腾讯文档访问令牌 - " + e.getMessage());
                    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();
                }
                if (sheetId == null || sheetId.isEmpty()) {
                    sheetId = tencentDocConfig.getSheetId();
                }
                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  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  文档: " + fileId + "/" + sheetId);
                } else {
                    System.err.println("✗ 订单追加到腾讯文档失败 - 单号: " + order.getRemark() +
                        "\n  API返回null，请检查access-token是否有效或文档是否存在");
                }
            } catch (Exception e) {
                // 写入失败不影响录单结果，仅记录错误日志
                System.err.println("✗ 异步写入腾讯文档失败 - 单号: " + order.getRemark() +
                    "\n  错误: " + e.getMessage() +
                    "\n  提示：" +
                    "\n    1. 检查腾讯文档授权是否有效" +
                    "\n    2. 检查文档ID和工作表ID是否正确" +
                    "\n    3. 使用 GET /jarvis/tencentDoc/config/test 测试配置");
                e.printStackTrace();
            }
        }, "TencentDoc-Writer-" + order.getRemark()).start();
    }

    /**
     * 处理价保/赔付消息，自动标记后返到账
     * 消息格式示例：
     * [爱心] 价保/赔付 ：  100 [爱心]
     * [裂开] 违规订单-违反京东平台规则
     * 京粉：凡
     * 订单：343216308806  (非plus)
     * ...
     */
    private void handleCompensationMessage(String message) {
        try {
            // 检测是否包含价保/赔付和违规订单-违反京东平台规则
            if (message.contains("价保/赔付") || message.contains("价保") || message.contains("赔付")) {
                if (message.contains("违规订单-违反京东平台规则")) {
                    // 提取订单号：订单：343216308806
                    Pattern orderPattern = Pattern.compile("订单[：:]\\s*(\\d+)");
                    Matcher matcher = orderPattern.matcher(message);

                    if (matcher.find()) {
                        String orderId = matcher.group(1);

                        // 查找订单
                        JDOrder order = jdOrderService.selectJDOrderByOrderId(orderId);
                        if (order != null) {
                            // 设置后返到账状态为1，并设置到账日期为当前时间
                            order.setIsRebateReceived(1);
                            order.setRebateReceivedDate(new Date());

                            // 更新订单
                            int updateResult = jdOrderService.updateJDOrder(order);
                            if (updateResult > 0) {
                                System.out.println("✓ 自动标记后返到账成功 - 订单号: " + orderId);
                            } else {
                                System.err.println("✗ 自动标记后返到账失败 - 订单号: " + orderId + " (更新失败)");
                            }
                        } else {
                            System.err.println("✗ 未找到订单 - 订单号: " + orderId);
                        }
                    } else {
                        System.err.println("✗ 无法从消息中提取订单号");
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("✗ 处理价保/赔付消息时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 一次性批量更新历史订单：将赔付金额大于0的订单标记为后返到账
     * 此方法只应执行一次，用于处理历史数据
     */
    public void batchMarkRebateReceivedForCompensation() {
        try {
            System.out.println("开始批量标记后返到账（赔付金额>0的订单）...");

            // 查询所有订单
            List<JDOrder> allOrders = jdOrderService.selectJDOrderList(new JDOrder());

            // 查询所有订单的赔付金额（从OrderRows表）
            int updatedCount = 0;
            int skippedCount = 0;

            for (JDOrder order : allOrders) {
                // 查询订单的赔付金额
                OrderRows orderRows = orderRowsService.selectOrderRowsByOrderId(order.getOrderId());

                if (orderRows != null && orderRows.getProPriceAmount() != null && orderRows.getProPriceAmount() > 0) {
                    // 如果赔付金额大于0，且后返到账状态不是1，则更新
                    if (order.getIsRebateReceived() == null || order.getIsRebateReceived() != 1) {
                        order.setIsRebateReceived(1);
                        // 如果到账日期为空，设置为当前时间
                        if (order.getRebateReceivedDate() == null) {
                            order.setRebateReceivedDate(new Date());
                        }

                        int updateResult = jdOrderService.updateJDOrder(order);
                        if (updateResult > 0) {
                            updatedCount++;
                            System.out.println("✓ 标记后返到账 - 订单号: " + order.getOrderId() +
                                ", 赔付金额: " + orderRows.getProPriceAmount());
                        } else {
                            System.err.println("✗ 更新失败 - 订单号: " + order.getOrderId());
                        }
                    } else {
                        skippedCount++;
                    }
                } else {
                    skippedCount++;
                }
            }

            System.out.println("批量标记完成！");
            System.out.println("更新数量: " + updatedCount);
            System.out.println("跳过数量: " + skippedCount);
            System.out.println("总计: " + allOrders.size());

        } catch (Exception e) {
            System.err.println("✗ 批量标记后返到账时发生异常: " + e.getMessage());
            e.printStackTrace();
        }
    }
}



