package com.sy.coinage.workshop.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.sy.coinage.core.exception.RetException;
import com.sy.coinage.core.redis.RedisService;
import com.sy.coinage.sys.model.Dict;
import com.sy.coinage.sys.service.IDictService;
import com.sy.coinage.workshop.req.DeliveryPlanReq;
import com.sy.coinage.workshop.service.*;
import com.sy.coinage.workshop.vo.BackVO;
import org.apache.commons.lang3.ArraySorter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.sy.coinage.workshop.dao.DeliveryPlanDao;
import com.sy.coinage.workshop.model.*;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * -ServiceImpl
 */
@Service
public class DeliveryPlanServiceImpl extends ServiceImpl<DeliveryPlanDao, DeliveryPlan> implements IDeliveryPlanService {
    @Autowired
    private IDeliveryPlanDetailService deliveryPlanDetailService;

    @Autowired
    private IProductService productService;

    @Autowired
    private IDeliveryJumpService deliveryJumpService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private IDictService dictService;

    @Autowired
    private IProductPlanService productPlanService;

    @Override
    public boolean validSecurities(Integer getSecuritiesId, Integer id) {
        LambdaQueryWrapper<DeliveryPlan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.lt(id != null && id > 0, DeliveryPlan::getId, id);
        queryWrapper.eq(DeliveryPlan::getSecuritiesId, getSecuritiesId);
        long count = this.count(queryWrapper);
        if (count > 0) {
            return true;
        }
        return false;
    }

    @Override
    @Transactional
    public DeliveryPlan getModel(DeliveryPlan model, List<DeliveryPlanDetail> detail, boolean type, Integer oldId) {
        if (StringUtils.isNotBlank(model.getAddressId())) {
            StringBuffer sb = new StringBuffer();
            String[] str = model.getAddressId().split(",");
            for (int i = 0; i < str.length; i++) {
                Dict dict = this.dictService.getByVal("addressOpt", Integer.parseInt(str[i]), null);
                if (i > 0) {
                    sb.append(",");
                }
                sb.append(dict.getName());
            }
            model.setAddressCn(sb.toString());
        }
        Product product = this.productService.getByCache(model.getSecuritiesId());
        if (model.getQty() * 10000 % product.getAmountPerBox() > 0) {
            throw new RetException("数量(万枚)不能转换为箱数");
        }
        model.setTotalBoxNum(model.getQty() * 10000 / product.getAmountPerBox());
        model.setSecurities(product.getSecurities());
        model.setTotalBoxNum(model.getQty() * 10000 / product.getAmountPerBox());
        // 按新号排列 1是 2否
        boolean b = false;
        if (model.getResetSerialNumber().intValue() == 2) {
            b = true;
        }
//        boolean b = this.validSecurities(model.getSecuritiesId(), model.getId());
        // 如果存在品名发货计划
        DeliveryPlan lastModel = null;
        if (b) {
            // 获取最后的发货计划
            lastModel = this.getLast(model.getSecuritiesId(), model.getId(), oldId);
            if (lastModel == null) {
                throw new RetException("不存在相同品名的发货计划, 必须按新号排列");
            }
            // 最后的发货计划 尾拍箱数
            Integer lastBox = (lastModel.getTotalBoxNum() - lastModel.getFirstBoxNum()) % product.getBoxAmount();
            // 跳号数量
            Integer jump = this.getJumpQty(lastModel);
            // 样式 佰21伍-003-0741
            String[] firstCrown = lastModel.getFirstCrown().split("-");
            Integer t = Integer.parseInt(firstCrown[2]) + lastModel.getTotalBoxNum() + jump * product.getBoxAmount();
            if (t > 5000) {
                model.setFirstCrown(firstCrown[0] + "-" + String.format("%03d", Integer.parseInt(firstCrown[1]) + 1) + "-" + String.format("%04d", t - 5000));
            } else {
                model.setFirstCrown(firstCrown[0] + "-" + firstCrown[1] + "-" + String.format("%04d", t));
            }
            String[] serialNumberArr = lastModel.getFirstSerialNumber().split("-");
            // TODO 考虑上一个计划是否跳号
            if (lastBox > 0 && lastModel.getFirstBoxNum() < product.getBoxAmount()) {
                jump = jump + 1;
            } else if (lastModel.getFirstBoxNum() < product.getBoxAmount()) {
                jump = jump + 1;
            } else if (lastBox == 0) {
                jump = jump + 1;
            }
            String serialNumber = serialNumberArr[0] + "-" + String.format("%04d", Integer.parseInt(serialNumberArr[1]) + lastModel.getPlanPar() + jump);
            model.setFirstSerialNumber(serialNumber);
            model.setFirstBoxNum(product.getBoxAmount() - lastBox);
        }
        String[] startIdsArr = null;
        String[] endIdsArr = null;
        // 修改时跳号
        if (StringUtils.isNotBlank(model.getStartJumpIds())) {
            startIdsArr = model.getStartJumpIds().split(",");
            String sb = this.getJumpStr(startIdsArr);
            // 开始跳号字符串
            model.setStartJumpStr(sb);
        }
        if (StringUtils.isNotBlank(model.getEndJumpIds())) {
            endIdsArr = model.getEndJumpIds().split(",");
            String sb = this.getJumpStr(endIdsArr);
            // 结束跳号字符串
            model.setEndJumpStr(sb);
        }
        if (startIdsArr != null && endIdsArr != null) {
            for (int i = 0; i < startIdsArr.length; i++) {
                for (int i1 = 0; i1 < endIdsArr.length; i1++) {
                    if (startIdsArr[i].equals(endIdsArr[i1])) {
                        throw new RetException("开始跳号与结束跳号存在相同拍号");
                    }
                }
            }
        }
        Integer par = model.getTotalBoxNum() / product.getBoxAmount();
        if (product.getBoxAmount() > model.getFirstBoxNum()) {
            par = (model.getTotalBoxNum() - model.getFirstBoxNum()) / product.getBoxAmount();
        }
        // 整拍数
        model.setPlanPar(par);
        Integer lastBox = (model.getTotalBoxNum() - model.getFirstBoxNum()) % product.getBoxAmount();
        if (model.getFirstBoxNum() < product.getBoxAmount()) {
            // 零箱数
            model.setPlanBoxNum(model.getFirstBoxNum() + lastBox);
        } else {
            model.setPlanBoxNum(lastBox);
        }
        // 拼接 冠字号范围和拍号
        this.getParCrown(type, product, model, detail);
        model.setSecurities(product.getSecurities());
        return model;
    }

