package com.jwipc.order.logic.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.enums.SqlLike;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.jwipc.aggregation.ModelAggregationService;
import com.jwipc.api.constant.RestCode;
import com.jwipc.api.dto.ApiResponse;
import com.jwipc.constant.DictCode;
import com.jwipc.constant.FlagConst;
import com.jwipc.exception.DaoException;
import com.jwipc.order.constant.*;
import com.jwipc.order.convert.LabelBarConvert;
import com.jwipc.order.convert.LabelPrintSettingConvert;
import com.jwipc.order.dto.*;
import com.jwipc.order.entity.LabelBar;
import com.jwipc.order.entity.LabelPrintSetting;
import com.jwipc.order.entity.PurchaseOrderDetail;
import com.jwipc.order.logic.LabelPrintSettingLogic;
import com.jwipc.order.model.query.LabelPrintSettingQuery;
import com.jwipc.order.model.query.LabelQuery;
import com.jwipc.order.service.ILabelBarService;
import com.jwipc.order.service.ILabelPrintSettingService;
import com.jwipc.order.service.IPurchaseOrderDetailService;
import com.jwipc.page.PageUtil;
import com.jwipc.page.PageView;
import com.jwipc.prop.SysProp;
import com.jwipc.security.LoginUtil;
import com.jwipc.supplier.clent.dto.SupplierDTO;
import com.jwipc.supplier.client.SupplierClient;
import com.jwipc.sys.client.FileClient;
import com.jwipc.sys.client.MaterialOriginClient;
import com.jwipc.sys.client.PrintTemplateClient;
import com.jwipc.sys.client.SequenceClient;
import com.jwipc.sys.client.dto.MaterialOriginDTO;
import com.jwipc.util.DateUtils;
import com.jwipc.util.PDFUtil;
import freemarker.template.Configuration;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.jwipc.constant.DbTypeConst.SLAVE;
import static com.jwipc.constant.SequenceConfigConst.REDIS_KEY_PREFIX;


/**
 * <p> </p>
 *
 * @author BOGON
 * @version 1.0
 * @since JDK 1.8
 */
@Service
@RequiredArgsConstructor
public class LabelPrintSettingLogicImpl implements LabelPrintSettingLogic {
    private final ILabelPrintSettingService labelPrintSettingService;
    private final LabelPrintSettingConvert labelPrintSettingConvert;
    private final ModelAggregationService modelAggregationService;
    private final LabelBarConvert labelBarConvert;
    private final ILabelBarService labelBarService;
    private final SequenceClient sequenceClient;
    private final PoMesClient poMesClient;
    private final SupplierClient supplierClient;
    private final IPurchaseOrderDetailService purchaseOrderDetailService;
    private final PrintTemplateClient printTemplateClient;
    private final FileClient fileClient;
    private final Configuration freemarkerConfig;
    private final SysProp sysProp;
    private final MaterialOriginClient materialOriginClient;

    private List<MaterialOriginDTO> materialOriginList;

    @PostConstruct
    public void init() {
        // 从数据库加载数据
        materialOriginList = materialOriginClient.getMaterialOriginList();
    }

    @Override
    public PageView list(LabelPrintSettingQuery query) {
        final Page<LabelPrintSetting> page = PageUtil.toPage(query);
        final LambdaQueryWrapper<LabelPrintSetting> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StrUtil.isNotEmpty(query.getMaterialCode()), LabelPrintSetting::getMaterialCode,
                SqlUtils.concatLike(query.getMaterialCode(), SqlLike.DEFAULT));
        queryWrapper.like(StrUtil.isNotEmpty(query.getOrderCode()), LabelPrintSetting::getPurchaseOrderCode,
                SqlUtils.concatLike(query.getOrderCode(), SqlLike.DEFAULT));
        queryWrapper.eq(StrUtil.isNotEmpty(query.getStatus()), LabelPrintSetting::getStatus, query.getStatus());
        //增加创建时间查询条件 2024-08-29 邓京术
        queryWrapper.ge(StrUtil.isNotEmpty(query.getStartCreateDate()), LabelPrintSetting::getCreateTime,
                query.getStartCreateDate());
        queryWrapper.le(StrUtil.isNotEmpty(query.getEndCreateDate()), LabelPrintSetting::getCreateTime,
                query.getEndCreateDate());

        if (LoginUtil.isSupplier()) {
            queryWrapper.eq(LabelPrintSetting::getSupplierId, LoginUtil.getSupplierId());
            //判断当前的供应商是否有角色编码
            final List<String> roleCodes = LoginUtil.getRoleCodes();
            //判断是否存在特殊角色 special 如果存在并且没有输入查询条件就返回空数据
            if (roleCodes.contains(DictCode.SUPPLIER_SPECIAL_ROLE)) {
                final Map<String, Object> map = BeanUtil.beanToMap(query, false, false);
                Set<String> keySet = Sets.newHashSet();
//                keySet.add("orderCode");
                keySet.add("materialCode");
                boolean isOk = keySet.stream().allMatch(key -> {
                    if (Objects.nonNull(map.get(key))) {
                        if (Strings.isNullOrEmpty(map.get(key).toString())) {
                            return true;
                        } else {
                            return false;
                        }
                    } else {
                        return true;
                    }
                });
                if (isOk) {
                    return PageUtil.getPV(Collections.emptyList(), 0);
                }
            }
        }
        queryWrapper.exists(StrUtil.isNotEmpty(query.getBarCode()),
                "select tlb.id from t_label_bar tlb where tlb.setting_id=t_label_print_setting.id and tlb" +
                        ".barcode_serial_number LIKE {0}",
                SqlUtils.concatLike(query.getBarCode(), SqlLike.DEFAULT));
        if (StrUtil.isNotEmpty(query.getSupplierName())) {
            queryWrapper.exists(
                    "select 1 from t_supplier a where a.id=t_label_print_setting.supplier_id and (a.supplier_name " +
                            "LIKE" +
                            " {0} OR a.supplier_code LIKE {0})",
                    SqlUtils.concatLike(query.getSupplierName(), SqlLike.DEFAULT));
        }
        queryWrapper.orderBy(true, false, LabelPrintSetting::getCreateTime);
        final List<LabelPrintSetting> list = labelPrintSettingService.list(page, queryWrapper);
        List<LabelPrintSettingBaseDTO> settings = labelPrintSettingConvert.entities2Dtos(list);
        modelAggregationService.processRelationAggregation(settings);
        return PageUtil.getPV(settings, page.getTotal());
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public List<LabelBarBaseDTO> print(LabelPrintSettingBaseDTO printSettingBaseDTO, Long userId) {
        String jsonString = JSON.toJSONString(printSettingBaseDTO.getOptionJson());
        jsonString = jsonString.substring(1, jsonString.length() - 1);
        jsonString = jsonString.replace(StrUtil.BACKSLASH, StrUtil.EMPTY);
        //需要根据配置生成二维码
        final LabelPrintOptionDTO labelPrintOptionDTO = JSON.parseObject(
                jsonString, LabelPrintOptionDTO.class);
        if (StrUtil.isEmpty(labelPrintOptionDTO.getMsl())) {
            throw new DaoException("MSL不能为空");
        }
        labelPrintOptionDTO.setLot(labelPrintOptionDTO.getLot().toUpperCase());
        labelPrintOptionDTO.setDc(labelPrintOptionDTO.getDc().toUpperCase());
        labelPrintOptionDTO.setManufacturer(labelPrintOptionDTO.getManufacturer().toUpperCase());
        labelPrintOptionDTO.setMpn(labelPrintOptionDTO.getMpn().toUpperCase());
        labelPrintOptionDTO.setUnit(labelPrintOptionDTO.getUnit().toUpperCase());
        //需要把 /替换成空格
        labelPrintOptionDTO.setManufacturer(labelPrintOptionDTO.getManufacturer().replace("/", " "));
        labelPrintOptionDTO.setLot(labelPrintOptionDTO.getLot().replace("/", " "));
        labelPrintOptionDTO.setMpn(labelPrintOptionDTO.getMpn().replace("/", " "));
        labelPrintOptionDTO.setVersion(labelPrintOptionDTO.getVersion().replace("/", " "));
        labelPrintOptionDTO.setOrigin(StringUtils.isEmpty(labelPrintOptionDTO.getOrigin()) ? "" : labelPrintOptionDTO.getOrigin());
        LabelPrintSetting printSetting = labelPrintSettingConvert.dto2Entity(printSettingBaseDTO);
        printSetting.setCreateId(userId);
        printSetting.setCreateTime(LocalDateTime.now());
        printSetting.setOptionJson(JSON.toJSONString(labelPrintOptionDTO));
        labelPrintSettingService.save(printSetting);

        //需要按照配置来打印
        final List<LabelBarBaseDTO> labelBarBaseDTOS = printProcessor(printSetting.getId(), labelPrintOptionDTO);
        //需要拼接工厂
        for (LabelBarBaseDTO labelBarBaseDTO : labelBarBaseDTOS) {
            labelBarBaseDTO.setUnit(labelBarBaseDTO.getUnit() + "  工厂:" + labelBarBaseDTO.getFactoryCode());
            if (!LabelBoxType.OUT.getCode().equals(labelBarBaseDTO.getBoxType()) && labelBarBaseDTO.getParentId() != null) {
                LabelBar parent = labelBarService.getById(labelBarBaseDTO.getParentId());
                // 打印显示的外箱条码
                String outsideBarcodeSerialNumber = "外" + parent.getBarcodeSerialNumber().substring(9);
                labelBarBaseDTO.setOutsideBarcodeSerialNumber(outsideBarcodeSerialNumber);
            }
        }
        return labelBarBaseDTOS;
    }
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    @Override
    public List<LabelBarBaseDTO> print_new(LabelPrintSettingBaseDTO printSettingBaseDTO, Long userId) {
        // 1) 解析并清洗 option（替代你原 substring/replace 的高风险写法）
        LabelPrintOptionDTO option = parseOption_new(printSettingBaseDTO);
        sanitizeOption_new(option);

        // 2) 提前生成 settingId，避免依赖DB生成策略
        final Long settingId = IdWorker.getId();

        // 3) 事务外生成所有条码明细（会调用 sequenceClient，但不持有DB锁）
        final List<LabelBar> barsToInsert = buildBars_new(settingId, option);

        // 4) 短事务：只做 insert 主表 + batch insert 明细表
        LabelPrintSetting settingEntity = labelPrintSettingConvert.dto2Entity(printSettingBaseDTO);
        settingEntity.setId(settingId);
        settingEntity.setCreateId(userId);
        settingEntity.setCreateTime(LocalDateTime.now());
        settingEntity.setOptionJson(JSON.toJSONString(option));
        persistSettingAndBars_new(settingEntity, barsToInsert);

        // 5) 组装返回（不查库、不N+1）
        List<LabelBarBaseDTO> result = buildResultFromBars_new(barsToInsert, true);
        return result;
    }

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void persistSettingAndBars_new(LabelPrintSetting setting, List<LabelBar> bars) {
        labelPrintSettingService.save(setting);
        if (CollUtil.isNotEmpty(bars)) {
            labelBarService.saveBatch(bars, 300);
        }
    }

//    private void safeRePush_new(Long settingId) {
//        try {
//            rePush(settingId); // 复用你原来的 rePush（它本身就是REQUIRES_NEW）
//        } catch (Exception e) {
//            // 建议你们加日志：
//             //log.error("rePush failed, settingId={}", settingId, e);
//        }
//    }

