package com.qmth.sop.business.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qmth.sop.business.bean.dto.DataPermissionDto;
import com.qmth.sop.business.bean.dto.DeviceDeliveryImportDto;
import com.qmth.sop.business.bean.dto.DeviceInfoDto;
import com.qmth.sop.business.bean.params.DeviceDeliveryParam;
import com.qmth.sop.business.bean.params.DeviceInOutForm;
import com.qmth.sop.business.bean.params.DeviceInOutSubmitParam;
import com.qmth.sop.business.bean.result.FlowFormWidgetResult;
import com.qmth.sop.business.bean.result.FlowTaskResult;
import com.qmth.sop.business.bean.result.TBDeviceDeliveryResult;
import com.qmth.sop.business.entity.*;
import com.qmth.sop.business.mapper.TBDeviceDeliveryMapper;
import com.qmth.sop.business.service.*;
import com.qmth.sop.business.util.excel.BasicExcelListener;
import com.qmth.sop.common.contant.SystemConstant;
import com.qmth.sop.common.enums.*;
import com.qmth.sop.common.util.FileUtil;
import com.qmth.sop.common.util.GsonUtil;
import com.qmth.sop.common.util.ServletUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 设备发货表 服务实现类
 * </p>
 *
 * @author wangliang
 * @since 2023-11-28
 */
