package cn.iocoder.yudao.module.order.service.orderCargoControl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.spring.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.dict.core.dto.DictDataRespDTO;
import cn.iocoder.yudao.framework.excel.handler.WaterMarkHandler;
import cn.iocoder.yudao.framework.i18n.core.I18nMessage;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.service.AbstractService;
import cn.iocoder.yudao.framework.mybatis.core.vo.PageVO;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.bpm.api.BpmCreateServiceFactory;
import cn.iocoder.yudao.module.bpm.enums.WorkFlowEmus;
import cn.iocoder.yudao.module.ecw.api.paramValid.ParamValidatorApi;
import cn.iocoder.yudao.module.ecw.service.paramValid.ParamValidatorService;
import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
import cn.iocoder.yudao.module.infra.service.file.FileService;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.UserRespDTO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.order.convert.orderCargoControl.OrderCargoControlConvert;
import cn.iocoder.yudao.module.order.dal.dataobject.approval.OrderApprovalDO;
import cn.iocoder.yudao.module.order.dal.dataobject.order.OrderDO;
import cn.iocoder.yudao.module.order.dal.dataobject.orderCargoControl.OrderCargoControlDO;
import cn.iocoder.yudao.module.order.dal.dataobject.orderConsignor.OrderConsignorDO;
import cn.iocoder.yudao.module.order.dal.mysql.approval.OrderApprovalMapper;
import cn.iocoder.yudao.module.order.dal.mysql.orderCargoControl.OrderCargoControlMapper;
import cn.iocoder.yudao.module.order.dto.OrderCargoControlReleaseInfoDto;
import cn.iocoder.yudao.module.order.enums.OrderApprovalTypeResultEnum;
import cn.iocoder.yudao.module.order.enums.OrderStatusEnum;
import cn.iocoder.yudao.module.order.service.order.OrderBusinessService;
import cn.iocoder.yudao.module.order.service.order.OrderConsignorService;
import cn.iocoder.yudao.module.order.service.order.OrderQueryService;
import cn.iocoder.yudao.module.order.service.order.OrderService;
import cn.iocoder.yudao.module.order.vo.order.*;
import cn.iocoder.yudao.module.order.vo.orderCargoControl.*;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
import cn.iocoder.yudao.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
import cn.iocoder.yudao.module.system.enums.sms.SmsNodeEnum;
import cn.iocoder.yudao.module.system.framework.ue.UeProperties;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.apollo.core.constants.Constants.CUSTOMER_USER_APPROVAL_ID;
import static cn.iocoder.yudao.framework.apollo.core.constants.Constants.CUSTOMER_USER_TO_ADMIN;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.excel.constant.ExportConstant.*;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.USER_PASSWORD_FAILED;
import static cn.iocoder.yudao.module.order.enums.ErrorCodeConstants.*;

/**
 * 订单控货人信息 Service 实现类
 *
 * @author 郑屹
 */
@Service
@Validated
@AllArgsConstructor
public class OrderCargoControlServiceImpl extends AbstractService<OrderCargoControlMapper, OrderCargoControlDO> implements OrderCargoControlService {

    private final OrderCargoControlMapper orderCargoControlMapper;
    private final OrderService orderService;
    private final OrderQueryService orderQueryService;
    private final OrderBusinessService orderBusinessService;
    private final OrderConsignorService consignorService;
    private final SmsCodeApi smsCodeApi;
    private final MemberUserApi memberUserApi;
    private final ParamValidatorService paramValidatorService;
    private final PasswordEncoder passwordEncoder;
    private final UeProperties ueProperties;
    private final FileService fileService;
    private final OrderApprovalMapper orderApprovalMapper;
    private final BpmCreateServiceFactory bpmCreateServiceFactory;
    private final DictDataApi dictDataApi;
    private final ParamValidatorApi paramValidatorApi;

