package cn.iocoder.foodnexus.module.erp.service.sale;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.foodnexus.framework.common.pojo.PageResult;
import cn.iocoder.foodnexus.framework.common.util.CommonUtil;
import cn.iocoder.foodnexus.framework.common.util.number.MoneyUtils;
import cn.iocoder.foodnexus.framework.common.util.object.BeanUtils;
import cn.iocoder.foodnexus.framework.redis.utils.RedisUtils;
import cn.iocoder.foodnexus.module.erp.api.vo.warehouse.WarehouseInfo;
import cn.iocoder.foodnexus.module.erp.controller.admin.sale.vo.order.*;
import cn.iocoder.foodnexus.module.erp.controller.admin.sale.vo.out.ErpSaleOutSaveReqVO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.sale.ErpSaleOrderDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.sale.ErpSaleOutDO;
import cn.iocoder.foodnexus.module.erp.dal.dataobject.sale.ErpSaleOutItemDO;
import cn.iocoder.foodnexus.module.erp.dal.mysql.purchase.ErpPurchaseOrderMapper;
import cn.iocoder.foodnexus.module.erp.dal.mysql.sale.ErpSaleOrderItemMapper;
import cn.iocoder.foodnexus.module.erp.dal.mysql.sale.ErpSaleOrderMapper;
import cn.iocoder.foodnexus.module.erp.dal.mysql.sale.ErpSaleOutItemMapper;
import cn.iocoder.foodnexus.module.erp.dal.mysql.sale.ErpSaleOutMapper;
import cn.iocoder.foodnexus.module.erp.dal.redis.no.ErpNoRedisDAO;
import cn.iocoder.foodnexus.module.erp.api.enums.ErpAuditStatus;
import cn.iocoder.foodnexus.module.erp.enums.SaleOrderPickUpStatus;
import cn.iocoder.foodnexus.module.erp.service.finance.ErpAccountService;
import cn.iocoder.foodnexus.module.erp.service.purchase.ErpPurchaseOrderService;
import cn.iocoder.foodnexus.module.order.api.CustomerOrderApi;
import cn.iocoder.foodnexus.module.order.api.CustomerOrderRecordApi;
import cn.iocoder.foodnexus.module.order.api.DeliveryStaffApi;
import cn.iocoder.foodnexus.module.order.dto.*;
import cn.iocoder.foodnexus.module.order.enums.CustomerOrderStatus;
import cn.iocoder.foodnexus.module.product.service.spu.ProductSpuService;
import cn.iocoder.foodnexus.module.system.api.user.AdminUserApi;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

import static cn.iocoder.foodnexus.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.foodnexus.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.foodnexus.module.erp.enums.ErrorCodeConstants.*;

// TODO 芋艿：记录操作日志