@Service
public class TBDeviceDeliveryServiceImpl extends ServiceImpl<TBDeviceDeliveryMapper, TBDeviceDelivery>
        implements TBDeviceDeliveryService {

    @Resource
    private SysDeviceService sysDeviceService;

    @Resource
    private SysDeviceModelService sysDeviceModelService;

    @Resource
    private SysSupplierService sysSupplierService;

    @Resource
    private SysUserService sysUserService;

    @Resource
    private TBCrmService tbCrmService;

    @Resource
    private TBDeviceInOutService tbDeviceInOutService;

    @Transactional
    @Override
    public void deviceDeliveryImport(MultipartFile file) throws IOException {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        InputStream inputStream = file.getInputStream();
        LinkedMultiValueMap<String, DeviceDeliveryImportDto> deviceDeliveryImportDtoLinkedMultiValueMap = new LinkedMultiValueMap<>();

        StringJoiner errorData = new StringJoiner("");
        EasyExcel.read(inputStream, DeviceDeliveryImportDto.class, new BasicExcelListener<DeviceDeliveryImportDto>() {

            @Override
            public void handle(LinkedMultiValueMap<String, DeviceDeliveryImportDto> dataList, StringJoiner errorDataSj,
                    Exception exception) {
                deviceDeliveryImportDtoLinkedMultiValueMap.addAll(dataList);
                errorData.add(errorDataSj.toString());
            }
        }).headRowNumber(2).sheet(0).doRead();

        List<DeviceDeliveryImportDto> deviceDeliveryImportDtoList = deviceDeliveryImportDtoLinkedMultiValueMap.get(
                BasicExcelListener.SUCCESS);

        // 派单序列号集合
        List<String> crmNoList = tbCrmService.list(new QueryWrapper<TBCrm>().lambda().eq(TBCrm::getEnable, true))
                .stream().map(TBCrm::getCrmNo).distinct().collect(Collectors.toList());

        if (CollectionUtils.isNotEmpty(deviceDeliveryImportDtoList)) {
            List<SysSupplier> sysSupplierList = sysSupplierService.list(
                    new QueryWrapper<SysSupplier>().lambda().eq(SysSupplier::getType, SupplierTypeEnum.DEVICE));
            // 供应商map
            Map<String, Long> supplierMap = sysSupplierList.stream()
                    .collect(Collectors.toMap(SysSupplier::getName, SysSupplier::getId));

            // serialNo - obj
            Map<String, DeviceInfoDto> deviceMap = new HashMap<>();
            for (DeviceDeliveryImportDto deviceDeliveryImportDto : deviceDeliveryImportDtoList) {
                String serialNo = deviceDeliveryImportDto.getSerialNo();
                String deviceNo = deviceDeliveryImportDto.getDeviceNo();
                // 品牌
                String brand = deviceDeliveryImportDto.getBrand();
                // 型号
                String model = deviceDeliveryImportDto.getModel();
                if (deviceMap.containsKey(serialNo)) {
                    throw ExceptionResultEnum.ERROR.exception(String.format("excel中存在重复的设备序列号[%s]", serialNo));
                } else {
                    DeviceInfoDto deviceInfo = new DeviceInfoDto();
                    deviceInfo.setSerialNo(serialNo);
                    deviceInfo.setDeviceNo(deviceNo);
                    deviceInfo.setBrand(brand);
                    deviceInfo.setModel(model);
                    deviceMap.put(serialNo, deviceInfo);
                }
                String crmNo = deviceDeliveryImportDto.getCrmNo();

                String supplierName = deviceDeliveryImportDto.getSupplierName();
                Long supplierId = supplierMap.get(supplierName);
                if (supplierId == null || supplierId == 0) {
                    throw ExceptionResultEnum.ERROR.exception(String.format("excel中存在系统中不存在的供应商名称[%s]", supplierName));
                }
                DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUser.getId());
                if (dpr.getHasDeviceDelivery() && !dpr.getHasAdmin() && !dpr.getHasPmo() && !dpr.getHasBusiness()
                        && !dpr.getHasRegionManager()) {
                    // 设备管理员权限
                    Long userSupplierId = requestUser.getSupplierId();
                    if (userSupplierId == null || userSupplierId == 0) {
                        throw ExceptionResultEnum.ERROR.exception("请先在用户管理中绑定设备发货员对应的供应商");
                    }
                    if (!userSupplierId.equals(supplierId)) {
                        throw ExceptionResultEnum.ERROR.exception(String.format("缺少供应商[%s]的设备管理权限", supplierName));
                    }
                }

                String mailingAddress = deviceDeliveryImportDto.getMailingAddress();
                String consignee = deviceDeliveryImportDto.getConsignee();
                String consigneePhone = deviceDeliveryImportDto.getConsigneePhone();
                String expressNo = deviceDeliveryImportDto.getExpressNo();

                // 查询设备表是否有该设备
                SysDevice sysDevice = sysDeviceService.getOne(
                        new QueryWrapper<SysDevice>().lambda().eq(SysDevice::getSerialNo, serialNo)
                                .last(SystemConstant.LIMIT1));

                if (Objects.isNull(sysDevice)) {
                    // 设备表不存在该设备 -> 新增设备
                    sysDeviceModelService.findOrAddDeviceModel(brand, model);

                    SysDevice willAddSysDevice = new SysDevice();
                    willAddSysDevice.setDeviceCode(deviceNo);
                    willAddSysDevice.setSerialNo(serialNo);
                    willAddSysDevice.setBrand(brand);
                    willAddSysDevice.setModel(model);
                    willAddSysDevice.setBuyTime(System.currentTimeMillis());
                    willAddSysDevice.setSupplierId(supplierId);
                    willAddSysDevice.setStatus(DeviceStatusEnum.NORMAL);
                    willAddSysDevice.setLocation(supplierName);
                    willAddSysDevice.setBound(InOutTypeEnum.IN);
                    willAddSysDevice.setEnable(true);
                    willAddSysDevice.setScanCount(0L);
                    sysDeviceService.save(willAddSysDevice);
                } else {
                    if (InOutTypeEnum.OUT.equals(sysDevice.getBound())) {
                        throw ExceptionResultEnum.ERROR.exception(
                                String.format("序列号为[%s]的设备在设备管理中已存在且状态为'出库',不能导入", sysDevice.getSerialNo()));
                    }
                }

                // 查询是否有处于发货过程中设备
                TBDeviceDelivery dbDeviceDelivery = this.getOne(
                        new QueryWrapper<TBDeviceDelivery>().lambda().eq(TBDeviceDelivery::getSerialNo, serialNo)
                                .eq(TBDeviceDelivery::getEffect, true).last(SystemConstant.LIMIT1));

                if (Objects.nonNull(dbDeviceDelivery)) {
                    throw ExceptionResultEnum.EXCEPTION_ERROR.exception(
                            String.format("excel中存在已发货过程中的设备(序列号)[%s]", serialNo));
                }

                TBDeviceDelivery tbDeviceDelivery = new TBDeviceDelivery();
                if (crmNo != null && crmNo.length() > 0) {
                    if (!crmNoList.contains(crmNo)) {
                        throw ExceptionResultEnum.ERROR.exception("派单号不存在");
                    }

                    tbDeviceDelivery.setCrmNo(crmNo);
                    tbDeviceDelivery.setUsageType(DeviceUsageTypeEnum.PROJECT);
                } else {
                    tbDeviceDelivery.setUsageType(DeviceUsageTypeEnum.OTHER);
                }
                tbDeviceDelivery.setSerialNo(serialNo);
                tbDeviceDelivery.setSupplierId(supplierId);
                tbDeviceDelivery.setMailingAddress(mailingAddress);
                tbDeviceDelivery.setConsignee(consignee);
                tbDeviceDelivery.setConsigneePhone(consigneePhone);
                tbDeviceDelivery.setExpressNo(expressNo);
                tbDeviceDelivery.setEnable(true);
                tbDeviceDelivery.setEffect(true);
                tbDeviceDelivery.setDeliveryType(InOutTypeEnum.OUT);
                tbDeviceDelivery.setStatus(DeviceDeliveryStatusEnum.UN_DELIVER);
                tbDeviceDelivery.setCreateId(requestUser.getId());
                this.save(tbDeviceDelivery);
            }
        }
    }

    @Transactional
    @Override
    public void editDeviceDelivery(DeviceDeliveryParam deviceDeliveryParam) {
        Long id = deviceDeliveryParam.getId();
        String mailingAddress = deviceDeliveryParam.getMailingAddress();
        String consignee = deviceDeliveryParam.getConsignee();
        String consigneePhone = deviceDeliveryParam.getConsigneePhone();
        String expressNo = deviceDeliveryParam.getExpressNo();
        this.update(new UpdateWrapper<TBDeviceDelivery>().lambda().eq(TBDeviceDelivery::getId, id)
                .set(TBDeviceDelivery::getMailingAddress, mailingAddress).set(TBDeviceDelivery::getConsignee, consignee)
                .set(TBDeviceDelivery::getConsigneePhone, consigneePhone)
                .set(TBDeviceDelivery::getExpressNo, expressNo));
    }

    @Override
    public IPage<TBDeviceDeliveryResult> page(IPage<TBDeviceDeliveryResult> iPage, Long serviceId, DeviceUsageTypeEnum usageType,
            String crmNo, DeviceDeliveryStatusEnum status, Long deliverUserId, Long deliveryStartTime, Long deliveryEndTime, String serialNo, String deviceNo, Long supplierId,
            Long receiveStartTime, Long receiveEndTime, String consignee, String mailingAddress, InOutTypeEnum deliveryType, String brand, String model, String expressNo) throws Exception {
        crmNo = SystemConstant.translateSpecificSign(crmNo);
        serialNo = SystemConstant.translateSpecificSign(serialNo);
        deviceNo = SystemConstant.translateSpecificSign(deviceNo);
        consignee = SystemConstant.translateSpecificSign(consignee);
        mailingAddress = SystemConstant.translateSpecificSign(mailingAddress);
        brand = SystemConstant.translateSpecificSign(brand);
        model = SystemConstant.translateSpecificSign(model);
        expressNo = SystemConstant.translateSpecificSign(expressNo);

        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUser.getId());
        if (dpr.getHasDeviceDelivery() && !dpr.getHasAdmin() && !dpr.getHasPmo() && !dpr.getHasBusiness()
                && !dpr.getHasRegionManager()) {
            // 是设备管理员
            Long userSupplierId = requestUser.getSupplierId();
            if (userSupplierId == null || userSupplierId == 0) {
                throw ExceptionResultEnum.ERROR.exception("请先在用户管理中绑定设备发货员对应的供应商");
            }
            supplierId = userSupplierId;
        }
        IPage<TBDeviceDeliveryResult> page = this.baseMapper.page(iPage, serviceId, usageType, crmNo, status,
                deliverUserId, deliveryStartTime, deliveryEndTime, serialNo, deviceNo, supplierId, receiveStartTime,
                receiveEndTime, consignee, mailingAddress, deliveryType, brand, model, expressNo);
        for (TBDeviceDeliveryResult e : page.getRecords()) {
            this.fillResultInfo(e);
        }
        return page;
    }

    @Override
    public List<TBDeviceDeliveryResult> list(Long serviceId, DeviceUsageTypeEnum usageType, String crmNo,
            DeviceDeliveryStatusEnum status, Long deliverUserId, Long deliveryStartTime, Long deliveryEndTime,
            String serialNo, String deviceNo, Long supplierId, Long receiveStartTime, Long receiveEndTime, String consignee, String mailingAddress,
            InOutTypeEnum deliveryType, String brand, String model, String expressNo) throws Exception {

        crmNo = SystemConstant.translateSpecificSign(crmNo);
        serialNo = SystemConstant.translateSpecificSign(serialNo);
        deviceNo = SystemConstant.translateSpecificSign(deviceNo);
        consignee = SystemConstant.translateSpecificSign(consignee);
        mailingAddress = SystemConstant.translateSpecificSign(mailingAddress);
        brand = SystemConstant.translateSpecificSign(brand);
        model = SystemConstant.translateSpecificSign(model);
        expressNo = SystemConstant.translateSpecificSign(expressNo);

        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUser.getId());
        if (dpr.getHasDeviceDelivery() && !dpr.getHasAdmin() && !dpr.getHasPmo() && !dpr.getHasBusiness()
                && !dpr.getHasRegionManager()) {
            // 设备
            Long userSupplierId = requestUser.getSupplierId();
            if (userSupplierId == null || userSupplierId == 0) {
                throw ExceptionResultEnum.ERROR.exception("请先在用户管理中绑定设备发货员对应的供应商");
            }
            supplierId = userSupplierId;
        }
        List<TBDeviceDeliveryResult> result = this.baseMapper.list(serviceId, usageType, crmNo, status, deliverUserId,
                deliveryStartTime, deliveryEndTime, serialNo, deviceNo, supplierId, receiveStartTime, receiveEndTime,
                consignee, mailingAddress, deliveryType, brand, model, expressNo);
        for (TBDeviceDeliveryResult e : result) {
            this.fillResultInfo(e);
        }
        return result;
    }

    @Override
    public void dataExport(Long serviceId, DeviceUsageTypeEnum usageType, String crmNo, DeviceDeliveryStatusEnum status,
            Long deliverUserId, Long deliveryStartTime, Long deliveryEndTime, String serialNo, String deviceNo,
            Long supplierId, Long receiveStartTime, Long receiveEndTime, String consignee, String mailingAddress,
            InOutTypeEnum deliveryType, String brand, String model, String expressNo) throws Exception {

        List<TBDeviceDeliveryResult> datasource = this.list(serviceId, usageType, crmNo, status, deliverUserId,
                deliveryStartTime, deliveryEndTime, serialNo, deviceNo, supplierId, receiveStartTime, receiveEndTime,
                consignee, mailingAddress, deliveryType, brand, model, expressNo);
        File fileTemp = null;
        try {
            fileTemp = SystemConstant.getFileTempVar(SystemConstant.XLSX_PREFIX);
            EasyExcel.write(fileTemp, TBDeviceDeliveryResult.class).sheet("设备发货管理列表").doWrite(datasource);
            HttpServletResponse response = ServletUtil.getResponse();
            FileUtil.outputFile(response, fileTemp, "设备发货管理列表导出.xlsx");
        } finally {
            if (Objects.nonNull(fileTemp)) {
                fileTemp.delete();
            }
        }
    }

    @Transactional
    @Override
    public void addInRecord(List<DeviceInOutForm> deviceInOutFormList) {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        for (DeviceInOutForm deviceInOutForm : deviceInOutFormList) {
            String serialNo = deviceInOutForm.getSerialNo();
            List<TBDeviceDelivery> outList = this.list(new QueryWrapper<TBDeviceDelivery>().lambda()
                    .eq(TBDeviceDelivery::getStatus, DeviceDeliveryStatusEnum.RECEIVE)
                    .eq(TBDeviceDelivery::getEffect, true).eq(TBDeviceDelivery::getSerialNo, serialNo)
                    .eq(TBDeviceDelivery::getDeliveryType, InOutTypeEnum.OUT));

            if (CollectionUtils.isNotEmpty(outList)) {
                if (outList.size() > 1) {
                    throw ExceptionResultEnum.ERROR.exception(String.format("存在多条有效的设备发货记录序列号[%s]", serialNo));
                }
                TBDeviceDelivery out = outList.get(0);
                out.setStatus(DeviceDeliveryStatusEnum.RETURN);
                out.setEffect(false);
                this.updateById(out);

                TBDeviceDelivery in = new TBDeviceDelivery();
                in.setCrmNo(out.getCrmNo());
                in.setSerialNo(serialNo);
                in.setUsageType(out.getUsageType());
                in.setSupplierId(out.getSupplierId());
                in.setMailingAddress(deviceInOutForm.getAddress());
                // 发货时发货人信息
                Long outDeliverUserId = out.getDeliverUserId();
                if (Objects.isNull(outDeliverUserId)) {
                    throw ExceptionResultEnum.ERROR.exception("发货人信息为空");
                }
                SysUser outDeliveryUser = sysUserService.getById(outDeliverUserId);
                in.setConsignee(outDeliveryUser.getRealName());
                in.setConsigneePhone(outDeliveryUser.getMobileNumber());
                in.setExpressPhotoPath(deviceInOutForm.getBasePhotoPath());
                in.setDeliveryType(InOutTypeEnum.IN);
                in.setStatus(DeviceDeliveryStatusEnum.DELIVER);
                in.setDeliverTime(System.currentTimeMillis());
                in.setDeliverUserId(requestUser.getId());
                in.setEffect(true);
                in.setEnable(true);
                this.save(in);
            }
        }
    }

    @Transactional
    @Override
    public void updateOutRecord(List<DeviceInOutForm> deviceInOutFormList) {
        for (DeviceInOutForm deviceInOutForm : deviceInOutFormList) {
            String serialNo = deviceInOutForm.getSerialNo();
            List<TBDeviceDelivery> outList = this.list(new QueryWrapper<TBDeviceDelivery>().lambda()
                    .eq(TBDeviceDelivery::getStatus, DeviceDeliveryStatusEnum.DELIVER)
                    .eq(TBDeviceDelivery::getEffect, true).eq(TBDeviceDelivery::getSerialNo, serialNo)
                    .eq(TBDeviceDelivery::getDeliveryType, InOutTypeEnum.OUT));

            if (CollectionUtils.isNotEmpty(outList)) {
                if (outList.size() > 1) {
                    throw ExceptionResultEnum.ERROR.exception(String.format("存在多条有效的设备发货记录序列号[%s]", serialNo));
                }
                TBDeviceDelivery out = outList.get(0);
                out.setExpressPhotoPath(deviceInOutForm.getBasePhotoPath());
                this.updateById(out);
            }
        }
    }

    @Transactional
    @Override
    public void updateStatus(List<Long> deviceDeliveryIdList, DeviceDeliveryStatusEnum status, Long requestUserId) {
        if (CollectionUtils.isNotEmpty(deviceDeliveryIdList)) {
            // 只有在生命周期的才能被操作
            List<TBDeviceDelivery> tbDeviceDeliveryList = this.listByIds(deviceDeliveryIdList);

            switch (status) {
            case DELIVER:
                // 变更为已发货状态(供应商点击批量发货时触发 UN_DELIVER -> DELIVER(或sop入库新增记录触发))
                if (tbDeviceDeliveryList.stream()
                        .anyMatch(e -> !DeviceDeliveryStatusEnum.UN_DELIVER.equals(e.getStatus()) || !e.getEffect())) {
                    throw ExceptionResultEnum.ERROR.exception(
                            String.format("只有[出入库过程中]且[%s]的记录才能[%s]", DeviceDeliveryStatusEnum.UN_DELIVER.getTitle(),
                                    "发货"));
                }
                tbDeviceDeliveryList = tbDeviceDeliveryList.stream().peek(e -> {
                    Long currenTime = System.currentTimeMillis();
                    e.setDeliverUserId(requestUserId);
                    e.setUpdateId(requestUserId);
                    e.setDeliverTime(currenTime);
                    e.setUpdateTime(currenTime);
                    e.setStatus(DeviceDeliveryStatusEnum.DELIVER);
                }).collect(Collectors.toList());
                this.updateBatchById(tbDeviceDeliveryList);
                break;
            case RECEIVE:
                // 变更为已签收状态(设备发货列表点击签收触发||sop现场测试签收 -> DELIVER -> RECEIVE)
                // 入库签收终止该入库记录的生命周期（出库签收不终止,设备发货出库生命周期的终止是在设备出库提交,生成设备发货入库记录时触发）
                List<Long> needAutoReceiveIdList = new ArrayList<>();
                if (tbDeviceDeliveryList.stream()
                        .anyMatch(e -> !DeviceDeliveryStatusEnum.DELIVER.equals(e.getStatus()) || !e.getEffect())) {
                    throw ExceptionResultEnum.ERROR.exception(
                            String.format("只有[出入库过程中]且[%s]的发货记录才能[%s]", DeviceDeliveryStatusEnum.DELIVER.getTitle(),
                                    "签收"));
                }
                tbDeviceDeliveryList = tbDeviceDeliveryList.stream().peek(e -> {
                    Long currenTime = System.currentTimeMillis();
                    e.setReceiveUserId(requestUserId);
                    e.setUpdateId(requestUserId);
                    e.setReceiveTime(currenTime);
                    e.setUpdateTime(currenTime);
                    e.setStatus(DeviceDeliveryStatusEnum.RECEIVE);
                    if (InOutTypeEnum.IN.equals(e.getDeliveryType())) {
                        e.setEffect(false);
                    } else {
                        // 出库签收自动生成出入库登记出库记录
                        if (DeviceUsageTypeEnum.PROJECT.equals(e.getUsageType())) {
                            throw ExceptionResultEnum.ERROR.exception("项目用途的设备不能手动签收(sop现场环境测试签收)");
                        }
                        needAutoReceiveIdList.add(e.getId());
                    }
                }).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(needAutoReceiveIdList)) {
                    this.autoOutDeviceForTrain(needAutoReceiveIdList);
                }
                this.updateBatchById(tbDeviceDeliveryList);
                break;
            case CANCEL:
                // 变更为已作废状态(只有带发货状态能被作废)
                // 作废触发时该出库生命周期终止
                if (tbDeviceDeliveryList.stream()
                        .anyMatch(e -> !DeviceDeliveryStatusEnum.UN_DELIVER.equals(e.getStatus()) || !e.getEffect())) {
                    throw ExceptionResultEnum.ERROR.exception(
                            String.format("只有[出入库过程中]且[%s]的发货记录才能[%s]", DeviceDeliveryStatusEnum.UN_DELIVER.getTitle(),
                                    "作废"));
                }
                tbDeviceDeliveryList = tbDeviceDeliveryList.stream().peek(e -> {
                    e.setUpdateId(requestUserId);
                    e.setUpdateTime(System.currentTimeMillis());
                    e.setStatus(DeviceDeliveryStatusEnum.CANCEL);
                    e.setEnable(false);
                    e.setEffect(false);
                }).collect(Collectors.toList());
                this.updateBatchById(tbDeviceDeliveryList);
                break;
            }
        }
    }

    @Override
    public List<SysUser> findDeliveryUserList() {
        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
        DataPermissionDto dpr = sysUserService.buildUserDataPermission(requestUser.getId());
        QueryWrapper<TBDeviceDelivery> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().select(TBDeviceDelivery::getDeliverUserId).eq(TBDeviceDelivery::getEnable, true)
                .ne(TBDeviceDelivery::getStatus, DeviceDeliveryStatusEnum.CANCEL);

        if (dpr.getHasDeviceDelivery() && !dpr.getHasAdmin() && !dpr.getHasPmo() && !dpr.getHasBusiness()
                && !dpr.getHasRegionManager()) {
            // 设备
            Long userSupplierId = requestUser.getSupplierId();
            if (userSupplierId == null || userSupplierId == 0) {
                throw ExceptionResultEnum.ERROR.exception("请先在用户管理中绑定设备发货员对应的供应商");
            }
            queryWrapper.lambda().eq(TBDeviceDelivery::getSupplierId, userSupplierId);
        }
        List<Long> deliveryUserIdList = this.list(queryWrapper).stream()
                .filter(e -> e != null && e.getDeliverUserId() != null && e.getDeliverUserId() > 0)
                .map(TBDeviceDelivery::getDeliverUserId).distinct().collect(Collectors.toList());
        List<SysUser> result = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(deliveryUserIdList)) {
            result = sysUserService.listByIds(deliveryUserIdList);
        }
        return result;
    }

    @Override
    @Transactional
    public void updateStatusBySop(FlowTaskResult flowTaskResult, DeviceDeliveryStatusEnum status, Long userId) {
        List<FlowFormWidgetResult> flowFormWidgetResultList = flowTaskResult.getFormProperty();
        List<String> deviceSerialNoList = new ArrayList<>();
        for (FlowFormWidgetResult t : flowFormWidgetResultList) {
            if (t.getFormId().contains(SystemConstant.DEVICE_OUT_TABLE)) {
                JSONObject jsonObject = JSONObject.parseObject(t.getValue());
                JSONArray jsonArray = jsonObject.getJSONArray(SystemConstant.VALUE);
                for (int i = 0; i < jsonArray.size(); i++) {
                    DeviceInOutForm deviceInOutForm = GsonUtil.fromJson(GsonUtil.toJson(jsonArray.getJSONObject(i)),
                            DeviceInOutForm.class);
                    deviceSerialNoList.add(deviceInOutForm.getSerialNo());
                }
            }
        }
        List<TBDeviceDelivery> tbDeviceDeliveryList = this.list(
                new QueryWrapper<TBDeviceDelivery>().lambda().eq(TBDeviceDelivery::getEffect, true)
                        .in(TBDeviceDelivery::getSerialNo, deviceSerialNoList));
        if (!CollectionUtils.isEmpty(tbDeviceDeliveryList)) {
            for (TBDeviceDelivery t : tbDeviceDeliveryList) {
                t.setStatus(status);
                t.setReceiveTime(System.currentTimeMillis());
                t.setReceiveUserId(userId);
                t.updateInfo(userId);
            }
            this.updateBatchById(tbDeviceDeliveryList);
        }
    }

    @Override
    @Transactional
    public void updateDeviceRelated(FlowTaskResult flowTaskResult, String sopNo, DeviceDeliveryStatusEnum status,
            Long userId, Boolean deviceDeliveryUpdate) {
        List<FlowFormWidgetResult> flowFormWidgetResultList = flowTaskResult.getFormProperty();
        List<String> deviceSerialNoList = new ArrayList<>();
        for (FlowFormWidgetResult t : flowFormWidgetResultList) {
            if (t.getFormId().contains(SystemConstant.DEVICE_OUT_TABLE)) {
                JSONObject jsonObject = JSONObject.parseObject(t.getValue());
                JSONArray jsonArray = jsonObject.getJSONArray(SystemConstant.VALUE);
                for (int i = 0; i < jsonArray.size(); i++) {
                    DeviceInOutForm deviceInOutForm = GsonUtil.fromJson(GsonUtil.toJson(jsonArray.getJSONObject(i)),
                            DeviceInOutForm.class);
                    deviceSerialNoList.add(deviceInOutForm.getSerialNo());

                    List<TBDeviceInOut> tbDeviceInOutList = tbDeviceInOutService.list(
                            new QueryWrapper<TBDeviceInOut>().lambda().eq(TBDeviceInOut::getSopNo, sopNo)
                                    .eq(TBDeviceInOut::getType, InOutTypeEnum.OUT)
                                    .eq(TBDeviceInOut::getDeviceSerialNo, deviceInOutForm.getSerialNo()));
                    if (!CollectionUtils.isEmpty(tbDeviceInOutList)) {
                        for (TBDeviceInOut tbDeviceInOut : tbDeviceInOutList) {
                            SysDevice sysDevice = sysDeviceService.getOne(new QueryWrapper<SysDevice>().lambda()
                                    .eq(SysDevice::getSerialNo, deviceInOutForm.getSerialNo()));
                            sysDevice.setLocation(tbDeviceInOut.getLocation());
                            sysDevice.setLocationArr(tbDeviceInOut.getAddressArr());
                            sysDevice.setBound(InOutTypeEnum.IN);
                            sysDevice.updateInfo(userId);
                            sysDeviceService.updateById(sysDevice);
                        }
                        tbDeviceInOutService.removeByIds(
                                tbDeviceInOutList.stream().map(TBDeviceInOut::getId).collect(Collectors.toList()));
                    }
                }
            }
        }
        if (deviceDeliveryUpdate) {
            List<TBDeviceDelivery> tbDeviceDeliveryList = this.list(
                    new QueryWrapper<TBDeviceDelivery>().lambda().eq(TBDeviceDelivery::getEffect, true)
                            .in(TBDeviceDelivery::getSerialNo, deviceSerialNoList));
            if (!CollectionUtils.isEmpty(tbDeviceDeliveryList)) {
                for (TBDeviceDelivery t : tbDeviceDeliveryList) {
                    t.setStatus(status);
                    t.setReceiveTime(System.currentTimeMillis());
                    t.setReceiveUserId(userId);
                    t.updateInfo(userId);
                }
                this.updateBatchById(tbDeviceDeliveryList);
            }
        }
    }

    @Transactional
    @Override
    public void autoOutDeviceForTrain(List<Long> deviceDeliveryIdList) {
        List<TBDeviceDelivery> tbDeviceDeliveryList = this.listByIds(deviceDeliveryIdList);
        if (CollectionUtils.isNotEmpty(tbDeviceDeliveryList)) {

            List<DeviceInOutForm> deviceInOutFormList = new ArrayList<>();
            for (TBDeviceDelivery tbDeviceDelivery : tbDeviceDeliveryList) {
                String serialNo = tbDeviceDelivery.getSerialNo();
                if (!tbDeviceDelivery.getEffect()) {
                    throw ExceptionResultEnum.ERROR.exception(String.format("所选设备序列号为[%s]的记录是已完成的数据，不能操作", serialNo));
                }
                TBDeviceInOut tbDeviceInOut = tbDeviceInOutService.getOne(
                        new QueryWrapper<TBDeviceInOut>().lambda().eq(TBDeviceInOut::getDeviceSerialNo, serialNo).orderByDesc(TBDeviceInOut::getSerialNo).last(SystemConstant.LIMIT1));

                SysDevice sysDevice = sysDeviceService.getOne(
                        new QueryWrapper<SysDevice>().lambda().eq(SysDevice::getSerialNo, serialNo).last(SystemConstant.LIMIT1));
                if (Objects.isNull(sysDevice)) {
                    throw ExceptionResultEnum.ERROR.exception(String.format("所选设备序列号为[%s]的设备在设备配置表中不存在", serialNo));
                }
                InOutTypeEnum sysType = sysDevice.getBound();
                if (Objects.nonNull(tbDeviceInOut)) {
                    InOutTypeEnum inOutType = tbDeviceInOut.getType();
                    if (!sysType.equals(inOutType)) {
                        throw ExceptionResultEnum.ERROR.exception(
                                String.format("所选设备序列号为[%s]的设备状态异常（设备配置和设备出入库明细不一致）", serialNo));
                    }

                    if (InOutTypeEnum.OUT.equals(sysType)) {
                        throw ExceptionResultEnum.ERROR.exception(String.format("所选设备序列号为[%s]的设备为出库状态,状态异常", serialNo));
                    }
                }

                DeviceInOutForm deviceInOutForm = new DeviceInOutForm();
                deviceInOutForm.setSerialNo(serialNo);
                deviceInOutForm.setDeviceBrand(sysDevice.getBrand());
                Long supplierId = tbDeviceDelivery.getSupplierId();
                SysSupplier sysSupplier = sysSupplierService.getById(supplierId);
                if (Objects.isNull(sysSupplier)) {
                    throw ExceptionResultEnum.ERROR.exception("供应商不存在");
                }
                deviceInOutForm.setSupplierName(sysSupplier.getName());
                deviceInOutForm.setDeviceStatus(sysDevice.getStatus());
                int scanCount = 0;
                Long scanCountL = sysDevice.getScanCount();
                if (scanCountL != null && scanCountL > 0) {
                    scanCount = scanCountL.intValue();
                }
                deviceInOutForm.setScanCount(scanCount);
                deviceInOutForm.setLocation(sysDevice.getLocation());
                deviceInOutForm.setAddress(tbDeviceDelivery.getMailingAddress());
                deviceInOutForm.setExpressNo(tbDeviceDelivery.getExpressNo());
                deviceInOutFormList.add(deviceInOutForm);
            }
            DeviceInOutSubmitParam deviceInOutSubmitParam = new DeviceInOutSubmitParam();
            deviceInOutSubmitParam.setInOutTime(System.currentTimeMillis());
            deviceInOutSubmitParam.setInOutType(InOutTypeEnum.OUT);
            deviceInOutSubmitParam.setPushSupplier(true);
            deviceInOutSubmitParam.setDeviceInOutFormList(deviceInOutFormList);
            tbDeviceInOutService.deviceInOutSubmit(deviceInOutSubmitParam);
        }
    }

    @Override
    public List<DeviceInOutForm> findDeviceReturnList(List<Long> deviceDeliveryIdList) {
        List<DeviceInOutForm> deviceInOutFormList = new ArrayList<>();
        List<TBDeviceDelivery> tbDeviceDeliveryList;
        if (CollectionUtils.isNotEmpty(deviceDeliveryIdList)) {
            tbDeviceDeliveryList = this.listByIds(deviceDeliveryIdList);
        } else {
            // 查询所有可返还设备（设备发货管理状态为（出库,effect,已签收,用途为培训））
            tbDeviceDeliveryList = this.list(
                    new QueryWrapper<TBDeviceDelivery>().lambda().eq(TBDeviceDelivery::getEffect, true)
                            .eq(TBDeviceDelivery::getDeliveryType, InOutTypeEnum.OUT)
                            .eq(TBDeviceDelivery::getStatus, DeviceDeliveryStatusEnum.RECEIVE)
                            .eq(TBDeviceDelivery::getUsageType, DeviceUsageTypeEnum.OTHER));
        }

        if (CollectionUtils.isNotEmpty(tbDeviceDeliveryList)) {
            for (TBDeviceDelivery tbDeviceDelivery : tbDeviceDeliveryList) {
                String serialNo = tbDeviceDelivery.getSerialNo();
                if (!tbDeviceDelivery.getEffect()) {
                    throw ExceptionResultEnum.ERROR.exception(String.format("所选设备序列号为[%s]的记录是已完成的数据，不能操作", serialNo));
                }
                InOutTypeEnum type = tbDeviceDelivery.getDeliveryType();
                DeviceDeliveryStatusEnum status = tbDeviceDelivery.getStatus();
                if (!InOutTypeEnum.OUT.equals(type) || !DeviceDeliveryStatusEnum.RECEIVE.equals(status) || DeviceUsageTypeEnum.PROJECT.equals(tbDeviceDelivery.getUsageType())) {
                    throw ExceptionResultEnum.ERROR.exception(
                            String.format("所选设备序列号为[%s]的记录的发货状态必须是[%s][%s],且不能是[%s]用途", serialNo, InOutTypeEnum.OUT.getTitle(),
                                    DeviceDeliveryStatusEnum.RECEIVE.getTitle(), DeviceUsageTypeEnum.PROJECT.getTitle()));
                }

                TBDeviceInOut tbDeviceInOut = tbDeviceInOutService.getOne(
                        new QueryWrapper<TBDeviceInOut>().lambda().eq(TBDeviceInOut::getDeviceSerialNo, serialNo).orderByDesc(TBDeviceInOut::getSerialNo).last(SystemConstant.LIMIT1));

                SysDevice sysDevice = sysDeviceService.getOne(
                        new QueryWrapper<SysDevice>().lambda().eq(SysDevice::getSerialNo, serialNo).last(SystemConstant.LIMIT1));
                if (Objects.isNull(sysDevice)) {
                    throw ExceptionResultEnum.ERROR.exception(String.format("所选设备序列号为[%s]的设备在设备配置表中不存在", serialNo));
                }
                InOutTypeEnum sysType = sysDevice.getBound();
                if (Objects.nonNull(tbDeviceInOut)) {
                    InOutTypeEnum inOutType = tbDeviceInOut.getType();
                    if (!sysType.equals(inOutType)) {
                        throw ExceptionResultEnum.ERROR.exception(
                                String.format("所选设备序列号为[%s]的设备状态异常（设备配置和设备出入库明细不一致）", serialNo));
                    }

                    if (InOutTypeEnum.IN.equals(sysType)) {
                        throw ExceptionResultEnum.ERROR.exception(String.format("所选设备序列号为[%s]的设备为入库状态,状态异常", serialNo));
                    }
                }

                DeviceInOutForm deviceInOutForm = new DeviceInOutForm();
                deviceInOutForm.setSerialNo(serialNo);
                deviceInOutForm.setDeviceBrand(sysDevice.getBrand());
                Long supplierId = tbDeviceDelivery.getSupplierId();
                SysSupplier sysSupplier = sysSupplierService.getById(supplierId);
                if (Objects.isNull(sysSupplier)) {
                    throw ExceptionResultEnum.ERROR.exception("供应商不存在");
                }
                deviceInOutForm.setSupplierName(sysSupplier.getName());
                deviceInOutForm.setDeviceStatus(sysDevice.getStatus());
                int scanCount = 0;
                Long scanCountL = sysDevice.getScanCount();
                if (scanCountL != null && scanCountL > 0) {
                    scanCount = scanCountL.intValue();
                }
                deviceInOutForm.setScanCount(scanCount);
                deviceInOutForm.setLocation(sysDevice.getLocation());
                deviceInOutForm.setAddress(sysSupplier.getName());
                deviceInOutFormList.add(deviceInOutForm);
            }
        }
        return deviceInOutFormList;
    }

    /**
     * 补充结果信息
     *
     * @param e 发货结果对象
     */
    private void fillResultInfo(TBDeviceDeliveryResult e) {
        DeviceUsageTypeEnum dbUsageType = e.getUsageType();
        e.setUsageTypeStr(Objects.nonNull(dbUsageType) ? dbUsageType.getTitle() : "");

        InOutTypeEnum dbDeliveryType = e.getDeliveryType();
        e.setDeliveryTypeStr(Objects.nonNull(dbDeliveryType) ? dbDeliveryType.getTitle() : "");

        DeviceDeliveryStatusEnum dbStatus = e.getStatus();
        e.setStatusStr(Objects.nonNull(dbStatus) ? dbStatus.getTitle() : "");

        Long dbDeliveryTime = e.getDeliverTime();
        e.setDeliverTimeStr(dbDeliveryTime != null && dbDeliveryTime > 0 ?
                DateFormatUtils.format(dbDeliveryTime, SystemConstant.DEFAULT_DATE_PATTERN) :
                "--");

        Long dbReceiveTime = e.getReceiveTime();
        e.setReceiveTimeStr(dbReceiveTime != null && dbReceiveTime > 0 ?
                DateFormatUtils.format(dbReceiveTime, SystemConstant.DEFAULT_DATE_PATTERN) :
                "--");

        String path = e.getExpressPhotoPath();
        if (path != null && path.length() > 0) {
            e.setExpressPhotoPath(path);
        }
    }
}