    @Override
    public Long createOrderCargoControl(OrderCargoControlCreateReqVO createReqVO, MemberUserDO memberUserDO) {
        if (StringUtils.isNotBlank(createReqVO.getPhone()) && Objects.nonNull(createReqVO.getDialCode())) {
            try {
                String mobileCode = createReqVO.getDialCode() + StrUtil.COLON + createReqVO.getPhone();
                paramValidatorService.validatorMobile(mobileCode);
//                String phone = PhoneUtil.formatPhone(mobileCode);
//                if (StringUtils.isNotBlank(phone)) {
//                    createReqVO.setPhone(phone);
//                }
            } catch (Exception e) {
                throw exception(NEW_CARGO_CONTROL_PHONE_INCORRECT_FORMAT);
            }
        }
        OrderDO orderDO = orderService.getById(createReqVO.getOrderId());
        if (Objects.isNull(orderDO)) {
            throw exception(ORDER_NOT_EXISTS);
        }
        if (orderDO.getStatus() < OrderStatusEnum.IN_WAREHOUSE.getValue() || orderDO.getStatus() == 88) {
            throw exception(ORDER_NOT_EXISTS_OR_STATUS_NO);
        }
        OrderCargoControlDO orgOrderCargoControlDO = this.getOne(new LambdaQueryWrapper<OrderCargoControlDO>()
                .eq(OrderCargoControlDO::getOrderId, createReqVO.getOrderId())
                .eq(OrderCargoControlDO::getIsActual, Boolean.TRUE)
                .orderByDesc(OrderCargoControlDO::getId)
                .last("limit 1"));
        if (Objects.isNull(orgOrderCargoControlDO)) {
            throw exception(ORIGINAL_CARGO_CONTROLLER_NOT_EXIST);
        }
        if (Objects.nonNull(memberUserDO) && !Objects.equals(orgOrderCargoControlDO.getPhone(), memberUserDO.getMobile())) {
            throw exception(YOU_NO_IS_CARGO_CONTROLLER);
        }
        String creator = String.valueOf(SecurityFrameworkUtils.getLoginUserId());
        Date now = new Date();
        boolean isPasswordError = false; // 是否密码错误
        boolean isPasswordNull = false; // 是否未设置密码
        boolean isPasswordPick = true; // 是否密码放货(默认是)
        if (StringUtils.isNotBlank(createReqVO.getControlPassword())) {
            // 没有短信验证码时就验证控货密码
            UserRespDTO user = memberUserApi.getUserByMobile(orgOrderCargoControlDO.getPhone());
            // 当控货人手机号没有注册web用户，或者用户没有设置控货密码时不能进行控货密码放货
            if ((user == null || StringUtils.isBlank(user.getControlPassword()))) {
                isPasswordNull = true;
            }
            // 参数：未加密密码，编码后的密码
            if (Objects.nonNull(user) && StringUtils.isNotBlank(user.getControlPassword()) && (!passwordEncoder.matches(createReqVO.getControlPassword(), user.getControlPassword()))) {
                isPasswordError = true;
            }
        }
        if (StringUtils.isNotBlank(createReqVO.getCode()) && (StringUtils.isBlank(createReqVO.getControlPassword()) || isPasswordError || isPasswordNull)) {
            SmsCodeUseReqDTO smsCodeSendReqDTO = new SmsCodeUseReqDTO();
            smsCodeSendReqDTO.setUsedIp(getClientIP());
            smsCodeSendReqDTO.setNodeValue(SmsNodeEnum.TRANSFER_CONTROL_GOODS.getNodeValue());
            smsCodeSendReqDTO.setCode(String.valueOf(createReqVO.getCode()));
            smsCodeSendReqDTO.setMobile(orgOrderCargoControlDO.getPhone());
            smsCodeApi.useSmsCode(smsCodeSendReqDTO);
            isPasswordPick = false;
        }
        if (StringUtils.isBlank(createReqVO.getCode()) && (isPasswordError || isPasswordNull)) {
            // 当控货人手机号没有注册web用户，或者用户没有设置控货密码时不能进行控货密码放货
            if (isPasswordNull) {
                throw exception(CONTROLLER_USER_NOT_SET_CONTROL_PASSWORD);
            } else {
                // 参数：未加密密码，编码后的密码
                throw exception(USER_PASSWORD_FAILED);
            }
        }

        orgOrderCargoControlDO.setUpdater(creator);
        orgOrderCargoControlDO.setUpdateTime(now);
        orgOrderCargoControlDO.setIsActual(Boolean.FALSE);
        orderCargoControlMapper.updateById(orgOrderCargoControlDO);

        // 插入
        OrderCargoControlDO orderCargoControl = OrderCargoControlConvert.INSTANCE.convert(createReqVO);
        orderCargoControl.setOrderNo(orderDO.getOrderNo());
        orderCargoControl.setTidanNo(orderDO.getTidanNo());
        orderCargoControl.setCreator(creator);
        orderCargoControl.setCreateTime(now);
        orderCargoControlMapper.insert(orderCargoControl);

        // 订单控货信息日志补充
        String msg = String.format("订单控货人信息由姓名%s，电话+%s，转移为姓名%s，电话+%s，校验方式：%s",
                orgOrderCargoControlDO.getName() + "/" + orgOrderCargoControlDO.getNameEn(), orgOrderCargoControlDO.getDialCode() + orgOrderCargoControlDO.getPhone(),
                orderCargoControl.getName() + "/" + (StringUtils.isNotBlank(orderCargoControl.getNameEn()) ? orderCargoControl.getNameEn() : "空"), orderCargoControl.getDialCode() + orderCargoControl.getPhone(),
                isPasswordPick ? "密码校验" : "验证码校验");
        String msgEn = String.format("The order controller information is transferred from name %s, phone number + %s to name %s, phone number + %s, verification method：%s",
                orgOrderCargoControlDO.getName() + "/" + orgOrderCargoControlDO.getNameEn(), orgOrderCargoControlDO.getDialCode() + orgOrderCargoControlDO.getPhone(),
                orderCargoControl.getName() + "/" + (StringUtils.isNotBlank(orderCargoControl.getNameEn()) ? orderCargoControl.getNameEn() : "null"), orderCargoControl.getDialCode() + orderCargoControl.getPhone(),
                isPasswordPick ? "Password verification" : "Verification code verification");
        String msgFr = String.format("Les informations du contrôleur de commande sont transférées de nom %s, numéro de téléphone + %s à nom %s, numéro de téléphone + %s, méthode de vérification：%s",
                orgOrderCargoControlDO.getName() + "/" + orgOrderCargoControlDO.getNameEn(), orgOrderCargoControlDO.getDialCode() + orgOrderCargoControlDO.getPhone(),
                orderCargoControl.getName() + "/" + (StringUtils.isNotBlank(orderCargoControl.getNameEn()) ? orderCargoControl.getNameEn() : "null"), orderCargoControl.getDialCode() + orderCargoControl.getPhone(),
                isPasswordPick ? "Vérification du mot de passe" : "Code de vérification vérification");
        orderBusinessService.createOrderControlLog(orderCargoControl.getOrderId(), 3, "控货人信息转移", msg,
                "Transfer of cargo controller information",msgEn,
                "Transfert des renseignements du contrôleur du fret",msgFr,0L);
        // 订单操作日志
        orderBusinessService.addOrderOperateLog(orderCargoControl.getOrderId(), "控货人信息转移", msg, "");
        // 返回
        return orderCargoControl.getId();
    }

    @Override
    public void updateOrderCargoControl(OrderCargoControlUpdateReqVO updateReqVO) {
        // 校验存在
//        this.validateOrderCargoControlExists(updateReqVO.getId());
        // 更新
        OrderCargoControlDO updateObj = OrderCargoControlConvert.INSTANCE.convert(updateReqVO);
        orderCargoControlMapper.updateById(updateObj);
    }