    private List<LabelBar> buildBars_new(Long settingId, LabelPrintOptionDTO option) {
        final LabelPrintSettingType settingType = LabelPrintSettingType.getByType(option.getType());
        if (settingType == null) {
            throw new DaoException("标签类型type非法");
        }

        return switch (settingType) {
            case API -> buildBarsForApi_new(settingId, option);
            case INNER -> buildBarsForInnerOnly_new(settingId, option, true);
            case INNER_AND_OUT -> buildBarsForInnerAndOut_new(settingId, option); // validateCount 放这里
        };
    }


    private List<LabelBar> buildBarsForInnerAndOut_new(Long settingId, LabelPrintOptionDTO option) {
        validateCount(option); // ✅仅内外标签才校验总数
        BigDecimal total = option.getNum();
        BigDecimal outPerBox = option.getFullQuantity();

        int outLabelSize = calcLabelSize_new(total, outPerBox);

        List<LabelBar> all = new ArrayList<>();
        BigDecimal remainder = total.remainder(outPerBox);

        for (int i = 0; i < outLabelSize; i++) {
            BigDecimal outQty = (i == outLabelSize - 1)
                    ? (remainder.compareTo(BigDecimal.ZERO) == 0 ? outPerBox : remainder)
                    : outPerBox;

            LabelBar out = baseBar_new(option, settingId, LabelBoxType.OUT);
            out.setId(IdWorker.getId());
            out.setSort(i + 1);
            out.setQuantity(outQty);

            out.setBarcodeSerialNumber(generateNumber(out.getSupplierCode(), LabelBoxType.OUT));
            out.setBarcodeContent(generateCode(out));

            // 计算内箱张数（沿用你原思路）
            BigDecimal innerPer = option.getMiddleQuantity().compareTo(BigDecimal.ZERO) != 0
                    ? option.getMiddleQuantity()
                    : option.getMinQuantity();
            out.setInnerBoxQuantity(calcLabelSize_new(outQty, innerPer));

            all.add(out);

            // 中箱
            if (option.getMiddleQuantity().compareTo(BigDecimal.ZERO) != 0) {
                List<LabelBar> middleList = buildChildBars_new(
                        option, settingId, out.getId(), out.getBarcodeContent(),
                        LabelBoxType.MIDDLE, outQty, option.getMiddleQuantity()
                );
                all.addAll(middleList);

                // 每个中箱生成内箱（注意：parentId 仍然是外箱id，与你原逻辑一致）
                for (LabelBar middle : middleList) {
                    List<LabelBar> innerList = buildChildBars_new(
                            option, settingId, out.getId(), out.getBarcodeContent(),
                            LabelBoxType.INNER, middle.getQuantity(), option.getMinQuantity()
                    );
                    all.addAll(innerList);
                }
            } else {
                // 只有外+内
                List<LabelBar> innerList = buildChildBars_new(
                        option, settingId, out.getId(), out.getBarcodeContent(),
                        LabelBoxType.INNER, outQty, option.getMinQuantity()
                );
                all.addAll(innerList);
            }
        }

        return all;
    }

    private List<LabelBar> buildBarsForInnerOnly_new(Long settingId, LabelPrintOptionDTO option, boolean validate) {
        BigDecimal total = option.getNum();
        BigDecimal per = option.getMinQuantity();

        int labelSize = calcLabelSize_new(total, per);

        if (validate) {
            if (labelSize > 2000 && option.getPrinterType() != PrinterType.DEFAULT.getType()) {
                throw new DaoException("标签数超过2000个无法打印");
            }
            if (labelSize > 300 && option.getPrinterType() == PrinterType.DEFAULT.getType()) {
                throw new DaoException("标签数超过200个无法打印");
            }
        }

        List<LabelBar> list = new ArrayList<>();
        BigDecimal remainder = total.remainder(per);
        boolean doubleInner = FlagConst.getFlag(option.getInnerFlag()) == FlagConst.Y;

        for (int i = 0; i < labelSize; i++) {
            BigDecimal qty = (i == labelSize - 1)
                    ? (remainder.compareTo(BigDecimal.ZERO) == 0 ? per : remainder)
                    : per;

            LabelBar bar = baseBar_new(option, settingId, LabelBoxType.INNER);
            bar.setId(IdWorker.getId());
            bar.setSort(i + 1);
            bar.setQuantity(qty);

            String serial = generateNumber(bar.getSupplierCode(), LabelBoxType.INNER);
            bar.setBarcodeSerialNumber(serial);
            bar.setBarcodeContent(generateCode(bar));
            if (doubleInner) {
                LabelBar copy = baseBar_new(option, settingId, LabelBoxType.INNER);
                copy.setId(IdWorker.getId());
                copy.setSort(i + 1);
                copy.setQuantity(qty);
                copy.setBarcodeSerialNumber(serial);
                copy.setBarcodeContent(bar.getBarcodeContent());
                list.add(copy);
            }
            list.add(bar);
        }
        return list;
    }

    private List<LabelBar> buildBarsForApi_new(Long settingId, LabelPrintOptionDTO option) {
        LabelBoxType typeEm = option.getLabelBoxType();
        if (typeEm == null) {
            throw new DaoException("labelBoxType不能为空");
        }

        // 非外箱：直接内箱
        if (typeEm.getType() != LabelBoxType.OUT.getType()) {
            return buildBarsForInnerOnly_new(settingId, option, false);
        }

        int sheets = option.getSheets();
        if (sheets > 2000) {
            throw new DaoException("标签数超过2000个无法打印");
        }

        List<LabelBar> all = new ArrayList<>();

        for (int i = 0; i < sheets; i++) {
            LabelBar out = baseBar_new(option, settingId, LabelBoxType.OUT);
            out.setId(IdWorker.getId());
            out.setSort(i + 1);
            out.setQuantity(option.getFullQuantity());

            out.setBarcodeSerialNumber(generateNumber(out.getSupplierCode(), LabelBoxType.OUT));
            out.setBarcodeContent(generateCode(out));

            List<LabelBar> inner = buildChildBars_new(
                    option, settingId, out.getId(), out.getBarcodeContent(),
                    LabelBoxType.INNER, out.getQuantity(), option.getMinQuantity()
            );

            out.setInnerBoxQuantity(inner.size());

            all.add(out);
            all.addAll(inner);
        }

        return all;
    }
    private List<LabelBar> buildChildBars_new(
            LabelPrintOptionDTO option,
            Long settingId,
            Long parentId,
            String parentBarcodeContent,
            LabelBoxType type,
            BigDecimal total,
            BigDecimal perBox
    ) {
        int labelSize = calcLabelSize_new(total, perBox);
        BigDecimal remainder = total.remainder(perBox);

        boolean doubleInner = (type == LabelBoxType.INNER)
                && (FlagConst.getFlag(option.getInnerFlag()) == FlagConst.Y);

        List<LabelBar> list = new ArrayList<>(doubleInner ? labelSize * 2 : labelSize);

        for (int i = 0; i < labelSize; i++) {
            BigDecimal qty = (i == labelSize - 1)
                    ? (remainder.compareTo(BigDecimal.ZERO) == 0 ? perBox : remainder)
                    : perBox;

            LabelBar bar = baseBar_new(option, settingId, type);
            bar.setId(IdWorker.getId());
            bar.setParentId(parentId);
            if (StrUtil.isNotEmpty(parentBarcodeContent)) {
                bar.setOuterBoxBarcodeContent(parentBarcodeContent);
            }
            bar.setSort(i + 1);
            bar.setQuantity(qty);

            String serial = generateNumber(bar.getSupplierCode(), type);
            bar.setBarcodeSerialNumber(serial);
            bar.setBarcodeContent(generateCode(bar));

            // ✅严格对齐原 processor：先 add copy，再 add original
            if (doubleInner) {
                LabelBar copy = baseBar_new(option, settingId, type);
                copy.setId(IdWorker.getId());
                copy.setParentId(parentId);
                if (StrUtil.isNotEmpty(parentBarcodeContent)) {
                    copy.setOuterBoxBarcodeContent(parentBarcodeContent);
                }
                copy.setSort(i + 1);
                copy.setQuantity(qty);
                copy.setBarcodeSerialNumber(serial);
                copy.setBarcodeContent(bar.getBarcodeContent());
                list.add(copy);
            }

            list.add(bar);
        }

        return list;
    }