    @Override
    @Transactional
    public void updateModel(DeliveryPlanReq req) {
        DeliveryPlan model = this.getById(req.getId());
        // 验证是否相同租户
        BeanUtils.copyProperties(req, model);
        model.setAddressId(req.getAddressIds());
        if (StringUtils.isNotBlank(req.getStartIds())) {
            model.setStartJumpIds(req.getStartIds());
        } else {
            model.setStartJumpIds(null);
            model.setStartJumpStr(null);
        }
        if (StringUtils.isNotBlank(req.getEndIds())) {
            model.setEndJumpIds(req.getEndIds());
        } else {
            model.setEndJumpIds(null);
            model.setEndJumpStr(null);
        }
        List<DeliveryPlanDetail> detail = new ArrayList<>();
        model = this.getModel(model, detail, true, null);
        this.updateById(model);
        LambdaQueryWrapper<DeliveryPlanDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DeliveryPlanDetail::getPlanId, model.getId());
        deliveryPlanDetailService.remove(queryWrapper);
        final Integer id = model.getId();
        detail.stream().forEach(item -> {
            item.setPlanId(id);
        });
        this.deliveryPlanDetailService.saveBatch(detail);
        // 计划修改后 变更后续计划的冠字号和拍号
        this.updateAfter(model);
    }

    @Override
    @Transactional
    public void updateModelNew(DeliveryPlanReq req) {
        DeliveryPlan model = this.getById(req.getId());
        boolean updateOrderNo = false;
        Integer oldId = null;
        if (StringUtils.isNotBlank(req.getNewOrderNo())) {
            String[] orderNoArr = model.getOrderNo().split("-");
            Integer orderNo = Integer.parseInt(orderNoArr[1]);
            String[] newOrderNoArr = req.getNewOrderNo().split("-");
            Integer newOrderNo = Integer.parseInt(newOrderNoArr[1]);
            if (newOrderNo >= orderNo) {
                throw new RetException("新发货单号必须小于原发货单号");
            }
            updateOrderNo = true;
            oldId = this.getIdByOrderNo(req.getNewOrderNo(), model.getSecuritiesId());

        }
        // 验证是否修改了参数影响拍号冠字号
        boolean isDelete = this.validParam(req, model);
        // 验证是否相同租户
        BeanUtils.copyProperties(req, model);
        model.setAddressId(req.getAddressIds());
        if (updateOrderNo) {
            model.setOrderNo(req.getNewOrderNo());
        }
        if (StringUtils.isNotBlank(req.getStartIds())) {
            model.setStartJumpIds(req.getStartIds());
        } else {
            model.setStartJumpIds(null);
            model.setStartJumpStr(null);
        }
        if (StringUtils.isNotBlank(req.getEndIds())) {
            model.setEndJumpIds(req.getEndIds());
        } else {
            model.setEndJumpIds(null);
            model.setEndJumpStr(null);
        }
        List<DeliveryPlanDetail> detail = new ArrayList<>();
        model = this.getModel(model, detail, true, oldId);
        this.updateById(model);
        LambdaQueryWrapper<DeliveryPlanDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DeliveryPlanDetail::getPlanId, model.getId());
        deliveryPlanDetailService.remove(queryWrapper);
        final Integer id = model.getId();
        detail.stream().forEach(item -> {
            item.setPlanId(id);
        });
        this.deliveryPlanDetailService.saveBatch(detail);
        if (updateOrderNo) {
            LambdaQueryWrapper<DeliveryPlan> updateWrapper = new LambdaQueryWrapper<>();
            updateWrapper.eq(DeliveryPlan::getSecuritiesId, model.getSecuritiesId());
            updateWrapper.ge(DeliveryPlan::getId, oldId);
            updateWrapper.ne(DeliveryPlan::getId, model.getId());
            this.remove(updateWrapper);
            LambdaQueryWrapper<DeliveryPlanDetail> updateWrapper1 = new LambdaQueryWrapper<>();
            updateWrapper1.ge(DeliveryPlanDetail::getPlanId, oldId);
            updateWrapper1.ne(DeliveryPlanDetail::getPlanId, model.getId());
            this.deliveryPlanDetailService.remove(updateWrapper1);
            this.updateOutOrderNo(model.getSecurities(), model.getOrderNo());
            return;
        }
        if (isDelete) {
            LambdaQueryWrapper<DeliveryPlan> updateWrapper = new LambdaQueryWrapper<>();
            updateWrapper.eq(DeliveryPlan::getSecuritiesId, model.getSecuritiesId());
            updateWrapper.gt(DeliveryPlan::getId, model.getId());
            this.remove(updateWrapper);
            LambdaQueryWrapper<DeliveryPlanDetail> updateWrapper1 = new LambdaQueryWrapper<>();
            updateWrapper1.gt(DeliveryPlanDetail::getPlanId, model.getId());
            this.deliveryPlanDetailService.remove(updateWrapper1);
            this.updateOutOrderNo(model.getSecurities(), model.getOrderNo());
        }
    }

    private Integer getIdByOrderNo(String newOrderNo, Integer securitiesId) {
        LambdaQueryWrapper<DeliveryPlan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DeliveryPlan::getSecuritiesId, securitiesId);
        queryWrapper.eq(DeliveryPlan::getOrderNo, newOrderNo);
        DeliveryPlan model = this.getOne(queryWrapper);
        if (model == null) {
            throw new RetException("新发货单号计划不存在");
        }
        // 验证是否有发货的计划
        queryWrapper.clear();
        queryWrapper.eq(DeliveryPlan::getSecuritiesId, securitiesId);
        queryWrapper.ge(DeliveryPlan::getId, model.getId());
        queryWrapper.eq(DeliveryPlan::getStatus, 2);
        long count = this.count(queryWrapper);
        if (count > 0) {
            throw new RetException("新发货单号计划之后存在已发货计划");
        }
        return model.getId();
    }

    private boolean validParam(DeliveryPlanReq req, DeliveryPlan model) {
        // 修改了 按新号排列 删除后续计划
        if (model.getResetSerialNumber().intValue() != req.getResetSerialNumber().intValue()) {
            return true;
        }
        // 修改了 首箱冠字号 删除后续计划
        if (!model.getFirstCrown().equals(req.getFirstCrown())) {
            return true;
        }
        // 修改了 首拍编号 删除后续计划
        if (!model.getFirstSerialNumber().equals(req.getFirstSerialNumber())) {
            return true;
        }
        // 修改了 首拍箱数 删除后续计划
        if (model.getFirstBoxNum().intValue() != req.getFirstBoxNum().intValue()) {
            return true;
        }
        // 修改了 数量 删除后续计划
        if (model.getQty().intValue() != req.getQty().intValue()) {
            return true;
        }
        // 修改了 开始跳号 删除后续计划
        if (StringUtils.isBlank(model.getStartJumpIds()) && StringUtils.isNotBlank(req.getStartIds())) {
            return true;
        } else if (StringUtils.isNotBlank(model.getStartJumpIds()) && StringUtils.isBlank(req.getStartIds())) {
            return true;
        } else if (StringUtils.isNotBlank(model.getStartJumpIds()) && StringUtils.isNotBlank(req.getStartIds())
                && !model.getStartJumpIds().equals(req.getStartIds())) {
            return true;
        }
        // 修改了 结束跳号 删除后续计划
        if (StringUtils.isBlank(model.getEndJumpIds()) && StringUtils.isNotBlank(req.getEndIds())) {
            return true;
        } else if (StringUtils.isNotBlank(model.getEndJumpIds()) && StringUtils.isBlank(req.getEndIds())) {
            return true;
        } else if (StringUtils.isNotBlank(model.getEndJumpIds()) && StringUtils.isNotBlank(req.getEndIds())
                && !model.getEndJumpIds().equals(req.getEndIds())) {
            return true;
        }
        // 修改了 首箱冠字号 删除后续计划
        return false;
    }

    private void updateAfter(DeliveryPlan updateModel) {
        LambdaQueryWrapper<DeliveryPlan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.gt(DeliveryPlan::getId, updateModel.getId());
        queryWrapper.eq(DeliveryPlan::getSecuritiesId, updateModel.getSecuritiesId());
        queryWrapper.eq(DeliveryPlan::getStatus, 2);
        long count = this.count(queryWrapper);
        if (count > 0) {
            throw new RetException("后续计划存在已发货计划，不能修改");
        }
        queryWrapper.clear();
        queryWrapper.gt(DeliveryPlan::getId, updateModel.getId());
        queryWrapper.eq(DeliveryPlan::getSecuritiesId, updateModel.getSecuritiesId());
        queryWrapper.orderByAsc(DeliveryPlan::getOrderNo);
        List<DeliveryPlan> list = this.list(queryWrapper);
        if (list != null && list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                DeliveryPlan model = list.get(i);
                if (model.getResetSerialNumber().intValue() == 1) {
                    break;
                }
                List<DeliveryPlanDetail> detail = new ArrayList<>();
                model = this.getModel(model, detail, true, null);
                this.updateById(model);
                LambdaQueryWrapper<DeliveryPlanDetail> queryWrapper1 = new LambdaQueryWrapper<>();
                queryWrapper1.eq(DeliveryPlanDetail::getPlanId, model.getId());
                deliveryPlanDetailService.remove(queryWrapper1);
                final Integer id = model.getId();
                detail.stream().forEach(item2 -> {
                    item2.setPlanId(id);
                });
                this.deliveryPlanDetailService.saveBatch(detail);
            }
            ;
        }
    }

    @Override
    @Transactional
    public void addModel(DeliveryPlanReq req) {
        DeliveryPlan model = new DeliveryPlan();
        BeanUtils.copyProperties(req, model);
        model.setAddressId(req.getAddressIds());
        List<DeliveryPlanDetail> detail = new ArrayList<>();
        model = this.getModel(model, detail, false, null);
        // 发货单状态  1:初始化 2 已发货
        model.setStatus(1);
        model.setOrderNo(this.creadeOutOrderNo(model.getSecurities()));
        this.save(model);
        final Integer id = model.getId();
        detail.stream().forEach(item -> {
            item.setPlanId(id);
        });
        this.deliveryPlanDetailService.saveBatch(detail);
        // 单号增加
        this.addOutOrderNo(model.getSecurities());
    }

    @Override
    public void updateCrown(Integer id) {
        DeliveryPlan model = this.getById(id);
        model.setStartJumpIds(null);
        model.setStartJumpStr(null);
        model.setEndJumpIds(null);
        model.setEndJumpStr(null);
        model.setResetSerialNumber(2);
        List<DeliveryPlanDetail> detail = new ArrayList<>();
        model = this.getModel(model, detail, true, null);
        this.updateById(model);
        LambdaQueryWrapper<DeliveryPlanDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DeliveryPlanDetail::getPlanId, model.getId());
        deliveryPlanDetailService.remove(queryWrapper);