    @Override
    public void deleteOrderCargoControl(Long id) {
        // 校验存在
//        this.validateOrderCargoControlExists(id);
        // 删除
        orderCargoControlMapper.deleteById(id);
    }

//    private void validateOrderCargoControlExists(Long id) {
//        if (orderCargoControlMapper.selectById(id) == null) {
//            throw exception(ORDER_CARGO_CONTROL_NOT_EXISTS);
//        }
//    }

    @Override
    public OrderCargoControlDO getOrderCargoControl(Long id) {
        return orderCargoControlMapper.selectById(id);
    }

    @Override
    public List<OrderCargoControlDO> getOrderCargoControlList(Collection<Long> ids) {
        return orderCargoControlMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<OrderCargoControlDO> getOrderCargoControlPage(OrderCargoControlQueryVO query, PageVO page) {
        return orderCargoControlMapper.selectPage(page, query);
    }

    @Override
    public List<OrderCargoControlDO> getOrderCargoControlList(OrderCargoControlQueryVO query) {
        return orderCargoControlMapper.selectList(query);
    }

    @Override
    public PageResult<OrderBackPageVO> cargoControlOrderPage(OrderQueryVO query, PageVO page) {
        return orderQueryService.adminCargoControlOrderPage(query, page);
    }

    @Override
    public PageResult<OrderBackPageVO> warningCargoControlOrderPage(OrderQueryVO query, PageVO page) {
        /* 满足条件：1、控货订单
         *         2、非海外仓订单
         *         3、控货订单放货三次未提货完
         *         4、控货订单已过免仓时效，未完成放货
         */
        query.setWarningOrder(1);
        return orderQueryService.adminCargoControlOrderPage(query, page);
    }

    @Override
    public OrderCargoControlBackVO getOrderCargoControlInfo(Long orderId) {
        OrderDO orderDO = orderService.getById(orderId);
        if (Objects.isNull(orderDO)) {
            throw exception(ORDER_NOT_EXISTS);
        }
        OrderConsignorDO orderConsignorDO = consignorService.getOne(new LambdaQueryWrapper<OrderConsignorDO>()
                .eq(OrderConsignorDO::getOrderId, orderId)
                .orderByDesc(OrderConsignorDO::getId)
                .last("limit 1"));
        OrderCargoControlBackVO backVO = new OrderCargoControlBackVO();

        if (orderDO.getIsCargoControl()) {
            // TODO 待下单时将发货人做为控货人插入控货人表
            OrderCargoControlDO orderCargoControlDO = this.getOne(new LambdaQueryWrapper<OrderCargoControlDO>()
                    .eq(OrderCargoControlDO::getOrderId, orderId)
                    .eq(OrderCargoControlDO::getIsActual, Boolean.TRUE)
                    .orderByDesc(OrderCargoControlDO::getId)
                    .last("limit 1"));
            if (Objects.isNull(orderCargoControlDO)) {
                throw exception(ORDER_CARGO_CONTROL_NOT_EXISTS);
            }
            return OrderCargoControlConvert.INSTANCE.convert(orderCargoControlDO);
        } else {
            throw exception(ORDER_CARGO_CONTROL_NOT_EXISTS);
        }

    }

    @Override
    public OrderCargoControlReleaseInfoDto getOrderCargoControlReleaseInfo(Long orderId) {
        OrderCargoControlReleaseInfoDto releaseInfoDto = orderCargoControlMapper.getOrderCargoControlReleaseInfo(orderId);
        if (Objects.isNull(releaseInfoDto.getReleaseNum())) {
            releaseInfoDto.setReleaseNum(0);
        }
        return releaseInfoDto;
    }

    @Override
    public void sendSmsCode(Long loginUserId, OrderSendSmsReqVO reqVO, MemberUserDO memberUserDO) {
        SmsCodeSendReqDTO smsCodeSendReqDTO = new SmsCodeSendReqDTO();
        smsCodeSendReqDTO.setCreateIp(getClientIP());
        smsCodeSendReqDTO.setNodeValue(reqVO.getNodeValue());
        smsCodeSendReqDTO.setIsTransport(reqVO.getIsTransport());
        smsCodeSendReqDTO.setTransportId(reqVO.getTransportId());
        smsCodeSendReqDTO.setIsOrders(reqVO.getIsOrders());
        smsCodeSendReqDTO.setMessageType(reqVO.getMessageType());

        OrderCargoControlDO orderCargoControlDO = this.getOne(new LambdaQueryWrapper<OrderCargoControlDO>()
                .eq(OrderCargoControlDO::getOrderId, reqVO.getOrderId())
                .eq(OrderCargoControlDO::getIsActual, Boolean.TRUE)
                .orderByDesc(OrderCargoControlDO::getId)
                .last("limit 1"));
        if (Objects.isNull(orderCargoControlDO)) {
            OrderConsignorDO orderConsignorDO = consignorService.getOne(new LambdaQueryWrapper<OrderConsignorDO>()
                    .eq(OrderConsignorDO::getOrderId, reqVO.getOrderId())
                    .orderByDesc(OrderConsignorDO::getId)
                    .last("limit 1"));
            if (Objects.isNull(orderConsignorDO)) {
                throw exception(ORDER_NOT_EXISTS);
            }
            if (Objects.nonNull(memberUserDO) && !Objects.equals(memberUserDO.getMobile(), orderConsignorDO.getPhone())) {
                throw exception(YOU_NO_IS_CARGO_CONTROLLER);
            }
            smsCodeSendReqDTO.setAreaCode(orderConsignorDO.getCountryCode());
            smsCodeSendReqDTO.setMobile(orderConsignorDO.getPhone());
        } else {
            if (Objects.nonNull(memberUserDO) && !Objects.equals(memberUserDO.getMobile(), orderCargoControlDO.getPhone())) {
                throw exception(YOU_NO_IS_CARGO_CONTROLLER);
            }
            smsCodeSendReqDTO.setAreaCode(String.valueOf(orderCargoControlDO.getDialCode()));
            smsCodeSendReqDTO.setMobile(orderCargoControlDO.getPhone());
        }
        orderBusinessService.addOrderOperateLog(orderCargoControlDO.getOrderId(),
                "验证码发送", SmsNodeEnum.getCodeByNodeValue(reqVO.getNodeValue()).getDescription(), "");
        smsCodeApi.sendSmsCode(smsCodeSendReqDTO);
    }


    @Override
    public List<OrderBackPageVO> seasoningCondimentsCargoControlOrderPage(Long orderId, Integer userType) {
        OrderCargoControlDO orderCargoControlDO = this.getOne(new LambdaQueryWrapper<OrderCargoControlDO>()
                .eq(OrderCargoControlDO::getOrderId, orderId)
                .eq(OrderCargoControlDO::getIsActual, Boolean.TRUE)
                .last("limit 1"));
        if (Objects.isNull(orderCargoControlDO) || StringUtils.isBlank(orderCargoControlDO.getPhone())) {
            throw exception(NOT_FIND_CONTROLLER);
        }
        List<OrderBackPageVO> list = orderQueryService.seasoningCondimentsCargoControlOrderPage(orderId, orderCargoControlDO.getPhone(), userType);
        if (CollectionUtil.isNotEmpty(list)) {
            list = list.stream().peek(backPageVO -> {
                backPageVO.setControlNum(backPageVO.getSumNum() - backPageVO.getReleaseNum());
                backPageVO.setControlQuantity(backPageVO.getSumQuantity() - backPageVO.getReleaseQuantity());
                backPageVO.setControlVolume(backPageVO.getSumVolume().subtract(backPageVO.getReleaseVolume()));
                backPageVO.setControlWeight(backPageVO.getSumWeight().subtract(backPageVO.getReleaseWeight()));
            }).collect(Collectors.toList());
        }
        return list;
    }

    @Override
    public OrderCargoControlDO getActualOrderCargoControlByOrderId(Long orderId) {
        return orderCargoControlMapper.selectOne(new LambdaQueryWrapperX<OrderCargoControlDO>()
                .eq(OrderCargoControlDO::getOrderId, orderId)
                .eq(OrderCargoControlDO::getIsActual, true)
                .orderByDesc(OrderCargoControlDO::getId).last("limit 1"));
    }

    @Override
    public void exportCargoControlOrderExcel(HttpServletResponse response, OrderQueryVO query) throws IOException {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATA_FORMAT);
        String nowTime = formatter.format(LocalDateTime.now());

        response.setContentType(CONTENT_TYPE);
        //设置字符集为utf-8
        response.setCharacterEncoding(CHARACTER_UTF_8);
        String sheetName = I18nMessage.getLang() == 0 ? "控货订单管理第{0}页" : "Cargo control order Management number {0}";
        String fileName = sheetName.concat("_").concat(nowTime).concat(".xlsx");

        //文件名乱码解决：用postman测正常，浏览器多了filename_=utf-8等字样
        response.setHeader(CONTENT_DISPOSITION,
                "attachment;filename=" + URLEncoder.encode(fileName, CHARACTER_UTF_8)
                        + ";filename*=utf-8''" + URLEncoder.encode(fileName, CHARACTER_UTF_8));

        //发送一个报头，告诉浏览器当前页面不进行缓存，每次访问的时间必须从服务器上读取最新的数据
        response.setHeader(CACHE_CONTROL, NO_STORE);
        response.addHeader(CACHE_CONTROL, MAX_AGE);

        ServletOutputStream out = response.getOutputStream();
        ExcelWriter excelWriter = EasyExcel.write(out, OrderCargoControlExcelVO.class).inMemory(true).build();

        query.setLang(I18nMessage.getLang());
        PageVO page = new PageVO();
        PageResult<OrderBackPageVO> pageResult = orderQueryService.adminCargoControlOrderPage(query, page);
        long total = pageResult.getTotal();
        int pageNumber = (int) Math.ceil((double) total / (double) PAGE_SIZE);    //分页条数看情况
        // 去调用写入,根据数据库分页的总的页数来
        for (int i = 1; i <= pageNumber; i++) {
            // 根据最大查询值配置来进行sheet分页
            WriteSheet writeSheet = EasyExcel.writerSheet(MessageFormat.format(sheetName, i)).registerWriteHandler(new WaterMarkHandler("捷道机密文件，禁止传播")).build();
            //先定义一个空集合每次循环使他变成null减少内存的占用
            page.setPage(i);
            page.setRows(PAGE_SIZE);
            query.setLang(I18nMessage.getLang());
            page.setField("o.order_id");
            PageResult<OrderBackPageVO> mpPage = orderQueryService.adminCargoControlOrderPage(query, page);
            List<OrderCargoControlExcelVO> pageList = mpPage.getList().stream().map(backPageVO -> {
                OrderCargoControlExcelVO excelVO = new OrderCargoControlExcelVO();
                BeanUtils.copyProperties(backPageVO, excelVO);
                return excelVO;
            }).collect(Collectors.toList());
            excelWriter.write(pageList, writeSheet);
            pageList.clear();
        }

        // 千万别忘记finish 会帮忙关闭流
        excelWriter.finish();
        out.flush();
    }