    private LabelBar baseBar_new(LabelPrintOptionDTO option, Long settingId, LabelBoxType type) {
        LabelBar bar = labelBarConvert.option2Entity(option);
        bar.setSettingId(settingId);
        bar.setBoxType(type.getCode());
        bar.setFactoryCode(option.getFactoryCode());
        bar.setPrintTime(LocalDateTime.now());
        return bar;
    }

    private List<LabelBarBaseDTO> buildResultFromBars_new(List<LabelBar> bars, boolean isPrint) {
        Map<Long, LabelBar> idMap = bars.stream()
                .filter(b -> b.getId() != null)
                .collect(Collectors.toMap(LabelBar::getId, b -> b, (a, b) -> a));

        List<LabelBarBaseDTO> dtos = labelBarConvert.entities2Dtos(bars);

        for (LabelBarBaseDTO dto : dtos) {
            dto.setUnit((dto.getUnit() == null ? "" : dto.getUnit()) + "  工厂:" + (dto.getFactoryCode() == null ? "" : dto.getFactoryCode()));

            if (!LabelBoxType.OUT.getCode().equals(dto.getBoxType()) && dto.getParentId() != null) {
                LabelBar parent = idMap.get(dto.getParentId());
                if (parent != null) {
                    String psn = parent.getBarcodeSerialNumber();
                    dto.setOutsideBarcodeSerialNumber(isPrint ? buildOutsideDisplay_new(psn) : (psn == null ? "" : psn));
                }
            }
        }
        return dtos;
    }

    private String buildOutsideDisplay_new(String parentSerial) {
        if (parentSerial == null) return "";
        if (parentSerial.length() <= 9) return "外" + parentSerial;
        return "外" + parentSerial.substring(9);
    }

    private LabelPrintOptionDTO parseOption_new(LabelPrintSettingBaseDTO dto) {
        Object obj = dto.getOptionJson();
        if (obj == null) {
            throw new DaoException("配置optionJson不能为空");
        }
        try {
            if (obj instanceof String s) {
                return JSON.parseObject(s, LabelPrintOptionDTO.class);
            }
            return JSON.parseObject(JSON.toJSONString(obj), LabelPrintOptionDTO.class);
        } catch (Exception e) {
            throw new DaoException("配置optionJson解析失败:" + e.getMessage());
        }
    }

    private void sanitizeOption_new(LabelPrintOptionDTO opt) {
        if (opt == null) throw new DaoException("打印配置不能为空");

        if (StrUtil.isEmpty(opt.getMsl())) {
            throw new DaoException("MSL不能为空");
        }

        opt.setLot(safeUpper_new(opt.getLot()));
        opt.setDc(safeUpper_new(opt.getDc()));
        opt.setManufacturer(safeUpper_new(opt.getManufacturer()));
        opt.setMpn(safeUpper_new(opt.getMpn()));
        opt.setUnit(safeUpper_new(opt.getUnit()));
        opt.setVersion(opt.getVersion() == null ? "" : opt.getVersion());

        opt.setManufacturer(replaceSlash_new(opt.getManufacturer()));
        opt.setLot(replaceSlash_new(opt.getLot()));
        opt.setMpn(replaceSlash_new(opt.getMpn()));
        opt.setVersion(replaceSlash_new(opt.getVersion()));

        opt.setOrigin(StringUtils.isEmpty(opt.getOrigin()) ? "" : opt.getOrigin());
//        if (opt.getMiddleQuantity() == null) {
//            opt.setMiddleQuantity(BigDecimal.ZERO);
//        }
//        if (opt.getSheets() == null) {
//            opt.setSheets(0);
//        }

    }

    private String safeUpper_new(String s) {
        return s == null ? "" : s.toUpperCase();
    }

    private String replaceSlash_new(String s) {
        return s == null ? "" : s.replace("/", " ");
    }

    private int calcLabelSize_new(BigDecimal total, BigDecimal perBox) {
        if (total == null || perBox == null) return 0;
        if (perBox.compareTo(BigDecimal.ZERO) <= 0) {
            throw new DaoException("每箱数量必须大于0");
        }
        BigDecimal[] divRem = total.divideAndRemainder(perBox);
        int size = divRem[0].intValue();
        if (divRem[1].compareTo(BigDecimal.ZERO) > 0) size++;
        return size;
    }



    @Override
    public List<LabelBarBaseDTO> getLabelBars(Long id, Integer isPrint) {
        //查询标签
        final List<LabelBar> list = labelBarService.list(
                new LambdaQueryWrapper<LabelBar>().eq(LabelBar::getSettingId, id));
        if (isPrint == FlagConst.Y.getCode()) {
            //需要处理打印次数
            LambdaUpdateWrapper<LabelBar> updateWrapper = new LambdaUpdateWrapper<LabelBar>();
            updateWrapper.setSql("print_size=print_size+1");
            updateWrapper.eq(LabelBar::getSettingId, id);
            labelBarService.update(updateWrapper);
        }
        final List<LabelBarBaseDTO> labelBarBaseDTOS = getLabelBarBaseDTOS(list, true);
        return labelBarBaseDTOS;
    }

    @Override
    public List<LabelPrintSetting> getLabelPrintSettingByIds(Collection<? extends Serializable> idList) {
        if (CollUtil.isEmpty(idList)) {
            return CollUtil.newArrayList();
        }
        return labelPrintSettingService.listByIds(idList);
    }