/**
 * ERP 销售订单 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
public class ErpSaleOrderServiceImpl implements ErpSaleOrderService {

    @Resource
    private ErpSaleOrderMapper saleOrderMapper;
    @Resource
    private ErpSaleOrderItemMapper saleOrderItemMapper;

    @Resource
    private ErpNoRedisDAO noRedisDAO;

    @Resource
    private ProductSpuService productService;
    @Resource
    private ErpCustomerService customerService;
    @Resource
    private ErpAccountService accountService;

    @Resource
    private AdminUserApi adminUserApi;

    @Autowired
    private CustomerOrderRecordApi orderRecordApi;

    @Autowired
    private CustomerOrderApi customerOrderApi;

    @Autowired
    private DeliveryStaffApi deliveryStaffApi;

    @Autowired
    private ErpSaleOutService saleOutService;

    @Autowired
    private ErpSaleOutMapper saleOutMapper;

    @Autowired
    private ErpSaleOutItemMapper saleOutItemMapper;

    @Autowired
    private ErpPurchaseOrderMapper purchaseOrderMapper;

    @Autowired
    @Lazy
    private ErpSaleReturnService saleReturnService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createSaleOrder(ErpSaleOrderSaveReqVO createReqVO) {
        // 1.1 校验订单项的有效性
        List<ErpSaleOrderItemDO> saleOrderItems = validateSaleOrderItems(createReqVO.getItems());
        // 1.2 校验客户
        customerService.validateCustomer(createReqVO.getCustomerId());
        // 1.3 校验结算账户
        if (createReqVO.getAccountId() != null) {
            accountService.validateAccount(createReqVO.getAccountId());
        }
        // 1.4 校验销售人员
        if (createReqVO.getSaleUserId() != null) {
            adminUserApi.validateUser(createReqVO.getSaleUserId());
        }
        // 1.5 生成订单号，并校验唯一性
        String no = noRedisDAO.generate(ErpNoRedisDAO.SALE_ORDER_NO_PREFIX);
        if (saleOrderMapper.selectByNo(no) != null) {
            throw exception(SALE_ORDER_NO_EXISTS);
        }

        // 2.1 插入订单
        ErpSaleOrderDO saleOrder = BeanUtils.toBean(createReqVO, ErpSaleOrderDO.class, in -> in
                .setNo(no).setStatus(ErpAuditStatus.APPROVE.getStatus()));
        calculateTotalPrice(saleOrder, saleOrderItems);
        saleOrderMapper.insert(saleOrder);
        // 2.2 插入订单项
        saleOrderItems.forEach(o -> o.setOrderId(saleOrder.getId()));
        saleOrderItemMapper.insertBatch(saleOrderItems);
        return saleOrder.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSaleOrder(ErpSaleOrderSaveReqVO updateReqVO) {
        // 1.1 校验存在
        ErpSaleOrderDO saleOrder = validateSaleOrderExists(updateReqVO.getId());
        if (ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) {
            throw exception(SALE_ORDER_UPDATE_FAIL_APPROVE, saleOrder.getNo());
        }
        // 1.2 校验客户
        customerService.validateCustomer(updateReqVO.getCustomerId());
        // 1.3 校验结算账户
        if (updateReqVO.getAccountId() != null) {
            accountService.validateAccount(updateReqVO.getAccountId());
        }
        // 1.4 校验销售人员
        if (updateReqVO.getSaleUserId() != null) {
            adminUserApi.validateUser(updateReqVO.getSaleUserId());
        }
        // 1.5 校验订单项的有效性
        List<ErpSaleOrderItemDO> saleOrderItems = validateSaleOrderItems(updateReqVO.getItems());

        // 2.1 更新订单
        ErpSaleOrderDO updateObj = BeanUtils.toBean(updateReqVO, ErpSaleOrderDO.class);
        calculateTotalPrice(updateObj, saleOrderItems);
        saleOrderMapper.updateById(updateObj);
        // 2.2 更新订单项
        updateSaleOrderItemList(updateReqVO.getId(), saleOrderItems);
    }

    private void calculateTotalPrice(ErpSaleOrderDO saleOrder, List<ErpSaleOrderItemDO> saleOrderItems) {
        saleOrder.setTotalCount(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getCount, Integer::sum));
        saleOrder.setTotalProductPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTotalPrice, Integer::sum, 0));
        saleOrder.setTotalTaxPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO));
        saleOrder.setTotalPrice(saleOrder.getTotalProductPrice());
        // 计算优惠价格
        if (saleOrder.getDiscountPercent() == null) {
            saleOrder.setDiscountPercent(BigDecimal.ZERO);
        }
        // saleOrder.setDiscountPrice(MoneyUtils.priceMultiplyPercent(saleOrder.getTotalPrice(), saleOrder.getDiscountPercent()));
        saleOrder.setTotalPrice(saleOrder.getTotalPrice()/*.subtract(saleOrder.getDiscountPrice()*/);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSaleOrderStatus(Long id, Integer status) {
        boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status);
        // 1.1 校验存在
        ErpSaleOrderDO saleOrder = validateSaleOrderExists(id);
        // 1.2 校验状态
        if (saleOrder.getStatus().equals(status)) {
            throw exception(approve ? SALE_ORDER_APPROVE_FAIL : SALE_ORDER_PROCESS_FAIL);
        }
        // 1.3 存在销售出库单，无法反审核
        if (!approve && saleOrder.getOutCount().compareTo(BigDecimal.ZERO) > 0) {
            throw exception(SALE_ORDER_PROCESS_FAIL_EXISTS_OUT);
        }
        // 1.4 存在销售退货单，无法反审核
        if (!approve && saleOrder.getReturnCount().compareTo(BigDecimal.ZERO) > 0) {
            throw exception(SALE_ORDER_PROCESS_FAIL_EXISTS_RETURN);
        }

        // 2. 更新状态
        int updateCount = saleOrderMapper.updateByIdAndStatus(id, saleOrder.getStatus(),
                new ErpSaleOrderDO().setStatus(status));
        if (updateCount == 0) {
            throw exception(approve ? SALE_ORDER_APPROVE_FAIL : SALE_ORDER_PROCESS_FAIL);
        }
    }

    private List<ErpSaleOrderItemDO> validateSaleOrderItems(List<ErpSaleOrderSaveReqVO.Item> list) {
        // 1. 校验产品存在
        // 2. 转化为 ErpSaleOrderItemDO 列表
        return convertList(list, o -> BeanUtils.toBean(o, ErpSaleOrderItemDO.class, item -> {
            // item.setProductUnit(productMap.get(item.getProductId()).getUni());
            item.setTotalPrice(item.getProductPrice() * item.getCount());
            if (item.getTotalPrice() == null) {
                return;
            }
            /*if (item.getTaxPercent() != null) {
                item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent()));
            }*/
        }));
    }

    private void updateSaleOrderItemList(Long id, List<ErpSaleOrderItemDO> newList) {
        // 第一步，对比新老数据，获得添加、修改、删除的列表
        List<ErpSaleOrderItemDO> oldList = saleOrderItemMapper.selectListByOrderId(id);
        List<List<ErpSaleOrderItemDO>> diffList = diffList(oldList, newList, // id 不同，就认为是不同的记录
                (oldVal, newVal) -> oldVal.getId().equals(newVal.getId()));

        // 第二步，批量添加、修改、删除
        if (CollUtil.isNotEmpty(diffList.get(0))) {
            diffList.get(0).forEach(o -> o.setOrderId(id));
            saleOrderItemMapper.insertBatch(diffList.get(0));
        }
        if (CollUtil.isNotEmpty(diffList.get(1))) {
            saleOrderItemMapper.updateBatch(diffList.get(1));
        }
        if (CollUtil.isNotEmpty(diffList.get(2))) {
            saleOrderItemMapper.deleteByIds(convertList(diffList.get(2), ErpSaleOrderItemDO::getId));
        }
    }

    @Override
    public void updateSaleOrderOutCount(Long id, Map<Long, BigDecimal> outCountMap) {
        List<ErpSaleOrderItemDO> orderItems = saleOrderItemMapper.selectListByOrderId(id);
        // 1. 更新每个销售订单项
        orderItems.forEach(item -> {
            BigDecimal outCount = outCountMap.getOrDefault(item.getId(), BigDecimal.ZERO);
            if (item.getOutCount().equals(outCount)) {
                return;
            }
            if (outCount.compareTo(BigDecimal.valueOf(item.getCount())) > 0) {
                throw exception(SALE_ORDER_ITEM_OUT_FAIL_PRODUCT_EXCEED,
                        productService.getSpu(item.getProductId()).getName(), item.getCount());
            }
            saleOrderItemMapper.updateById(new ErpSaleOrderItemDO().setId(item.getId()).setOutCount(outCount));
        });
        // 2. 更新销售订单
        BigDecimal totalOutCount = getSumValue(outCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO);
        saleOrderMapper.updateById(new ErpSaleOrderDO().setId(id).setOutCount(totalOutCount));
    }

    @Override
    public void updateSaleOrderReturnCount(Long orderId, Map<Long, BigDecimal> returnCountMap) {
        List<ErpSaleOrderItemDO> orderItems = saleOrderItemMapper.selectListByOrderId(orderId);
        // 1. 更新每个销售订单项
        orderItems.forEach(item -> {
            BigDecimal returnCount = returnCountMap.getOrDefault(item.getId(), BigDecimal.ZERO);
            if (item.getReturnCount().equals(returnCount)) {
                return;
            }
            if (returnCount.compareTo(item.getOutCount()) > 0) {
                throw exception(SALE_ORDER_ITEM_RETURN_FAIL_OUT_EXCEED,
                        productService.getSpu(item.getProductId()).getName(), item.getOutCount());
            }
            saleOrderItemMapper.updateById(new ErpSaleOrderItemDO().setId(item.getId()).setReturnCount(returnCount));
        });
        // 2. 更新销售订单
        BigDecimal totalReturnCount = getSumValue(returnCountMap.values(), value -> value, BigDecimal::add, BigDecimal.ZERO);
        saleOrderMapper.updateById(new ErpSaleOrderDO().setId(orderId).setReturnCount(totalReturnCount));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteSaleOrder(List<Long> ids) {
        // 1. 校验不处于已审批
        List<ErpSaleOrderDO> saleOrders = saleOrderMapper.selectByIds(ids);
        if (CollUtil.isEmpty(saleOrders)) {
            return;
        }
        saleOrders.forEach(saleOrder -> {
            if (ErpAuditStatus.APPROVE.getStatus().equals(saleOrder.getStatus())) {
                throw exception(SALE_ORDER_DELETE_FAIL_APPROVE, saleOrder.getNo());
            }
        });

        // 2. 遍历删除，并记录操作日志
        saleOrders.forEach(saleOrder -> {
            // 2.1 删除订单
            saleOrderMapper.deleteById(saleOrder.getId());
            // 2.2 删除订单项
            saleOrderItemMapper.deleteByOrderId(saleOrder.getId());
        });
    }

    private ErpSaleOrderDO validateSaleOrderExists(Long id) {
        ErpSaleOrderDO saleOrder = saleOrderMapper.selectById(id);
        if (saleOrder == null) {
            throw exception(SALE_ORDER_NOT_EXISTS);
        }
        return saleOrder;
    }

    @Override
    public ErpSaleOrderDO getSaleOrder(Long id) {
        return saleOrderMapper.selectById(id);
    }

    @Override
    public ErpSaleOrderDO validateSaleOrder(Long id) {
        ErpSaleOrderDO saleOrder = validateSaleOrderExists(id);
        /*if (ObjectUtil.notEqual(saleOrder.getStatus(), ErpAuditStatus.APPROVE.getStatus())) {
            throw exception(SALE_ORDER_NOT_APPROVE);
        }*/
        return saleOrder;
    }

    @Override
    public PageResult<ErpSaleOrderDO> getSaleOrderPage(ErpSaleOrderPageReqVO pageReqVO) {
        return saleOrderMapper.selectPage(pageReqVO);
    }

    // ==================== 订单项 ====================

    @Override
    public List<ErpSaleOrderItemDO> getSaleOrderItemListByOrderId(Long orderId) {
        return saleOrderItemMapper.selectListByOrderId(orderId);
    }

    @Override
    public List<ErpSaleOrderItemDO> getSaleOrderItemListByOrderIds(Collection<Long> orderIds) {
        if (CollUtil.isEmpty(orderIds)) {
            return Collections.emptyList();
        }
        return saleOrderItemMapper.selectListByOrderIds(orderIds);
    }

    /**
     * 更新销售订单配送人员
     *
     * @param id
     * @param deliveryStaffId
     */
    @Override
    public boolean updateDeliveryStaff(Long id, Long deliveryStaffId) {
        return saleOrderMapper.update(Wrappers.<ErpSaleOrderDO>lambdaUpdate()
                .set(ErpSaleOrderDO::getDeliveryStaffId, deliveryStaffId)
                .eq(ErpSaleOrderDO::getId, id)) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePickUp(Long id, Long itemId) {
        ErpSaleOrderDO saleOrder = getSaleOrder(id);
        if (CommonUtil.isEmpty(saleOrder)) {
            throw exception(SALE_ORDER_NOT_EXISTS);
        }
        ErpSaleOrderItemDO saleItemOrder = saleOrderItemMapper.selectById(itemId);
        if (CommonUtil.isEmpty(saleItemOrder)) {
            throw exception(SALE_ORDER_NOT_EXISTS);
        }
        String lock = String.format(SALE_ORDER_LOCK, id);
        RedisUtils.tryLockRds(lock);
        try {
            CustomerOrderDTO customerOrder = customerOrderApi.queryById(saleOrder.getCustomerOrderId());

            saleOrderItemMapper.update(Wrappers.<ErpSaleOrderItemDO>lambdaUpdate()
                    .set(ErpSaleOrderItemDO::getPickUpTime, LocalDateTime.now())
                    .set(ErpSaleOrderItemDO::getPickUpStatus, SaleOrderPickUpStatus.PICK_UP.getType())
                    .eq(ErpSaleOrderItemDO::getId, itemId));

            Long allCount = saleOrderItemMapper.selectCount(ErpSaleOrderItemDO::getOrderId, saleOrder.getId());
            Long pickUpCount = saleOrderItemMapper.selectCount(Wrappers.<ErpSaleOrderItemDO>lambdaQuery()
                    .eq(ErpSaleOrderItemDO::getOrderId, saleOrder.getId())
                    .eq(ErpSaleOrderItemDO::getPickUpStatus, SaleOrderPickUpStatus.PICK_UP.getType()));
            if (allCount > 0 && Objects.equals(pickUpCount, allCount)) {
                saleOrderMapper.update(Wrappers.<ErpSaleOrderDO>lambdaUpdate()
                        .set(ErpSaleOrderDO::getPickUpStatus, SaleOrderPickUpStatus.PICK_UP.getType())
                        .eq(ErpSaleOrderDO::getId, saleOrder.getId()));

                String warehouseName = Optional.ofNullable(customerOrder.getWarehouseInfo()).map(WarehouseInfo::getName).orElse("");
                CustomerOrderRecordEvent event = new CustomerOrderRecordEvent();
                event.setOrderStatus(CustomerOrderStatus.SORTING);
                event.setCustomerOrderId(saleOrder.getCustomerOrderId());
                event.setCopyWriter(CommonUtil.asList(warehouseName));
                orderRecordApi.recordEvent(event);
            }
        } finally {
            RedisUtils.unLockRds(lock);
        }

    }

    /**
     * 确认取货
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delivery(Long id, Long deliveryStaffId) {
        ErpSaleOrderDO saleOrder = this.validateSaleOrder(id);
        if (saleOrder.getPickUpStatus().isReturns()) {
            saleReturnService.delivery(id, deliveryStaffId);
            return ;
        }
        if (!SaleOrderPickUpStatus.ACCEPT.equals(saleOrder.getPickUpStatus())) {
            throw exception(SALE_ORDER_PICKUP_STATUS_FAIL);
        }
        String key = String.format(SALE_ORDER_LOCK, id);
        RedisUtils.tryLockRds(key);
        try {

            LocalDateTime deliveryTime = LocalDateTime.now();
            saleOrderMapper.update(Wrappers.<ErpSaleOrderDO>lambdaUpdate()
                    .set(ErpSaleOrderDO::getPickUpStatus, SaleOrderPickUpStatus.ALREADY.getType())
                    .set(ErpSaleOrderDO::getDeliveryTime, deliveryTime)
                    .set(ErpSaleOrderDO::getDeliveryStaffId, deliveryStaffId)
                    .eq(ErpSaleOrderDO::getId, saleOrder.getId()));
            saleOrderItemMapper.update(Wrappers.<ErpSaleOrderItemDO>lambdaUpdate()
                    .set(ErpSaleOrderItemDO::getPickUpStatus, SaleOrderPickUpStatus.ALREADY.getType())
                    .eq(ErpSaleOrderItemDO::getOrderId, saleOrder.getId()));
            List<ErpSaleOrderItemDO> saleOrderItems = saleOrderItemMapper.selectListByOrderId(id);

            // 新增销售出库单
            ErpSaleOutSaveReqVO saleOutSaveReqVO = new ErpSaleOutSaveReqVO();
            saleOutSaveReqVO.setSaleUserId(saleOrder.getSaleUserId());
            saleOutSaveReqVO.setAccountId(saleOrder.getAccountId());
            saleOutSaveReqVO.setOutTime(deliveryTime);
            saleOutSaveReqVO.setOrderId(saleOrder.getId());
            saleOutSaveReqVO.setDiscountPercent(saleOrder.getDiscountPercent());
            saleOutSaveReqVO.setFileUrl(saleOrder.getFileUrl());
            saleOutSaveReqVO.setRemark(saleOrder.getRemark());

            saleOutSaveReqVO.setCustomerOrderId(saleOrder.getCustomerOrderId());
            saleOutSaveReqVO.setDeliveryMode(saleOrder.getDeliveryMode());
            saleOutSaveReqVO.setAcceptTime(saleOrder.getAcceptTime());
            saleOutSaveReqVO.setDeliveryTime(deliveryTime);
            saleOutSaveReqVO.setDeliveryStaffId(deliveryStaffId);
            saleOutSaveReqVO.setAddressId(saleOrder.getAddressId());
            saleOutSaveReqVO.setAddressInfo(saleOrder.getAddressInfo());
            saleOutSaveReqVO.setWarehouseId(saleOrder.getWarehouseId());
            saleOutSaveReqVO.setWarehouseAreaId(saleOrder.getWarehouseAreaId());
            saleOutSaveReqVO.setWarehouseInfo(saleOrder.getWarehouseInfo());
            saleOutSaveReqVO.setPlanDeliveryStartTime(saleOrder.getPlanDeliveryStartTime());
            saleOutSaveReqVO.setPlanDeliveryEndTime(saleOrder.getPlanDeliveryEndTime());
            saleOutSaveReqVO.setPickUpStatus(SaleOrderPickUpStatus.ALREADY);

            // CustomerOrderDTO customerOrderDTO = customerOrderApi.queryById(saleOrder.getCustomerOrderId());
            saleOutSaveReqVO.setItems(CommonUtil.listConvert(saleOrderItems, saleOrderItem -> {
                ErpSaleOutSaveReqVO.Item item = new ErpSaleOutSaveReqVO.Item();
                item.setOrderItemId(saleOrderItem.getId());
                item.setWarehouseId(saleOrder.getWarehouseAreaId());
                item.setProductId(saleOrderItem.getProductId());
                item.setProductUnit(saleOrderItem.getProductUnit());
                item.setProductPrice(saleOrderItem.getProductPrice());
                item.setCount(BigDecimal.valueOf(saleOrderItem.getCount()));
                item.setTaxPercent(saleOrderItem.getTaxPercent());
                item.setRemark(saleOrderItem.getRemark());
                item.setCustomerOrderItemId(saleOrderItem.getCustomerOrderItemId());
                item.setPickUpStatus(SaleOrderPickUpStatus.ALREADY);
                return item;
            }));
            saleOutService.createSaleOut(saleOutSaveReqVO);

            DeliveryStaffSimpleInfo deliveryStaffSimpleInfo = deliveryStaffApi.querybyStaffId(saleOrder.getDeliveryStaffId());
            CustomerOrderRecordEvent event = new CustomerOrderRecordEvent();
            event.setOrderStatus(CustomerOrderStatus.DELIVERY);
            event.setCustomerOrderId(saleOrder.getCustomerOrderId());
            event.setCopyWriter(CommonUtil.asList(deliveryStaffSimpleInfo.getName(), deliveryStaffSimpleInfo.getContact()));
            orderRecordApi.recordEvent(event);
        } finally {
            RedisUtils.unLockRds(key);
        }
    }

    /**
     * 接单
     *
     * @param id
     * @param deliveryStaffId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void accept(Long id, Long deliveryStaffId) {
        ErpSaleOrderDO saleOrder = this.validateSaleOrder(id);
        if (saleOrder.getPickUpStatus().isReturns()) {
            saleReturnService.accept(id, deliveryStaffId);
            return ;
        }
        if (!SaleOrderPickUpStatus.PICK_UP.equals(saleOrder.getPickUpStatus())) {
            throw exception(SALE_ORDER_PICKUP_STATUS_FAIL);
        }

        String key = String.format(SALE_ORDER_LOCK, id);
        RedisUtils.tryLockRds(key);
        try {
            saleOrderMapper.update(Wrappers.<ErpSaleOrderDO>lambdaUpdate()
                    .set(ErpSaleOrderDO::getPickUpStatus, SaleOrderPickUpStatus.ACCEPT.getType())
                    .set(ErpSaleOrderDO::getAcceptTime, LocalDateTime.now())
                    .set(ErpSaleOrderDO::getDeliveryStaffId, deliveryStaffId)
                    .eq(ErpSaleOrderDO::getId, saleOrder.getId()));
            saleOrderItemMapper.update(Wrappers.<ErpSaleOrderItemDO>lambdaUpdate()
                    .set(ErpSaleOrderItemDO::getPickUpStatus, SaleOrderPickUpStatus.ACCEPT.getType())
                    .eq(ErpSaleOrderItemDO::getOrderId, saleOrder.getId()));
        } finally {
            RedisUtils.unLockRds(key);
        }
    }

    /**
     * 到货
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void arrival(DeliveryOrderUpdateReqVO reqVO, Long deliveryStaffId) {
        Long orderId = reqVO.getId();
        ErpSaleOutDO saleOut = saleOutMapper.selectOne(Wrappers.<ErpSaleOutDO>lambdaQuery()
                .eq(ErpSaleOutDO::getCustomerOrderId, orderId)
                .last("LIMIT 1"));
        if (CommonUtil.isEmpty(saleOut)) {
            throw exception(SALE_OUT_NOT_EXISTS);
        }
        ErpSaleOrderDO saleOrder = this.validateSaleOrder(saleOut.getOrderId());
        if (!SaleOrderPickUpStatus.ALREADY.equals(saleOrder.getPickUpStatus())) {
            throw exception(SALE_ORDER_PICKUP_STATUS_FAIL);
        }
        String key = String.format(SALE_ORDER_LOCK, saleOrder.getId());
        RedisUtils.tryLockRds(key);
        try {
            LocalDateTime arrivalTime = LocalDateTime.now();
            saleOrderMapper.update(Wrappers.<ErpSaleOrderDO>lambdaUpdate()
                    .set(ErpSaleOrderDO::getPickUpStatus, SaleOrderPickUpStatus.ARRIVAL.getType())
                    .set(ErpSaleOrderDO::getDeliveryStaffId, deliveryStaffId)
                    .set(ErpSaleOrderDO::getArrivalTime, arrivalTime)
                    .eq(ErpSaleOrderDO::getId, saleOrder.getId()));
            saleOrderItemMapper.update(Wrappers.<ErpSaleOrderItemDO>lambdaUpdate()
                    .set(ErpSaleOrderItemDO::getPickUpStatus, SaleOrderPickUpStatus.ARRIVAL.getType())
                    .eq(ErpSaleOrderItemDO::getOrderId, saleOrder.getId()));

            saleOutMapper.update(Wrappers.<ErpSaleOutDO>lambdaUpdate()
                    .set(ErpSaleOutDO::getPickUpStatus, SaleOrderPickUpStatus.ARRIVAL.getType())
                    .set(ErpSaleOutDO::getDeliveryStaffId, deliveryStaffId)
                    .set(ErpSaleOutDO::getArrivalTime, arrivalTime)
                    .set(ErpSaleOutDO::getReceiptFile, reqVO.getReceiptFile())
                    .eq(ErpSaleOutDO::getId, saleOut.getId()));
            saleOutItemMapper.update(Wrappers.<ErpSaleOutItemDO>lambdaUpdate()
                    .set(ErpSaleOutItemDO::getPickUpStatus, SaleOrderPickUpStatus.ARRIVAL.getType())
                    .eq(ErpSaleOutItemDO::getOutId, saleOut.getId()));

            // 更新出库单价格
            if (CommonUtil.isNotEmpty(reqVO.getOrderItems())) {
                Map<Long, DeliveryOrderUpdateReqVO.DeliveryOrderUpdateItems> itemMap = CommonUtil.listConvertMap(reqVO.getOrderItems(), DeliveryOrderUpdateReqVO.DeliveryOrderUpdateItems::getId);
                List<ErpSaleOutItemDO> saleOutItems = saleOutService.getSaleOutItemListByOutId(saleOut.getId());
                List<ErpSaleOutItemDO> updateBatch = new ArrayList<>();
                saleOutItems.forEach(saleOutItem -> {
                    if (itemMap.containsKey(saleOutItem.getCustomerOrderItemId())) {
                        DeliveryOrderUpdateReqVO.DeliveryOrderUpdateItems item = itemMap.get(saleOutItem.getCustomerOrderItemId());
                        ErpSaleOutItemDO updateItem = new ErpSaleOutItemDO();
                        updateItem.setId(saleOutItem.getId());
                        updateItem.setCount(item.getSignedQuantity());
                        updateItem.setRemark(item.getRemark());
                        updateItem.setRemarkFiles(item.getRemarkFiles());
                        updateItem.setTotalPrice(MoneyUtils.multiplyAndRoundToInt(item.getSignedQuantity(), saleOutItem.getProductPrice()));
                        updateBatch.add(updateItem);
                    }
                });
                if (CommonUtil.isNotEmpty(updateBatch)) {
                    saleOutItemMapper.updateBatch(updateBatch);
                }
            }

            DeliveryStaffSimpleInfo deliveryStaffSimpleInfo = deliveryStaffApi.querybyStaffId(saleOrder.getDeliveryStaffId());
            CustomerOrderRecordEvent event = new CustomerOrderRecordEvent();
            event.setOrderStatus(CustomerOrderStatus.ARRIVAL);
            event.setCustomerOrderId(saleOrder.getCustomerOrderId());
            event.setCopyWriter(CommonUtil.asList(deliveryStaffSimpleInfo.getName(), deliveryStaffSimpleInfo.getContact()));
            orderRecordApi.recordEvent(event);
        } finally {
            RedisUtils.unLockRds(key);
        }
    }

    /**
     * 根据客户订单新建配货单
     *
     * @param customerOrderId
     */
    @Override
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void createByCustomerOrderId(Long customerOrderId) {
        CustomerOrderDTO order = customerOrderApi.queryById(customerOrderId);
        List<CustomerOrderItemDTO> orderItems = customerOrderApi.queryItemsByOrderId(customerOrderId);
        if (CommonUtil.isEmpty(order) || CommonUtil.isEmpty(orderItems)) {
            return ;
        }
        ErpSaleOrderDO erpSaleOrderDO = this.queryByCustomerOrderId(customerOrderId);
        if (CommonUtil.isNotEmpty(erpSaleOrderDO)) {
            return ;
        }

        ErpSaleOrderSaveReqVO saveReqVO = new ErpSaleOrderSaveReqVO();
        saveReqVO.setCustomerId(order.getCustomerId());
        saveReqVO.setCustomerDeptId(order.getCustomerDeptId());
        saveReqVO.setOrderTime(order.getCreateTime());
        saveReqVO.setDepositPrice(order.getOrderAmount());
        saveReqVO.setCustomerOrderId(order.getId());
        saveReqVO.setDeliveryMode(order.getDeliveryMode());
        saveReqVO.setAddressId(order.getAddressId());
        saveReqVO.setAddressInfo(order.getAddressInfo());
        saveReqVO.setWarehouseId(order.getWarehouseId());
        saveReqVO.setWarehouseAreaId(order.getWarehouseAreaId());
        saveReqVO.setWarehouseInfo(order.getWarehouseInfo());
        saveReqVO.setPlanDeliveryStartTime(order.getPlanDeliveryStartTime());
        saveReqVO.setPlanDeliveryEndTime(order.getPlanDeliveryEndTime());
        saveReqVO.setPickUpStatus(SaleOrderPickUpStatus.TO_BE);
        saveReqVO.setItems(CommonUtil.listConvert(orderItems, orderItem -> {
            ErpSaleOrderSaveReqVO.Item item = new ErpSaleOrderSaveReqVO.Item();
            item.setCustomerOrderItemId(orderItem.getId());
            item.setProductId(orderItem.getProductId());
            item.setProductUnit(orderItem.getProductInfo().getUnitName());
            item.setProductPrice(orderItem.getOrderItemPrice());
            item.setCount(orderItem.getOrderItemQuantity());
            item.setSupplierId(orderItem.getSupplierId());
            item.setPickUpStatus(SaleOrderPickUpStatus.TO_BE);
            item.setPurchaseOrderId(Optional.ofNullable(purchaseOrderMapper.selectOne(Wrappers.<ErpPurchaseOrderDO>lambdaQuery()
                    .eq(ErpPurchaseOrderDO::getCustomerOrderId, order.getId())
                    .eq(ErpPurchaseOrderDO::getSupplierId, orderItem.getSupplierId())
                    .last("LIMIT 1"))).map(ErpPurchaseOrderDO::getId).orElse(null));
            return item;
        }));
        this.createSaleOrder(saveReqVO);
    }

    @Override
    public ErpSaleOrderDO queryByCustomerOrderId(Long customerOrderId) {
        return saleOrderMapper.selectOne(ErpSaleOrderDO::getCustomerOrderId, customerOrderId);
    }

    @Override
    public ErpSaleOrderItemDO queryItemByCustomerOrderItemId(Long customerOrderItemId) {
        return saleOrderItemMapper.selectOne(ErpSaleOrderItemDO::getCustomerOrderItemId, customerOrderItemId);
    }

    @Override
    public void receiptByCustomerOrderId(Long customerOrderId, List<Long> customerOrderItemId) {
        LocalDateTime receiptTime = LocalDateTime.now();
        saleOrderMapper.update(Wrappers.<ErpSaleOrderDO>lambdaUpdate()
                .set(ErpSaleOrderDO::getPickUpStatus, SaleOrderPickUpStatus.SIGN.getType())
                .set(ErpSaleOrderDO::getSignTime, receiptTime)
                .eq(ErpSaleOrderDO::getCustomerOrderId, customerOrderId)
                .eq(ErpSaleOrderDO::getPickUpStatus, SaleOrderPickUpStatus.ARRIVAL.getType()));

        if (CommonUtil.isNotEmpty(customerOrderItemId)) {
            saleOrderItemMapper.update(Wrappers.<ErpSaleOrderItemDO>lambdaUpdate()
                    .set(ErpSaleOrderItemDO::getPickUpStatus, SaleOrderPickUpStatus.SIGN.getType())
                    .in(ErpSaleOrderItemDO::getCustomerOrderItemId, customerOrderItemId));
        }
    }

    /**
     * "个人中心 - 配送订单数"
     */
    @Override
    public DeliveryMyCountRespVO queryMyCount(Long userId) {
        return saleOrderMapper.queryMyCount(userId);
    }

    /**
     * "接单大厅 - 配送状态订单数统计"
     */
    @Override
    public List<DeliveryStatusCountRespVO> statusCount(Long userId, Set<Long> deptIds) {
        List<DeliveryStatusCountRespVO> result = new ArrayList<>();
        for (int type = 0; type < 3;type ++) {
            DeliveryStatusCountRespVO count = new DeliveryStatusCountRespVO();
            count.setType(type);
            switch (type) {
                case 0 :
                    Long outCount = saleOutMapper.statusCount(CommonUtil.asList(SaleOrderPickUpStatus.RETURNS_APPROVE.getType()), deptIds);
                    Long orderCount = saleOrderMapper.statusCount(CommonUtil.asList(
                            SaleOrderPickUpStatus.PICK_UP.getType()), deptIds);
                    count.setCount(CommonUtil.getEls(orderCount, 0L) + CommonUtil.getEls(outCount, 0L));
                    break;
                case 1:
                    count.setCount(CommonUtil.getEls(saleOrderMapper.statusCount(CommonUtil.asList(SaleOrderPickUpStatus.ACCEPT.getType()), userId), 0L) +
                            CommonUtil.getEls(saleOutMapper.statusCount(CommonUtil.asList(
                                    SaleOrderPickUpStatus.RETURNS_STAFF_ACCEPT.getType()), userId), 0L));
                    break;
                case 2:
                    count.setCount(CommonUtil.getEls(saleOrderMapper.statusCount(CommonUtil.asList(SaleOrderPickUpStatus.ARRIVAL.getType(),
                            SaleOrderPickUpStatus.SIGN.getType(),
                            SaleOrderPickUpStatus.RETURNS_PROCESS.getType(),
                            SaleOrderPickUpStatus.RETURNS_APPROVE.getType(),
                            SaleOrderPickUpStatus.RETURNS_REJECT.getType(),
                            SaleOrderPickUpStatus.RETURNS_STAFF_ACCEPT.getType(),
                            SaleOrderPickUpStatus.RETURNS_STAFF_ALREADY.getType(),
                            SaleOrderPickUpStatus.RETURNS_FINISH.getType()), userId), 0L) +
                            CommonUtil.getEls(saleOutMapper.statusCount(CommonUtil.asList(
                                    SaleOrderPickUpStatus.RETURNS_FINISH.getType()), userId), 0L));
                    break;
                default:break;
            }
            result.add(count);
        }
        return result;
    }

    @Override
    public void update(ErpSaleOrderDO updateSaleOrder, List<ErpSaleOrderItemDO> updateSaleOrderItems) {
        saleOrderMapper.updateById(updateSaleOrder);
        if (CommonUtil.isNotEmpty(updateSaleOrderItems)) {
            saleOrderItemMapper.updateBatch(updateSaleOrderItems);
        } else {
            saleOrderItemMapper.update(Wrappers.<ErpSaleOrderItemDO>lambdaUpdate()
                    .set(ErpSaleOrderItemDO::getPickUpStatus, updateSaleOrder.getPickUpStatus())
                    .eq(ErpSaleOrderItemDO::getOrderId, updateSaleOrder.getId()));
        }
    }

}