//        final Integer planId = model.getId();
        detail.stream().forEach(item -> {
            item.setPlanId(id);
        });
        this.deliveryPlanDetailService.saveBatch(detail);
    }


    private String getParCrown(boolean type, Product product, DeliveryPlan model, List<DeliveryPlanDetail> detailList) {
        // 尾拍箱数不是整拍
        Integer lastBox = (model.getTotalBoxNum() - model.getFirstBoxNum()) % product.getBoxAmount();
        // 拍号字符串
        StringBuffer parSb = new StringBuffer();
        // 冠字号范围字符串
        StringBuffer crownSb = new StringBuffer();
        // 首拍编号
        String[] serialNumberArr = model.getFirstSerialNumber().split("-");
        // 首拍编号后尾数
        Integer snNumber = Integer.parseInt(serialNumberArr[1]);
        // 总拍数
        Integer totalPar = model.getPlanPar();
        // 拼结束跳号，方法内已拼接冠字号/拍号 返回结束跳号总拍数
        Integer endParQty = this.getEndjumpPar(product, parSb, model.getEndJumpIds(), detailList, type, crownSb);
        // 总拍数 - 结束跳号总拍数
        totalPar = totalPar - endParQty;
        // 拼接首拍
//        BackVO backVO = new BackVO();
        // 样式 佰21伍-003-0741
        String[] crownArr = model.getFirstCrown().split("-");
        // 冠字号前缀
        String crownPrefix = crownArr[0];
        // 冠字号中间编号字符串
        String lastCrownBetweenStr = crownArr[1];
        // 冠字号中间编号数
//        Integer lastCrownBetweenNum = Integer.parseInt(crownArr[1]);
        // 冠字号最尾编号数
        Integer lastCrownNum = Integer.parseInt(crownArr[2]);
//        backVO.setLastSerialNumber(snNumber);
//        backVO.setLastCrown(lastCrownNum);
//        backVO.setNewCrownStr(crownArr[1]);
        // 首箱是否非整拍
        boolean first = product.getBoxAmount() > model.getFirstBoxNum();
        if (first) {
            parSb.append("<span class='red'>");
            parSb.append(model.getFirstSerialNumber() + "(后" + model.getFirstBoxNum() + "箱)");
            parSb.append("</span>");
            parSb.append(" ");
            snNumber = snNumber + 1;
//            backVO.setLastSerialNumber(snNumber);
            String lastCrown = crownArr[1] + "-" + crownArr[2];
            BackVO detailVO = this.addDetailList(1, 3, serialNumberArr[0], snNumber, model.getFirstBoxNum(), detailList, crownPrefix, lastCrown);
            lastCrownNum = detailVO.getLastCrown();
            lastCrownBetweenStr = detailVO.getNewCrownStr();
        }
//        String lastCrown = lastCrownBetweenStr + "-" + String.format("%04d", lastCrownNum);
        //==========
        // 是否跳号
        boolean isJump = false;
        Integer par = 0;
//        String[] crownArrJump = lastCrown.split("-");
//        Integer lastCrownNumJump = Integer.parseInt(crownArr[2]);
//        backVO.setNewCrownStr(crownArr[1]);
        if (StringUtils.isNotBlank(model.getStartJumpIds())) {
            isJump = true;
            String[] idsArr = model.getStartJumpIds().split(",");
            // 处理第一条
            for (int i = 0; i < idsArr.length; i++) {
                DeliveryJump jump0 = this.deliveryJumpService.getById(Integer.parseInt(idsArr[i]));
                // 取开始编号数组
                String[] snStartArr = jump0.getSerialNumberStart().split("-");
                // 取开始编号
                Integer startSN = Integer.parseInt(snStartArr[1]);
                if (startSN < snNumber) {
                    continue;
                }
                // 修改时更新状态
                if (type && jump0.getStatus().intValue() == 0) {
                    jump0.setStatus(1);
                    this.deliveryJumpService.updateById(jump0);
                }
                // 拼接首拍冠字号到开始跳号第一拍冠字号
                String[] jumpCrownFrom = jump0.getCrownFrom().split("-");
                String[] jumpCrownTo = jump0.getCrownTo().split("-");
                // 取结束编号
                String[] snEndArr = jump0.getSerialNumberEnd().split("-");
                Integer endSN = Integer.parseInt(snEndArr[1]);
                if (i == 0) {
                    Integer from = Integer.parseInt(crownArr[2]);
                    Integer to = Integer.parseInt(jumpCrownFrom[1]);
                    if (lastCrownBetweenStr.equals(jumpCrownFrom[0])) { // 冠字号中间相同
                        // 连续的冠字号
                        this.joinJumpCrown(crownSb, from, to - 1, crownPrefix, crownArr[1]);
                        //  冠字号中间等于结束跳号尾号的中间 尾号从跳号的结束加1开始计算
                        lastCrownBetweenStr = jumpCrownTo[0];
                        lastCrownNum = Integer.parseInt(jump0.getCrownTo().split("-")[1]) + 1;
//                        backVO.setNewCrownStr(jumpCrownFrom[0]);
                    } else {
                        // 大于5000记录两条 冠字号中间等于结束跳号尾号的中间 尾号从跳号的结束加1开始计算
                        this.joinJumpCrown(crownSb, from, 5000, crownPrefix, crownArr[1]);
                        this.joinJumpCrown(crownSb, 1, to, crownPrefix, jumpCrownFrom[0]);
                        //  冠字号中间等于结束跳号尾号的中间 尾号从跳号的结束加1开始计算
                        lastCrownBetweenStr = jumpCrownTo[0];
                        lastCrownNum = Integer.parseInt(jumpCrownTo[1]) + 1;
//                        lastCrownBetweenNum = Integer.parseInt(jumpCrownTo[1]) + 1;
                    }
                }
                String lastCrown1 = lastCrownBetweenStr + "-" + String.format("%04d", lastCrownNum);
                // 跳号的开始编号减 开始编号等于1
                if ((startSN - snNumber) == 1) {
                    parSb.append(" ");
                    parSb.append(snStartArr[0] + "-" + String.format("%04d", snNumber) + "(" + product.getBoxAmount() + "箱)");
                    if (i > 0) {
                        Integer to = lastCrownNum + 60 - 1;
                        this.joinJumpCrown(crownSb, lastCrownNum, to, crownPrefix, lastCrownBetweenStr);
                    }
                    BackVO detailVO = this.addDetailList(1, 1, snStartArr[0], snNumber, product.getBoxAmount(), detailList, crownPrefix, lastCrown1);
                    lastCrownBetweenStr = detailVO.getNewCrownStr();
//                    backVO.setNewCrownStr(detailVO.getNewCrownStr());
                    par = par + 1;
                } else if ((startSN - snNumber) > 1) {
                    String start = snStartArr[0] + "-" + String.format("%04d", snNumber);
                    String end = snStartArr[0] + "-" + String.format("%04d", startSN - 1);
                    parSb.append(" ");
                    parSb.append(start + " 至 " + end + "(" + (startSN - snNumber) * product.getBoxAmount() + "箱)");
                    if (i > 0) {
                        Integer to = lastCrownNum + (startSN - snNumber) * product.getBoxAmount() - 1;
                        this.joinJumpCrown(crownSb, lastCrownNum, to, crownPrefix, lastCrownBetweenStr);
                    }
                    BackVO detailVO = this.addDetailList(jump0.getJumpQty(), 1, snStartArr[0], snNumber, product.getBoxAmount(), detailList, crownPrefix, lastCrown1);
                    lastCrownBetweenStr = detailVO.getNewCrownStr();
//                    backVO.setNewCrownStr(detailVO.getNewCrownStr());
                    par = par + (startSN - snNumber);
                }
                lastCrownNum = Integer.parseInt(jump0.getCrownTo().split("-")[1]) + 1;
                snNumber = endSN + 1;
            }
        }
//        backVO.setLastSerialNumber(snNumber);
//        backVO.setLastCrown(lastCrownNum);
        //==========
        String lastCrown1 = lastCrownBetweenStr + "-" + String.format("%04d", lastCrownNum);
        totalPar = totalPar - par;
        if (totalPar < 0) {
            throw new RetException("跳号编号超出计划范围");
        }
        String lastCrown = this.addLastPar(crownArr, isJump, crownSb, product, parSb, serialNumberArr, snNumber, totalPar, lastBox, detailList, product.getCrownPrefix(), lastCrown1, model.getFirstBoxNum());
        model.setParBetween(parSb.toString());
        if (crownSb.toString().endsWith(",")) {
            model.setCrownBetween(crownSb.toString().substring(0, crownSb.toString().length() - 1));
        } else {
            model.setCrownBetween(crownSb.toString());
        }

        ProductPlan productPlan = this.productPlanService.getMaxBySecurities(product.getSecurities());
        // 验证是否超出产品的冠字号范围
        this.validCrown(productPlan, model.getFirstCrown(), lastCrown);
        return parSb.toString();
    }

    private void validCrown(ProductPlan product, String firstCrown, String lastCrown) {
        String[] startArr = product.getCrownFrom().split("-");
        String[] endArr = product.getCrownTo().split("-");
        String[] firstArr = firstCrown.split("-");
        String[] lastArr1 = lastCrown.split("至");
        String[] lastArr2 = lastArr1[1].split("-");
        Integer productStartCrown1 = Integer.parseInt(startArr[0]);
        Integer productStartCrown2 = Integer.parseInt(startArr[1]);
        Integer productEndCrown1 = Integer.parseInt(endArr[0]);
        Integer productEndCrown2 = Integer.parseInt(endArr[1]);
        Integer firstStartCrown1 = Integer.parseInt(firstArr[1]);
        Integer firstStartCrown2 = Integer.parseInt(firstArr[2]);
        Integer lastStartCrown1 = Integer.parseInt(lastArr2[1]);
        Integer lastStartCrown2 = Integer.parseInt(lastArr2[2]);
        if (firstStartCrown1.intValue() < productStartCrown1.intValue() || firstStartCrown1.intValue() > productEndCrown1.intValue()) {
            throw new RetException("发货计划超出了产品的冠字号范围");
        } else if (firstStartCrown1.intValue() == productStartCrown1.intValue()) {
            if (firstStartCrown2.intValue() < productStartCrown2.intValue()) {
                throw new RetException("发货计划超出了产品的冠字号范围");
            }
            if (lastStartCrown2.intValue() < productStartCrown2.intValue()) {
                throw new RetException("发货计划超出了产品的冠字号范围");
            }
        }
        if (lastStartCrown1.intValue() < productStartCrown1.intValue() || lastStartCrown1.intValue() > productEndCrown1.intValue()) {
            throw new RetException("发货计划超出了产品的冠字号范围");
        } else if (lastStartCrown1.intValue() == productStartCrown1.intValue()) {
            if (firstStartCrown2.intValue() > productEndCrown2.intValue()) {
                throw new RetException("发货计划超出了产品的冠字号范围");
            }
            if (lastStartCrown2.intValue() > productEndCrown2.intValue()) {
                throw new RetException("发货计划超出了产品的冠字号范围");
            }
        }
    }

    /**
     * @param crownSb
     * @param lastCrownNum
     * @param to
     * @param crownPrefix
     * @param crownStr
     */
    private String joinJumpCrown(StringBuffer crownSb, Integer lastCrownNum, Integer to, String crownPrefix, String crownStr) {
        String resp = null;
        if (to == 5000) {
            resp = this.joinStr(crownPrefix, crownStr, lastCrownNum, crownStr, 5000);
            crownSb.append(resp);
        } else if (to > 5000) {
            String newCrownStr = String.format("%03d", Integer.parseInt(crownStr) + 1);
            crownSb.append(this.joinStr(crownPrefix, crownStr, lastCrownNum, crownStr, 5000));
            crownSb.append(",");
            resp = this.joinStr(crownPrefix, newCrownStr, 1, newCrownStr, to - 5000);
            crownSb.append(resp);
        } else {
            resp = this.joinStr(crownPrefix, crownStr, lastCrownNum, crownStr, to);
            crownSb.append(resp);
        }
        crownSb.append(",");
        return resp;
    }

    private String joinStr(String prefix, String prefix1, Integer prefix2,
                           String prefix3, Integer prefix4) {
        return prefix + "-" + prefix1 + "-" + String.format("%04d", prefix2) + " 至 "
                + prefix + "-" + prefix3 + "-" + String.format("%04d", prefix4);
    }

    // 添加明细
    private BackVO addDetailList(int num, int type, String serialNumber, Integer firstSerialNumber, Integer qty,
                                 List<DeliveryPlanDetail> detailList, String crownPrefix, String crownFrom) {
        BackVO vo = new BackVO();
        String[] crownArr = crownFrom.split("-");
        Integer lastCrown = Integer.parseInt(crownArr[1]);
        vo.setNewCrownStr(crownArr[0]);
        if (detailList != null) {
            for (int j = 0; j < num; j++) {
                DeliveryPlanDetail detail = new DeliveryPlanDetail();
                // 拍子类型1:整拍 2: 前  3:后(由拍子类型、数量决定拍子取的箱子)
                detail.setType(type);
                detail.setSerialNumber(serialNumber + "-" + String.format("%04d", firstSerialNumber + j));
                detail.setQty(qty);
                detail.setCrownFrom(vo.getNewCrownStr() + "-" + String.format("%04d", lastCrown));
                if ((lastCrown + qty - 1) == 5000) {
                    detail.setCrownTo(vo.getNewCrownStr() + "-" + 5000);
                    String newCrownStr = String.format("%03d", Integer.parseInt(vo.getNewCrownStr()) + 1);
                    vo.setNewCrownStr(newCrownStr);
                    lastCrown = 1;
                } else if ((lastCrown + qty) > 5000) {
                    lastCrown = lastCrown - 5000;
                    String newCrownStr = String.format("%03d", Integer.parseInt(vo.getNewCrownStr()) + 1);
                    vo.setNewCrownStr(newCrownStr);
                    detail.setCrownTo(newCrownStr + "-" + String.format("%04d", (lastCrown + qty - 1)));
                    lastCrown = lastCrown + qty;
                } else {
                    detail.setCrownTo(vo.getNewCrownStr() + "-" + String.format("%04d", lastCrown + qty - 1));
                    lastCrown = lastCrown + qty;
                }
                detail.setCrownBetweenStr(crownPrefix + detail.getCrownFrom() + " - " + crownPrefix + detail.getCrownTo());
                detailList.add(detail);
            }
        }
        vo.setLastCrown(lastCrown);
        return vo;
    }

    /**
     * 结束跳号 拼接冠字号/拍号
     *
     * @param product    产品信息
     * @param parSb      拍号字符串
     * @param endJumpIds 跳号ids串 逗号分隔
     * @param detailList 明细集合
     * @param type       是否更新结束跳叼状态 是 否
     * @param crownSb    冠字号字符串
     * @return 结束跳号总拍数
     */
    private Integer getEndjumpPar(Product product, StringBuffer parSb, String endJumpIds, List<DeliveryPlanDetail> detailList, boolean type, StringBuffer crownSb) {
        Integer total = 0;
        if (StringUtils.isNotBlank(endJumpIds)) {
            String[] idsArr = endJumpIds.split(",");
            for (int i = 0; i < idsArr.length; i++) {
                DeliveryJump jump = this.deliveryJumpService.getById(Integer.parseInt(idsArr[i]));
                // 修改时更新状态
                if (type && jump.getStatus().intValue() == 1) {
                    jump.setStatus(2);
                    this.deliveryJumpService.updateById(jump);
                }
                String[] serialNumberArr = jump.getSerialNumberStart().split("-");
                // 拼冠字号
                total = total + jump.getJumpQty();
                crownSb.append(product.getCrownPrefix() + "-" + jump.getCrownFrom() + " 至 "
                        + product.getCrownPrefix() + "-" + jump.getCrownTo());
                crownSb.append(",");
                // 拼拍号
                // 跳多拍
                if (jump.getJumpQty() > 1) {
                    parSb.append(jump.getSerialNumberStart() + " 至 " + jump.getSerialNumberEnd() + "(" + product.getBoxAmount() * jump.getJumpQty() + "箱)");
                    this.addDetailList(jump.getJumpQty(), 1, serialNumberArr[0], Integer.parseInt(serialNumberArr[1]), product.getBoxAmount(), detailList, product.getCrownPrefix(), jump.getCrownFrom());
                } else {
                    parSb.append(jump.getSerialNumberStart() + "(" + product.getBoxAmount() + "箱)");
                    this.addDetailList(1, 1, serialNumberArr[0], Integer.parseInt(serialNumberArr[1]), product.getBoxAmount(), detailList, product.getCrownPrefix(), jump.getCrownFrom());
                }
                parSb.append(" ");
            }
        }
        return total;
    }

    // 拼接开始跳号之后最后的拍数和尾箱数
    private String addLastPar(String[] initCrown, boolean isJump, StringBuffer crownBetween, Product product, StringBuffer parSb, String[] serialNumberArr, Integer lastSerialNumber, Integer totalPar, Integer lastBox, List<DeliveryPlanDetail> detailList, String crownPrefix, String lastCrown, Integer fristBox) {
        String resp = null;
        // 004-0001
        String[] crownArr = lastCrown.split("-");
        Integer lastCrownNum = Integer.parseInt(crownArr[1]);
        String newCrownStr = crownArr[0];
        if (totalPar > 0) {
            String start = serialNumberArr[0] + "-" + String.format("%04d", lastSerialNumber);
            String end = serialNumberArr[0] + "-" + String.format("%04d", lastSerialNumber + totalPar - 1);
            parSb.append(" ");
            BackVO detailVO = null;
            if (start.equals(end)) {
                parSb.append(start + "(" + product.getBoxAmount() + "箱)");
                detailVO = this.addDetailList(1, 1, serialNumberArr[0], lastSerialNumber, product.getBoxAmount(), detailList, crownPrefix, lastCrown);
            } else {
                parSb.append(start + " 至 " + end + "(" + totalPar * product.getBoxAmount() + "箱)");
                detailVO = this.addDetailList(totalPar, 1, serialNumberArr[0], lastSerialNumber, product.getBoxAmount(), detailList, crownPrefix, lastCrown);
            }
            lastCrownNum = detailVO.getLastCrown();
            newCrownStr = detailVO.getNewCrownStr();
        }
        if (lastBox > 0) {
            // 减去首拍箱数取整拍数量的余大等0 说明最后不是整拍
            parSb.append(" ");
            String lastEnd = serialNumberArr[0] + "-" + String.format("%04d", lastSerialNumber + totalPar);
            parSb.append("<span class='red'>");
            parSb.append(lastEnd + "(前" + lastBox + "箱)");
            parSb.append("</span>");
            String lastCrown1 = newCrownStr + "-" + String.format("%04d", lastCrownNum);
            this.addDetailList(1, 2, serialNumberArr[0], lastSerialNumber + totalPar, lastBox, detailList, crownPrefix, lastCrown1);
        }
        if (isJump) {
            Integer to = lastCrownNum + totalPar * product.getBoxAmount() + lastBox - 1;
            resp = this.joinJumpCrown(crownBetween, lastCrownNum, to, crownPrefix, crownArr[0]);
        } else {
            Integer to = 0;
            if (fristBox < product.getBoxAmount()) {
                to = to + fristBox;
            }
            to = to + Integer.parseInt(initCrown[2]) + totalPar * product.getBoxAmount() + lastBox - 1;
            resp = this.joinJumpCrown(crownBetween, Integer.parseInt(initCrown[2]), to, crownPrefix, initCrown[1]);
        }
        return resp;
    }

    private String getJumpStr(String[] idsArr) {
        ArraySorter.sort(idsArr);
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < idsArr.length; i++) {
            DeliveryJump jump = this.deliveryJumpService.getById(Integer.parseInt(idsArr[i]));
            if (i > 0) {
                sb.append(",");
            }
            sb.append(jump.getSerialNumberStart() + "至" + jump.getSerialNumberEnd());
        }
        return sb.toString();
    }


    // 获取跳号的拍数
    private Integer getJumpQty(DeliveryPlan lastModel) {
        Integer resp = 0;
        if (StringUtils.isNotBlank(lastModel.getStartJumpIds())) {
            String[] str = lastModel.getStartJumpIds().split(",");
            for (int i = 0; i < str.length; i++) {
                DeliveryJump jump = this.deliveryJumpService.getById(Integer.parseInt(str[i]));
                resp = resp + jump.getJumpQty();
            }
        }
        if (StringUtils.isNotBlank(lastModel.getEndJumpIds())) {
            String[] str = lastModel.getEndJumpIds().split(",");
            for (int i = 0; i < str.length; i++) {
                DeliveryJump jump = this.deliveryJumpService.getById(Integer.parseInt(str[i]));
                resp = resp - jump.getJumpQty();
            }
        }
        return resp;
    }

    private DeliveryPlan getLast(Integer securitiesId, Integer id, Integer oldId) {
        LambdaQueryWrapper<DeliveryPlan> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DeliveryPlan::getSecuritiesId, securitiesId);
        queryWrapper.lt(id != null && id > 0, DeliveryPlan::getId, id);
        queryWrapper.lt(oldId != null && oldId > 0, DeliveryPlan::getId, oldId);
        queryWrapper.orderByDesc(DeliveryPlan::getId);
        Page<DeliveryPlan> page = new Page<>(1, 1);
        IPage<DeliveryPlan> data = this.page(page, queryWrapper);
        if (data.getRecords() != null && data.getRecords().size() > 0) {
            return data.getRecords().get(0);
        }
        return null;
    }

    public String creadeOutOrderNo(String securities) {
        // 车间出库单号由系统自动生成，编号由“品名+部门编号+T2+3位数字编号”构成，Y211-1T2001
        // 车间出库单号
        String resp = null;
        String key = "out_num:" + securities;
        if (redisService.hasKey(key)) {
            resp = securities + "-" + String.format("%04d", Integer.parseInt(this.redisService.get(key)) + 1);
        } else {
            this.redisService.set(key, "0");
            resp = securities + "-" + String.format("%04d", 1);
        }
        return resp;
    }

    public void updateOutOrderNo(String securities, String orderNo) {
        String key = "out_num:" + securities;
        String[] orderNoArr = orderNo.split("-");
        Integer order = Integer.parseInt(orderNoArr[1]);
        this.redisService.set(key, String.valueOf(order));
    }

    public void addOutOrderNo(String securities) {
        String key = "out_num:" + securities;
        this.redisService.set(key, String.valueOf(Integer.parseInt(this.redisService.get(key)) + 1));
    }
}