package com.prolog.rdc.tms.service.executeStrategy;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.prolog.framework.common.message.RestMessage;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Restriction;
import com.prolog.framework.core.restriction.Restrictions;
import com.prolog.product.core.framework.entity.UserEntity;
import com.prolog.rdc.tms.constant.BmsConstant;
import com.prolog.rdc.tms.dao.mapper.*;
import com.prolog.rdc.tms.model.dto.LoadingBillPriceDTO;
import com.prolog.rdc.tms.model.dto.ShipBillPriceDTO;
import com.prolog.rdc.tms.model.dto.SySParameterDTO;
import com.prolog.rdc.tms.model.entity.*;
import com.prolog.rdc.tms.rmi.feign.MaindataFeign;
import com.prolog.rdc.tms.service.strategy.IncomeAbstractSplitStrategyRuleHandler;
import com.prolog.rdc.tms.service.strategy.IncomeCostStrategyRuleHandler;
import org.apache.ibatis.plugin.PluginException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Component
@IncomeCostStrategyRuleHandler(BmsConstant.PRICE_MERGE_INCOME)
public class IncomeMergePriceStrategy extends IncomeAbstractSplitStrategyRuleHandler {

    private final Logger logger = LoggerFactory.getLogger(IncomeMergePriceStrategy.class);

    @Autowired
    private MergeBillStrategyMapper mergeBillStrategyMapper;
    @Autowired
    private ShipBillHeadMapper headMapper;
    @Autowired
    private MaindataFeign maindataTmsResource;
    @Autowired
    private ShipBillFeeTemporaryDetailMapper temporaryDetailMapper;
    @Autowired
    private ExpenseManagementMapper managementMapper;
    @Autowired
    private ExpenseAccountMapper expenseAccountMapper;

    @Autowired
    private BmsBillIncomeFeeHeaderMapper bmsBillIncomeFeeHeaderMapper;

    @Autowired
    private BmsBillIncomeFeeDetailMapper bmsBillIncomeFeeDetailMapper;

    /**
     * 派车单执行阶梯计费
     *
     * @param priceModel   计费数据
     * @param costStrategy 运单信息
     */
    public List<LoadingBillPriceDTO> executeStrategyLoading(BmsLogisticsContractModel contractModel, BmsFreightPriceModel priceModel, BmsLoadingBillHeaderModel costStrategy, String source, UserEntity user) {
        return null;
    }

