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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.thread.ThreadUtil;
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.exception.PrologException;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.Restriction;
import com.prolog.framework.core.restriction.Restrictions;
import com.prolog.framework.lock.DistributedLock;
import com.prolog.product.core.framework.entity.UserEntity;
import com.prolog.product.core.framework.util.PlgFxUsrUtils;
import com.prolog.rdc.tms.constant.BmsConstant;
import com.prolog.rdc.tms.constant.enums.LogTypeEnum;
import com.prolog.rdc.tms.dao.mapper.*;
import com.prolog.rdc.tms.model.dto.SySParameterDTO;
import com.prolog.rdc.tms.model.dto.income.InventedLoadingBillIncomeFeeDTO;
import com.prolog.rdc.tms.model.entity.*;
import com.prolog.rdc.tms.model.vo.*;
import com.prolog.rdc.tms.model.vo.income.InventedLoadingFeeVO;
import com.prolog.rdc.tms.rmi.feign.MaindataFeign;
import com.prolog.rdc.tms.service.BillDownloadService;
import com.prolog.rdc.tms.service.BillIncomeCostStrategyService;
import com.prolog.rdc.tms.service.BillPayCostStrategyService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.plugin.PluginException;
import org.redisson.api.RLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
@Slf4j
public class BillDownloadServiceImpl implements BillDownloadService {

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

    @Autowired
    private ShipBillHeadMapper shipBillHeadMapper;
    @Autowired
    private LoadingBillHeadMapper loadingBillHeadMapper;
    @Autowired
    private ShipBillDetailMapper shipBillDetailMapper;
    @Autowired
    private MaindataFeign maindataTmsResource;
    @Autowired
    private BmsLogOperationMapper logOperationMapper;
    @Autowired
    private BmsShipBillAdjustMapper shipBillAdjustMapper;
    @Autowired
    private BmsShipBillPackageMapper shipBillPackageMapper;
    @Autowired
    private BillIncomeCostStrategyService billIncomeCostStrategyService;
    @Autowired
    private BillPayCostStrategyService billPayCostStrategyService;

    @Autowired
    private BmsBillIncomeFeeHeaderMapper bmsBillIncomeFeeHeaderMapper;
    @Autowired
    private BmsBillIncomeFeeDetailMapper bmsBillIncomeFeeDetailMapper;

    @Autowired
    private DistributedLock distributedLock;

    @Autowired
    private BillDownloadService downloadService;