    @Override
    public FileDO makeCargoControlOrderExcelFile(Long userId, Integer userType, OrderQueryVO query) throws Exception {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATA_FORMAT);
        String nowTime = formatter.format(LocalDateTime.now());

        String sheetName = query.getLang() == 0 ? "控货订单管理第{0}页" : "Cargo control order Management number {0}";
        if (Objects.isNull(userId)) {
            userId = 0L;
        }
        if (Objects.isNull(userType)) {
            userType = 2;
        }
        String fileName = userId.toString().concat(StrUtil.DASHED).concat(userType.toString()).concat(StrUtil.DASHED).concat(nowTime).concat(".xlsx");
        String dir = ueProperties.getTempDir().concat("/cargo_control/excel/");
        // 创建需要写入的文件目录
        File fileDir = new File(dir);
        if (!fileDir.exists()) {
            // 不存在则创建一个目录
            fileDir.mkdirs();
        }
        String path = dir + fileName;
        // 创建写入的输出流
        OutputStream out = new FileOutputStream(path);
        ExcelWriter excelWriter = EasyExcel.write(out, OrderCargoControlExcelVO.class).inMemory(true).build();
        PageVO page = new PageVO();
        PageResult<OrderBackPageVO> pageResult = orderQueryService.adminCargoControlMyOrderPage(query, page);
        long total = pageResult.getTotal();
        int pageNumber = (int) Math.ceil((double) total / (double) PAGE_SIZE);    //分页条数看情况
        // 去调用写入,根据数据库分页的总的页数来
        for (int i = 1; i <= pageNumber; i++) {
            // 根据最大查询值配置来进行sheet分页
            WriteSheet writeSheet = EasyExcel.writerSheet(MessageFormat.format(sheetName, i)).registerWriteHandler(new WaterMarkHandler("捷道机密文件，禁止传播")).build();
            //先定义一个空集合每次循环使他变成null减少内存的占用
            page.setPage(i);
            page.setRows(PAGE_SIZE);
            page.setField("o.order_id");
            PageResult<OrderBackPageVO> mpPage = orderQueryService.adminCargoControlMyOrderPage(query, page);
            List<OrderCargoControlExcelVO> pageList = mpPage.getList().stream().map(backPageVO -> {
                OrderCargoControlExcelVO excelVO = new OrderCargoControlExcelVO();
                BeanUtils.copyProperties(backPageVO, excelVO);
                return excelVO;
            }).collect(Collectors.toList());
            excelWriter.write(pageList, writeSheet);
            pageList.clear();
        }