    /**
     * 单价+合并计费
     *
     * @param priceModel   计费数据
     * @param costStrategy 运单信息
     */
    @Override
    public List<ShipBillPriceDTO> executeStrategy(BmsLogisticsContractModel contractModel, BmsFreightPriceModel priceModel, BmsShipBillHeaderModel costStrategy, String source, UserEntity user) {
        logger.info("执行单据价合并计费....");

        Criteria criteria = Criteria.forClass(BmsMergeBillStrategyModel.class);
        Restriction r1 = Restrictions.eq("status", 1);
        Restriction r2 = Restrictions.eq("id", priceModel.getMergeStrategyId());
        Restriction r3 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
        criteria.setRestriction(Restrictions.and(r1, r2, r3));
        List<BmsMergeBillStrategyModel> models = mergeBillStrategyMapper.findByCriteria(criteria);
        if (ObjectUtil.isEmpty(models)) {
            throw new PluginException("合并策略不存在，请先配置");
        }
        BmsMergeBillStrategyModel model = models.get(0);
        Criteria sCriteria = Criteria.forClass(BmsShipBillHeaderModel.class);
        Restriction r4 = null;
        Restriction r5 = null;
        Restriction r6 = null;
        Restriction r7 = null;
        Restriction r8 = null;
        Restriction r9 = null;
        Restriction r10 = null;
        Restriction r11 = null;
        Restriction r14 = null;
        Restriction r15 = null;
        Restriction r16 = null;
        Restriction r17 = null;
        Restriction r18 = null;
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

        Integer isSameDeliveryArea = model.getIsSameDeliveryArea();
        if (Integer.valueOf(1).equals(isSameDeliveryArea)) {
            // 相同配送区域
            if (ObjectUtil.isEmpty(costStrategy.getDeliveryAreas())) {
                throw new PluginException("配送区域不能为空");
            }
            String deliveryAreas = costStrategy.getDeliveryAreas();
            r15 = Restrictions.eq(BmsShipBillHeaderModel::getDeliveryAreas, deliveryAreas);
        }
        Integer isSameDeliveryStation = model.getIsSameDeliveryStation();
        if (Integer.valueOf(1).equals(isSameDeliveryStation )) {
            // 相同送货站点
            if (ObjectUtil.isEmpty(costStrategy.getDeliveryStationCode())) {
                throw new PluginException("送货站点不能为空");
            }
            String deliveryStation = costStrategy.getDeliveryStationCode();
            r16 = Restrictions.eq(BmsShipBillHeaderModel::getDeliveryStationCode, deliveryStation);
        }
        Integer isSameLine = model.getIsSameLine();
        if (Integer.valueOf(1).equals(isSameLine )) {
            // 相同线路
            if (ObjectUtil.isEmpty(costStrategy.getLineCode())) {
                throw new PluginException("运单线路不能为空");
            }
            String lineCode = costStrategy.getLineCode();
            r17 = Restrictions.eq(BmsShipBillHeaderModel::getLineCode, lineCode);
        }
        Integer isSameCargoOwner = model.getIsSameCargoOwner();
        if (Integer.valueOf(1).equals(isSameCargoOwner )) {
            // 相同货主
            if (ObjectUtil.isEmpty(costStrategy.getCargoOwnerCode())) {
                throw new PluginException("货主不能为空");
            }
            String cargoOwnerCode = costStrategy.getCargoOwnerCode();
            r18 = Restrictions.eq(BmsShipBillHeaderModel::getCargoOwnerCode, cargoOwnerCode);
        }

        Integer isSameStartDate = model.getIsSameStartDate();
        if (Integer.valueOf(1).equals(isSameStartDate )) {
            // 相同启运时间
            Date departureTime = costStrategy.getStartDate();
            if (ObjectUtil.isEmpty(departureTime)) {
                throw new PluginException("启运时间不能为空");
            }
            String format = sdf.format(departureTime);
            r4 = Restrictions.between("startDate", format + " 00:00:00", format + " 23:59:59");
        }

        Integer isSameCar = model.getIsSameCar();
        if (Integer.valueOf(1).equals(isSameCar )) {
            // 相同的车辆
            if (ObjectUtil.isEmpty(costStrategy.getVehicleCode())) {
                throw new PluginException("车辆编号不能为空");
            }
            String vehicleCode = costStrategy.getVehicleCode();
            r5 = Restrictions.eq("vehicleCode", vehicleCode);
        }
        Integer isSameDriver = model.getIsSameDriver();
        if (Integer.valueOf(1).equals(isSameDriver )) {
            // 相同的司机
            if (ObjectUtil.isEmpty(costStrategy.getDriverCode())) {
                throw new PluginException("司机编号不能为空");
            }
            String driverCode = costStrategy.getDriverCode();
            r6 = Restrictions.eq("driverCode", driverCode);
        }
        Integer isSameReceive = model.getIsSameReceive();
        if (Integer.valueOf(1).equals(isSameReceive )) {
            // 相同收货方
            if (ObjectUtil.isEmpty(costStrategy.getConsigneeUnitCode())) {
                throw new PluginException("收货方单位编码不能为空");
            }
            String consigneeUnitCode = costStrategy.getConsigneeUnitCode();
            r7 = Restrictions.eq("consigneeUnitCode", consigneeUnitCode);
        }
        Integer isSameSender = model.getIsSameSender();
        if (Integer.valueOf(1).equals(isSameSender )) {
            // 相同发货方
            if (ObjectUtil.isEmpty(costStrategy.getShipperCode())) {
                throw new PluginException("发货方编码不能为空");
            }
            String shipperCode = costStrategy.getShipperCode();
            r8 = Restrictions.eq("shipperCode", shipperCode);
        }
        Integer isSameElectronicBackDate = model.getIsSameElectronicBackDate();
        if (Integer.valueOf(1).equals(isSameElectronicBackDate )) {
            // 相同电子回单日期
            Date receiptDate = costStrategy.getReceiptDate();
            if (ObjectUtil.isEmpty(receiptDate)) {
                throw new PluginException("电子回单日期不能为空");
            }
            String format = sdf.format(receiptDate);
            r9 = Restrictions.between("receiptDate", format + " 00:00:00", format + " 23:59:59");
        }
        Integer isSameEndDate = model.getIsSameEndDate();
        if (Integer.valueOf(1).equals(isSameEndDate )) {
            // 相同实际送达日期
            Date deliveryDate = costStrategy.getDeliveryDate();
            if (ObjectUtil.isEmpty(deliveryDate)) {
                throw new PluginException("实际送达日期不能为空");
            }
            String format = sdf.format(deliveryDate);
            r10 = Restrictions.between("deliveryDate", format + " 00:00:00", format + " 23:59:59");
        }
        Integer isSameOriginalBackDate = model.getIsSameOriginalBackDate();
        if (Integer.valueOf(1).equals(isSameOriginalBackDate )) {
            // 相同正本回单日期
            Date originalBackDate = costStrategy.getOriginalBackDate();
            if (ObjectUtil.isEmpty(originalBackDate)) {
                throw new PluginException("正本回单日期不能为空");
            }
            String format = sdf.format(originalBackDate);
            r11 = Restrictions.between("originalBackDate", format + " 00:00:00", format + " 23:59:59");
        }
        Integer isSameCarrier = model.getIsSameCarrier();
        if (Integer.valueOf(1).equals(isSameCarrier)) {
            if (costStrategy.getCarrierType().intValue() == 1) {
                // 相同承运商
                String carrierCode = costStrategy.getCarrierCode();
                if (ObjectUtil.isEmpty(carrierCode)) {
                    throw new PluginException("承运商不能为空");
                }
                r14 = Restrictions.eq("carrierCode", carrierCode);
            } else if (costStrategy.getCarrierType().intValue() == 2) {
                String affiliationCode = costStrategy.getAffiliationCode();
                if (ObjectUtil.isEmpty(affiliationCode)) {
                    throw new PluginException("所属机构不能为空");
                }
                r14 = Restrictions.eq(BmsShipBillHeaderModel::getAffiliationCode, affiliationCode);
            }
        }
        Restriction r13 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
        Restriction r19 = Restrictions.ne(BmsShipBillHeaderModel::getFeeCltType, 20);
        sCriteria.setRestriction(Restrictions.and(r4, r5, r6, r7, r8, r9, r10, r11, r13, r14, r15, r16, r17, r18,r19));
        List<BmsShipBillHeaderModel> headerModels = headMapper.findByCriteria(sCriteria);

        List<Long> shipBillIds = headerModels.stream().map(BmsShipBillHeaderModel::getId).collect(Collectors.toList());
        Criteria criteriaIncomeFeeHeader = Criteria.forClass(BmsBillIncomeFeeHeaderModel.class);
        Restriction r20 = Restrictions.in(BmsBillIncomeFeeHeaderModel::getBillId,shipBillIds.stream().toArray());
        Restriction r21 = Restrictions.eq(BmsBillIncomeFeeHeaderModel::getCheckStatus,0);
        Integer[] settlementStatus = {0,3};
        Restriction r22 = Restrictions.in(BmsBillIncomeFeeHeaderModel::getSettlementStatus,settlementStatus);
        Restriction r23 = Restrictions.eq(BmsBillIncomeFeeHeaderModel::getBillType,10);
        criteriaIncomeFeeHeader.setRestriction(Restrictions.and(r20,r21,r22,r23));
        List<BmsBillIncomeFeeHeaderModel> feeHeaderModels = bmsBillIncomeFeeHeaderMapper.findByCriteria(criteriaIncomeFeeHeader);

        List<BmsShipBillHeaderModel> shipBillHeaderModels = Lists.newArrayList();
        if (ObjectUtil.isNotEmpty(feeHeaderModels)) {
            List<Long> billIds = feeHeaderModels.stream().map(BmsBillIncomeFeeHeaderModel::getBillId).collect(Collectors.toList());
            headerModels.stream().forEach(headerModel->{
                for(Long id:billIds) {
                    if (id.equals(headerModel.getId())) {
                        shipBillHeaderModels.add(headerModel);
                        break;
                    }
                }
            });
        }

        // 过滤符合条件的运单
        RestMessage<SySParameterDTO> parameterModel = maindataTmsResource.queryParameter();
        if (ObjectUtil.isEmpty(parameterModel) || !parameterModel.isSuccess()) {
            throw new PluginException("系统参数未配置");
        }
        Integer costInteger = parameterModel.getData().getCostInteger();
        Integer costPrecision = parameterModel.getData().getCostPrecision();
        List<String> strings = shipBillHeaderModels.stream().map(BmsShipBillHeaderModel::getShipbillNo).collect(Collectors.toList());
        if (!strings.contains(costStrategy.getShipbillNo())) {
            shipBillHeaderModels.add(costStrategy);
        }
        logger.info("合并计费获取到所有的运单:{}" + JSONObject.toJSONString(shipBillHeaderModels.stream().map(BmsShipBillHeaderModel::getShipbillNo).collect(Collectors.joining(","))));
        List<ShipBillPriceDTO> list = Lists.newArrayList();

        BigDecimal allTotalWeight = shipBillHeaderModels.stream().filter(bill->ObjectUtil.isNotEmpty(bill.getTotalWeight())).map(BmsShipBillHeaderModel::getTotalWeight).reduce(BigDecimal.ZERO, NumberUtil::add);
        logger.info(StrUtil.format("合并计费获取到所有的运单总重量={}", allTotalWeight));

        BigDecimal totPrice = BigDecimal.ZERO;
        if (ObjectUtil.isNotEmpty(priceModel.getPrice()) && ObjectUtil.isNotEmpty(allTotalWeight)) {
            totPrice = priceModel.getPrice().multiply(allTotalWeight);
        }
        // 价格分配
        Integer feeRule = model.getFeeRule();
        if (feeRule == 1) {
            // 	按单数分摊
            BigDecimal size = BigDecimal.valueOf(shipBillHeaderModels.size());
            // (100 - (n-1)*(100/n))
            BigDecimal price = BigDecimal.valueOf(0);
            BigDecimal averagePrice = BigDecimal.valueOf(0);
            // 四舍五入
            if (costInteger == 1) {
                // 平均价格
                averagePrice = totPrice.divide(size, costPrecision, RoundingMode.HALF_UP);
                // 100 - (n - 1)*subtract
                price = (size.subtract(BigDecimal.valueOf(1))).multiply(averagePrice).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.HALF_UP);

            }
            // 向上取整
            if (costInteger == 2) {
                // 平均价格
                averagePrice = totPrice.divide(size, costPrecision, RoundingMode.UP);
                // 100 - (n - 1)*subtract
                price = (size.subtract(BigDecimal.valueOf(1))).multiply(averagePrice).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.UP);
            }
            // 向下取整
            if (costInteger == 3) {
                // 平均价格
                averagePrice = totPrice.divide(size, costPrecision, RoundingMode.DOWN);
                // 100 - (n - 1)*subtract
                price = (size.subtract(BigDecimal.valueOf(1))).multiply(averagePrice).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.DOWN);
            }
            // 最后一个的价格
            BigDecimal endPrice = totPrice.subtract(price);
            AtomicInteger num = new AtomicInteger(1);
            for (BmsShipBillHeaderModel shipBillHeaderModel : shipBillHeaderModels) {
                ShipBillPriceDTO dto = new ShipBillPriceDTO();
                int i = num.get();
                if (shipBillHeaderModels.size() == i) {
                    dto.setPrice(endPrice);
                } else {
                    dto.setPrice(averagePrice);
                }
                num.getAndIncrement();
                dto.setShipbillNo(shipBillHeaderModel.getShipbillNo());
                dto.setCostContract(contractModel.getContractName());
                dto.setCostPriceTable(priceModel.getPriceName());

                dto.setFirstPartyId(contractModel.getFirstPartyId());
                dto.setFirstPartyCode(contractModel.getFirstPartyCode());
                dto.setFirstPartyName(contractModel.getFirstPartyName());

                this.saveFee(shipBillHeaderModel.getShipbillNo(), shipBillHeaderModel.getId(), contractModel, priceModel, user, dto.getPrice(), source);
                list.add(dto);

                if (shipBillHeaderModel.getShipbillNo().equals(costStrategy.getShipbillNo())) {
                    continue;
                }
                upShipBillFee(shipBillHeaderModel);
            }
        }
        if (feeRule == 2) {
            // 按件数分摊
            BigDecimal totalPcs = shipBillHeaderModels.stream().map(BmsShipBillHeaderModel::getTotalPcs).reduce(BigDecimal.ZERO, BigDecimal::add);
            // 平均价格
            BigDecimal averagePrice = BigDecimal.valueOf(0);
            // 四舍五入
            if (costInteger == 1) {
                // 平均价格
                averagePrice = totPrice.divide(totalPcs, costPrecision, RoundingMode.HALF_UP);
            }
            // 向上取整
            if (costInteger == 2) {
                // 平均价格
                averagePrice = totPrice.divide(totalPcs, costPrecision, RoundingMode.UP);
            }
            // 向下取整
            if (costInteger == 3) {
                // 平均价格
                averagePrice = totPrice.divide(totalPcs, costPrecision, RoundingMode.DOWN);
            }
            AtomicInteger num = new AtomicInteger(1);
            BigDecimal endPrice = new BigDecimal(0);
            for (BmsShipBillHeaderModel shipBillHeaderModel : shipBillHeaderModels) {
                ShipBillPriceDTO dto = new ShipBillPriceDTO();
                int i = num.get();
                if (shipBillHeaderModels.size() == i) {
                    dto.setPrice(totPrice.subtract(endPrice));
                } else {
                    // 四舍五入
                    if (costInteger == 1) {
                        endPrice = endPrice.add(averagePrice.multiply(shipBillHeaderModel.getTotalPcs()));
                        dto.setPrice(averagePrice.multiply(shipBillHeaderModel.getTotalPcs()).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.HALF_UP));
                    }
                    // 向上取整
                    if (costInteger == 2) {
                        endPrice = endPrice.add(averagePrice.multiply(shipBillHeaderModel.getTotalPcs()));
                        dto.setPrice(averagePrice.multiply(shipBillHeaderModel.getTotalPcs()).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.UP));
                    }
                    // 向下取整
                    if (costInteger == 3) {
                        endPrice = endPrice.add(averagePrice.multiply(shipBillHeaderModel.getTotalPcs()));
                        dto.setPrice(averagePrice.multiply(shipBillHeaderModel.getTotalPcs()).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.DOWN));
                    }
                }
                num.getAndIncrement();
                dto.setShipbillNo(shipBillHeaderModel.getShipbillNo());
                dto.setCostContract(contractModel.getContractName());
                dto.setCostPriceTable(priceModel.getPriceName());
                dto.setFirstPartyId(contractModel.getFirstPartyId());
                dto.setFirstPartyCode(contractModel.getFirstPartyCode());
                dto.setFirstPartyName(contractModel.getFirstPartyName());

                this.saveFee(shipBillHeaderModel.getShipbillNo(), shipBillHeaderModel.getId(), contractModel, priceModel, user, dto.getPrice(), source);
                list.add(dto);
                if (shipBillHeaderModel.getShipbillNo().equals(costStrategy.getShipbillNo())) {
                    continue;
                }
                upShipBillFee(shipBillHeaderModel);
            }
        }
        if (feeRule == 3) {
            // 按重量分摊
            BigDecimal totalWeight = shipBillHeaderModels.stream().map(BmsShipBillHeaderModel::getTotalWeight).reduce(BigDecimal.ZERO, BigDecimal::add);
            // 平均价格
            BigDecimal averagePrice = BigDecimal.valueOf(0);
            // 四舍五入
            if (costInteger == 1) {
                // 平均价格
                averagePrice = totPrice.divide(totalWeight, costPrecision, RoundingMode.HALF_UP);
            }
            // 向上取整
            if (costInteger == 2) {
                // 平均价格
                averagePrice = totPrice.divide(totalWeight, costPrecision, RoundingMode.UP);
            }
            // 向下取整
            if (costInteger == 3) {
                // 平均价格
                averagePrice = totPrice.divide(totalWeight, costPrecision, RoundingMode.DOWN);
            }
            AtomicInteger num = new AtomicInteger(1);
            BigDecimal endPrice = new BigDecimal(0);
            for (BmsShipBillHeaderModel shipBillHeaderModel : shipBillHeaderModels) {
                ShipBillPriceDTO dto = new ShipBillPriceDTO();
                int i = num.get();
                if (shipBillHeaderModels.size() == i) {
                    dto.setPrice(totPrice.subtract(endPrice));
                } else {
                    // 四舍五入
                    if (costInteger == 1) {
                        endPrice = endPrice.add(averagePrice.multiply(shipBillHeaderModel.getTotalWeight()));
                        dto.setPrice(averagePrice.multiply(shipBillHeaderModel.getTotalWeight()).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.HALF_UP));
                    }
                    // 向上取整
                    if (costInteger == 2) {
                        endPrice = endPrice.add(averagePrice.multiply(shipBillHeaderModel.getTotalWeight()));
                        dto.setPrice(averagePrice.multiply(shipBillHeaderModel.getTotalWeight()).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.UP));
                    }
                    // 向下取整
                    if (costInteger == 3) {
                        endPrice = endPrice.add(averagePrice.multiply(shipBillHeaderModel.getTotalWeight()));
                        dto.setPrice(averagePrice.multiply(shipBillHeaderModel.getTotalWeight()).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.DOWN));
                    }

                }
                num.getAndIncrement();
                dto.setShipbillNo(shipBillHeaderModel.getShipbillNo());
                dto.setFirstPartyId(contractModel.getFirstPartyId());
                dto.setFirstPartyCode(contractModel.getFirstPartyCode());
                dto.setFirstPartyName(contractModel.getFirstPartyName());

                this.saveFee(shipBillHeaderModel.getShipbillNo(), shipBillHeaderModel.getId(), contractModel, priceModel, user, dto.getPrice(), source);
                list.add(dto);
                if (shipBillHeaderModel.getShipbillNo().equals(costStrategy.getShipbillNo())) {
                    continue;
                }
                upShipBillFee(shipBillHeaderModel);
            }
        }
        if (feeRule == 4) {
            // 按体积分摊
            BigDecimal totalVolume = shipBillHeaderModels.stream().map(BmsShipBillHeaderModel::getTotalVolume).reduce(BigDecimal.ZERO, BigDecimal::add);
            // 平均价格
            BigDecimal averagePrice = new BigDecimal(0);
            // 四舍五入
            if (costInteger == 1) {
                averagePrice = totPrice.divide(totalVolume, costPrecision, RoundingMode.HALF_UP);
            }
            // 向上取整
            if (costInteger == 2) {
                averagePrice = totPrice.divide(totalVolume, costPrecision, RoundingMode.UP);
            }
            // 向下取整
            if (costInteger == 3) {
                averagePrice = totPrice.divide(totalVolume, costPrecision, RoundingMode.DOWN);
            }
            AtomicInteger num = new AtomicInteger(1);
            BigDecimal endPrice = new BigDecimal(0);
            for (BmsShipBillHeaderModel shipBillHeaderModel : shipBillHeaderModels) {
                ShipBillPriceDTO dto = new ShipBillPriceDTO();
                int i = num.get();
                if (shipBillHeaderModels.size() == i) {
                    dto.setPrice(totPrice.subtract(endPrice));
                } else {
                    // 四舍五入
                    if (costInteger == 1) {
                        endPrice = endPrice.add(averagePrice.multiply(shipBillHeaderModel.getTotalVolume()));
                        dto.setPrice(averagePrice.multiply(shipBillHeaderModel.getTotalVolume()).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.HALF_UP));
                    }
                    // 向上取整
                    if (costInteger == 2) {
                        endPrice = endPrice.add(averagePrice.multiply(shipBillHeaderModel.getTotalVolume()));
                        dto.setPrice(averagePrice.multiply(shipBillHeaderModel.getTotalVolume()).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.UP));
                    }
                    // 向下取整
                    if (costInteger == 3) {
                        endPrice = endPrice.add(averagePrice.multiply(shipBillHeaderModel.getTotalVolume()));
                        dto.setPrice(averagePrice.multiply(shipBillHeaderModel.getTotalVolume()).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.DOWN));
                    }
                }
                num.getAndIncrement();
                dto.setShipbillNo(shipBillHeaderModel.getShipbillNo());
                dto.setCostContract(contractModel.getContractName());
                dto.setCostPriceTable(priceModel.getPriceName());
                dto.setFirstPartyId(contractModel.getFirstPartyId());
                dto.setFirstPartyCode(contractModel.getFirstPartyCode());
                dto.setFirstPartyName(contractModel.getFirstPartyName());
                this.saveFee(shipBillHeaderModel.getShipbillNo(), shipBillHeaderModel.getId(), contractModel, priceModel, user, dto.getPrice(), source);
                list.add(dto);

                if (shipBillHeaderModel.getShipbillNo().equals(costStrategy.getShipbillNo())) {
                    continue;
                }
                upShipBillFee(shipBillHeaderModel);
            }
        }
        if (feeRule == 5) {
            // 计费到其中一单，其余为0
            AtomicInteger num = new AtomicInteger(1);
            for (BmsShipBillHeaderModel shipBillHeaderModel : shipBillHeaderModels) {
                ShipBillPriceDTO dto = new ShipBillPriceDTO();
                int i = num.get();
                if (1 == i) {
                    dto.setPrice(totPrice);
                } else {
                    dto.setPrice(BigDecimal.valueOf(0));
                }
                num.getAndIncrement();
                dto.setShipbillNo(shipBillHeaderModel.getShipbillNo());
                dto.setCostContract(contractModel.getContractName());
                dto.setCostPriceTable(priceModel.getPriceName());
                dto.setFirstPartyId(contractModel.getFirstPartyId());
                dto.setFirstPartyCode(contractModel.getFirstPartyCode());
                dto.setFirstPartyName(contractModel.getFirstPartyName());
                this.saveFee(shipBillHeaderModel.getShipbillNo(), shipBillHeaderModel.getId(), contractModel, priceModel, user, dto.getPrice(), source);
                list.add(dto);

                if (shipBillHeaderModel.getShipbillNo().equals(costStrategy.getShipbillNo())) {
                    continue;
                }
                upShipBillFee(shipBillHeaderModel);
                break;
            }
        }
        return list;
    }

    private BmsExpenseManagementModel getExpenseCode(BmsFreightPriceModel priceModel,UserEntity user) {
        //  费用编码、费用名称、费用科目名称、费用科目编码、费用类型 为价格表的费用反写（只有附加费才有）
        String expenseName = priceModel.getExpenseName();
        Criteria forClass = Criteria.forClass(BmsExpenseManagementModel.class);
        Restriction r7 = Restrictions.eq("status", 1);
        Restriction r8 = Restrictions.eq("expenseName", expenseName);
        Restriction r9 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
        forClass.setRestriction(Restrictions.and(r7, r8, r9));
        List<BmsExpenseManagementModel> managementModels = managementMapper.findByCriteria(forClass);
        if (ObjectUtil.isEmpty(managementModels)) {
            throw new PluginException("获取费用信息失败");
        }
        return managementModels.get(0);
    }

    private void saveFee(String billNo, Long billId, BmsLogisticsContractModel contractModel, BmsFreightPriceModel priceModel, UserEntity user, BigDecimal totPrice, String source) {
        // 新增费用信息
        BmsBillIncomeFeeDetailModel feeDetailModel = new BmsBillIncomeFeeDetailModel();
        RestMessage<String> expenseNo = maindataTmsResource.getCodeNumberByCode(BmsConstant.SHIP_BILL_EXPENSE_NO, "L");
        if (!expenseNo.isSuccess() || ObjectUtil.isEmpty(expenseNo)) {
            throw new PluginException("获取费用单号为空");
        }
        BmsExpenseManagementModel managementModel = this.getExpenseCode(priceModel,user);
        // 删除之前的费用
        this.deleteFee(billNo, billId, user, managementModel.getExpenseCode(), source);
        feeDetailModel.setExpenseNo(expenseNo.getData());
        feeDetailModel.setBillId(billId);
        feeDetailModel.setBillNo(billNo);
        feeDetailModel.setTaxPoint(contractModel.getTaxPoint());
        feeDetailModel.setBillingSource(1);
        feeDetailModel.setBillingStatus(1);
        feeDetailModel.setBillingDate(DateUtil.date());
        feeDetailModel.setBillingContract(contractModel.getContractName());
        feeDetailModel.setBillingPrice(priceModel.getPriceName());

        feeDetailModel.setExpenseCode(managementModel.getExpenseCode());
        feeDetailModel.setExpenseName(managementModel.getExpenseName());
        feeDetailModel.setExpenseAccountCode(managementModel.getExpenseAccountCode());
        feeDetailModel.setExpenseAccountName(managementModel.getExpenseAccountName());
        // 支出，收入
        String expenseAccountCode = managementModel.getExpenseAccountCode();
        Criteria aClass = Criteria.forClass(ExpenseAccountModel.class);
        Restriction r11 = Restrictions.eq("expenseCode", expenseAccountCode);
        Restriction r12 = Restrictions.eq("status", 1);
        aClass.setRestriction(Restrictions.and(r11, r12));
        List<ExpenseAccountModel> mapper = expenseAccountMapper.findByCriteria(aClass);
        if (ObjectUtil.isNotEmpty(mapper)) {
            feeDetailModel.setExpenseType(mapper.get(0).getExpenseClassify());
            feeDetailModel.setExpenseTypeName(Objects.equals(mapper.get(0).getExpenseClassify(), 1) ? "收入" : "支出");
        }
        RestMessage<SySParameterDTO> parameterModel = maindataTmsResource.queryParameter();
        int taxPoint = Integer.parseInt(contractModel.getTaxPoint());
        if (ObjectUtil.isNotEmpty(parameterModel) && parameterModel.isSuccess()) {
            // 1：含税，2：未税
            Integer isTax = parameterModel.getData().getCarrierPriceType();
            // 含税金额 = 未税金额（1 + 税点）
            BigDecimal point = BigDecimal.valueOf(taxPoint).divide(BigDecimal.valueOf(100), 5, RoundingMode.HALF_UP).add(BigDecimal.valueOf(1));
            Integer costPrecision = parameterModel.getData().getCostPrecision();
            Integer costInteger = parameterModel.getData().getCostInteger();
            if (isTax == 1) {
                // 四舍五入
                if (costInteger == 1) {
                    feeDetailModel.setNoTaxAmt(totPrice.divide(point, costPrecision, RoundingMode.HALF_UP));
                    feeDetailModel.setTaxAmt(totPrice.divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.HALF_UP));
                }
                // 向上取整
                if (costInteger == 2) {
                    feeDetailModel.setNoTaxAmt(totPrice.divide(point, costPrecision, RoundingMode.UP));
                    feeDetailModel.setTaxAmt(totPrice.divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.UP));
                }
                // 向下取整
                if (costInteger == 3) {
                    feeDetailModel.setNoTaxAmt(totPrice.divide(point, costPrecision, RoundingMode.DOWN));
                    feeDetailModel.setTaxAmt(totPrice.divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.DOWN));
                }
                feeDetailModel.setTax(feeDetailModel.getTaxAmt().subtract(feeDetailModel.getNoTaxAmt()));
            } else {
                // 四舍五入
                if (costInteger == 1) {
                    feeDetailModel.setNoTaxAmt(totPrice.divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.HALF_UP));
                    feeDetailModel.setTaxAmt(feeDetailModel.getNoTaxAmt().multiply(point).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.HALF_UP));
                }
                // 向上取整
                if (costInteger == 2) {
                    feeDetailModel.setNoTaxAmt(totPrice.divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.UP));
                    feeDetailModel.setTaxAmt(feeDetailModel.getNoTaxAmt().multiply(point).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.UP));
                }
                // 向上取整
                if (costInteger == 3) {
                    feeDetailModel.setNoTaxAmt(totPrice.divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.DOWN));
                    feeDetailModel.setTaxAmt(feeDetailModel.getNoTaxAmt().multiply(point).divide(BigDecimal.valueOf(1), costPrecision, RoundingMode.DOWN));
                }
                feeDetailModel.setTax(feeDetailModel.getTaxAmt().subtract(feeDetailModel.getNoTaxAmt()));
            }
        } else {
            feeDetailModel.setNoTaxAmt(BigDecimal.valueOf(0));
            feeDetailModel.setTaxAmt(totPrice);
            feeDetailModel.setTax(BigDecimal.valueOf(0));
        }

        feeDetailModel.setBillingType("");
        feeDetailModel.setCreateperson(user.getNickname());
        feeDetailModel.setCreatetime(DateUtil.date());
        feeDetailModel.setUpdateperson(user.getNickname());
        feeDetailModel.setUpdatetime(DateUtil.date());
        feeDetailModel.setEnterpriseId(user.getEnterpriseId());
        feeDetailModel.setEnterpriseName(user.getEnterpriseName());
        feeDetailModel.setExpenseStatus(2);
        feeDetailModel.setCheckStatus(0);
        // 0：试验，1：非试验
        if (source.equals("0")) {
            BmsShipBillFeeTemporaryDetailModel temporaryDetailModel = new BmsShipBillFeeTemporaryDetailModel();
            BeanUtils.copyProperties(feeDetailModel, temporaryDetailModel);
            temporaryDetailMapper.save(temporaryDetailModel);
        } else {
            bmsBillIncomeFeeDetailMapper.save(feeDetailModel);
        }
    }

    private void deleteFee(String billNo, Long billId, UserEntity user, String expenseCode, String source) {
        // 0：试验，1：非试验
        if (source.equals("0")) {
            Criteria tcriteria = Criteria.forClass(BmsShipBillFeeTemporaryDetailModel.class);
            Restriction r8 = Restrictions.eq("expenseCode", expenseCode);
            Restriction r9 = Restrictions.eq("shipbillNo", billNo);
            Restriction r10 = Restrictions.eq("enterpriseId", user.getEnterpriseId());
            tcriteria.setRestriction(Restrictions.and(r8, r9, r10));
            List<BmsShipBillFeeTemporaryDetailModel> tmodels = temporaryDetailMapper.findByCriteria(tcriteria);
            if (ObjectUtil.isNotEmpty(tmodels)) {
                temporaryDetailMapper.deleteByCriteria(tcriteria);
            }
        } else {
            // 判断是否存 运单合同 + 价格表 + 来源
            Criteria criteria = Criteria.forClass(BmsBillIncomeFeeDetailModel.class);
            Restriction r2 = Restrictions.eq("billingSource", 1);
            Restriction r3 = Restrictions.eq(BmsBillIncomeFeeDetailModel::getBillNo, billNo);
            Restriction r4 = Restrictions.eq(BmsBillIncomeFeeDetailModel::getBillId, billId);
            Restriction r5 = Restrictions.eq(BmsBillIncomeFeeDetailModel::getEnterpriseId, user.getEnterpriseId());
            Restriction r6 = Restrictions.eq(BmsBillIncomeFeeDetailModel::getExpenseCode, expenseCode);
            criteria.setRestriction(Restrictions.and(r2, r3, r4, r5, r6));
            List<BmsBillIncomeFeeDetailModel> models = bmsBillIncomeFeeDetailMapper.findByCriteria(criteria);
            if (ObjectUtil.isNotEmpty(models)) {
                bmsBillIncomeFeeDetailMapper.deleteByCriteria(criteria);
            }
        }
    }

    public void upShipBillFee (BmsShipBillHeaderModel model) {
        Criteria criteriaIncomeFeeHeader = Criteria.forClass(BmsBillIncomeFeeHeaderModel.class);
        Restriction r13 = Restrictions.eq(BmsBillIncomeFeeHeaderModel::getBillId, model.getId());
        Restriction r14 = Restrictions.eq(BmsBillIncomeFeeHeaderModel::getBillNo, model.getShipbillNo());
        criteriaIncomeFeeHeader.setRestriction(Restrictions.and(r13, r14));
        List<BmsBillIncomeFeeHeaderModel> feeHeaderModels = bmsBillIncomeFeeHeaderMapper.findByCriteria(criteriaIncomeFeeHeader);
        if (ObjectUtil.isEmpty(feeHeaderModels)) {
            return;
        }
        BmsBillIncomeFeeHeaderModel feeHeaderModel = feeHeaderModels.get(0);
        // 收入总价 类型为收入的含税金额
        BigDecimal incomeAmount = getShipbillTaxAmt(model.getId(), model.getShipbillNo(), null);
        feeHeaderModel.setIncomeAmount(incomeAmount);
        // 类型为收入的来源是价格表生成 含税金额
        BigDecimal contractIncomeAmount = getShipbillTaxAmt(model.getId(), model.getShipbillNo(), 1);
        feeHeaderModel.setContractIncomeAmount(contractIncomeAmount);
        feeHeaderModel.setUpdatetime(DateUtil.date());
        feeHeaderModel.setCostStatus(1);
        feeHeaderModel.setAbnormalStatus(0);
        feeHeaderModel.setCostFailReason(null);
        feeHeaderModel.setUpdatetime(DateUtil.date());
        bmsBillIncomeFeeHeaderMapper.update(feeHeaderModel);

        model.setFeeCltType(10);
        model.setUpdatetime(DateUtil.date());
        headMapper.update(model);
    }

    public BigDecimal getShipbillTaxAmt(Long billId, String billNo, Integer billingSource) {
        BigDecimal taxAmt = BigDecimal.ZERO;

        Criteria bmsFeeCriteria = Criteria.forClass(BmsBillIncomeFeeDetailModel.class);
        Restriction r1 = Restrictions.eq(BmsBillIncomeFeeDetailModel::getBillId, billId);
        Restriction r2 = Restrictions.eq(BmsBillIncomeFeeDetailModel::getBillNo, billNo);
        Restriction r3 = ObjectUtil.isEmpty(billingSource) ? null : Restrictions.eq(BmsBillIncomeFeeDetailModel::getBillingSource, billingSource);
        bmsFeeCriteria.setRestriction(Restrictions.and(r1, r2, r3));
        List<BmsBillIncomeFeeDetailModel> feeDetailModels = bmsBillIncomeFeeDetailMapper.findByCriteria(bmsFeeCriteria);
        if (ObjectUtil.isNotEmpty(feeDetailModels)) {
            taxAmt = feeDetailModels.stream().filter(dto -> ObjectUtil.isNotEmpty(dto.getTaxAmt())).map(BmsBillIncomeFeeDetailModel::getTaxAmt).reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        return taxAmt;
    }

}