    /**
     * 单据下传
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void downloadSave(List<BillDownloadVO> vos) {
        logger.info("下传单据：" + JSONObject.toJSONString(vos));
        if (ObjectUtil.isEmpty(vos)) {
            return;
        }
        UserEntity user = new UserEntity();
        user.setEnterpriseId(vos.get(0).getEnterpriseId());
        user.setEnterpriseName(vos.get(0).getEnterpriseName());
        user.setUsername(vos.get(0).getUserName());
        user.setNickname(vos.get(0).getNickName());

        Date date = DateUtil.date();

        for (BillDownloadVO vo : vos) {
            // 运单保存
            saveShipBillHeader(vo, user, date);

//            String loadingBillNo = vo.getShipBillVOList().get(0).getLoadingBillNo();
//            Criteria loadingBillHeaderCriteria = Criteria.forClass(BmsLoadingBillHeaderModel.class);
//            Restriction r1 = Restrictions.eq(BmsLoadingBillHeaderModel::getEnterpriseId, user.getEnterpriseId());
//            Restriction r2 = Restrictions.eq(BmsLoadingBillHeaderModel::getLoadingBillNo, loadingBillNo);
//            try {
//                // 派车单不重复下传
//                loadingBillHeaderCriteria.setRestriction(Restrictions.and(r1, r2));
//                List<BmsLoadingBillHeaderModel> loadingBillHeaderModels = loadingBillHeadMapper.findByCriteria(loadingBillHeaderCriteria);
//                if (ObjectUtil.isEmpty(loadingBillHeaderModels)) {
//                    // 派车单保存
//                    saveLoadingBillHeader(vo, user, date);
//                } else {
//                    List<LoadingBillVO> loadingBillVOS = vo.getLoadingBillVOList();
//                    if (ObjectUtil.isNotEmpty(loadingBillVOS)) {
//                        loadingBillHeaderModels.stream().forEach(loadingBill -> {
//                            List<Integer> sts = loadingBillVOS.stream().filter(billVO -> loadingBill.getLoadingBillNo().equals(billVO.getLoadingBillNo())).map(LoadingBillVO::getStatus).collect(Collectors.toList());
//                            if (ObjectUtil.isNotEmpty(sts)) {
//                                loadingBill.setStatus(sts.get(0));
//                                loadingBill.setUpdatetime(DateUtil.date());
//                                loadingBillHeadMapper.update(loadingBill);
//                            }
//                        });
//                    }
//                    log.info("派车单{}已经下传,本次{}下传的派车单不重新计费", loadingBillNo, date);
//                }
//            } catch (Exception e) {
//                e.printStackTrace();
//                log.info("派车单:{},下传失败:{}", JSON.toJSONString(loadingBillNo), ExceptionUtil.stacktraceToString(e));
//                throw new PrologException(StrUtil.format("派车单下传失败"));
//            }
        }
        log.info("单据下传保存完成");
        ThreadUtil.execute(() -> {
            for (BillDownloadVO vo : vos) {
                // redis key
                List<String> redisKeys = Lists.newArrayList("REDIS_LOCK_KEY");
                redisKeys.add(vo.getShipBillVOList().get(0).getLoadingBillNo());
                String redisKey = redisKeys.stream().collect(Collectors.joining("-"));
                // 锁等待时间
                long waittimeout = 50 * 1000;
                // 锁过期时间，可根据业务代码执行时间配置
                long timeout = 50 * 1000;
                RLock lock = distributedLock.lockForLock(redisKey, waittimeout, timeout);
                if (ObjectUtil.isNotNull(lock)) {
                    try {
                        // 支出费用计算
                        downloadService.calculatePayFee(vo, user);
                    } catch (Exception e) {
                    } finally {
                        if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                            lock.unlock();
                        }
                    }
                } else {
                    throw new PrologException("获取单据计费锁资源失败,相同单据不能同时计费");
                }
            }
        });
    }

    /**
     * 计算支出
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void calculatePayFee(BillDownloadVO vo, UserEntity user) {
        List<ShipBillVO> shipBillVOList = vo.getShipBillVOList();
        for (ShipBillVO shipBillVO : shipBillVOList) {
            // 计算运单
            Criteria headerCriteria = Criteria.forClass(BmsShipBillHeaderModel.class);
            Restriction r11 = Restrictions.eq(BmsShipBillHeaderModel::getEnterpriseId, user.getEnterpriseId());
            Restriction r21 = Restrictions.eq(BmsShipBillHeaderModel::getShipbillNo, shipBillVO.getShipbillNo());
            headerCriteria.setRestriction(Restrictions.and(r11, r21));
            List<BmsShipBillHeaderModel> shipBillHeaderModels = shipBillHeadMapper.findByCriteria(headerCriteria);
            if (ObjectUtil.isEmpty(shipBillHeaderModels)) {
                throw new PrologException(StrUtil.format("运单{}不存在", shipBillVO.getShipbillNo()));
            }
            BmsShipBillHeaderModel shipBillHeaderModel = shipBillHeaderModels.get(0);

            List<BmsShipBillDetailModel> detailModels = getShipBillDeatils(shipBillHeaderModel.getShipbillNo(), user);
            try {
                // 计算托运物流支出费用
                Integer rs = billPayCostStrategyService.costShipBillTrailerStrategy(shipBillHeaderModel, detailModels, "1", user);
                if (Integer.valueOf(200).equals(rs)) {
                    shipBillHeaderModel.setPayFeeCltType(10);
                    shipBillHeadMapper.update(shipBillHeaderModel);
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.info(StrUtil.format("运单{},支出计费失败:{}"), JSON.toJSONString(shipBillHeaderModel), ExceptionUtil.stacktraceToString(e));
            }
        }
    }

    public List<BmsShipBillDetailModel> getShipBillDeatils(String shipBillNo, UserEntity user) {
        Criteria detailCriteria = Criteria.forClass(BmsShipBillDetailModel.class);
        Restriction r11 = Restrictions.eq(BmsShipBillDetailModel::getEnterpriseId, user.getEnterpriseId());
        Restriction r21 = Restrictions.eq(BmsShipBillDetailModel::getShipbillNo, shipBillNo);
        detailCriteria.setRestriction(Restrictions.and(r11, r21));
        List<BmsShipBillDetailModel> detailModels = shipBillDetailMapper.findByCriteria(detailCriteria);
        return detailModels;
    }

    // 计算收入
    public void calculateIncomeFee(BillDownloadVO vo, UserEntity user) {
        List<ShipBillVO> shipBillVOList = vo.getShipBillVOList();
        // 计算派车单和运单
        Criteria loadingBillHeaderCriteria = Criteria.forClass(BmsLoadingBillHeaderModel.class);
        Restriction r1 = Restrictions.eq(BmsLoadingBillHeaderModel::getEnterpriseId, user.getEnterpriseId());
        Restriction r2 = Restrictions.eq(BmsLoadingBillHeaderModel::getLoadingBillNo, shipBillVOList.get(0).getLoadingBillNo());
        loadingBillHeaderCriteria.setRestriction(Restrictions.and(r1, r2));
        List<BmsLoadingBillHeaderModel> loadingBillHeaderModels = loadingBillHeadMapper.findByCriteria(loadingBillHeaderCriteria);

        Integer shipBillFeeCltType = 30;

        if (ObjectUtil.isNotEmpty(loadingBillHeaderModels)) {
            for (BmsLoadingBillHeaderModel loadingBillHeaderModel : loadingBillHeaderModels) {
                // 派车单收入计费
                try {
                    Integer rs = billIncomeCostStrategyService.loadingBillCostStrategy(loadingBillHeaderModel, "1", user);
                    if (Integer.valueOf(200).equals(rs)) {
                        loadingBillHeaderModel.setFeeCltType(20);
                        loadingBillHeadMapper.update(loadingBillHeaderModel);
                        shipBillFeeCltType = 20;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info("派车单收入计费失败:{}", JSON.toJSONString(loadingBillHeaderModel));
                }
            }
        }

        Criteria shipBillHeaderCriteria = Criteria.forClass(BmsShipBillHeaderModel.class);
        Boolean invented = Boolean.FALSE;
        for (ShipBillVO shipBillVO : shipBillVOList) {
            r1 = Restrictions.eq(BmsShipBillHeaderModel::getEnterpriseId, user.getEnterpriseId());
            r2 = Restrictions.eq(BmsShipBillHeaderModel::getShipbillNo, shipBillVO.getShipbillNo());
            shipBillHeaderCriteria.setRestriction(Restrictions.and(r1, r2));
            List<BmsShipBillHeaderModel> shipBillHeaderModels = shipBillHeadMapper.findByCriteria(shipBillHeaderCriteria);
            if (ObjectUtil.isEmpty(shipBillHeaderModels)) {
                throw new PrologException(StrUtil.format("运单{}不存在", shipBillVO.getShipbillNo()));
            }
            BmsShipBillHeaderModel shipBillHeaderModel = shipBillHeaderModels.get(0);
            if (Integer.valueOf(20).equals(shipBillFeeCltType)) {
                shipBillFeeCltType = 20;
                log.info(StrUtil.format("按派车单计费,运单{}不再计费", shipBillVO.getShipbillNo()));
            } else {
                // 运单收入计费
                List<BmsShipBillDetailModel> detailModels = getShipBillDeatils(shipBillHeaderModel.getShipbillNo(), user);
                try {
                    Integer rs = billIncomeCostStrategyService.shipBillCostStrategy(shipBillHeaderModel, detailModels, "1", user);
                    if (Integer.valueOf(200).equals(rs)) {
                        shipBillFeeCltType = 10;
                        invented = Boolean.TRUE;
                    } else {
                        shipBillFeeCltType = 30;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    log.info(StrUtil.format("运单收入计费失败:{}"), JSON.toJSONString(shipBillHeaderModel));
                }
            }
            shipBillHeaderModel.setFeeCltType(shipBillFeeCltType);
            Long loadingId = null;
            if (ObjectUtil.isNotEmpty(loadingBillHeaderModels)) {
                List<BmsLoadingBillHeaderModel> loadingModels = loadingBillHeaderModels.stream().filter(load -> load.getCargoOwnerId().equals(shipBillHeaderModel.getCargoOwnerId())).collect(Collectors.toList());
                if (ObjectUtil.isNotEmpty(loadingModels)) {
                    loadingId = loadingModels.get(0).getId();
                }
            }
            shipBillHeaderModel.setLoadingId(loadingId);
            shipBillHeadMapper.update(shipBillHeaderModel);
        }

        // 运单计费成功时，维护虚拟派车单的计费数据
        if (Boolean.FALSE.equals(invented)) {
            return;
        }

        // 维护虚拟派车单的计费数据
        for (BmsLoadingBillHeaderModel loadingHeaderModel : loadingBillHeaderModels) {
            loadingHeaderModel.setFeeCltType(shipBillFeeCltType);
            loadingBillHeadMapper.update(loadingHeaderModel);

            r1 = Restrictions.eq(BmsShipBillHeaderModel::getEnterpriseId, user.getEnterpriseId());
            r2 = Restrictions.eq(BmsShipBillHeaderModel::getLoadingBillNo, loadingHeaderModel.getLoadingBillNo());
            Restriction r3 = Restrictions.eq(BmsShipBillHeaderModel::getCargoOwnerCode, loadingHeaderModel.getCargoOwnerCode());
            Restriction r4 = Restrictions.eq(BmsShipBillHeaderModel::getFeeCltType, 10);
            shipBillHeaderCriteria.setRestriction(Restrictions.and(r1, r2, r3,r4));
            List<BmsShipBillHeaderModel> allShipBillHeaderModels = shipBillHeadMapper.findByCriteria(shipBillHeaderCriteria);

            inventedLoadingFee(loadingHeaderModel, allShipBillHeaderModels, user);
        }
    }

    // 维护虚拟派车单的计费数据
    public void inventedLoadingFee(BmsLoadingBillHeaderModel loadingBillHeaderModel, List<BmsShipBillHeaderModel> shipBillHeaderModels, UserEntity user) {
        log.info("下传计费,维护虚拟派车单费用...");
        if (ObjectUtil.isEmpty(shipBillHeaderModels)) {
            log.info("计费成功的运单不存在,不维护虚拟派车单费用");
        }
        List<BmsBillIncomeFeeDetailModel> feeDetailModels = Lists.newArrayList();
        List<BmsBillIncomeFeeHeaderModel> feeHeaderModels = Lists.newArrayList();
        for (BmsShipBillHeaderModel headerModel : shipBillHeaderModels) {
            Criteria bmsFeeHeaderCriteria = Criteria.forClass(BmsBillIncomeFeeHeaderModel.class);
            Restriction r1 = Restrictions.eq(BmsBillIncomeFeeHeaderModel::getBillId, headerModel.getId());
            Restriction r2 = Restrictions.eq(BmsBillIncomeFeeHeaderModel::getBillNo, headerModel.getShipbillNo());
            Restriction r3 = Restrictions.eq(BmsBillIncomeFeeHeaderModel::getCostStatus, 1);
            bmsFeeHeaderCriteria.setRestriction(Restrictions.and(r1, r2,r3));
            List<BmsBillIncomeFeeHeaderModel> tmpFeeHeaderModels = bmsBillIncomeFeeHeaderMapper.findByCriteria(bmsFeeHeaderCriteria);
            if (ObjectUtil.isEmpty(tmpFeeHeaderModels)) {
                log.info("运单的费用主表不存在,虚拟派车单费用不维护");
                continue;
            }
            feeHeaderModels.addAll(tmpFeeHeaderModels);

            Criteria bmsFeeDetailCriteria = Criteria.forClass(BmsBillIncomeFeeDetailModel.class);
            r1 = Restrictions.eq(BmsBillIncomeFeeDetailModel::getBillId, headerModel.getId());
            r2 = Restrictions.eq(BmsBillIncomeFeeDetailModel::getBillNo, headerModel.getShipbillNo());
            bmsFeeDetailCriteria.setRestriction(Restrictions.and(r1, r2));
            List<BmsBillIncomeFeeDetailModel> tmpFeeDetailModels = bmsBillIncomeFeeDetailMapper.findByCriteria(bmsFeeDetailCriteria);
            if (ObjectUtil.isEmpty(tmpFeeDetailModels)) {
                log.info("运单的费用明细不存在,虚拟派车单费用不维护");
                continue;
            }
            feeDetailModels.addAll(tmpFeeDetailModels);
        }
        if (ObjectUtil.isEmpty(feeHeaderModels)) {
            return;
        }
        Date date = DateUtil.date();
        BmsBillIncomeFeeHeaderModel shipBillHeaderFee = feeHeaderModels.get(0);
        BigDecimal allIncomeAmount = feeHeaderModels.stream().map(BmsBillIncomeFeeHeaderModel::getIncomeAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal allContractIncomeAmount = feeHeaderModels.stream().map(BmsBillIncomeFeeHeaderModel::getContractIncomeAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

        InventedLoadingFeeVO feeVO = new InventedLoadingFeeVO();
        feeVO.setBillId(loadingBillHeaderModel.getId());
        feeVO.setBillNo(loadingBillHeaderModel.getLoadingBillNo());
        List<InventedLoadingBillIncomeFeeDTO> inventedLoadingFeeDTOS = bmsBillIncomeFeeHeaderMapper.findInventedLoadingBillFee(feeVO);
        log.info("获取的虚拟派车单费用主表数据:{}", JSON.toJSONString(inventedLoadingFeeDTOS));

        if (ObjectUtil.isNotEmpty(inventedLoadingFeeDTOS)) {
            bmsBillIncomeFeeHeaderMapper.deleteByIds(inventedLoadingFeeDTOS.stream().map(InventedLoadingBillIncomeFeeDTO::getId).toArray(), BmsBillIncomeFeeHeaderModel.class);
        }

        // 新增费用主表
        BmsBillIncomeFeeHeaderModel feeHeaderModel = new BmsBillIncomeFeeHeaderModel();
        feeHeaderModel.setBillNo(loadingBillHeaderModel.getLoadingBillNo());
        feeHeaderModel.setBillId(loadingBillHeaderModel.getId());
        feeHeaderModel.setBillType(20);
        feeHeaderModel.setSettlementDepartment(loadingBillHeaderModel.getSettlementDepartment());
        feeHeaderModel.setSettlementDepartmentId(loadingBillHeaderModel.getSettlementDepartmentId());
        feeHeaderModel.setSettlementDepartmentCode(loadingBillHeaderModel.getSettlementDepartmentCode());
        feeHeaderModel.setCheckStatus(0);
        feeHeaderModel.setSettlementStatus(0);
        feeHeaderModel.setCostContract(shipBillHeaderFee.getCostContract());
        feeHeaderModel.setCostDate(date);
        feeHeaderModel.setSettlementCompanyId(shipBillHeaderFee.getSettlementCompanyId());
        feeHeaderModel.setSettlementCompanyCode(shipBillHeaderFee.getSettlementCompanyCode());
        feeHeaderModel.setSettlementCompany(shipBillHeaderFee.getSettlementCompany());
        feeHeaderModel.setAbnormalStatus(0);
        feeHeaderModel.setCostStatus(1);
        // 支出总价 类型为收入的含税金额
        feeHeaderModel.setIncomeAmount(allIncomeAmount);
        // 类型为收入的来源是价格表生成 含税金额
        feeHeaderModel.setContractIncomeAmount(allContractIncomeAmount);
        feeHeaderModel.setCreateperson(user.getNickname());
        feeHeaderModel.setCreatetime(date);
        feeHeaderModel.setUpdateperson(user.getNickname());
        feeHeaderModel.setUpdatetime(date);
        feeHeaderModel.setEnterpriseId(user.getEnterpriseId());
        feeHeaderModel.setEnterpriseName(user.getEnterpriseName());
        feeHeaderModel.setSettlementStatus(BmsConstant.SETTLEMENT_STATUS_0);
        feeHeaderModel.setOriginalFeeId(feeHeaderModels.stream().map(fee -> fee.getId().toString()).collect(Collectors.joining(",")));
        bmsBillIncomeFeeHeaderMapper.save(feeHeaderModel);

        // 虚拟费用明细新增
        for (BmsBillIncomeFeeDetailModel feeDetailModel : feeDetailModels) {
            feeDetailModel.setOriginalFeeDetailId(feeDetailModel.getId());
            feeDetailModel.setBillId(loadingBillHeaderModel.getId());
            feeDetailModel.setBillNo(loadingBillHeaderModel.getLoadingBillNo());
            RestMessage<String> expenseNo = maindataTmsResource.getCodeNumberByCode(BmsConstant.SHIP_BILL_EXPENSE_NO, "L");
            if (!expenseNo.isSuccess() || ObjectUtil.isEmpty(expenseNo)) {
                throw new PluginException("获取费用单号为空");
            }
            feeDetailModel.setExpenseNo(expenseNo.getData());
            feeDetailModel.setId(null);
        }
        bmsBillIncomeFeeDetailMapper.saveBatch(feeDetailModels);
    }

    public void saveLoadingBillHeader(BillDownloadVO vo, UserEntity user, Date date) {
        List<LoadingBillVO> loadingBillVOS = vo.getLoadingBillVOList();
        if (ObjectUtil.isEmpty(loadingBillVOS)) {
            return;
        }
        for (LoadingBillVO billVO : loadingBillVOS) {
            BmsLoadingBillHeaderModel loadingBillHeaderModel = new BmsLoadingBillHeaderModel();
            BeanUtil.copyProperties(billVO, loadingBillHeaderModel);
            loadingBillHeaderModel.setSource(BmsConstant.TMS_DOWN);
            loadingBillHeaderModel.setSynchronousStatus(BmsConstant.SYNCHRONOUS_STATUS_SUCCESS);
            loadingBillHeaderModel.setSynchronousDate(date);

            loadingBillHeaderModel.setShipbillNoList(billVO.getShipbillNos());
            loadingBillHeaderModel.setOrganizationId(user.getOrganizationId());
            loadingBillHeaderModel.setOrganizationCode(String.valueOf(user.getOrganizationId()));
            loadingBillHeaderModel.setOrganizationName(user.getOrgnaizationName());
            loadingBillHeaderModel.setCreateperson(user.getNickname());
            loadingBillHeaderModel.setCreatetime(DateUtil.date());
            loadingBillHeaderModel.setUpdateperson(user.getNickname());
            loadingBillHeaderModel.setUpdatetime(DateUtil.date());
            loadingBillHeaderModel.setEnterpriseId(user.getEnterpriseId());
            loadingBillHeaderModel.setEnterpriseName(user.getEnterpriseName());
            loadingBillHeaderModel.setTotalPayPcs(billVO.getTotalPcs());
            loadingBillHeaderModel.setTotalPayWeight(billVO.getTotalWeight());
            loadingBillHeaderModel.setTotalPayVolume(billVO.getTotalVolume());
            loadingBillHeaderModel.setTotalPayMileage(billVO.getTotalMileage());
            loadingBillHeaderModel.setTotalPayMileageMap(billVO.getTotalMileageMap());
            loadingBillHeaderModel.setFeeCltType(30);
            loadingBillHeaderModel.setPayFeeCltType(30);
            loadingBillHeaderModel.setVehicleTypeCategory(billVO.getVehicleTypeCategory());
            loadingBillHeadMapper.save(loadingBillHeaderModel);

            // 日志操作
            BmsLogOperationModel logVO = new BmsLogOperationModel();
            logVO.setCreatetime(DateUtil.date());
            logVO.setCreateperson(user.getNickname());
            logVO.setEnterpriseId(user.getEnterpriseId());
            logVO.setEnterpriseName(user.getEnterpriseName());
            logVO.setOrderNo(loadingBillHeaderModel.getLoadingBillNo() + "," + loadingBillHeaderModel.getCargoOwnerName());
            logVO.setLogMemo("新增派车单");
            logVO.setLogType(LogTypeEnum.LOADING_BILL.getCode());
            logOperationMapper.save(logVO);
        }
    }

    public void saveShipBillHeader(BillDownloadVO vo, UserEntity user, Date date) {
        // 运单
        List<ShipBillVO> shipBillVOS = vo.getShipBillVOList();
        if (ObjectUtil.isEmpty(shipBillVOS)) {
            throw new PrologException("下传运单为空");
        }
        for (ShipBillVO shipBillVO : shipBillVOS) {
            Criteria criteria = Criteria.forClass(BmsShipBillHeaderModel.class);
            Restriction r1 = Restrictions.eq(BmsShipBillHeaderModel::getShipbillNo, shipBillVO.getShipbillNo());
            Restriction r2 = Restrictions.eq(BmsShipBillHeaderModel::getEnterpriseId, user.getEnterpriseId());
            criteria.setRestriction(Restrictions.and(r1, r2));
            List<BmsShipBillHeaderModel> modelList = shipBillHeadMapper.findByCriteria(criteria);
            if (ObjectUtil.isNotEmpty(modelList)) {
                log.info("下传运单{}重复", shipBillVO.getShipbillNo());
                continue;
            }
            BmsShipBillHeaderModel model = new BmsShipBillHeaderModel();
            BeanUtil.copyProperties(shipBillVO, model);
            model.setSource(BmsConstant.TMS_DOWN);
            model.setSynchronousStatus(BmsConstant.SYNCHRONOUS_STATUS_SUCCESS);
            model.setSynchronousDate(date);
            model.setShipbillNo(shipBillVO.getShipbillNo());
            model.setOrganizationId(user.getOrganizationId());
            model.setOrganizationCode(String.valueOf(user.getOrganizationId()));
            model.setOrganizationName(user.getOrgnaizationName());
            model.setCreateperson(user.getNickname());
            model.setCreatetime(DateUtil.date());
            model.setUpdateperson(user.getNickname());
            model.setUpdatetime(DateUtil.date());
            model.setEnterpriseId(user.getEnterpriseId());
            model.setEnterpriseName(user.getEnterpriseName());
            model.setLoadingBillNo(shipBillVO.getLoadingBillNo());
            model.setTotalPayPcs(shipBillVO.getTotalPcs());
            model.setTotalPayWeight(shipBillVO.getTotalWeight());
            model.setTotalPayVolume(shipBillVO.getTotalVolume());
            model.setTotalPayMileage(shipBillVO.getTotalMileage());

            model.setStatus(BmsConstant.SHIPBILL_STATUS_END);
            model.setShipBillStatus(BmsConstant.SHIPBILL_STATUS_END);
            List<String> provinceName = Lists.newArrayList();
            provinceName.add(shipBillVO.getShipperProvinceName());
            provinceName.add(shipBillVO.getShipperCityName());
            provinceName.add(shipBillVO.getShipperDistrictName());
            model.setShipperProvinceCityDistrict(provinceName.stream().collect(Collectors.joining("")));

            provinceName.clear();
            provinceName.add(shipBillVO.getConsigneeProvinceName());
            provinceName.add(shipBillVO.getConsigneeCityName());
            provinceName.add(shipBillVO.getConsigneeDistrictName());
            model.setConsigneeProvinceCityDistrict(provinceName.stream().collect(Collectors.joining("")));
            model.setCompletionDate(date);

            model.setSettlementStatus(BmsConstant.SETTLEMENT_STATUS_0);

            List<BmsShipBillDetailVO> list = shipBillVO.getBmsShipBillDetailList();
            AtomicInteger seqNo = new AtomicInteger(1);
            RestMessage<SySParameterDTO> parameterModel = maindataTmsResource.queryParameter();
            if (ObjectUtil.isEmpty(parameterModel) || !parameterModel.isSuccess()) {
                throw new PluginException("请先配置系统参数");
            }
            Integer weightPrecision = parameterModel.getData().getWeightPrecision();
            Integer weightInteger = parameterModel.getData().getWeightInteger();

            Integer volumePrecision = parameterModel.getData().getVolumePrecision();
            Integer volumeInteger = parameterModel.getData().getVolumeInteger();
            BigDecimal totWeight = new BigDecimal(0);
            BigDecimal totVolume = new BigDecimal(0);
            List<BmsShipBillDetailModel> detailModels = Lists.newArrayList();
            for (BmsShipBillDetailVO shipBillDetailVO : list) {
                log.info("运单明细数据:" + JSON.toJSONString(list));
                BmsShipBillDetailModel detailModel = new BmsShipBillDetailModel();
                BeanUtils.copyProperties(shipBillDetailVO, detailModel);
                BigDecimal weight = shipBillDetailVO.getWeight();
                BigDecimal volume = shipBillDetailVO.getVolume();

                // 四舍五入
                if (weightInteger == 1) {
                    detailModel.setWeight(weight.divide(BigDecimal.valueOf(1), weightPrecision, RoundingMode.HALF_UP));
                }
                // 向上取整
                if (weightInteger == 2) {
                    detailModel.setWeight(weight.divide(BigDecimal.valueOf(1), weightPrecision, RoundingMode.UP));
                }
                // 向下取整
                if (weightInteger == 3) {
                    detailModel.setWeight(weight.divide(BigDecimal.valueOf(1), weightPrecision, RoundingMode.DOWN));
                }
                // 四舍五入
                if (volumeInteger == 1) {
                    detailModel.setVolume(volume.divide(BigDecimal.valueOf(1), volumePrecision, RoundingMode.HALF_UP));
                }
                // 向上取整
                if (volumeInteger == 2) {
                    detailModel.setVolume(volume.divide(BigDecimal.valueOf(1), volumePrecision, RoundingMode.UP));
                }
                // 向下取整
                if (volumeInteger == 3) {
                    detailModel.setVolume(volume.divide(BigDecimal.valueOf(1), volumePrecision, RoundingMode.DOWN));
                }
                detailModel.setShipbillNo(model.getShipbillNo());
                detailModel.setCreateperson(user.getNickname());
                detailModel.setCreatetime(DateUtil.date());
                detailModel.setUpdateperson(user.getNickname());
                detailModel.setUpdatetime(DateUtil.date());
                detailModel.setEnterpriseId(user.getEnterpriseId());
                detailModel.setEnterpriseName(user.getEnterpriseName());
                detailModel.setSeqno(seqNo.get());
                detailModel.setPayPcs(detailModel.getPcs());
                detailModel.setPayWeight(detailModel.getWeight());
                detailModel.setPayVolume(detailModel.getVolume());

                shipBillDetailMapper.save(detailModel);
                totWeight = totWeight.add(detailModel.getWeight());
                totVolume = totVolume.add(detailModel.getVolume());
                seqNo.getAndIncrement();
            }

            List<BmsShipBillPackVO> packList = shipBillVO.getPackList();
            AtomicInteger packSeqNo = new AtomicInteger(1);
            BigDecimal packageTotalVolume = BigDecimal.valueOf(0);
            BigDecimal packageTotalWeight = BigDecimal.valueOf(0);
            if (ObjectUtil.isNotEmpty(packList)) {
                for (BmsShipBillPackVO shipBillDetailVO : packList) {
                    BmsShipBillPackageModel detailModel = new BmsShipBillPackageModel();
                    BeanUtils.copyProperties(shipBillDetailVO, detailModel);
                    detailModel.setPackageTotalWeight(shipBillDetailVO.getWeight());
                    detailModel.setPackageTotalVolume(shipBillDetailVO.getVolume());
                    detailModel.setShipbillNo(model.getShipbillNo());
                    detailModel.setCreateperson(user.getNickname());
                    detailModel.setCreatetime(DateUtil.date());
                    packageTotalVolume = NumberUtil.add(packageTotalVolume, detailModel.getPackageTotalVolume());
                    packageTotalWeight = NumberUtil.add(packageTotalWeight, detailModel.getPackageTotalWeight());
                    detailModel.setUpdateperson(user.getNickname());
                    detailModel.setUpdatetime(DateUtil.date());
                    detailModel.setEnterpriseId(user.getEnterpriseId());
                    detailModel.setEnterpriseName(user.getEnterpriseName());
                    detailModel.setSeqNo(packSeqNo.get());
                    packSeqNo.getAndIncrement();
                    shipBillPackageMapper.save(detailModel);
                }
            }
            // 调整单
            BmsShipBillAdjustModel adjustModel = new BmsShipBillAdjustModel();
            adjustModel.setShipbillNo(shipBillVO.getShipbillNo());
            adjustModel.setAdjustMemo("新增件数" + model.getTotalPcs());
            adjustModel.setAdjustDate(DateUtil.date());
            adjustModel.setAdjustName(user.getNickname());
            adjustModel.setCreateperson(user.getNickname());
            adjustModel.setCreatetime(DateUtil.date());
            adjustModel.setUpdateperson(user.getNickname());
            adjustModel.setUpdatetime(DateUtil.date());
            adjustModel.setEnterpriseId(user.getEnterpriseId());
            adjustModel.setEnterpriseName(user.getEnterpriseName());
            shipBillAdjustMapper.save(adjustModel);

            // 日志操作
            BmsLogOperationModel logVO = new BmsLogOperationModel();
            logVO.setCreatetime(DateUtil.date());
            logVO.setCreateperson(user.getNickname());
            logVO.setEnterpriseId(user.getEnterpriseId());
            logVO.setEnterpriseName(user.getEnterpriseName());
            logVO.setOrderNo(shipBillVO.getShipbillNo());
            logVO.setLogMemo("新增运单");
            logVO.setLogType(LogTypeEnum.SHIP_BILL.getCode());
            logOperationMapper.save(logVO);

            model.setPackageTotalVolume(packageTotalVolume);
            model.setPackageTotalWeight(packageTotalWeight);
            model.setAbnormalStatus(0);
            model.setTotalWeight(shipBillVO.getTotalWeight());
            model.setTotalVolume(shipBillVO.getTotalVolume());

            model.setSettlementDepartment(shipBillVO.getSettlementDepartment());
            model.setSettlementDepartmentId(shipBillVO.getSettlementDepartmentId());
            model.setSettlementDepartmentCode(shipBillVO.getSettlementDepartmentCode());
            model.setDepartmentName(shipBillVO.getDepartmentName());
            model.setFeeCltType(30);
            model.setPayFeeCltType(30);
            model.setCarrierId(shipBillVO.getCarriersId());
            model.setCarrierCode(shipBillVO.getCarriersCode());
            model.setCarrierName(shipBillVO.getCarriersName());
            shipBillHeadMapper.save(model);
        }
    }

    /**
     * 单据收入费用重新核算
     */
    @Override
    public Integer calculateBillFee(CalculateBillFeeVO vo) {
        if (ObjectUtil.isEmpty(vo)) {
            throw new PrologException("单据费用核算请求不为空");
        }
        UserEntity user = PlgFxUsrUtils.getUserEntity();
        BmsLoadingBillHeaderModel loadingBillHeaderModel = vo.getLoadingBillHeaderModel();
        if (ObjectUtil.isEmpty(loadingBillHeaderModel)) {
            throw new PrologException("派车单不为空");
        }
        List<CalculateFeeShipBillVO> shipBillHeadModels = vo.getShipBillModels();
        if (ObjectUtil.isEmpty(shipBillHeadModels)) {
            throw new PrologException("运单不为空");
        }
        Integer feeCltType = loadingBillHeaderModel.getFeeCltType();

        // 过滤符合条件的运单
        Integer priceCostType = null;
        RestMessage<SySParameterDTO> parameterModel = maindataTmsResource.queryParameter();
        if (ObjectUtil.isNotEmpty(parameterModel) && parameterModel.isSuccess()) {
            priceCostType = parameterModel.getData().getPriceCostType();
        }
        // redis key
        List<String> redisKeys = Lists.newArrayList("REDIS_LOCK_KEY");
        redisKeys.add(loadingBillHeaderModel.getLoadingBillNo());
        redisKeys.add(loadingBillHeaderModel.getCargoOwnerId().toString());
        String redisKey = redisKeys.stream().collect(Collectors.joining("-"));
        // 锁等待时间
        long waittimeout = 50 * 1000;
        // 锁过期时间，可根据业务代码执行时间配置
        long timeout = 50 * 1000;
        RLock lock = distributedLock.lockForLock(redisKey, waittimeout, timeout);
        if (ObjectUtil.isNotNull(lock)) {
            try {
                // 派车单支出计费 ...
                if (BmsConstant.PRICE_COST_TYPE_PAY.equals(priceCostType)) {
                    log.info("价格表计费方式为只计算支出");
                    return feeCltType;
                }
                // 派车单收入计费
                Integer rs = billIncomeCostStrategyService.loadingBillCostStrategy(loadingBillHeaderModel, "1", user);
                if (Integer.valueOf(200).equals(rs)) {
                    loadingBillHeaderModel.setFeeCltType(20);
                    feeCltType = 20;
                } else {
                    log.info("派车单收入计费返回:{},核算计费失败:{}", rs, JSON.toJSONString(loadingBillHeaderModel));
                    throw new PrologException(StrUtil.format("派车单{}收入计费失败", loadingBillHeaderModel.getLoadingBillNo()));
                }
            } catch (Exception e) {
                feeCltType = 30;
                e.printStackTrace();
                log.info("派车单核算计费失败:{}", JSON.toJSONString(loadingBillHeaderModel.getLoadingBillNo()));
            } finally {
                if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            log.info("核算计费获取锁失败:{}", JSON.toJSONString(loadingBillHeaderModel.getLoadingBillNo()));
            throw new PrologException(StrUtil.format("核算计费获取锁失败"));
        }
        // 运单核算计费
        Boolean invented = Boolean.FALSE;
        List<BmsShipBillHeaderModel> shipBillHeaderModels = Lists.newArrayList();
        for (CalculateFeeShipBillVO shipBillVO : shipBillHeadModels) {
            BmsShipBillHeaderModel shipBillHeaderModel = shipBillVO.getShipBillHeaderModel();
            billIncomeCostStrategyService.deleteFeeDtl(shipBillHeaderModel.getShipbillNo(), shipBillHeaderModel.getId(), user, "1");

            if (Integer.valueOf(20).equals(feeCltType)) {
                continue;
            }
            List<BmsShipBillDetailModel> detailModels = shipBillVO.getDetailModels();
            try {
                Integer rs = billIncomeCostStrategyService.shipBillCostStrategy(shipBillHeaderModel, detailModels, "1", user);
                if (Integer.valueOf(200).equals(rs)) {
                    feeCltType = Integer.valueOf(10);
                    invented = Boolean.TRUE;
                    shipBillHeaderModels.add(shipBillHeaderModel);
                    log.info("运单收入计费返回:{},核算计费成功:{}", rs, JSON.toJSONString(shipBillHeaderModel));
                } else {
                    log.info("运单收入计费返回:{},核算计费失败:{}", rs, JSON.toJSONString(shipBillHeaderModel));
                    throw new PrologException(StrUtil.format("运单{}收入计费失败", shipBillHeaderModel.getShipbillNo()));
                }
            } catch (Exception e) {
                feeCltType = 30;
                e.printStackTrace();
                throw new PrologException(StrUtil.format("运单{}收入核算计费失败", shipBillHeaderModel.getShipbillNo()));
            }
        }

        if (Boolean.TRUE.equals(invented)) {
            // 构建虚拟派车单费用
            inventedLoadingFee(loadingBillHeaderModel, shipBillHeaderModels, user);
        }
        return feeCltType;
    }

    @Override
    public Integer calculatePayBillFee(CalculateBillFeeVO vo) {
        if (ObjectUtil.isEmpty(vo)) {
            throw new PrologException("单据费用核算请求不为空");
        }
        UserEntity user = PlgFxUsrUtils.getUserEntity();
//        BmsLoadingBillHeaderModel loadingBillHeaderModel = vo.getLoadingBillHeaderModel();

//        if (ObjectUtil.isEmpty(loadingBillHeaderModel)) {
//            throw new PrologException("派车单不为空");
//        }
        List<CalculateFeeShipBillVO> shipBillHeadModels = vo.getShipBillModels();
        if (ObjectUtil.isEmpty(shipBillHeadModels)) {
            throw new PrologException("运单不为空");
        }
        Integer feeCltType = 30;

        // 计算运单
        for (CalculateFeeShipBillVO shipBillVO : shipBillHeadModels) {
            BmsShipBillHeaderModel shipBillHeaderModel = shipBillVO.getShipBillHeaderModel();
            List<BmsShipBillDetailModel> detailModels = shipBillVO.getDetailModels();
//            if (Integer.valueOf(2).equals(shipBillHeaderModel.getCarrierType()) || ObjectUtil.isEmpty(shipBillHeaderModel.getCarrierCode())) {
//                continue;
//            }
            try {
                // 计算托运物流支出费用
                Integer rs = billPayCostStrategyService.costShipBillTrailerStrategy(shipBillHeaderModel, detailModels, "1", user);
                if (Integer.valueOf(200).equals(rs)) {
                    feeCltType = 10;
                } else {
                    throw new PrologException(StrUtil.format("运单{}支出计费失败", shipBillHeaderModel.getShipbillNo()));
                }
            } catch (Exception e) {
                feeCltType = 30;
                e.printStackTrace();
                log.info(StrUtil.format("运单支出计费失败:{}"), JSON.toJSONString(shipBillHeaderModel));
            }
        }
        return feeCltType;
    }
}