        // 千万别忘记finish 会帮忙关闭流
        excelWriter.finish();
        out.flush();
        // 获取到临时文件
        File file = new File(path);
        // 创建FileInputStream对象
        FileInputStream fileInputStream = new FileInputStream(file);
        // 读取文件内容
        byte[] fileBytes = new byte[(int) file.length()];
        fileInputStream.read(fileBytes);
        // 关闭文件流
        fileInputStream.close();
        // 将文件上传到资源服务器
        FileDO fileDO = fileService.createFile(dir, fileName, fileBytes);

        return fileDO;
    }

    @Override
    public Boolean checkControlPassword(Long orderId) {
        OrderCargoControlBackVO backVO = this.getOrderCargoControlInfo(orderId);
        UserRespDTO userRespDTO = memberUserApi.getUserByMobile(backVO.getPhone());
        return Objects.nonNull(userRespDTO) && StringUtils.isNotBlank(userRespDTO.getControlPassword());
    }

    @Override
    public void approvalOrderCargoControl(String approveId, Integer result) {
        OrderApprovalDO orderApprovalDO = orderApprovalMapper.selectById(approveId);
        if (Objects.isNull(orderApprovalDO)) {
            throw exception(ORDER_APPROVAL_INFO_NOT_FIND);
        }
        OrderCargoControlApplyVO applyVO = JSONObject.parseObject(orderApprovalDO.getDetails(), OrderCargoControlApplyVO.class);
        orderApprovalDO.setStatus(result);
        applyVO.setApplyStatus(result);
        orderApprovalDO.setDetails(JSONObject.toJSONString(applyVO));
        List<ApplyInfoVO> list = new ArrayList<>();
        String msg = "";
        if (result == 1) {
            // 进行中的审批直接返回
            return;
        }
        OrderDO orderDO = orderService.getById(applyVO.getOrderId());
        if (result == 3 || result == 4) {
            // TODO  审批取消、审批拒绝,待补充业务日志及其他细节
            if (orderApprovalDO.getType() == 61) {
                msg = result == 4 ? "收货人限制修改申请取消" : "收货人限制修改申请拒绝";
                ApplyInfoVO applyInfoVO;
                if (orderDO.getIsLimitUpdateConsignee() != applyVO.getNewIsLimitUpdateConsignee()) {
                    applyInfoVO = new ApplyInfoVO();
                    applyInfoVO.setName("是否限制修改收货人");
                    applyInfoVO.setOrgValue(orderDO.getIsLimitUpdateConsignee() ? "是" : "否");
                    applyInfoVO.setNewValue(applyVO.getNewIsLimitUpdateConsignee() ? "是" : "否");
                    list.add(applyInfoVO);
                }
                if ((Objects.nonNull(orderDO.getLockConsigneeDay()) && Objects.nonNull(applyVO.getNewLockConsigneeDay())
                        && !Objects.equals(orderDO.getLockConsigneeDay(), applyVO.getNewLockConsigneeDay()))
                        || (Objects.isNull(orderDO.getLockConsigneeDay()) && Objects.nonNull(applyVO.getNewLockConsigneeDay()))
                        || (Objects.nonNull(orderDO.getLockConsigneeDay()) && Objects.isNull(applyVO.getNewLockConsigneeDay()))) {
                    applyInfoVO = new ApplyInfoVO();
                    applyInfoVO.setName("锁定收货人天数");
                    applyInfoVO.setOrgValue(Objects.isNull(orderDO.getLockConsigneeDay()) ? "空" : orderDO.getLockConsigneeDay() + "天");
                    applyInfoVO.setNewValue(Objects.isNull(applyVO.getNewLockConsigneeDay()) ? "空" : applyVO.getNewLockConsigneeDay() + "天");
                    list.add(applyInfoVO);
                }
            } else if (orderApprovalDO.getType() == 62) {
                msg = result == 4 ? "海外仓修改申请取消" : "海外仓修改申请拒绝";
                ApplyInfoVO applyInfoVO = new ApplyInfoVO();
                applyInfoVO.setName("订单修改海外仓模式");
                applyInfoVO.setOrgValue(applyVO.getOrgIsOverseasWarehouse() ? "订单修改为海外仓" : "订单修改为非海外仓");
                applyInfoVO.setNewValue(applyVO.getNewIsOverseasWarehouse() ? "订单修改为海外仓" : "订单修改为非海外仓");
                list.add(applyInfoVO);
                if (StringUtils.isNotBlank(applyVO.getReasonZh())) {
                    applyInfoVO = new ApplyInfoVO();
                    applyInfoVO.setName("订单修改海外仓模式理由");
                    applyInfoVO.setNewValue(applyVO.getReasonZh());
                    list.add(applyInfoVO);
                }
            }
        }
        if (result == 2) {
            if (orderApprovalDO.getType() == 61) {
                // 修改放货限制
                Integer newLockConsigneeDay = applyVO.getNewIsLimitUpdateConsignee() ? applyVO.getNewLockConsigneeDay() : null;
                Date newLockConsigneeTime = Objects.isNull(orderDO.getUnloadTime()) || Objects.isNull(newLockConsigneeDay) ? null : DateUtil.endOfDay(DateUtil.offsetDay(orderDO.getUnloadTime(), newLockConsigneeDay));
                orderService.update(new LambdaUpdateWrapper<OrderDO>()
                        .set(OrderDO::getIsLimitUpdateConsignee, applyVO.getNewIsLimitUpdateConsignee())
                        .set(OrderDO::getLockConsigneeDay, newLockConsigneeDay)
                        .set(OrderDO::getLockConsigneeTime, newLockConsigneeTime)
                        .eq(OrderDO::getOrderId, orderApprovalDO.getOrderId()));
                msg = "收货人限制修改申请审批通过";
                ApplyInfoVO applyInfoVO;
                if (orderDO.getIsLimitUpdateConsignee() != applyVO.getNewIsLimitUpdateConsignee()) {
                    applyInfoVO = new ApplyInfoVO();
                    applyInfoVO.setName("是否限制修改收货人");
                    applyInfoVO.setOrgValue(orderDO.getIsLimitUpdateConsignee() ? "是" : "否");
                    applyInfoVO.setNewValue(applyVO.getNewIsLimitUpdateConsignee() ? "是" : "否");
                    list.add(applyInfoVO);
                }
                if ((Objects.nonNull(orderDO.getLockConsigneeDay()) && Objects.nonNull(applyVO.getNewLockConsigneeDay())
                        && !Objects.equals(orderDO.getLockConsigneeDay(), applyVO.getNewLockConsigneeDay()))
                        || (Objects.isNull(orderDO.getLockConsigneeDay()) && Objects.nonNull(applyVO.getNewLockConsigneeDay()))
                        || (Objects.nonNull(orderDO.getLockConsigneeDay()) && Objects.isNull(applyVO.getNewLockConsigneeDay()))) {
                    applyInfoVO = new ApplyInfoVO();
                    applyInfoVO.setName("锁定收货人天数");
                    applyInfoVO.setOrgValue(Objects.isNull(orderDO.getLockConsigneeDay()) ? "空" : orderDO.getLockConsigneeDay() + "天");
                    applyInfoVO.setNewValue(Objects.isNull(applyVO.getNewLockConsigneeDay()) ? "空" : applyVO.getNewLockConsigneeDay() + "天");
                    list.add(applyInfoVO);
                }
            } else if (orderApprovalDO.getType() == 62) {
                // TODO 海外仓修改申请
                String type = orderDO.getType();
                if (StringUtils.isNotBlank(type)) {
                    type = applyVO.getNewIsOverseasWarehouse() ? type.concat(",2") : Arrays.stream(type.split(",")).filter(s -> !StringUtils.equals("2", s)).collect(Collectors.joining(","));
                } else {
                    type = "2";
                }
                // TODO 修改订单海外仓模式, 不确定对订单其他方面是否有影响，订单编号生成规则与海外仓无关联
                orderService.update(new LambdaUpdateWrapper<OrderDO>().set(OrderDO::getType, type).eq(OrderDO::getOrderId, orderApprovalDO.getOrderId()));
                msg = "海外仓修改申请审批通过";
                ApplyInfoVO applyInfoVO = new ApplyInfoVO();
                applyInfoVO.setName("订单修改海外仓模式");
                applyInfoVO.setOrgValue(applyVO.getOrgIsOverseasWarehouse() ? "订单修改为海外仓" : "订单修改为非海外仓");
                applyInfoVO.setNewValue(applyVO.getNewIsOverseasWarehouse() ? "订单修改为海外仓" : "订单修改为非海外仓");
                list.add(applyInfoVO);
                if (StringUtils.isNotBlank(applyVO.getReasonZh())) {
                    applyInfoVO = new ApplyInfoVO();
                    applyInfoVO.setName("订单修改海外仓模式理由");
                    applyInfoVO.setNewValue(applyVO.getReasonZh());
                    list.add(applyInfoVO);
                }
            }
        }
        orderApprovalMapper.updateById(orderApprovalDO);
        orderService.updateStatus(orderApprovalDO.getOrderId(), "", null, null, null, null, 0, OrderApprovalTypeResultEnum.typeAndResultOf(orderApprovalDO.getType(), result).getDesc(), false);
        orderBusinessService.addOrderOperateLog(orderApprovalDO.getOrderId(), "订单操作", msg, list);
    }


    @Override
    public OrderCargoControlApplyVO approvalOrderCargoControlPickLimitUpdateByOrderId(Long orderId) {
        OrderApprovalDO orderApprovalDO = orderApprovalMapper.orderApprovalInfoByOrderIdAndType(orderId, 61);
        if (Objects.isNull(orderApprovalDO) || orderApprovalDO.getStatus() > 1) {
            OrderCargoControlReleaseInfoDto releaseInfoDto = this.getOrderCargoControlReleaseInfo(orderId);
            OrderCargoControlApplyVO orderCargoControlApplyVO = new OrderCargoControlApplyVO();
            BeanUtils.copyProperties(releaseInfoDto, orderCargoControlApplyVO);
            orderCargoControlApplyVO.setApplyStatus(0);
            return orderCargoControlApplyVO;
        } else {
            OrderCargoControlApplyVO applyVO = JSONObject.parseObject(orderApprovalDO.getDetails(), OrderCargoControlApplyVO.class);
            applyVO.setFormId(orderApprovalDO.getFormId());
            return applyVO;
        }
    }

    @Override
    public OrderCargoControlApplyVO approvalOrderCargoControlOverseasWarehouseUpdateByOrderId(Long orderId) {
        OrderApprovalDO orderApprovalDO = orderApprovalMapper.orderApprovalInfoByOrderIdAndType(orderId, 62);
        if (Objects.isNull(orderApprovalDO) || orderApprovalDO.getStatus() > 1) {
            OrderCargoControlReleaseInfoDto releaseInfoDto = this.getOrderCargoControlReleaseInfo(orderId);
            OrderCargoControlApplyVO orderCargoControlApplyVO = new OrderCargoControlApplyVO();
            BeanUtils.copyProperties(releaseInfoDto, orderCargoControlApplyVO);
            orderCargoControlApplyVO.setApplyStatus(0);
            orderCargoControlApplyVO.setOrderType(releaseInfoDto.getType());
            orderCargoControlApplyVO.setOrgIsOverseasWarehouse(StringUtils.isNotBlank(releaseInfoDto.getType()) && releaseInfoDto.getType().contains("2"));
            orderCargoControlApplyVO.setNewIsOverseasWarehouse(!orderCargoControlApplyVO.getOrgIsOverseasWarehouse());
            return orderCargoControlApplyVO;
        } else {
            OrderCargoControlApplyVO applyVO = JSONObject.parseObject(orderApprovalDO.getDetails(), OrderCargoControlApplyVO.class);
            applyVO.setFormId(orderApprovalDO.getFormId());
            return applyVO;
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderCargoControlApply(OrderCargoControlApplyVO orderCargoControlApplyVO) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        Integer userType = loginUser != null ? loginUser.getUserType() : null;
        Long userId = loginUser != null ? loginUser.getId() : null;
        String creator = String.valueOf(userId);
        if (Objects.equals(userType, UserTypeEnum.MEMBER.getValue())) {
            DictDataRespDTO dto = dictDataApi.parseDictDataFromCache(CUSTOMER_USER_TO_ADMIN, CUSTOMER_USER_APPROVAL_ID);
            userId = Long.valueOf(dto.getValue());
        }
        Date now = new Date();
        OrderDO orderDO = orderService.getById(orderCargoControlApplyVO.getOrderId());
        // TODO 待确认具体拦截哪些审批任务
        int count = orderApprovalMapper.processingItemApproval(null, orderCargoControlApplyVO.getOrderId(), Arrays.asList(orderCargoControlApplyVO.getApplyType(), 14));
        if (count > 0) {
            throw exception(ORDER_IS_APPROVAL_IN_PROCESS);
        }
        OrderCargoControlReleaseInfoDto releaseInfoDto = this.getOrderCargoControlReleaseInfo(orderCargoControlApplyVO.getOrderId());
        orderCargoControlApplyVO.setOrderNo(releaseInfoDto.getOrderNo());
        orderCargoControlApplyVO.setPickQuantity(releaseInfoDto.getPickQuantity());
        orderCargoControlApplyVO.setPickVolume(releaseInfoDto.getPickVolume());
        orderCargoControlApplyVO.setPickWeight(releaseInfoDto.getPickWeight());
        orderCargoControlApplyVO.setSumNum(releaseInfoDto.getSumNum());
        orderCargoControlApplyVO.setSumVolume(releaseInfoDto.getSumVolume());
        orderCargoControlApplyVO.setSumWeight(releaseInfoDto.getSumWeight());
        orderCargoControlApplyVO.setSumQuantity(releaseInfoDto.getSumQuantity());
        orderCargoControlApplyVO.setReleaseNum(releaseInfoDto.getReleaseNum());
        orderCargoControlApplyVO.setIsCargoControl(releaseInfoDto.getIsCargoControl());
        orderCargoControlApplyVO.setTidanNo(releaseInfoDto.getTidanNo());
        orderCargoControlApplyVO.setCargoControlStatus(releaseInfoDto.getCargoControlStatus());
        orderCargoControlApplyVO.setIsLimitUpdateConsignee(releaseInfoDto.getIsLimitUpdateConsignee());
        orderCargoControlApplyVO.setLockConsigneeDay(releaseInfoDto.getLockConsigneeDay());
        orderCargoControlApplyVO.setLockConsigneeTime(releaseInfoDto.getLockConsigneeTime());
        orderCargoControlApplyVO.setUnloadTime(releaseInfoDto.getUnloadTime());
        orderCargoControlApplyVO.setApplyStatus(1);

        OrderApprovalDO orderApprovalDO = new OrderApprovalDO();
        orderApprovalDO.setFollowUpSalesmanId(String.valueOf(orderDO.getSalesmanId()));//跟进业务员为订单的跟进业务员
        orderApprovalDO.setDetails(JSONObject.toJSONString(orderCargoControlApplyVO));
        orderApprovalDO.setCreator(creator);
        orderApprovalDO.setUpdater(creator);
        orderApprovalDO.setCreateTime(now);
        orderApprovalDO.setUpdateTime(now);
        orderApprovalDO.setStatus(1);
        List<ApplyInfoVO> list = new ArrayList<>();
        String msg = "";
        if (orderCargoControlApplyVO.getApplyType() == 61) {
            orderApprovalDO.setOrderId(orderCargoControlApplyVO.getOrderId());
            orderApprovalDO.setType(61);
            orderApprovalDO.setDetails(JSONObject.toJSONString(orderCargoControlApplyVO));
            orderApprovalMapper.insert(orderApprovalDO);
            String formId = bpmCreateServiceFactory.createBmp(userId, orderApprovalDO.getOrderApprovalId(), WorkFlowEmus.ORDER_CONSIGNEE_LIMIT_CHANGE.getKey(), orderDO.getOrderNo(),
                    StringUtils.isNotBlank(orderCargoControlApplyVO.getCcIds()) ? orderCargoControlApplyVO.getCcIds().split(StrUtil.COMMA) : null);
            orderApprovalDO.setFormId(formId);
            msg = "收货人限制修改申请";
            ApplyInfoVO applyInfoVO;
            if (orderDO.getIsLimitUpdateConsignee() != orderCargoControlApplyVO.getNewIsLimitUpdateConsignee()) {
                applyInfoVO = new ApplyInfoVO();
                applyInfoVO.setName("是否限制修改收货人");
                applyInfoVO.setOrgValue(orderDO.getIsLimitUpdateConsignee() ? "是" : "否");
                applyInfoVO.setNewValue(orderCargoControlApplyVO.getNewIsLimitUpdateConsignee() ? "是" : "否");
                list.add(applyInfoVO);
            }
            if ((Objects.nonNull(orderDO.getLockConsigneeDay()) && Objects.nonNull(orderCargoControlApplyVO.getNewLockConsigneeDay())
                    && !Objects.equals(orderDO.getLockConsigneeDay(), orderCargoControlApplyVO.getNewLockConsigneeDay()))
                    || (Objects.isNull(orderDO.getLockConsigneeDay()) && Objects.nonNull(orderCargoControlApplyVO.getNewLockConsigneeDay()))
                    || (Objects.nonNull(orderDO.getLockConsigneeDay()) && Objects.isNull(orderCargoControlApplyVO.getNewLockConsigneeDay()))) {
                applyInfoVO = new ApplyInfoVO();
                applyInfoVO.setName("锁定收货人天数");
                applyInfoVO.setOrgValue(Objects.isNull(orderDO.getLockConsigneeDay()) ? "空" : orderDO.getLockConsigneeDay() + "天");
                applyInfoVO.setNewValue(Objects.isNull(orderCargoControlApplyVO.getNewLockConsigneeDay()) ? "空" : orderCargoControlApplyVO.getNewLockConsigneeDay() + "天");
                list.add(applyInfoVO);
            }
        } else if (orderCargoControlApplyVO.getApplyType() == 62) {
            if (Objects.isNull(orderCargoControlApplyVO.getNewIsOverseasWarehouse())) {
                throw exception(ORDER_OVERSEAS_WAREHOUSE_UPDATE_IS_OR_NO);
            }
            if (StringUtils.isNotBlank(orderDO.getType()) && orderDO.getType().contains("2") && orderCargoControlApplyVO.getNewIsOverseasWarehouse()) {
                throw exception(ORDER_IS_OVERSEAS_WAREHOUSE_ORDER);
            }
            if ((StringUtils.isBlank(orderDO.getType()) || !orderDO.getType().contains("2")) && !orderCargoControlApplyVO.getNewIsOverseasWarehouse()) {
                throw exception(ORDER_NOT_IS_OVERSEAS_WAREHOUSE_ORDER);
            }
            orderCargoControlApplyVO.setOrgIsOverseasWarehouse(StringUtils.isNotBlank(orderDO.getType()) && orderDO.getType().contains("2"));
            orderApprovalDO.setOrderId(orderCargoControlApplyVO.getOrderId());
            orderApprovalDO.setType(62);
            orderApprovalDO.setDetails(JSONObject.toJSONString(orderCargoControlApplyVO));
            orderApprovalMapper.insert(orderApprovalDO);
            String formId = bpmCreateServiceFactory.createBmp(userId, orderApprovalDO.getOrderApprovalId(), WorkFlowEmus.ORDER_OVERSEAS_WAREHOUSE_CHANGE.getKey(), orderDO.getOrderNo(),
                    StringUtils.isNotBlank(orderCargoControlApplyVO.getCcIds()) ? orderCargoControlApplyVO.getCcIds().split(StrUtil.COMMA) : null);
            orderApprovalDO.setFormId(formId);
            msg = "海外仓修改申请";
            ApplyInfoVO applyInfoVO = new ApplyInfoVO();
            applyInfoVO.setName("订单修改海外仓模式");
            applyInfoVO.setOrgValue(orderCargoControlApplyVO.getOrgIsOverseasWarehouse() ? "订单修改为海外仓" : "订单修改为非海外仓");
            applyInfoVO.setNewValue(orderCargoControlApplyVO.getNewIsOverseasWarehouse() ? "订单修改为海外仓" : "订单修改为非海外仓");
            list.add(applyInfoVO);
            if (StringUtils.isNotBlank(orderCargoControlApplyVO.getReasonZh())) {
                applyInfoVO = new ApplyInfoVO();
                applyInfoVO.setName("订单修改海外仓模式理由");
                applyInfoVO.setNewValue(orderCargoControlApplyVO.getReasonZh());
                list.add(applyInfoVO);
            }
        }
        orderApprovalMapper.updateById(orderApprovalDO);
        orderService.updateStatus(orderApprovalDO.getOrderId(), "", null, null, null, null, orderApprovalDO.getType(), OrderApprovalTypeResultEnum.typeAndResultOf(orderApprovalDO.getType(), 1).getDesc(), false);
        orderBusinessService.addOrderOperateLog(orderApprovalDO.getOrderId(), "订单操作", msg, list);
    }
}