    private @NotNull List<LabelBarBaseDTO> getLabelBarBaseDTOS(List<LabelBar> list, Boolean isPrint) {
        List<LabelBar> out;
        //如果out是空，说明是内箱打印
        out = list.stream().filter(a -> a.getBoxType().equals(LabelBoxType.OUT.getCode()))
                .collect(Collectors.toList());
        List<LabelBarBaseDTO> result = Lists.newArrayList();
        if (out.size() > 0) {
            for (LabelBar labelBar : out) {
                final Long id1 = labelBar.getId();
                LabelBarBaseDTO labelBarBaseDTO = labelBarConvert.entity2Dto(labelBar);
                result.add(labelBarBaseDTO);
                //处理中箱
                final List<LabelBar> collect = list.stream()
                        .filter(a -> Objects.nonNull(a.getParentId()) && a.getParentId().equals(id1) &&
                                a.getBoxType().equals(LabelBoxType.MIDDLE.getCode())).collect(Collectors.toList());
                // 使用 Comparator 对象按照 sort 字段升序排序
                Collections.sort(collect, Comparator.comparing(LabelBar::getSort));
                // 打印排序后的结果
                for (LabelBar bar : collect) {
                    LabelBarBaseDTO baseDTO = labelBarConvert.entity2Dto(bar);
                    String outsideBarcodeSerialNumber;
                    if (isPrint) {
                        // 打印显示的外箱条码
                        outsideBarcodeSerialNumber = "外" + labelBar.getBarcodeSerialNumber().substring(9);
                    } else {
                        // 查询界面的外箱条码
                        outsideBarcodeSerialNumber = labelBar.getBarcodeSerialNumber();
                    }
                    baseDTO.setOutsideBarcodeSerialNumber(outsideBarcodeSerialNumber);
                    result.add(baseDTO);
                }

                //处理内箱
                final List<LabelBar> collect1 = list.stream()
                        .filter(a -> Objects.nonNull(a.getParentId()) && a.getParentId().equals(id1) &&
                                a.getBoxType().equals(LabelBoxType.INNER.getCode())).collect(Collectors.toList());
                // 使用 Comparator 对象按照 sort 字段升序排序
                Collections.sort(collect1, Comparator.comparing(LabelBar::getSort));

                // 打印排序后的结果
                for (LabelBar bar : collect1) {
                    LabelBarBaseDTO baseDTO = labelBarConvert.entity2Dto(bar);
                    String outsideBarcodeSerialNumber;
                    if (isPrint) {
                        // 打印显示的外箱条码
                        outsideBarcodeSerialNumber = "外" + labelBar.getBarcodeSerialNumber().substring(9);
                    } else {
                        // 查询界面的外箱条码
                        outsideBarcodeSerialNumber = labelBar.getBarcodeSerialNumber();
                    }
                    baseDTO.setOutsideBarcodeSerialNumber(outsideBarcodeSerialNumber);
                    result.add(baseDTO);
                }
            }
        } else {
            for (LabelBar labelBar : list) {
                LabelBarBaseDTO baseDTO = labelBarConvert.entity2Dto(labelBar);
                if (!LabelBoxType.OUT.getCode().equals(baseDTO.getBoxType()) && baseDTO.getParentId() != null) {
                    LabelBar parent = labelBarService.getById(baseDTO.getParentId());
                    String outsideBarcodeSerialNumber;
                    if (isPrint) {
                        // 打印显示的外箱条码
                        outsideBarcodeSerialNumber = "外" + parent.getBarcodeSerialNumber().substring(9);
                    } else {
                        // 查询界面的外箱条码
                        outsideBarcodeSerialNumber = parent.getBarcodeSerialNumber();
                    }
                    baseDTO.setOutsideBarcodeSerialNumber(outsideBarcodeSerialNumber);
                }
                result.add(baseDTO);
            }
        }
        //需要拼接工厂
        for (LabelBarBaseDTO labelBarBaseDTO : result) {
            labelBarBaseDTO.setUnit(labelBarBaseDTO.getUnit() + "  工厂:" + labelBarBaseDTO.getFactoryCode());
        }
        return result;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public String rePush(Long id) {
        final LambdaUpdateWrapper<LabelBar> updateWrapper = new LambdaUpdateWrapper<LabelBar>()
                .set(LabelBar::getPrintTime, LocalDateTime.now())
                .eq(LabelBar::getSettingId, id);
        if (StpUtil.isLogin()) {
            updateWrapper.set(LabelBar::getPrinter, LoginUtil.getAccount() + StrUtil.DASHED + LoginUtil.getName());
        }
        labelBarService.update(updateWrapper);
        final List<LabelBar> list = labelBarService.list(
                new LambdaQueryWrapper<LabelBar>().ne(LabelBar::getBoxType, LabelBoxType.MIDDLE.getCode())
                        .eq(LabelBar::getSettingId, id).groupBy(LabelBar::getBarcodeSerialNumber));
        final ApiResponse apiResponse = push(list, id);
        if (apiResponse.getCode().equals(RestCode.MES_SUCCESS.getCode())) {
            final LambdaUpdateWrapper<LabelBar> updateWrapper1 = new LambdaUpdateWrapper<LabelBar>()
                    .set(LabelBar::getStatus, FlagConst.Y.getCode())
                    .eq(LabelBar::getSettingId, id);
            labelBarService.update(updateWrapper1);
            return StrUtil.EMPTY;
        } else {
            final LambdaUpdateWrapper<LabelBar> updateWrapper1 = new LambdaUpdateWrapper<LabelBar>()
                    .set(LabelBar::getStatus, FlagConst.N.getCode())
                    .eq(LabelBar::getSettingId, id);
            labelBarService.update(updateWrapper1);
            return apiResponse.getMsg();
        }

    }

    @Override
    public PageView labelList(LabelQuery query) {
        Page<LabelBar> page = PageUtil.toPage(query);
        List<LabelBarBaseDTO> labelBarBaseDTOS = getQueryResult(query);
        if (labelBarBaseDTOS == null) {
            return PageUtil.getPV(Collections.emptyList(), 0);
        }
        modelAggregationService.processRelationAggregation(labelBarBaseDTOS);
        return PageUtil.getPV(labelBarBaseDTOS, page.getTotal());
    }

    @Override
    public List<LabelBarBaseDTO> getExportData(LabelQuery query) {
        List<LabelBarBaseDTO> labelBarBaseDTOS = getQueryResult(query);
        for (LabelBarBaseDTO base : labelBarBaseDTOS) {
            base.setBoxType(LabelBoxType.getByCode(base.getBoxType()) != null? LabelBoxType.getByCode(base.getBoxType()).getTitle() : "");
        }
        return labelBarBaseDTOS;
    }

    @Override
    public List<LabelBarBaseDTO> getLabelBars(String ids, Integer isPrint) {
        if (StrUtil.isEmpty(ids)) {
            return Lists.newArrayList();
        }
        final List<String> idArray = StrUtil.split(ids, StrUtil.COMMA);
        //查询标签
        final List<LabelBar> list = labelBarService.list(
                new LambdaQueryWrapper<LabelBar>().in(LabelBar::getId, idArray));
        if (isPrint == FlagConst.Y.getCode()) {
            //需要处理打印次数
            LambdaUpdateWrapper<LabelBar> updateWrapper = new LambdaUpdateWrapper<LabelBar>();
            updateWrapper.setSql("print_size=print_size+1");
            updateWrapper.in(LabelBar::getId, idArray);
            labelBarService.update(updateWrapper);
        }
        return getLabelBarBaseDTOS(list, true);
    }

    private ApiResponse push(List<LabelBar> labelBars, Long id) {
        List<QcodeToMesDTO.IT_HEAD_Item> itHeadItems = Lists.newArrayList();
        for (LabelBar labelBar : labelBars) {
            final QcodeToMesDTO.IT_HEAD_Item itHeadItem = new QcodeToMesDTO.IT_HEAD_Item();
            itHeadItem.setDC(labelBar.getDc());
            itHeadItem.setCPN(labelBar.getMaterialCode());
            itHeadItem.setPO(labelBar.getOrderNumber());
            itHeadItem.setLabelLevel(labelBar.getBoxType());
            itHeadItem.setLOT(labelBar.getLot());
            itHeadItem.setBarcodeNo(labelBar.getBarcodeSerialNumber());
            itHeadItem.setMPN(labelBar.getMpn());
            itHeadItem.setMSL(labelBar.getMsl());
            itHeadItem.setQTY(labelBar.getQuantity());
            itHeadItem.setUNIT(labelBar.getUnit());
            itHeadItem.setInnerNum(labelBar.getInnerBoxQuantity());
            itHeadItem.setManufacture(labelBar.getManufacturer());
            itHeadItem.setVname(labelBar.getSupplierName());
            itHeadItem.setVENDOR(labelBar.getSupplierCode());
            itHeadItem.setInnerNum(labelBar.getInnerBoxQuantity());
            itHeadItem.setVER(labelBar.getVersion());
            itHeadItem.setBOXLabelContent(labelBar.getOuterBoxBarcodeContent());
            itHeadItem.setPrintContent(labelBar.getBarcodeContent());
            itHeadItem.setLasteditby(labelBar.getPrinter());
            itHeadItem.setLasteditdt(DateUtils.formatYYYYMMDDHms(labelBar.getPrintTime()));
            itHeadItem.setReserve1(StringUtils.isEmpty(labelBar.getOrigin()) ? "" : labelBar.getOrigin());
            itHeadItems.add(itHeadItem);
        }
        final QcodeToMesDTO.IT_DATA itHead = new QcodeToMesDTO.IT_DATA();
        itHead.setItem(itHeadItems);
        final QcodeToMesDTO qcodeToMesDTO = new QcodeToMesDTO();
        qcodeToMesDTO.setIT_DATA(itHead);
        final ApiResponse apiResponse = poMesClient.pushBarcode(qcodeToMesDTO);
        if (apiResponse.getCode().equals(RestCode.MES_SUCCESS.getCode())) {
            if (Objects.nonNull(id)) {
                labelPrintSettingService.update(new LambdaUpdateWrapper<LabelPrintSetting>()
                        .eq(LabelPrintSetting::getId, id)
                        .set(LabelPrintSetting::getStatus, FlagConst.Y.getCode()));
            }
        } else {
            if (Objects.nonNull(id)) {
                labelPrintSettingService.update(new LambdaUpdateWrapper<LabelPrintSetting>()
                        .eq(LabelPrintSetting::getId, id)
                        .set(LabelPrintSetting::getStatus, FlagConst.N.getCode()));
            }
        }
        return apiResponse;
    }

    /**
     * 处理器
     *
     * @return
     */
    private List<LabelBarBaseDTO> printProcessor(Long id, LabelPrintOptionDTO labelPrintOptionDTO) {
        //需要转换成大写
        // TODO: 2024/5/28 需要转大写 jiez
        final LabelPrintSettingType settingType = LabelPrintSettingType.getByType(labelPrintOptionDTO.getType());
        List<LabelBarBaseDTO> result = Lists.newArrayList();
        switch (settingType) {
            case API -> result = apiProcessor(id, labelPrintOptionDTO);
            case INNER -> result = innerProcessor(id, labelPrintOptionDTO, true);
            case INNER_AND_OUT -> result = innerAndOutProcessor(id, labelPrintOptionDTO);
        }
        rePush(id);
        return result;
    }

    private List<LabelBarBaseDTO> innerAndOutProcessor(Long id, LabelPrintOptionDTO labelPrintOptionDTO) {
        final BigDecimal fullQuantity = labelPrintOptionDTO.getFullQuantity();
        final BigDecimal num = labelPrintOptionDTO.getNum();
        final List<LabelBarBaseDTO> result = Lists.newArrayList();
        //需要校验数量是否超过300个
        validateCount(labelPrintOptionDTO);
        //这个地方需要做特殊处理,
        final List<LabelBarBaseDTO> processor = processorForOut(num, fullQuantity, id, labelPrintOptionDTO,
                LabelBoxType.OUT);
        for (LabelBarBaseDTO labelBarBaseDTO : processor) {
            result.add(labelBarBaseDTO);
            final BigDecimal quantity = labelBarBaseDTO.getQuantity();
            if (!Objects.equals(labelPrintOptionDTO.getMiddleQuantity(), BigDecimal.ZERO)) {
                final List<LabelBarBaseDTO> processor1 = processor(quantity,
                        labelPrintOptionDTO.getMiddleQuantity(),
                        id, labelBarBaseDTO.getId(), labelPrintOptionDTO, LabelBoxType.MIDDLE,
                        labelBarBaseDTO.getBarcodeContent());
                result.addAll(processor1);
                for (LabelBarBaseDTO barBaseDTO : processor1) {
                    final List<LabelBarBaseDTO> processor2 = processor(barBaseDTO.getQuantity(),
                            labelPrintOptionDTO.getMinQuantity(), id, labelBarBaseDTO.getId(), labelPrintOptionDTO,
                            LabelBoxType.INNER, labelBarBaseDTO.getBarcodeContent());
                    result.addAll(processor2);
                }
            } else {
                final List<LabelBarBaseDTO> processor1 = processor(labelBarBaseDTO.getQuantity(),
                        labelPrintOptionDTO.getMinQuantity(), id, labelBarBaseDTO.getId(), labelPrintOptionDTO,
                        LabelBoxType.INNER, labelBarBaseDTO.getBarcodeContent());
                result.addAll(processor1);
            }
        }
        return result;
    }

    private List<LabelBarBaseDTO> processorForOut(BigDecimal total, BigDecimal perBox, Long id,
                                                  LabelPrintOptionDTO labelPrintOptionDTO, LabelBoxType type)
    {
        //！！！！！！！注意不能使用批量插入，这会导致流水号失效！！！！！！！！！
        //总数%每箱数量生成条码
        BigDecimal remainder = total.remainder(perBox);
        //然后这个余数如果是不等于0就需要把条码数量+1
        int labelSize = total.divide(perBox, 0, BigDecimal.ROUND_DOWN).intValue();
        if (remainder.compareTo(BigDecimal.ZERO) > 0) {
            labelSize = labelSize + 1;
        }
        List<LabelBar> insertList = Lists.newArrayList();
        for (int i = 0; i < labelSize; i++) {
            LabelBar labelBar = labelBarConvert.option2Entity(labelPrintOptionDTO);
            labelBar.setId(IdWorker.getId());
            labelBar.setPrintTime(LocalDateTime.now());
            labelBar.setSettingId(id);
            labelBar.setBoxType(type.getCode());
            labelBar.setFactoryCode(labelPrintOptionDTO.getFactoryCode());
            labelBar.setBarcodeSerialNumber(generateNumber(labelBar.getSupplierCode(), type));
            BigDecimal quantity;
            //需要判断是否是最后一个，如果是需要把取余的数量放进去
            if (i == labelSize - 1) {
                if (remainder.compareTo(BigDecimal.ZERO) == 0) {
                    quantity = perBox;
                } else {
                    quantity = remainder;
                }

            } else {
                quantity = perBox;
            }
            labelBar.setQuantity(quantity);

            //需要提前计算内箱数量
            BigDecimal innerTotal = quantity;
            BigDecimal innerPerBox = BigDecimal.ZERO;
            if (!Objects.equals(labelPrintOptionDTO.getMiddleQuantity(), BigDecimal.ZERO)) {
                innerPerBox = labelPrintOptionDTO.getMiddleQuantity();
            } else {
                innerPerBox = labelPrintOptionDTO.getMinQuantity();
            }
            BigDecimal innerRemainder = innerTotal.remainder(innerPerBox);
            //然后这个余数如果是不等于0就需要把条码数量+1
            int innerLabelSize = innerTotal.divide(innerPerBox, 0, BigDecimal.ROUND_DOWN).intValue();
            if (innerRemainder.compareTo(BigDecimal.ZERO) > 0) {
                innerLabelSize = innerLabelSize + 1;
            }
            labelBar.setInnerBoxQuantity(innerLabelSize);

            labelBar.setBarcodeContent(generateCode(labelBar));
            labelBar.setSort(i + 1);
            //需要注意如果是内箱需要判断是否打印双份
            if (type.equals(LabelBoxType.INNER)) {
                final FlagConst flag = FlagConst.getFlag(labelPrintOptionDTO.getInnerFlag());
                if (flag.equals(FlagConst.Y)) {
                    //表示有内盒，

                }
            }
            insertList.add(labelBar);
        }
        if (CollUtil.isNotEmpty(insertList)) {
            labelBarService.saveBatch(insertList, 300);
        }
        return labelBarConvert.entities2Dtos(insertList);
    }

    private void validateCount(LabelPrintOptionDTO labelPrintOptionDTO) {
//        if (labelPrintOptionDTO.getPrinterType() != PrinterType.DEFAULT.getType()) {
//            return;
//        }
        final BigDecimal num = labelPrintOptionDTO.getNum();
        final BigDecimal middleQuantity = labelPrintOptionDTO.getMiddleQuantity();
        final BigDecimal minQuantity = labelPrintOptionDTO.getMinQuantity();
        final BigDecimal fullQuantity = labelPrintOptionDTO.getFullQuantity();
        //总数%每箱数量生成条码
        BigDecimal remainder = num.remainder(fullQuantity);
        //外箱的数量
        int outLabelSize = num.divide(fullQuantity, 0, BigDecimal.ROUND_DOWN).intValue();
        if (remainder.compareTo(BigDecimal.ZERO) > 0) {
            outLabelSize = outLabelSize + 1;
        }
        //内标签数量
        final BigDecimal innerRemainder = fullQuantity.remainder(minQuantity);
        int innerLabelSize = fullQuantity.divide(minQuantity, 0, BigDecimal.ROUND_DOWN).intValue();
        if (innerRemainder.compareTo(BigDecimal.ZERO) > 0) {
            innerLabelSize = innerLabelSize + 1;
        }
        if (labelPrintOptionDTO.getInnerFlag().equals(FlagConst.Y.getCode())) {
            innerLabelSize = innerLabelSize * outLabelSize * 2;
        } else {
            innerLabelSize = innerLabelSize * outLabelSize;
        }
        //中签数量
        int middleLabelSize = 0;
        if (middleQuantity.compareTo(BigDecimal.ZERO) != 0) {
            final BigDecimal middleRemainder = fullQuantity.remainder(middleQuantity);
            middleLabelSize = fullQuantity.divide(middleQuantity, 0, BigDecimal.ROUND_DOWN).intValue();
            if (middleRemainder.compareTo(BigDecimal.ZERO) > 0) {
                middleLabelSize = middleLabelSize + 1;
            }
        }
        //统计总数不能超过300个
        final int total = outLabelSize + innerLabelSize + middleLabelSize;
        if (total > 300 && labelPrintOptionDTO.getPrinterType() == PrinterType.DEFAULT.getType()) {
            throw new DaoException("标签数超过300个无法打印");
        }
        if (total > 2000 && labelPrintOptionDTO.getPrinterType() != PrinterType.DEFAULT.getType()) {
            throw new DaoException("标签数超过2000个无法打印");
        }
    }

    private List<LabelBarBaseDTO> innerProcessor(Long id, LabelPrintOptionDTO labelPrintOptionDTO, boolean isValidata) {
        final List<LabelBarBaseDTO> result = Lists.newArrayList();
        final BigDecimal minQuantity = labelPrintOptionDTO.getMinQuantity();//修改为包装数需要进行计算
        final BigDecimal total = labelPrintOptionDTO.getNum();
        //总数%每箱数量生成条码
        BigDecimal remainder = total.remainder(minQuantity);
        //然后这个余数如果是不等于0就需要把条码数量+1
        int labelSize = total.divide(minQuantity, 0, BigDecimal.ROUND_DOWN).intValue();
        if (remainder.compareTo(BigDecimal.ZERO) > 0) {
            labelSize = labelSize + 1;
        }
        if (isValidata && labelSize > 2000 && labelPrintOptionDTO.getPrinterType() == PrinterType.TYPE1.getType()) {
            throw new DaoException("标签数超过2000个无法打印");
        }
        if (isValidata && labelSize > 2000 && labelPrintOptionDTO.getPrinterType() == PrinterType.TYPE2.getType()) {
            throw new DaoException("标签数超过2000个无法打印");
        }
        if (isValidata && labelSize > 300 && labelPrintOptionDTO.getPrinterType() == PrinterType.DEFAULT.getType()) {
            throw new DaoException("标签数超过200个无法打印");
        }

        List<LabelBar> insertList = Lists.newArrayList();
        //需要设置排序
        for (int i = 0; i < labelSize; i++) {
            LabelBar labelBar = labelBarConvert.option2Entity(labelPrintOptionDTO);
            BigDecimal quantity;
            //需要判断是否是最后一个，如果是需要把取余的数量放进去
            if (i == labelSize - 1) {
                if (remainder.compareTo(BigDecimal.ZERO) == 0) {
                    quantity = minQuantity;
                } else {
                    quantity = remainder;
                }

            } else {
                quantity = minQuantity;
            }
            labelBar.setQuantity(quantity);
            labelBar.setSettingId(id);
            labelBar.setFactoryCode(labelPrintOptionDTO.getFactoryCode());
            labelBar.setPrintTime(LocalDateTime.now());
            labelBar.setBoxType(LabelBoxType.INNER.getCode());
            labelBar.setBarcodeSerialNumber(generateNumber(labelBar.getSupplierCode(), LabelBoxType.INNER));
            labelBar.setSort(i + 1);
            labelBar.setBarcodeContent(generateCode(labelBar));
            final FlagConst flag = FlagConst.getFlag(labelPrintOptionDTO.getInnerFlag());
            if (flag.equals(FlagConst.Y)) {
                //表示有内盒，
                LabelBar labelBar1 = labelBarConvert.option2Entity(labelPrintOptionDTO);
                labelBar1.setSettingId(id);
                labelBar1.setPrintTime(LocalDateTime.now());
                labelBar1.setFactoryCode(labelPrintOptionDTO.getFactoryCode());
                labelBar1.setBarcodeSerialNumber(labelBar.getBarcodeSerialNumber());
                labelBar1.setBoxType(LabelBoxType.INNER.getCode());
                //需要判断是否是最后一个，如果是需要把取余的数量放进去
                labelBar1.setQuantity(quantity);
                labelBar1.setSort(i + 1);
                labelBar1.setBarcodeContent(labelBar.getBarcodeContent());
                insertList.add(labelBar1);
            }
            insertList.add(labelBar);
        }
        if (CollUtil.isNotEmpty(insertList)) {
            labelBarService.saveBatch(insertList, 300);
            result.addAll(labelBarConvert.entities2Dtos(insertList));
        }
        return result;
    }

    private List<LabelBarBaseDTO> apiProcessor(Long id, LabelPrintOptionDTO labelPrintOptionDTO) {
        /**
         * 1.需要判断是不是外箱
         */
        final LabelBoxType typeEm = labelPrintOptionDTO.getLabelBoxType();
        final List<LabelBarBaseDTO> baseDTOS = Lists.newArrayList();
        List<LabelBar> insertLists = Lists.newArrayList();
        if (typeEm.getType() == LabelBoxType.OUT.getType()) {
            final BigDecimal fullQuantity = labelPrintOptionDTO.getFullQuantity();
            final int labelSize = labelPrintOptionDTO.getSheets();
            if (labelSize > 2000) {
                throw new DaoException("标签数超过2000个无法打印");
            }
            for (int i = 0; i < labelSize; i++) {
                LabelBar labelBar = labelBarConvert.option2Entity(labelPrintOptionDTO);
                labelBar.setId(IdWorker.getId());
                labelBar.setSettingId(id);
                labelBar.setFactoryCode(labelPrintOptionDTO.getFactoryCode());
                labelBar.setBoxType(LabelBoxType.OUT.getCode());
                labelBar.setBarcodeSerialNumber(generateNumber(labelBar.getSupplierCode(), typeEm));
                labelBar.setQuantity(fullQuantity);
                labelBar.setSort(i + 1);
                labelBar.setPrintTime(LocalDateTime.now());
                labelBar.setBarcodeContent(generateCode(labelBar));
//                labelBarService.save(labelBar);
                baseDTOS.add(labelBarConvert.entity2Dto(labelBar));
                final BigDecimal quantity = labelBar.getQuantity();
                final List<LabelBarBaseDTO> processor = processor(quantity, labelPrintOptionDTO.getMinQuantity(), id,
                        labelBar.getId(), labelPrintOptionDTO, LabelBoxType.INNER, labelBar.getBarcodeContent());
                labelBar.setInnerBoxQuantity(processor.size());
                insertLists.add(labelBar);
                baseDTOS.addAll(processor);
            }
            if (CollUtil.isNotEmpty(insertLists)) {
                labelBarService.saveBatch(insertLists, 300);
            }
            return baseDTOS;
        } else {
            //直接打印内箱
            final List<LabelBarBaseDTO> baseDTOS1 = innerProcessor(id, labelPrintOptionDTO, false);
            return baseDTOS1;
        }
    }

    /**
     * 生成标签
     *
     * @param total               总数
     * @param perBox              每箱数量
     * @param id                  打印设置的主键
     * @param labelPrintOptionDTO 打印配置
     * @param type                类型1:外箱;2:中箱;3:内箱
     * @return
     */
    public List<LabelBarBaseDTO> processor(BigDecimal total, BigDecimal perBox, Long id, Long parentId,
                                           LabelPrintOptionDTO labelPrintOptionDTO, LabelBoxType type,
                                           String parentBarcodeContent)
    {
        //！！！！！！！注意不能使用批量插入，这会导致流水号失效！！！！！！！！！
        //总数%每箱数量生成条码
        BigDecimal remainder = total.remainder(perBox);
        //然后这个余数如果是不等于0就需要把条码数量+1
        int labelSize = total.divide(perBox, 0, BigDecimal.ROUND_DOWN).intValue();
        if (remainder.compareTo(BigDecimal.ZERO) > 0) {
            labelSize = labelSize + 1;
        }
        List<LabelBar> insertList = Lists.newArrayList();
        for (int i = 0; i < labelSize; i++) {
            LabelBar labelBar = labelBarConvert.option2Entity(labelPrintOptionDTO);
            labelBar.setId(IdWorker.getId());
            labelBar.setPrintTime(LocalDateTime.now());
            labelBar.setSettingId(id);
            labelBar.setBoxType(type.getCode());
            labelBar.setFactoryCode(labelPrintOptionDTO.getFactoryCode());
            labelBar.setBarcodeSerialNumber(generateNumber(labelBar.getSupplierCode(), type));
            if (Objects.nonNull(parentId)) {
                labelBar.setParentId(parentId);
            }
            if (StrUtil.isNotEmpty(parentBarcodeContent)) {
                labelBar.setOuterBoxBarcodeContent(parentBarcodeContent);
            }
            BigDecimal quantity;
            //需要判断是否是最后一个，如果是需要把取余的数量放进去
            if (i == labelSize - 1) {
                if (remainder.compareTo(BigDecimal.ZERO) == 0) {
                    quantity = perBox;
                } else {
                    quantity = remainder;
                }

            } else {
                quantity = perBox;
            }
            labelBar.setQuantity(quantity);
            labelBar.setBarcodeContent(generateCode(labelBar));
            labelBar.setSort(i + 1);
            //需要注意如果是内箱需要判断是否打印双份
            if (type.equals(LabelBoxType.INNER)) {
                final FlagConst flag = FlagConst.getFlag(labelPrintOptionDTO.getInnerFlag());
                if (flag.equals(FlagConst.Y)) {
                    //表示有内盒，
                    LabelBar labelBar1 = labelBarConvert.option2Entity(labelPrintOptionDTO);
                    labelBar1.setSettingId(id);
                    labelBar1.setId(IdWorker.getId());
                    labelBar1.setBarcodeSerialNumber(labelBar.getBarcodeSerialNumber());
                    labelBar1.setBoxType(type.getCode());
                    labelBar1.setFactoryCode(labelPrintOptionDTO.getFactoryCode());
                    labelBar1.setPrintTime(LocalDateTime.now());
                    if (Objects.nonNull(parentId)) {
                        labelBar1.setParentId(parentId);
                    }
                    if (StrUtil.isNotEmpty(parentBarcodeContent)) {
                        labelBar1.setOuterBoxBarcodeContent(parentBarcodeContent);
                    }
                    //需要判断是否是最后一个，如果是需要把取余的数量放进去
                    labelBar1.setQuantity(quantity);
                    labelBar1.setSort(i + 1);
                    labelBar1.setBarcodeContent(labelBar.getBarcodeContent());
                    insertList.add(labelBar1);
                }
            }
            insertList.add(labelBar);
        }
        if (CollUtil.isNotEmpty(insertList)) {
            labelBarService.saveBatch(insertList, 300);
        }
        return labelBarConvert.entities2Dtos(insertList);
    }

    public String generateNumber(String supplierCode, LabelBoxType type) {
        return StrUtil.format("S{}{}{}{}", supplierCode, DateUtils.formatYYMMDD(LocalDate.now()), type.getCode(),
                sequenceClient.getBarCodeSequenceNo(SequenceConst.BAR_CODE.getCode(), 6, false));
        // sequenceClient.createSequenceNo(SequenceConst.BAR_CODE.getCode()));
    }

    /**
     * @param labelBar
     * @return
     */
    public String generateCode(LabelBar labelBar) {
        return StrUtil.format(
                "{materialCode} {{quantity} {{unit} {{supplierCode} {{dc} {  {{orderNumber} {  {{barcodeSerialNumber}" +
                        " {{lot" +
                        "} {{manufacturer} {{mpn} {{version}", BeanUtil.beanToMap(labelBar, false, false));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @DS(SLAVE)
    public void importLabel(List<LabelBarImportDTO> data) {
        //导入的是内外一起的，需要做分组处理
        final Map<String, List<LabelBarImportDTO>> collect = data.stream()
                .collect(Collectors.groupingBy(LabelBarImportDTO::getOut));
        List<LabelBar> insertList = Lists.newArrayList();
        int i = 1;
        Map<String, SupplierDTO> supplierCache = Maps.newHashMap();
        Map<String, String> defaultStockCache = Maps.newHashMap();
        String printer = LoginUtil.getAccount() + StrUtil.DASHED + LoginUtil.getName();
        final String importCode = IdWorker.get32UUID();
        //只允许导入单个供应商的数据
        String importSupplierCode = "";
        if (LoginUtil.isSupplier()) {
            importSupplierCode = LoginUtil.getSupplierCode();
        }
        LocalDateTime now = LocalDateTime.now();
        Map<String, String> factoryCache = Maps.newHashMap();
        for (String s : collect.keySet()) {
            final List<LabelBarImportDTO> labelBarImportDTOS = collect.get(s);
            //获取第一个的msl
            final LabelBarImportDTO labelBarImportDTO = labelBarImportDTOS.get(0);
            final LabelBar out = analysis(s);
            out.setPrintTime(now);
            out.setImportCode(importCode);
            final String supplierCode = out.getSupplierCode();
            if (StrUtil.isEmpty(importSupplierCode)) {
                importSupplierCode = supplierCode;
            }
            if (!importSupplierCode.equals(supplierCode)) {
                throw new DaoException("只允许导入同一个供应商的标签!");
            }
            if (!supplierCache.containsKey(supplierCode)) {
                final SupplierDTO supplierByCode = supplierClient.getSupplierByCode(supplierCode);
                supplierCache.put(supplierCode, supplierByCode);
            }

            final String key = out.getOrderNumber() + StrUtil.DASHED + out.getMaterialCode();
            final PurchaseOrderDetail orderDetail1 = purchaseOrderDetailService.getOne(
                    new LambdaQueryWrapper<PurchaseOrderDetail>()
                            .eq(PurchaseOrderDetail::getOrderCode, out.getOrderNumber())
                            .eq(PurchaseOrderDetail::getMaterialCode, out.getMaterialCode()), false);
            if (Objects.isNull(orderDetail1)) {
                throw new DaoException("订单信息不存在!");
            }
            factoryCache.put(key, orderDetail1.getFactoryCode());
            if (!defaultStockCache.containsKey(key)) {
                defaultStockCache.put(key, orderDetail1.getDefaultStock());
            }
            final String defaultStock = defaultStockCache.get(key);
            out.setDefaultStock(defaultStock);
            final SupplierDTO supplierDTO = supplierCache.get(supplierCode);
            out.setMsl(labelBarImportDTO.getMsl());
            out.setBarcodeContent(s);
            out.setSupplierName(supplierDTO.getSupplierName());
            out.setInnerBoxQuantity(labelBarImportDTOS.size());
            out.setId(IdWorker.getId());
            out.setSort(i);
            out.setPrinter(printer);
            out.setBoxType(LabelBoxType.OUT.getCode());
            out.setFactoryCode(factoryCache.get(key));
            out.setOrigin(StringUtils.isEmpty(labelBarImportDTO.getOrigin()) ? "" : labelBarImportDTO.getOrigin());
            insertList.add(out);
            int ii = 1;
            BigDecimal innerBoxQuantity = BigDecimal.ZERO;
            for (LabelBarImportDTO barImportDTO : labelBarImportDTOS) {
                final LabelBar inner = analysis(barImportDTO.getInner());
                inner.setImportCode(importCode);
                inner.setPrintTime(now);
                inner.setBarcodeContent(barImportDTO.getInner());
                inner.setOuterBoxBarcodeContent(s);
                innerBoxQuantity = innerBoxQuantity.add(inner.getQuantity());
                final String supplierCode1 = inner.getSupplierCode();
                if (!supplierCache.containsKey(supplierCode1)) {
                    final SupplierDTO supplierByCode = supplierClient.getSupplierByCode(supplierCode1);
                    supplierCache.put(supplierCode1, supplierByCode);
                }
                if (!importSupplierCode.equals(supplierCode1)) {
                    throw new DaoException("只允许导入同一个供应商的标签!");
                }
                //校验外箱和内箱料号是否一致
                if (!out.getMaterialCode().equals(inner.getMaterialCode())) {
                    throw new DaoException("外箱和内箱料号不一致!");
                }
                if (!out.getMpn().equals(inner.getMpn())) {
                    throw new DaoException("外箱和内箱MPN不一致!");
                }
                if (!out.getManufacturer().equals(inner.getManufacturer())) {
                    throw new DaoException("外箱和内箱制造商不一致!");
                }
                final String key1 = out.getOrderNumber() + StrUtil.DASHED + out.getMaterialCode();
                if (!defaultStockCache.containsKey(key1)) {
                    final PurchaseOrderDetail orderDetail = purchaseOrderDetailService.getOne(
                            new LambdaQueryWrapper<PurchaseOrderDetail>()
                                    .eq(PurchaseOrderDetail::getOrderCode, inner.getOrderNumber())
                                    .eq(PurchaseOrderDetail::getMaterialCode, inner.getMaterialCode()), false);
                    if (Objects.isNull(orderDetail)) {
                        throw new DaoException("订单信息不存在!");
                    }
                    defaultStockCache.put(key1, orderDetail.getDefaultStock());
                }
                inner.setFactoryCode(factoryCache.get(key1));
                final String defaultStock1 = defaultStockCache.get(key1);
                inner.setDefaultStock(defaultStock1);
                final SupplierDTO supplierDTO1 = supplierCache.get(supplierCode);
                inner.setSupplierName(supplierDTO1.getSupplierName());
                inner.setParentId(out.getId());
                inner.setMsl(barImportDTO.getMsl());
                inner.setOuterBoxBarcodeContent(s);
                inner.setSort(ii);
                inner.setPrinter(printer);
                inner.setBoxType(LabelBoxType.INNER.getCode());
                inner.setOrigin(StringUtils.isEmpty(barImportDTO.getOrigin()) ? "" : barImportDTO.getOrigin());;
                ii++;
                insertList.add(inner);
            }
            if (labelBarImportDTOS.size() > 0) {
                //需要做验证外箱和内箱的数量总和保持一致
                if (innerBoxQuantity.compareTo(out.getQuantity()) != 0) {
                    throw new DaoException("外箱和内箱的数量总和不一致!");
                }
            }
            i++;

        }
        labelBarService.saveBatch(insertList);
        final Set<Long> ids = insertList.stream().map(LabelBar::getId).collect(Collectors.toSet());
        final ApiResponse apiResponse = push(insertList, null);
        if (apiResponse.getCode().equals(RestCode.MES_SUCCESS.getCode())) {
            final LambdaUpdateWrapper<LabelBar> updateWrapper1 = new LambdaUpdateWrapper<LabelBar>()
                    .set(LabelBar::getStatus, FlagConst.Y.getCode())
                    .in(LabelBar::getId, ids);
            labelBarService.update(updateWrapper1);
        } else {
            final LambdaUpdateWrapper<LabelBar> updateWrapper1 = new LambdaUpdateWrapper<LabelBar>()
                    .set(LabelBar::getStatus, FlagConst.N.getCode())
                    .in(LabelBar::getId, ids);
            labelBarService.update(updateWrapper1);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(String ids) {
        final List<String> split = StrUtil.split(ids, StrUtil.COMMA);
        //这个全部是外箱，需要内箱和外箱全部删除
        labelBarService.removeByIds(split);
        labelBarService.remove(new LambdaQueryWrapper<LabelBar>()
                .in(LabelBar::getParentId, split));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void removeByBatch(Long id) {
        final LabelBar byId = labelBarService.getById(id);
        if (Objects.isNull(byId)) {
            throw new DaoException("标签不存在!");
        }
        final String importCode = byId.getImportCode();
        if (StrUtil.isEmpty(importCode)) {
            throw new DaoException("批导标记不存在!");
        }
        labelBarService.remove(new LambdaQueryWrapper<>(LabelBar.class)
                .eq(LabelBar::getImportCode, importCode));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String rePushByBatch(Long id) {
        final LabelBar byId = labelBarService.getById(id);
        if (Objects.isNull(byId)) {
            throw new DaoException("标签不存在!");
        }
        final String importCode = byId.getImportCode();
        if (StrUtil.isEmpty(importCode)) {
            throw new DaoException("批导标记不存在!");
        }
        final LambdaUpdateWrapper<LabelBar> updateWrapper = new LambdaUpdateWrapper<LabelBar>()
                .set(LabelBar::getPrintTime, LocalDateTime.now())
                .eq(LabelBar::getImportCode, importCode);
        if (StpUtil.isLogin()) {
            updateWrapper.set(LabelBar::getPrinter, LoginUtil.getAccount() + StrUtil.DASHED + LoginUtil.getName());
        }
        labelBarService.update(updateWrapper);
        final List<LabelBar> list = labelBarService.list(
                new LambdaQueryWrapper<LabelBar>().ne(LabelBar::getBoxType, LabelBoxType.MIDDLE.getCode())
                        .eq(LabelBar::getImportCode, importCode));
        final ApiResponse apiResponse = push(list, null);
        if (apiResponse.getCode().equals(RestCode.MES_SUCCESS.getCode())) {
            final LambdaUpdateWrapper<LabelBar> updateWrapper1 = new LambdaUpdateWrapper<LabelBar>()
                    .set(LabelBar::getStatus, FlagConst.Y.getCode())
                    .eq(LabelBar::getImportCode, importCode);
            labelBarService.update(updateWrapper1);
            return StrUtil.EMPTY;
        } else {
            final LambdaUpdateWrapper<LabelBar> updateWrapper1 = new LambdaUpdateWrapper<LabelBar>()
                    .set(LabelBar::getStatus, FlagConst.N.getCode())
                    .eq(LabelBar::getImportCode, importCode);
            labelBarService.update(updateWrapper1);
            return apiResponse.getMsg();
        }
    }

    @Override
    public void exportPDF(String data, HttpServletResponse response) {
        //创建pdf空文件
        File file = fileClient.createBarFile(
                IdWorker.getIdStr() + ".pdf");
        try {
            String html = "<html>\n" +
                    "<head>\n" +
                    "     <meta charset=\"utf-8\">\n" +
                    "     <link href=\"" + sysProp.getHostUrl() +
                    "/static/plugins/hiprint-v2.5.3/css/hiprint.css\" rel=\"stylesheet\">\n" +
                    "     <link href=\"" + sysProp.getHostUrl() +
                    "/static/plugins/hiprint-v2.5.3/css/print-lock.css\" " +
                    "rel=\"stylesheet\">\n" +
                    "</head>\n" +
                    "<body>\n" + data +
                    "</body>\n" +
                    "</html>";
            PDFUtil.generateBarPdfFromHtml(html, file.getPath());
            // 设置响应头
            FileInputStream fis = new FileInputStream(file);
            response.setContentType("application/pdf");
            response.setHeader("Content-Disposition", "attachment;filename=" + file.getName());
            // 将文件流复制到响应输出流
            OutputStream os = response.getOutputStream();
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = fis.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }

            // 关闭文件输入流和响应输出流
            fis.close();
            os.flush();
            os.close();
        } catch (
                IOException e) {
            e.printStackTrace();
        } finally {
            // 删除文件
            if (file.exists() && file.isFile()) {
                file.delete();
            }
        }
    }

    @Override
    public List<LabelBarBaseDTO> getLabelBarsBySettingIds(String ids) {
        if (StrUtil.isEmpty(ids)) {
            return Lists.newArrayList();
        }
        final List<String> idArray = StrUtil.split(ids, StrUtil.COMMA);
        //查询标签
        final List<LabelBar> list = labelBarService.list(
                new LambdaQueryWrapper<LabelBar>().in(LabelBar::getSettingId, idArray));
        //需要处理打印次数
        LambdaUpdateWrapper<LabelBar> updateWrapper = new LambdaUpdateWrapper<LabelBar>();
        updateWrapper.setSql("print_size=print_size+1");
        updateWrapper.in(LabelBar::getId, idArray);
        labelBarService.update(updateWrapper);
        return getLabelBarBaseDTOS(list, true);
    }

    //物料产地
    @Override
    public boolean originIsRequired(String materialCode) {
        if(StringUtils.isEmpty(materialCode)){
            return false;
        }
        List<String> materiaList = materialOriginList.stream().map(MaterialOriginDTO::getMaterialCode).collect(Collectors.toList());
        if(materiaList.contains(materialCode)){
            return true;
        }
        return false;
    }

    /**
     * 更新打印次数
     * @param id 标签打印配置项ID
     */
    @Override
    public void updatePrintSize(Long id) {
        final LambdaUpdateWrapper<LabelPrintSetting> updateWrapper = new LambdaUpdateWrapper<LabelPrintSetting>()
                .setSql("print_size = print_size + 1")
                .eq(LabelPrintSetting::getId, id);
        labelPrintSettingService.update(updateWrapper);
    }

    public static LabelBar analysis(String barContent) {
        String template =
                "{materialCode} {{quantity} {{unit} {{supplierCode} {{dc} {  {{orderNumber} {  {{barcodeSerialNumber}" +
                        " {{lot} {{manufacturer} {{mpn} {{version}";
        String[] templateParts = template.split(" \\{");
        final String[] actualParts = barContent.split(" \\{");
        Map<String, String> valuesMap = new HashMap<>();
        for (int i = 0; i < templateParts.length; i++) {
            String key = templateParts[i].replaceAll("[{}]", "").trim(); // Remove curly braces and trim
            String value = actualParts[i].trim();
            valuesMap.put(key, value);
        }

        LabelBar labelBar = new LabelBar();
        BeanUtil.copyProperties(valuesMap, labelBar);
        return labelBar;
    }

    /**
     * 按条件获取数据
     * @param query 查询条件
     * @return List<LabelBarBaseDTO>
     */
    @Nullable
    private List<LabelBarBaseDTO> getQueryResult(LabelQuery query) {
        final LambdaQueryWrapper<LabelBar> queryWrapper = new LambdaQueryWrapper<LabelBar>()
                .like(StrUtil.isNotBlank(query.getSupplierName()), LabelBar::getSupplierName,
                        SqlUtils.concatLike(query.getSupplierName(), SqlLike.DEFAULT))
                .like(StrUtil.isNotBlank(query.getSupplierCode()), LabelBar::getSupplierCode,
                        SqlUtils.concatLike(query.getSupplierCode(), SqlLike.DEFAULT))
                .like(StrUtil.isNotBlank(query.getOrderCode()), LabelBar::getOrderNumber,
                        SqlUtils.concatLike(query.getOrderCode(), SqlLike.DEFAULT))
                .like(StrUtil.isNotBlank(query.getMaterialCode()), LabelBar::getMaterialCode,
                        SqlUtils.concatLike(query.getMaterialCode(), SqlLike.DEFAULT))
                .like(StrUtil.isNotBlank(query.getImportCode()), LabelBar::getImportCode,
                        SqlUtils.concatLike(query.getImportCode(), SqlLike.DEFAULT))
                .like(StrUtil.isNotBlank(query.getBarCode()), LabelBar::getBarcodeSerialNumber,
                        SqlUtils.concatLike(query.getBarCode(), SqlLike.DEFAULT))
                .eq(StrUtil.isNotBlank(query.getStatus()), LabelBar::getStatus, query.getStatus());
        if (Objects.isNull(query.getType())) {
        } else if (query.getType() == 1) {
            queryWrapper.isNull(LabelBar::getSettingId);
        } else if (query.getType() == 0) {
            queryWrapper.isNotNull(LabelBar::getSettingId);
        }
        if (StrUtil.isNotEmpty(query.getIds())) {
            queryWrapper.in(LabelBar::getSettingId, query.getIds().split(","));
        }
        queryWrapper.orderByDesc(LabelBar::getSettingId).orderByDesc(LabelBar::getImportCode)
                .orderByDesc(LabelBar::getSort);
        if (LoginUtil.isSupplier()) {
            queryWrapper.eq(LabelBar::getSupplierCode, LoginUtil.getSupplierCode());
            //判断当前的供应商是否有角色编码
            final List<String> roleCodes = LoginUtil.getRoleCodes();
            //判断是否存在特殊角色 special 如果存在并且没有输入查询条件就返回空数据
            if (roleCodes.contains(DictCode.SUPPLIER_SPECIAL_ROLE)) {
                final Map<String, Object> map = BeanUtil.beanToMap(query, false, false);
                Set<String> keySet = Sets.newHashSet();
//                keySet.add("orderCode");
//                keySet.add("barCode");
                keySet.add("materialCode");
//                keySet.add("importCode");
//                keySet.add("type");
                boolean isOk = keySet.stream().allMatch(key -> {
                    if (Objects.nonNull(map.get(key))) {
                        if (Strings.isNullOrEmpty(map.get(key).toString())) {
                            return true;
                        } else {
                            return false;
                        }
                    } else {
                        return true;
                    }
                });
                if (isOk) {
                    return null;
                }
            }
        }
        final List<LabelBar> list = labelBarService.list(null, queryWrapper);
        List<LabelBarBaseDTO> labelBarBaseDTOS = labelBarConvert.entities2Dtos(list);
        modelAggregationService.processRelationAggregation(labelBarBaseDTOS);
        labelBarBaseDTOS = getLabelBarBaseDTOS(list, false);
        return labelBarBaseDTOS;
    }
}