
package cn.ysk.cashier.service.impl.shopimpl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.ysk.cashier.config.security.security.TokenProvider;
import cn.ysk.cashier.cons.RedisConstant;
import cn.ysk.cashier.cons.TableConstant;
import cn.ysk.cashier.cons.rabbit.RabbitConstants;
import cn.ysk.cashier.dto.shop.TbShopTableDto;
import cn.ysk.cashier.dto.shop.TbShopTableQueryCriteria;
import cn.ysk.cashier.dto.shoptable.*;
import cn.ysk.cashier.enums.*;
import cn.ysk.cashier.exception.BadRequestException;
import cn.ysk.cashier.mapper.shop.TbShopTableMapper;
import cn.ysk.cashier.mybatis.mapper.*;
import cn.ysk.cashier.mybatis.service.MpCashierCartService;
import cn.ysk.cashier.mybatis.service.MpOrderDetailService;
import cn.ysk.cashier.mybatis.service.MpOrderInfoService;
import cn.ysk.cashier.mybatis.service.MpShopTableService;
import cn.ysk.cashier.pojo.TbShopPayType;
import cn.ysk.cashier.pojo.order.TbCashierCart;
import cn.ysk.cashier.pojo.order.TbOrderDetail;
import cn.ysk.cashier.pojo.order.TbOrderInfo;
import cn.ysk.cashier.pojo.product.TbProduct;
import cn.ysk.cashier.pojo.product.TbProductSku;
import cn.ysk.cashier.pojo.product.TbProductStockDetail;
import cn.ysk.cashier.pojo.shop.*;
import cn.ysk.cashier.repository.TbShopPayTypeRepository;
import cn.ysk.cashier.repository.order.TbCashierCartRepository;
import cn.ysk.cashier.repository.product.TbProductSkuRepository;
import cn.ysk.cashier.repository.shop.TbShopInfoRepository;
import cn.ysk.cashier.repository.shop.TbShopTableRepository;
import cn.ysk.cashier.service.PayService;
import cn.ysk.cashier.service.impl.TbPayServiceImpl;
import cn.ysk.cashier.service.order.TbOrderInfoService;
import cn.ysk.cashier.service.shop.TbShopTableService;
import cn.ysk.cashier.utils.*;
import cn.ysk.cashier.vo.PendingCountVO;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.dianguang.cloud.ossservice.model.DateUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * @author lyf
 * @website https://eladmin.vip
 * @description 服务实现
 * @date 2024-01-18
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class TbShopTableServiceImpl implements TbShopTableService {

    private final TbShopTableRepository tbShopTableRepository;
    private final TbShopTableMapper tbShopTableMapper;
    private final TbProductSkuRepository productSkuRepository;
    private final TbCashierCartRepository cashierCartRepository;
    private final TbProductMapper productMapper;
    private final TbCashierCartMapper cashierCartMapper;
    private final TokenProvider tokenProvider;
    private final TbMerchantAccountMapper merchantAccountMapper;
    private final TbOrderInfoMapper orderInfoMapper;
    private final TbOrderDetailMapper orderDetailMapper;
    private final TbProducSkutMapper producSkutMapper;
    private final RabbitTemplate rabbitTemplate;
    private final TbShopInfoRepository shopInfoRepository;
    private final WxMsgUtils wxMsgUtils;
    private final TbShopPayTypeRepository payTypeRepository;
    private final MpShopTableMapper mpShopTableMapper;
    private final TbShopPayTypeMapper tbShopPayTypeMapper;
    private final MpShopTableService mpShopTableService;
    private final MpShopUnitMapper mpShopUnitMapper;
    private final MpProductStockDetailMapper mpProductStockDetailMapper;


    /**
     * 桌码前缀
     */
    private final String QRCODE = "https://kysh.sxczgkj.cn/codeplate?code=";
    private final RabbitMsgUtils rabbitMsgUtils;
    private final TbPayServiceImpl tbPayServiceImpl;
    private final TbCashierCartMapper tbCashierCartMapper;
    private final TbOrderDetailMapper tbOrderDetailMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final MpShopAreaMapper mpShopAreaMapper;
    private final MpShopInfoMapper mpShopInfoMapper;
    private final MpOrderDetailService mpOrderDetailService;
    private final MpCashierCartService mpCashierCartService;
    private final MpMerchantThirdApplyMapper mpMerchantThirdApplyMapper;
    private final PayService payService;
    private final TbOrderInfoService orderInfoService;
    private final MpOrderInfoService mpOrderInfoService;

    private TbOrderInfo getCurrentOrder(ShopEatTypeInfoDTO eatTypeInfoDTO) {
        // 获取当前台桌最新订单，先付款模式不获取
        if (eatTypeInfoDTO.isDineInBefore()) {
            return null;
        }
        List<TbOrderInfo> orderInfoList = orderInfoMapper.selectPage(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(1, 1), new LambdaQueryWrapper<TbOrderInfo>()
                .eq(TbOrderInfo::getStatus, "unpaid")
                .eq(TbOrderInfo::getUseType, eatTypeInfoDTO.getUseType())
                .eq(TbOrderInfo::getShopId, eatTypeInfoDTO.getShopId())
                .eq(TbOrderInfo::getTableId, eatTypeInfoDTO.getTableId())
                .eq(TbOrderInfo::getTradeDay, DateUtils.getDay())
                .orderByDesc(TbOrderInfo::getId)).getRecords();
        return orderInfoList.isEmpty() ? null : orderInfoList.get(0);
    }

    private Integer getCurrentOrderId(ShopEatTypeInfoDTO eatTypeInfoDTO) {
        TbOrderInfo currentOrder = getCurrentOrder(eatTypeInfoDTO);
        return currentOrder == null ? null : currentOrder.getId();
    }


    /**
     * 校验就餐模式是否存在并返回就餐类型信息
     *
     * @param tableId 台桌id
     * @param shopId  店铺id
     * @return 就餐类型信息
     */
    public ShopEatTypeInfoDTO checkEatModel(Object shopId, String tableId) {
        String eatModel = StrUtil.isBlank(tableId) ? ShopInfoEatModelEnum.TAKE_OUT.getValue() : ShopInfoEatModelEnum.DINE_IN.getValue();
        return getShopEatTypeInfoDTO(shopId, eatModel, tableId);
    }

    /**
     * 校验就餐模式是否存在并返回就餐类型信息
     *
     * @param tableId 台桌id
     * @param shopId  店铺id
     * @return 就餐类型信息
     */
    public ShopEatTypeInfoDTO checkEatModel(Object shopId, String tableId, String useType) {
        String eatModel = OrderUseTypeEnum.TAKEOUT.getValue().equals(useType) ? ShopInfoEatModelEnum.TAKE_OUT.getValue() : ShopInfoEatModelEnum.DINE_IN.getValue();
        return getShopEatTypeInfoDTO(shopId, eatModel, tableId);
    }

    /**
     * 通过店铺id和就餐模式获取当前店铺的就餐类型信息
     *
     * @param shopId   店铺id
     * @param eatModel 就餐模式
     * @param tableId  台桌id
     * @return just餐类型信息
     */
    private ShopEatTypeInfoDTO getShopEatTypeInfoDTO(Object shopId, String eatModel, String tableId) {
        TbShopInfo shopInfo = mpShopInfoMapper.selectOne(new LambdaQueryWrapper<TbShopInfo>()
                .eq(TbShopInfo::getId, shopId)
                .eq(TbShopInfo::getStatus, 1));
        if (shopInfo == null) {
            throw new BadRequestException("店铺信息不存在");
        }

        if (!shopInfo.getEatModel().contains(eatModel)) {
            throw new BadRequestException("当前店铺未开启此就餐模式");
        }
        boolean isTakeout = ShopInfoEatModelEnum.TAKE_OUT.getValue().equals(eatModel);
        // 是否是快餐版/先付费
        boolean isMunchies = StrUtil.isNotBlank(shopInfo.getRegisterType()) &&
                ShopInfoRegisterlEnum.MUNCHIES.getValue().equals(shopInfo.getRegisterType());

        boolean isDineInAfter = !isMunchies && !isTakeout;
        boolean isDineInBefore = isMunchies && !isTakeout;

        boolean needSeatFee = shopInfo.getIsTableFee() == null || shopInfo.getIsTableFee() == 0;
        boolean isNoneTable = StrUtil.isBlank(tableId) && !isTakeout;

        return new ShopEatTypeInfoDTO(isTakeout, isMunchies, isDineInAfter, isDineInBefore, needSeatFee, isNoneTable, shopInfo, isTakeout ? OrderUseTypeEnum.TAKEOUT.getValue() :
                isMunchies ? OrderUseTypeEnum.DINE_IN_BEFORE.getValue() : OrderUseTypeEnum.DINE_IN_AFTER.getValue(),
                shopId, tableId, isTakeout ? OrderSendTypeEnums.TAKE_SELF.getValue() : OrderSendTypeEnums.TABLE.getValue());
    }


    @Override
    public Map<String, Object> queryAll(TbShopTableQueryCriteria criteria, Pageable pageable) {
        if (criteria.getAreaId() == 0) {
            criteria.setAreaId(null);
        }
        Page<TbShopTable> page = tbShopTableRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        for (TbShopTable date : page.getContent()) {
            if (!"".equals(date.getQrcode())) {
                date.setQrcode(QRCODE + date.getQrcode().trim());
            }
        }
        HashMap<String, Object> map = new HashMap<>();
        map.put("content", page.getContent());
        map.put("totalElements", page.getTotalElements());
        return map;
    }

    @Override
    public Map<String, Object> queryAllPage(TbShopTableQueryCriteria criteria) {
        if (null == criteria.getAreaId() || criteria.getAreaId() == 0) {
            criteria.setAreaId(null);
        }

        LambdaQueryWrapper<TbShopTable> query = new LambdaQueryWrapper<TbShopTable>()
                .eq(TbShopTable::getShopId, criteria.getShopId());
        if (StrUtil.isNotBlank(criteria.getName())) {
            query.like(TbShopTable::getName, criteria.getName());
        }

        if (criteria.getAreaId() != null) {
            query.eq(TbShopTable::getAreaId, criteria.getAreaId());
        }

        if (criteria.getQrcode() != null) {
            query.eq(TbShopTable::getQrcode, criteria.getQrcode());
        }

        if (StrUtil.isNotBlank(criteria.getState())) {
            query.eq(TbShopTable::getStatus, criteria.getState()).isNotNull(TbShopTable::getQrcode).ne(TbShopTable::getQrcode, "");
        }

        com.baomidou.mybatisplus.extension.plugins.pagination.Page<TbShopTable> shopTablePage =
                mpShopTableService.page(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(criteria.getPage(), criteria.getSize()), query);
        List<TbShopTable> tbShopTableList = shopTablePage.getRecords();
        ArrayList<Map<String, Object>> infoList = new ArrayList<>();
        for (TbShopTable date : tbShopTableList) {
            if (StrUtil.isBlank(date.getQrcode())) {
                date.setStatus("unbind");
            } else if (tbCashierCartMapper.selectCount(new LambdaQueryWrapper<TbCashierCart>()
                    .eq(TbCashierCart::getShopId, date.getShopId())
                    .eq(TbCashierCart::getTableId, date.getQrcode())
                    .gt(TbCashierCart::getCreatedAt, DateUtil.offsetDay(DateUtil.date(), -1).getTime())
                    .eq(TbCashierCart::getStatus, "create")) < 1 && !TableStateEnum.CLEANING.getState().equals(date.getStatus())
            ) {
                date.setStatus("idle");
                mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
                        .eq(TbShopTable::getQrcode, date.getQrcode())
                        .set(TbShopTable::getStatus, TableStateEnum.IDLE.getState()));
            }
            Map<String, Object> itemMap = BeanUtil.beanToMap(date, false, false);
            if ((date.getStatus().equals("using") || date.getStatus().equals("cleaning")) && date.getUseTime() != null) {
                itemMap.put("durationTime", DateUtil.current() - date.getUseTime().getTime());
            } else {
                itemMap.put("durationTime", 0);
            }
            if (!"".equals(date.getQrcode())) {
                itemMap.put("qrcode", QRCODE + date.getQrcode().trim());
                itemMap.put("tableId", date.getQrcode());
            }

            TbOrderInfo orderInfo = null;
            if (StrUtil.isNotBlank(date.getQrcode())) {
                try {

                    ShopEatTypeInfoDTO shopEatTypeInfoDTO = checkEatModel(criteria.getShopId(), date.getQrcode());
                    orderInfo = getCurrentOrder(shopEatTypeInfoDTO);
                } catch (Exception e) {
                    log.info(e.getMessage());
                }
            }

            itemMap.put("orderId", orderInfo == null ? null : orderInfo.getId());
            itemMap.put("useType", orderInfo == null ? null : orderInfo.getUseType());
            itemMap.put("masterId", orderInfo == null ? null : orderInfo.getMasterId());
            infoList.add(itemMap);
        }
        HashMap<String, Object> map = new HashMap<>();
        map.put("content", infoList);
        map.put("total", shopTablePage.getTotal());
        return map;
    }

    @Override
    public List<TbShopTableDto> queryAll(TbShopTableQueryCriteria criteria) {
        return tbShopTableMapper.toDto(tbShopTableRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder)));
    }

    @Override
    public List<TbShopTable> queryListByShopId(Integer shopId) {
        return tbShopTableRepository.findByShopId(shopId);
    }

    @Override
    @Transactional
    public TbShopTableDto findById(Integer id) {
        TbShopTable tbShopTable = tbShopTableRepository.findById(id).orElseGet(TbShopTable::new);
        ValidationUtil.isNull(tbShopTable.getId(), "TbShopTable", "id", id);
        return tbShopTableMapper.toDto(tbShopTable);
    }

    @Override
    public void binding(TbShopTable resources) {
        //判是否绑定过
        TbShopTable byQrcode = tbShopTableRepository.findByQrcode(resources.getQrcode());
        if (byQrcode != null) {
            throw new BadRequestException("已绑定");
        }
        TbShopTable tbShopTable = tbShopTableRepository.findById(resources.getId()).orElseGet(TbShopTable::new);
        if (tbShopTable.getId() == null) {
            throw new BadRequestException("找不到台桌");
        }
        ValidationUtil.isNull(tbShopTable.getId(), "TbShopTable", "id", resources.getId());
        tbShopTable.copy(resources);
        tbShopTableRepository.save(tbShopTable);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TbShopTable create(TbShopTable resources) {
        resources.setCreatedAt(Instant.now().toEpochMilli());
        resources.setUpdatedAt(Instant.now().toEpochMilli());
        resources.setQrcode("");
        resources.setStatus("closed");
        return tbShopTableRepository.save(resources);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(TbShopTable resources) {
        TbShopTable tbShopTable = tbShopTableRepository.findById(resources.getId()).orElseGet(TbShopTable::new);
        ValidationUtil.isNull(tbShopTable.getId(), "TbShopTable", "id", resources.getId());
        tbShopTable.copy(resources);
        if ("idle".equals(resources.getStatus())) {
            tbShopTable.setUseNum(0);
        }
        tbShopTableRepository.save(tbShopTable);
    }

    @Override
    public void deleteAll(Integer[] ids) {
        for (Integer id : ids) {
            tbShopTableRepository.deleteById(id);
        }
    }

    @Override
    public void download(List<TbShopTableDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (TbShopTableDto tbShopTable : all) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put(" name", tbShopTable.getName());
            map.put(" shopId", tbShopTable.getShopId());
            map.put("客座数,允许的客座数量", tbShopTable.getMaxCapacity());
            map.put("台桌排序", tbShopTable.getSort());
            map.put("区域Id", tbShopTable.getAreaId());
            map.put("是否接受网络预定", tbShopTable.getIsPredate());
            map.put("网络预定台桌支付金额", tbShopTable.getPredateAmount());
            map.put("subscribe预定，closed--关台， opening 开台中，cleaning 台桌清理中", tbShopTable.getStatus());
            map.put("台桌计算价格类型，0-低消类型，amount    2计时类型", tbShopTable.getType());
            map.put("当type=0时，amount生效,为台桌的低消金额", tbShopTable.getAmount());
            map.put("当type=2时perhour生效，为计时类型,每小时收款金额", tbShopTable.getPerhour());
            map.put("台桌展示图---预留", tbShopTable.getView());
            map.put(" createdAt", tbShopTable.getCreatedAt());
            map.put(" updatedAt", tbShopTable.getUpdatedAt());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    @Override
    public TbCashierCart updateCart(UpdateCartDTO updateCartDTO) {
        TbCashierCart tbCashierCart = cashierCartMapper.selectById(updateCartDTO.getCartId());
        if (tbCashierCart == null) {
            throw new BadRequestException("购物车商品不存在");
        }
        if (tbCashierCart.getPlaceNum() != null) {
            throw new BadRequestException("已下单商品仅支持退单操作");
        }

        if (updateCartDTO.getNum() == 0) {
            cashierCartRepository.deleteById(updateCartDTO.getCartId());
            return null;
        }
        TbProductSku productSku = productMapper.selectSkuByIdAndShopId(updateCartDTO.getShopId(), updateCartDTO.getSkuId());
        TbProduct product = productMapper.selectByIdAndShopId(updateCartDTO.getShopId(), updateCartDTO.getProductId());
        if (product == null || productSku == null) {
            throw new BadRequestException("商品不存在或已下架, id: " + updateCartDTO.getSkuId());
        }

        tbCashierCart.setCoverImg(product.getCoverImg());
        tbCashierCart.setCreatedAt(System.currentTimeMillis());
        tbCashierCart.setIsSku(product.getTypeEnum());
        tbCashierCart.setName(product.getName());
        tbCashierCart.setProductId(String.valueOf(product.getId()));
        tbCashierCart.setSalePrice(productSku.getSalePrice());
        tbCashierCart.setSkuId(productSku.getId().toString());
        tbCashierCart.setTradeDay(DateUtils.getDay());
        tbCashierCart.setStatus("create");
        tbCashierCart.setSalePrice(productSku.getSalePrice());
        tbCashierCart.setTotalAmount(new BigDecimal(updateCartDTO.getNum()).multiply(productSku.getSalePrice()));
        tbCashierCart.setNote(updateCartDTO.getNote());

        if (updateCartDTO.getIsPack() != null) {
            if (!updateCartDTO.getIsPack()) {
                tbCashierCart.setPackFee(BigDecimal.ZERO);
            } else {
                tbCashierCart.setPackFee(new BigDecimal(updateCartDTO.getNum()).multiply(product.getPackFee()));
                tbCashierCart.setTotalAmount(tbCashierCart.getTotalAmount().add(tbCashierCart.getPackFee()));
            }
            tbCashierCart.setIsPack(updateCartDTO.getIsPack() ? "true" : "false");
        }

        if (updateCartDTO.getIsGift() != null) {
            tbCashierCart.setTotalAmount(updateCartDTO.getIsGift() ? tbCashierCart.getPackFee() : tbCashierCart.getTotalAmount());
            tbCashierCart.setIsGift(updateCartDTO.getIsGift() ? "true" : "false");
        }

        tbCashierCart.setTotalNumber(updateCartDTO.getNum());
        tbCashierCart.setNumber(updateCartDTO.getNum());
        tbCashierCart.setCategoryId(product.getCategoryId());
        tbCashierCartMapper.updateById(tbCashierCart);

        if (tbCashierCart.getOrderId() != null && StrUtil.isNotBlank(updateCartDTO.getNote())) {
            orderDetailMapper.update(null, new LambdaUpdateWrapper<TbOrderDetail>()
                    .eq(TbOrderDetail::getCartId, tbCashierCart.getId())
                    .set(TbOrderDetail::getNote, updateCartDTO.getNote()));
        }

        return tbCashierCart;
    }

    /**
     * 获取当前台桌当前下单次数
     *
     * @param shopEatTypeInfoDTO 用餐类型
     * @return 对应的次数，为order最后次数+1
     */
    private int getCurrentPlaceNum(ShopEatTypeInfoDTO shopEatTypeInfoDTO) {
        TbOrderInfo orderInfo = getCurrentOrder(shopEatTypeInfoDTO);
        return orderInfo == null ? 1 : orderInfo.getPlaceNum() + 1;
    }

    @Override
    public TbCashierCart addCartForUser(AddCartDTO addCartDTO) {
        addCartDTO.setTableId(OrderUseTypeEnum.TAKEOUT.getValue().equals(addCartDTO.getUseType()) ? null : addCartDTO.getTableId());
        ShopEatTypeInfoDTO shopEatTypeInfoDTO = checkEatModel(addCartDTO.getShopId(), addCartDTO.getTableId(), addCartDTO.getUseType());

        TbProductSku productSku = productMapper.selectSkuByIdAndShopId(addCartDTO.getShopId(), addCartDTO.getSkuId());
        TbProduct product = productMapper.selectByIdAndShopId(addCartDTO.getShopId(), addCartDTO.getProductId());

        if (product == null || productSku == null) {
            throw new BadRequestException("商品不存在或已下架, id: " + addCartDTO.getSkuId());
        }

        if (product.getIsStock() != null && product.getIsStock() == 1 && product.getStockNumber() < 1) {
            throw new BadRequestException(product.getName() + "商品库存不足");
        }

        LambdaQueryWrapper<TbCashierCart> query = new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getShopId, addCartDTO.getShopId())
                .gt(TbCashierCart::getCreatedAt, DateUtil.offsetDay(DateUtil.date(), -1).getTime())
                .eq(TbCashierCart::getSkuId, addCartDTO.getSkuId())
                .eq(TbCashierCart::getProductId, addCartDTO.getProductId())
                .isNull(TbCashierCart::getPlaceNum)
                .eq(TbCashierCart::getUseType, shopEatTypeInfoDTO.getUseType())
                .eq(TbCashierCart::getStatus, "create")
                .eq(TbCashierCart::getIsGift, addCartDTO.isGift())
                .and(q -> q.eq(TbCashierCart::getMasterId, addCartDTO.getMasterId()).or().isNull(TbCashierCart::getMasterId));

        if (addCartDTO.getCartId() != null) {
            query.eq(TbCashierCart::getId, addCartDTO.getCartId());
        }

        // 外带只查询pc和收银机商品
        if (shopEatTypeInfoDTO.isTakeout()) {
            query.and(q -> q.isNull(TbCashierCart::getTableId).or().eq(TbCashierCart::getTableId, ""))
                    .in(TbCashierCart::getPlatformType, "pc", "cash");
        } else {
            query.eq(TbCashierCart::getTableId, addCartDTO.getTableId());
        }

        TbCashierCart tbCashierCart = cashierCartMapper.selectOne(query);
        // 首次加入
        if (tbCashierCart == null) {
            tbCashierCart = new TbCashierCart();
            tbCashierCart.setUseType(addCartDTO.getUseType());
            tbCashierCart.setCoverImg(product.getCoverImg());
            tbCashierCart.setCreatedAt(System.currentTimeMillis());
            tbCashierCart.setIsSku(product.getTypeEnum());
            if (StrUtil.isNotBlank(addCartDTO.getTableId())) {
                tbCashierCart.setTableId(addCartDTO.getTableId());
            }
            tbCashierCart.setName(product.getName());
            tbCashierCart.setProductId(String.valueOf(product.getId()));
            tbCashierCart.setSalePrice(productSku.getSalePrice());
            tbCashierCart.setSkuId(productSku.getId().toString());
            tbCashierCart.setMasterId(addCartDTO.getMasterId());
            tbCashierCart.setShopId(String.valueOf(addCartDTO.getShopId()));
            tbCashierCart.setTradeDay(DateUtils.getDay());
            tbCashierCart.setStatus("create");
            tbCashierCart.setIsPack(String.valueOf(addCartDTO.isPack()));
            tbCashierCart.setIsGift(String.valueOf(addCartDTO.isGift()));
            tbCashierCart.setSalePrice(productSku.getSalePrice());
            tbCashierCart.setTotalAmount(new BigDecimal(addCartDTO.getNum()).multiply(productSku.getSalePrice()));
            tbCashierCart.setSkuName(productSku.getSpecSnap());
            // 设置打包费
            if (!addCartDTO.isPack() && !shopEatTypeInfoDTO.isTakeout()) {
                tbCashierCart.setPackFee(BigDecimal.ZERO);
            } else {
                tbCashierCart.setPackFee(product.getPackFee() != null ?
                        product.getPackFee().multiply(BigDecimal.valueOf(addCartDTO.getNum())) : BigDecimal.ZERO);
                tbCashierCart.setTotalAmount(tbCashierCart.getTotalAmount().add(tbCashierCart.getPackFee()));

            }
            if (addCartDTO.isGift()) {
                tbCashierCart.setTotalAmount(BigDecimal.ZERO);
            }
            tbCashierCart.setTotalNumber(addCartDTO.getNum());
            tbCashierCart.setNumber(addCartDTO.getNum());
            tbCashierCart.setCategoryId(product.getCategoryId());
            tbCashierCart.setNote(addCartDTO.getNote());
            tbCashierCart.setPlatformType(OrderPlatformTypeEnum.PC.getValue());
            cashierCartRepository.save(tbCashierCart);

        } else {
            tbCashierCart.setNote(addCartDTO.getNote());
            tbCashierCart.setTotalAmount(new BigDecimal(addCartDTO.getNum()).multiply(productSku.getSalePrice()));

            if (!addCartDTO.isPack()) {
                tbCashierCart.setPackFee(BigDecimal.ZERO);
            } else {
                tbCashierCart.setPackFee(new BigDecimal(addCartDTO.getNum()).multiply(product.getPackFee()));
                tbCashierCart.setTotalAmount(tbCashierCart.getTotalAmount().add(tbCashierCart.getPackFee()));

            }

            if (addCartDTO.isGift()) {
                tbCashierCart.setTotalAmount(BigDecimal.ZERO);
            }

            tbCashierCart.setIsPack(String.valueOf(addCartDTO.isPack()));
            tbCashierCart.setIsGift(String.valueOf(addCartDTO.isGift()));
            tbCashierCart.setTotalNumber(addCartDTO.getNum());
            tbCashierCart.setNumber(addCartDTO.getNum());
            tbCashierCart.setUpdatedAt(DateUtil.current());
            // 设置打包费
            if (shopEatTypeInfoDTO.isTakeout()) {
                tbCashierCart.setPackFee(product.getPackFee() != null ?
                        product.getPackFee().multiply(BigDecimal.valueOf(addCartDTO.getNum())) : BigDecimal.ZERO);
            }
            cashierCartMapper.updateById(tbCashierCart);
        }

        if (StrUtil.isNotBlank(addCartDTO.getTableId())) {
            setRedisTableCartInfo(addCartDTO.getTableId(), addCartDTO.getShopId().toString(), Collections.singletonList(tbCashierCart), true);
        }

        return tbCashierCart;
    }

    private void setRedisTableCartInfo(String tableId, String shopId, List<TbCashierCart> tbCashierCartList, boolean isAdd) {
        String tableCartKey = RedisConstant.getTableCartKey(tableId, shopId);
        String tableCart = redisTemplate.opsForValue().get(tableCartKey);

        List<TbCashierCart> cartArrayList = new ArrayList<>();
        if (tableCart != null) {
            cartArrayList = JSONObject.parseArray(tableCart, TbCashierCart.class);
        }

        for (TbCashierCart cashierCart : tbCashierCartList) {
            cartArrayList = cartArrayList.stream().filter(d -> !d.getId().equals(cashierCart.getId())).collect(Collectors.toList());
            if (isAdd) {
                cartArrayList.add(cashierCart);
            }
        }

        List<TbCashierCart> finalCartArrayList = cartArrayList;
        Utils.runFunAndCheckKey(() -> {
            redisTemplate.opsForValue().set(tableCartKey,
                    JSONObject.toJSONString(finalCartArrayList));
            return null;
        }, redisTemplate, RedisConstant.ADD_TABLE_CART_LOCK);
    }


    @Override
    public void removeCart(RemoveCartDTO removeCartDTO) {
        // 会员点单
        TbCashierCart cashierCart = cashierCartMapper.selectOne(new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getShopId, removeCartDTO.getShopId())
                .eq(TbCashierCart::getUseType, removeCartDTO.getUseType())
                .eq(TbCashierCart::getId, removeCartDTO.getCartId()));
        if (cashierCart == null) {
            throw new BadRequestException("购物车商品不存在");
        }

        if (removeCartDTO.getTableId() != null && cashierCart.getPlaceNum() != null) {
            throw new BadRequestException("已下单商品仅支持退单操作");
        }

        if (cashierCart.getOrderId() != null) {
            orderDetailMapper.delete(new LambdaQueryWrapper<TbOrderDetail>()
                    .eq(TbOrderDetail::getShopId, cashierCart.getShopId())
                    .eq(TbOrderDetail::getProductId, cashierCart.getProductId())
                    .eq(TbOrderDetail::getProductSkuId, cashierCart.getSkuId())
                    .eq(TbOrderDetail::getUseType, removeCartDTO.getUseType())
                    .isNull(TbOrderDetail::getPlaceNum)
                    .eq(TbOrderDetail::getOrderId, cashierCart.getOrderId()));
        }

        // 修改订单餐位费信息
        if (cashierCart.getOrderId() != null && cashierCart.getId() == -999) {
            orderInfoMapper.update(null, new LambdaUpdateWrapper<TbOrderInfo>()
                    .eq(TbOrderInfo::getId, cashierCart.getOrderId())
                    .eq(TbOrderInfo::getShopId, removeCartDTO.getShopId())
                    .set(TbOrderInfo::getSeatCount, null)
                    .set(TbOrderInfo::getSeatAmount, null));
        }

        cashierCartMapper.deleteById(cashierCart.getId());

        if (cashierCart.getTableId() != null && removeCartDTO.getTableId() != null) {
            // 清空购物车 出票
            long carCount = countCar(Long.valueOf(cashierCart.getTableId()), cashierCart.getShopId());
            log.info("购物车数量: {}", carCount);
            setRedisTableCartInfo(removeCartDTO.getTableId(), removeCartDTO.getShopId().toString(), Collections.singletonList(cashierCart), false);
        }

    }

    @Override
    public void returnCart(ReturnCartDTO removeCartDTO) {
        // 会员点单
        TbCashierCart cashierCart = cashierCartMapper.selectOne(new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getShopId, removeCartDTO.getShopId())
                .in(TbCashierCart::getStatus, "create", "refund")
                .isNotNull(TbCashierCart::getOrderId)
                .eq(TbCashierCart::getId, removeCartDTO.getCartId()));

        if (cashierCart == null) {
            throw new BadRequestException("购物车商品不存在或已退单");
        }

        if (removeCartDTO.getNum() > cashierCart.getNumber()) {
            throw new BadRequestException("最大退菜数量为: {}", cashierCart.getNumber());
        }

        TbOrderDetail tbOrderDetail = orderDetailMapper.selectOne(new LambdaQueryWrapper<TbOrderDetail>()
                .eq(TbOrderDetail::getShopId, removeCartDTO.getShopId())
                .eq(TbOrderDetail::getCartId, cashierCart.getId())
                .eq(TbOrderDetail::getProductId, cashierCart.getProductId())
                .eq(TbOrderDetail::getProductSkuId, cashierCart.getSkuId())
                .eq(TbOrderDetail::getOrderId, cashierCart.getOrderId()));
        if (tbOrderDetail == null) {
            throw new BadRequestException("购物车商品不存在或已退单");
        }

        if (cashierCart.getOrderId() == null) {
            throw new BadRequestException("此商品还未下单，无需退单");
        }

        // 餐位费直接删除
        TbOrderDetail orderDetail = orderDetailMapper.selectOne(new LambdaQueryWrapper<TbOrderDetail>()
                .eq(TbOrderDetail::getCartId, cashierCart.getId()));

        if (cashierCart.getProductId().equals("-999")) {
            cashierCartMapper.deleteById(cashierCart.getId());
            cashierCartMapper.update(null, new LambdaUpdateWrapper<TbCashierCart>()
                    .eq(TbCashierCart::getId, cashierCart.getId())
                    .set(TbCashierCart::getStatus, "return"));
            orderDetailMapper.update(null, new LambdaUpdateWrapper<TbOrderDetail>()
                    .eq(TbOrderDetail::getId, tbOrderDetail.getId())
                    .set(TbOrderDetail::getUpdateTime, DateUtil.date())
                    .set(TbOrderDetail::getStatus, "return"));

        } else {

            if (cashierCart.getNumber().equals(removeCartDTO.getNum())) {
                cashierCartMapper.update(null, new LambdaUpdateWrapper<TbCashierCart>()
                        .eq(TbCashierCart::getId, cashierCart.getId())
                        .set(TbCashierCart::getStatus, "return"));
                orderDetailMapper.update(null, new LambdaUpdateWrapper<TbOrderDetail>()
                        .eq(TbOrderDetail::getId, tbOrderDetail.getId())
                        .set(TbOrderDetail::getUpdateTime, DateUtil.date())
                        .set(TbOrderDetail::getStatus, "return"));
                rabbitMsgUtils.printDishesTicket(tbOrderDetail.getOrderId(), true, tbOrderDetail);
            } else {
                //生成退菜的购物车记录
                TbCashierCart returnCart = BeanUtil.copyProperties(cashierCart, TbCashierCart.class);
                returnCart.setNumber(removeCartDTO.getNum());
                returnCart.setId(null);
                returnCart.setTotalNumber(removeCartDTO.getNum());
                BigDecimal returnCartAmount = returnCart.getSalePrice().multiply(BigDecimal.valueOf(returnCart.getNumber()));
                returnCart.setTotalAmount(returnCartAmount);
                returnCart.setStatus("return");
                cashierCartMapper.insert(returnCart);

                TbOrderDetail returnOrderDetail = BeanUtil.copyProperties(tbOrderDetail, TbOrderDetail.class);
                returnOrderDetail.setId(null);
                returnOrderDetail.setNum(returnCart.getNumber());
                returnOrderDetail.setCartId(returnCart.getId());
                returnOrderDetail.setPrice(returnCart.getSalePrice());
                returnOrderDetail.setPriceAmount(returnCart.getTotalAmount());
                returnOrderDetail.setStatus("return");
                returnOrderDetail.setRefundNumber(returnCart.getRefundNumber());
                returnOrderDetail.setRefundRemark(removeCartDTO.getNote());
                orderDetailMapper.insert(returnOrderDetail);

                cashierCartMapper.updateNumAmountStatus(cashierCart.getId(), cashierCart.getStatus(), -returnCart.getNumber());
                orderDetailMapper.updateNumAmountStatus(orderDetail.getId(), orderDetail.getStatus(), -returnCart.getNumber());
                rabbitMsgUtils.printDishesTicket(returnOrderDetail.getOrderId(), true, returnOrderDetail);
            }

        }

        if (cashierCart.getOrderId() != null) {
            // 减少订单金额
            orderInfoMapper.updateOrderAmount(cashierCart.getOrderId(), orderDetail.getPriceAmount().divide(BigDecimal.valueOf(orderDetail.getNum()), RoundingMode.HALF_UP)
                    .multiply(BigDecimal.valueOf(removeCartDTO.getNum())), orderDetail.getPackAmount().divide(BigDecimal.valueOf(orderDetail.getNum()), RoundingMode.HALF_UP)
                    .multiply(BigDecimal.valueOf(removeCartDTO.getNum())));
        }


    }

    @Override
    public void clearCart(ClearCartDTO clearCartDTO) {
        Integer orderId;
        ShopEatTypeInfoDTO shopEatTypeInfoDTO = checkEatModel(clearCartDTO.getShopId(), clearCartDTO.getTableId());
        if (clearCartDTO.getTableId() != null) {
            orderId = getCurrentOrderId(shopEatTypeInfoDTO);
        } else {
            orderId = null;
        }

        LambdaQueryWrapper<TbCashierCart> query = new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getShopId, clearCartDTO.getShopId())
                .isNull(TbCashierCart::getPlaceNum)
                .eq(TbCashierCart::getUseType, clearCartDTO.getUseType())
                .and(q -> q.eq(TbCashierCart::getMasterId, clearCartDTO.getMasterId())
                        .or()
                        .isNull(TbCashierCart::getMasterId)
                        .or()
                        .eq(TbCashierCart::getMasterId, ""))
                .and(q -> q.eq(TbCashierCart::getOrderId, orderId)
                        .or()
                        .isNull(TbCashierCart::getOrderId));

        if (clearCartDTO.getTableId() != null) {
            query.eq(TbCashierCart::getTableId, clearCartDTO.getTableId());

        }

        cashierCartMapper.delete(query);
    }

    private long countCar(Long tableId, String shopId) {
        String orderId = redisTemplate.opsForValue().get(RedisConstant.getCurrentOrderKey(tableId.toString(), shopId));
        return tbOrderDetailMapper.selectCount(new LambdaQueryWrapper<TbOrderDetail>()
                .eq(TbOrderDetail::getShopId, shopId)
                .eq(TbOrderDetail::getStatus, "unpaid")
                .eq(TbOrderDetail::getOrderId, orderId));
    }

    @Override
    public Map<String, Object> getCart(String tableId, Integer page,
                                       Integer size, Integer shopId, String masterId, String useType) {
        ShopEatTypeInfoDTO shopEatTypeInfoDTO = checkEatModel(shopId, OrderUseTypeEnum.TAKEOUT.getValue().equals(useType) ? null : tableId, useType);
        LambdaQueryWrapper<TbCashierCart> queryWrapper = new LambdaQueryWrapper<TbCashierCart>()
                .in(TbCashierCart::getStatus, "create", "return")
                .eq(TbCashierCart::getShopId, shopId)
                .eq(TbCashierCart::getUseType, useType)
                .gt(TbCashierCart::getCreatedAt, DateUtil.offsetDay(DateUtil.date(), -1).getTime())
                .and(q -> q.eq(TbCashierCart::getMasterId, masterId).or().isNull(TbCashierCart::getMasterId));

        if (!shopEatTypeInfoDTO.isTakeout()) {
            if (StrUtil.isBlank(tableId)) {
                queryWrapper.isNull(TbCashierCart::getTableId);
            } else {
                queryWrapper.eq(TbCashierCart::getTableId, tableId);
            }
        } else {
            queryWrapper.and(q -> q.isNull(TbCashierCart::getTableId).or().eq(TbCashierCart::getTableId, ""))
                    .in(TbCashierCart::getPlatformType, OrderPlatformTypeEnum.PC.getValue(), OrderPlatformTypeEnum.CASH.getValue());
        }

        com.baomidou.mybatisplus.extension.plugins.pagination.Page<TbCashierCart> cartPage = cashierCartMapper
                .selectPage(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, size), queryWrapper);
        List<TbCashierCart> records = cartPage.getRecords();

        ArrayList<Integer> skuIds = new ArrayList<>();
        records.forEach(item -> skuIds.add(Integer.valueOf(item.getSkuId())));


        AtomicReference<TbCashierCart> mealCashierCart = new AtomicReference<>();
        if (!skuIds.isEmpty()) {
            List<TbProductSku> skuList = productSkuRepository.findAllById(skuIds);
            HashMap<String, TbProductSku> skuMap = new HashMap<>();
            skuList.forEach(item -> skuMap.put(item.getId().toString(), item));

            ArrayList<Map<String, Object>> infos = new ArrayList<>();
            records.forEach(item -> {
                if (item.getProductId().equals("-999")) {
                    mealCashierCart.set(item);
                    return;
                }
                Map<String, Object> map = BeanUtil.beanToMap(item, false, false);
                TbProductSku tbProductSku = skuMap.get(item.getSkuId());
                map.put("specSnap", tbProductSku != null ? tbProductSku.getSpecSnap() : null);
                map.put("placeNum", item.getPlaceNum() == null ? 0 : item.getPlaceNum());
                infos.add(map);
            });

            com.baomidou.mybatisplus.extension.plugins.pagination.Page copyPage = BeanUtil.copyProperties(cartPage, com.baomidou.mybatisplus.extension.plugins.pagination.Page.class);

            // 根据placeNum进行分组
            Map<Object, List<Map<String, Object>>> groupedByPlaceNum = infos.stream()
                    .collect(Collectors.groupingBy(info -> info.get("placeNum")));

            ArrayList<HashMap<String, Object>> list = new ArrayList<>();
            groupedByPlaceNum.forEach((k, v) -> {
                HashMap<String, Object> item = new HashMap<>();
                item.put("placeNum", k);
                item.put("info", v);
                list.add(item);
            });
            copyPage.setRecords(list);
            Map<String, Object> map = BeanUtil.beanToMap(copyPage, false, false);
            map.put("seatFee", mealCashierCart);
            return map;
        }

        return BeanUtil.beanToMap(cartPage);
    }

    @Override
    public void pack(PackCartDTO packCartDTO) {
        LambdaQueryWrapper<TbCashierCart> queryWrapper = new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getShopId, packCartDTO.getShopId())
                .in(TbCashierCart::getStatus, "create", "refund")
                .and(query2 -> query2.or(query3 -> query3.eq(TbCashierCart::getTradeDay, DateUtils.getDay())
                                .eq(TbCashierCart::getMasterId, packCartDTO.getMasterId()))
                        .or((query4 -> query4.isNull(TbCashierCart::getTradeDay)
                                .eq(TbCashierCart::getMasterId, ""))));

        if (StrUtil.isNotBlank(packCartDTO.getTableId())) {
            queryWrapper.eq(TbCashierCart::getTableId, packCartDTO.getTableId());
        }

        List<TbCashierCart> tbCashierCarts = cashierCartMapper.selectList(queryWrapper);
        tbCashierCarts.forEach(item -> {
            if (packCartDTO.getState().equals(0) && item.getIsPack().equals("true")) {
                item.setIsPack("false");
                item.setTotalAmount(item.getTotalAmount().subtract(item.getPackFee()));
            } else {
                item.setIsPack("true");
                item.setTotalAmount(item.getTotalAmount().add(item.getPackFee()));
            }
            cashierCartMapper.updateById(item);
        });

    }


    /**
     * 增加库存
     *
     * @param productId 商品id
     * @param skuId     sku
     * @param addNum    增加的库存数量
     */
    public void incrStock(Integer productId, Integer skuId, Integer addNum) {
        productMapper.incrStock(productId, addNum);
    }

    /**
     * 减少库存
     *
     * @param productId 商品数据
     * @param skuId     sku
     * @param decrNum   减少的数量
     */
    public void decrStock(Integer productId, String skuId, int decrNum) {
        TbProduct product = productMapper.selectById(productId);
        if (product.getIsStock() == 1) {
            if (productMapper.decrStock(productId, decrNum) < 1) {
                throw new BadRequestException(product.getName() + "库存不足，下单失败");
            }
        } else {
            productMapper.decrStockUnCheck(productId, decrNum);
        }
    }

    /**
     * 推送耗材消耗信息
     */
    private void pushConsMsg(TbOrderInfo orderInfo, List<TbCashierCart> cashierCarts) {
        log.info("创建订单，发送更新耗材消息，订单id：{}", orderInfo.getId());
        //修改耗材数据
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("orderId", orderInfo.getId());
        jsonObject.put("type", "create");
        rabbitTemplate.convertAndSend(RabbitConstants.CONS_COLLECT_PUT, RabbitConstants.CONS_COLLECT_ROUTINGKEY_PUT,
                jsonObject.toJSONString(), new CorrelationData(UUID.randomUUID().toString()));

        ThreadUtil.execAsync(() -> {
            ThreadUtil.sleep(5, TimeUnit.SECONDS);
            for (TbCashierCart cashierCart : cashierCarts) {
                JSONObject objectMsg = new JSONObject();
                objectMsg.put("skuId", Integer.valueOf(cashierCart.getSkuId()));
                objectMsg.put("shopId", Integer.valueOf(cashierCart.getShopId()));
                rabbitTemplate.convertAndSend(RabbitConstants.CONS_MSG_COLLECT_PUT, RabbitConstants.CONS_MSG_COLLECT_ROUTINGKEY_PUT,
                        objectMsg.toJSONString(), new CorrelationData(UUID.randomUUID().toString()));
            }
        });
    }

    /**
     * 更新库存
     *
     * @param cashierCart 购物车
     * @return 是否是第一次添加的商品
     */
    private boolean updateStock(TbCashierCart cashierCart) {
        if (cashierCart.getOrderId() != null) {
            String message = redisTemplate.opsForValue().get(RedisConstant.ORDER_PRODUCT_NUM + cashierCart.getId());
            if (message != null) {
                int lastNum = Integer.parseInt(message);
                // 数量减少, 返还库存
                if (lastNum > cashierCart.getNumber()) {
                    incrStock(Integer.parseInt(cashierCart.getProductId()), Integer.parseInt(cashierCart.getSkuId()), lastNum - cashierCart.getNumber());
                } else {
                    decrStock(Integer.parseInt(cashierCart.getProductId()), cashierCart.getSkuId(), cashierCart.getNumber() - lastNum);
                }
                redisTemplate.opsForValue().set(RedisConstant.ORDER_PRODUCT_NUM + cashierCart.getId(), cashierCart.getNumber().toString(), 24 * 60 * 60, TimeUnit.SECONDS);
            }
            return false;

            // 首次添加的商品
        } else {
            redisTemplate.opsForValue().set(RedisConstant.ORDER_PRODUCT_NUM + cashierCart.getId(), cashierCart.getNumber().toString(), 24 * 60 * 60, TimeUnit.SECONDS);
            // 修改库存
            decrStock(Integer.parseInt(cashierCart.getProductId()), cashierCart.getSkuId(), cashierCart.getNumber());
            return true;
        }
    }

    /**
     * 更新取餐号
     *
     * @param shopId 店铺id
     * @return 当前取餐号
     */
    public Integer updateOutNumber(String shopId) {
        JSONObject object = new JSONObject();
        String outNumber = redisTemplate.opsForValue().get(RedisConstant.OUT_NUMBER.concat(shopId));
        Integer number = 1;
        if (Objects.isNull(outNumber)) {
            object.put("outNumber", number);
            object.put("times", DateUtils.getDay());
        } else {
            object = JSONObject.parseObject(outNumber);
            if (object.getString("times").equals(DateUtils.getDay())) {
                number = object.getInteger("outNumber") + 1;
                object.put("outNumber", number);
            } else {
                object.put("outNumber", number);
                object.put("times", DateUtils.getDay());
            }
        }
        redisTemplate.opsForValue().set(RedisConstant.OUT_NUMBER.concat(shopId), object.toString());

        return number;
    }

    /**
     * 校验商品库存警戒线并通知商户
     *
     * @param productSku sku
     */
    private void checkWarnLineAndSendMsg(TbProductSku productSku, TbProduct product, Integer shopId, Integer num) {
        TbShopInfo shopInfo = shopInfoRepository.getById(shopId);
        if (product.getWarnLine() == null) {
            return;
        }

        if (product.getIsStock() == null || product.getIsStock() != 1) {
            return;
        }

        if (product.getStockNumber() == null) {
            product.setStockNumber(0);
        }
        if (product.getStockNumber() - num <= product.getWarnLine()) {
            wxMsgUtils.aboardStockMsg(shopInfo.getShopName(), shopId, product.getName(),
                    product.getStockNumber() - num);
        }
    }

    public String generateOrderNumber(String prefix) {
        String date = DateUtils.getSdfTimes();
        Random random = new Random();
        int randomNum = random.nextInt(900) + 100;
        return StrUtil.isBlank(prefix) ? "DDPL" + date + randomNum : prefix + date + randomNum;
    }

    public synchronized void addGlobalCode(String day, String clientType, String shopId) {
        String code = redisTemplate.opsForValue().get("SHOP:CODE:" + clientType + ":" + shopId + ":" + day);
        if (StrUtil.isBlank(code)) {
            redisTemplate.opsForValue().set("SHOP:CODE:" + clientType + ":" + shopId + ":" + day, "1");
        } else {
            redisTemplate.opsForValue().get("SHOP:CODE:" + clientType + ":" + shopId + ":" + (Integer.parseInt(code) + 1));
        }
        redisTemplate.opsForValue().setIfAbsent("SHOP:CODE:SET" + clientType + ":" + shopId + ":" + day, "1");
    }

    public synchronized String generateOrderCode(String day, String clientType, String shopId) {
        String code = redisTemplate.opsForValue().get("SHOP:CODE:" + clientType + ":" + shopId + ":" + day);
        // 使用顺序递增的计数器生成取餐码
        String orderCode = "";
        if (StringUtils.isEmpty(code) || "null".equals(code)) {
            orderCode = "1";
            redisTemplate.opsForValue().set("SHOP:CODE:" + clientType + ":" + shopId + ":" + day, "1");
        } else {
            orderCode = String.valueOf(Integer.parseInt(code.replace("#", "")) + 1);
        }
        redisTemplate.opsForValue().set("SHOP:CODE:" + clientType + ":" + shopId + ":" + day, orderCode);
        boolean flag = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent("SHOP:CODE:SET" + clientType + ":" + shopId + ":" + day, orderCode));
        if (flag) {
            return generateOrderCode(day, clientType, shopId);
        }
        // 增加计数器

        return orderCode;
    }


    private final StringRedisTemplate redisTemplate;

    @Override
    public JSONObject getMasterId(Integer shopId, String tableId, String useType, Integer orderId) {
        if (orderId != null) {
            TbOrderInfo orderInfo = orderInfoMapper.selectById(orderId);
            if (orderInfo != null) {
                JSONObject jsonObject = new JSONObject();
                if (StrUtil.isNotBlank(orderInfo.getMasterId())) {
                    jsonObject.put("masterId", orderInfo.getMasterId());
                    return jsonObject;
                }
            }
        }
        String account = tokenProvider.getSubject();
        if (account == null) {
            throw new BadRequestException("token解析失败");
        }
        TbMerchantAccount merchantAccount = merchantAccountMapper.selectOne(new LambdaQueryWrapper<TbMerchantAccount>().eq(TbMerchantAccount::getAccount, account));
        String day = DateUtils.getDay();
        JSONObject jsonObject = new JSONObject();
        // 当前台桌码
        String key = "SHOP:CODE:USER:pc" + ":" + shopId + ":" + day + ":" + tableId + useType;
        String userCode = redisTemplate.opsForValue().get(key);

        if (StringUtils.isEmpty(userCode) || "null".equals(userCode) || "#null".equals(userCode)) {
            String code = "#" + generateOrderCode(day, "pc", String.valueOf(shopId));
            redisTemplate.opsForValue().set(key, code);
            jsonObject.put("masterId", code);
        } else {
            jsonObject.put("masterId", userCode);
        }
        return jsonObject;
    }

    private void getOutNumber(String shopId, String tableId) {
        String shopKey = RedisConstant.PC_OUT_NUMBER + ":" + shopId;
        String outNumber = redisTemplate.opsForValue().get(shopKey);
        if (outNumber == null) {
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(shopKey, "1");
            if (flag != null && flag.equals(Boolean.FALSE)) {
                outNumber = redisTemplate.opsForValue().get(shopKey);
            }
        }

        String shopTableKey = RedisConstant.PC_OUT_NUMBER + ":" + shopId;
        redisTemplate.opsForValue().get(shopTableKey);
    }


    @Override
    @Transactional
    public TbOrderInfo createOrder(CreateOrderDTO createOrderDTO, boolean addMaterId, boolean isPrint) {
        createOrderDTO.setTableId(OrderUseTypeEnum.TAKEOUT.getValue().equals(createOrderDTO.getUseType()) ? null : createOrderDTO.getTableId());
        return Utils.runFunAndCheckKey(() -> {
            TbShopInfo shopInfo = shopInfoRepository.findById(createOrderDTO.getShopId()).orElse(null);
            if (shopInfo == null) throw new BadRequestException("店铺信息不存在");

            // 就餐模式信息
            ShopEatTypeInfoDTO shopEatTypeInfoDTO = checkEatModel(createOrderDTO.getShopId(), createOrderDTO.getTableId(), createOrderDTO.getUseType());

            // 传递orderId直接取否则取当前缓存id
            Integer orderId = shopEatTypeInfoDTO.isDineInAfter() ?
                    getCurrentOrderId(shopEatTypeInfoDTO) : null;

            LambdaQueryWrapper<TbCashierCart> queryWrapper = new LambdaQueryWrapper<TbCashierCart>()
                    .eq(TbCashierCart::getShopId, createOrderDTO.getShopId())
                    .eq(TbCashierCart::getUseType, createOrderDTO.getUseType())
                    .in(TbCashierCart::getStatus, "create", "return")
                    .gt(TbCashierCart::getCreatedAt, DateUtil.offsetDay(DateUtil.date(), -1).getTime())
                    .and(q -> q.eq(TbCashierCart::getMasterId, createOrderDTO.getMasterId()).or().isNull(TbCashierCart::getMasterId));

            // 非堂食校验台桌状态
            TbShopTable tbShopTable = null;
            if (shopEatTypeInfoDTO.isTakeout()) {
                queryWrapper.and(q -> q.isNull(TbCashierCart::getTableId).or().eq(TbCashierCart::getTableId, ""))
                        .in(TbCashierCart::getPlatformType, OrderPlatformTypeEnum.PC.getValue(), OrderPlatformTypeEnum.CASH.getValue());
            } else {
                if (StrUtil.isNotBlank(createOrderDTO.getTableId())) {
                    tbShopTable = mpShopTableMapper.selectOne(new LambdaQueryWrapper<TbShopTable>()
                            .eq(TbShopTable::getQrcode, createOrderDTO.getTableId())
                            .in(TbShopTable::getStatus, "idle", "using"));
                    if (tbShopTable == null) {
                        throw new BadRequestException("台桌未开台或不存在");
                    }
                    queryWrapper.eq(TbCashierCart::getTableId, createOrderDTO.getTableId());
                } else {
                    queryWrapper.isNull(TbCashierCart::getTableId);
                }

            }

            List<TbCashierCart> allCashierCarts = cashierCartMapper
                    .selectList(queryWrapper);

            List<TbCashierCart> cashierCarts = new ArrayList<>();
            TbCashierCart seatCart = null;
            for (TbCashierCart allCashierCart : allCashierCarts) {
                if (TableConstant.CART_SEAT_ID.equals(allCashierCart.getProductId())) {
                    seatCart = allCashierCart;
                }

                if (OrderStatusEnums.CREATE.getValue().equals(allCashierCart.getStatus())) {
                    cashierCarts.add(allCashierCart);
                }
            }

            if (StrUtil.isNotBlank(createOrderDTO.getTableId())
                    && (cashierCarts.isEmpty() ||
                    (shopEatTypeInfoDTO.isNeedSeatFee() && !shopEatTypeInfoDTO.isTakeout() && seatCart != null && cashierCarts.size() < 2)
                    || (shopEatTypeInfoDTO.isNeedSeatFee() && !shopEatTypeInfoDTO.isTakeout() && seatCart == null))) {
                throw new BadRequestException("购物车为空或未选择餐位费，请先添加商品或选择餐位费");
            }

            ArrayList<Integer> cartIdList = new ArrayList<>();
            for (TbCashierCart tbCashierCart : cashierCarts) {
                if (tbCashierCart.getOrderId() != null) {
                    orderId = tbCashierCart.getOrderId();
                }
                cartIdList.add(tbCashierCart.getId());
            }
            // 查询历史orderDetail
            Integer finalOrderId = orderId;
            LambdaQueryWrapper<TbOrderDetail> query = new LambdaQueryWrapper<TbOrderDetail>()
                    .and(q -> q.in(TbOrderDetail::getCartId, cartIdList).or().eq(TbOrderDetail::getOrderId, finalOrderId))
                    .eq(TbOrderDetail::getShopId, createOrderDTO.getShopId());

            List<TbOrderDetail> oldOrderDetailList = orderDetailMapper.selectList(query);
            ArrayList<Integer> removeOrderDetailIds = new ArrayList<>();
            ArrayList<TbOrderDetail> removeOrderDetailList = new ArrayList<>();
            HashMap<String, TbOrderDetail> oldOrderDetailMap = new HashMap<>();
            oldOrderDetailList.forEach(item -> {
                if (cartIdList.contains(item.getCartId())) {
                    oldOrderDetailMap.put(item.getOrderId().toString() + item.getCartId(), item);
                } else {
                    removeOrderDetailIds.add(item.getId());
                    removeOrderDetailList.add(item);
                }
            });

            BigDecimal totalAmount = BigDecimal.ZERO;
            BigDecimal packAMount = BigDecimal.ZERO;
            BigDecimal feeAmount = BigDecimal.ZERO;
            BigDecimal saleAmount = BigDecimal.ZERO;
            // 当前下单次数
            int placeNum = getCurrentPlaceNum(shopEatTypeInfoDTO);
            List<TbOrderDetail> orderDetails = new ArrayList<>();
            List<TbOrderDetail> addOrderDetails = new ArrayList<>();


            boolean hasNewInfo = false;
            for (TbCashierCart cashierCart : cashierCarts) {
                if (!"return".equals(cashierCart.getStatus())) {
                    totalAmount = totalAmount.add(cashierCart.getTotalAmount());
                    packAMount = packAMount.add(cashierCart.getPackFee());
                    feeAmount = cashierCart.getPackFee();
                }

                TbProductSku productSku = productSkuRepository.findById(Integer.valueOf(cashierCart.getSkuId())).orElse(null);
                TbOrderDetail orderDetail = null;
                if (cashierCart.getOrderId() != null) {
                    orderDetail = oldOrderDetailMap.get(cashierCart.getOrderId().toString() + cashierCart.getId());
                }

                if (orderDetail == null) {
                    orderDetail = new TbOrderDetail();
                    hasNewInfo = true;
                    addOrderDetails.add(orderDetail);
                }

                if (Objects.nonNull(productSku)) {
                    saleAmount = saleAmount.add(productSku.getSalePrice());
                    orderDetail.setProductSkuName(productSku.getSpecSnap());
                }

                orderDetail.setNote(cashierCart.getNote());
                orderDetail.setCreateTime(DateUtil.date().toTimestamp());
                orderDetail.setNum(cashierCart.getNumber());
                orderDetail.setPrice(cashierCart.getSalePrice());
                orderDetail.setPriceAmount(cashierCart.getTotalAmount());
                orderDetail.setProductId(Integer.valueOf(cashierCart.getProductId()));
                orderDetail.setProductSkuId(Integer.valueOf(cashierCart.getSkuId()));
                orderDetail.setProductName(cashierCart.getName());
                orderDetail.setShopId(Integer.valueOf(cashierCart.getShopId()));
                orderDetail.setPackAmount(cashierCart.getPackFee());
                orderDetail.setStatus("unpaid");
                orderDetail.setUseType(createOrderDTO.getUseType());
                orderDetail.setProductImg(cashierCart.getCoverImg());
                orderDetail.setCartId(cashierCart.getId());
                if (cashierCart.getOrderId() != null) {
                    orderId = cashierCart.getOrderId();
                }
                orderDetail.setOrderId(orderId);
                orderDetails.add(orderDetail);
            }


            // 查询订单
            TbOrderInfo orderInfo = null;
            if (orderId != null) {
                orderInfo = orderInfoMapper.selectById(orderId);
            }
            // 是否是第一次创建订单
            boolean isFirst = false;
            // 修改订单信息
            if (orderInfo != null) {
                // 更新取餐号
                orderInfo.setOutNumber(updateOutNumber(String.valueOf(createOrderDTO.getShopId())).toString());
                orderInfo.setUpdatedAt(System.currentTimeMillis());
                orderInfo.setSettlementAmount(totalAmount);
                orderInfo.setAmount(totalAmount);
                orderInfo.setOriginAmount(totalAmount);
//            orderInfo.setStatus("unpaid");
                orderInfo.setOrderAmount(totalAmount);
                orderInfo.setRemark(createOrderDTO.getNote());
                orderInfo.setFreightAmount(feeAmount);
                orderInfo.setProductAmount(saleAmount);
                orderInfo.setTradeDay(DateUtils.getDay());
                orderInfo.setUseType(createOrderDTO.getUseType());
                orderInfo.setCreatedAt(DateUtil.current());
                if (seatCart != null) {
                    orderInfo.setSeatAmount(seatCart.getTotalAmount());
                    orderInfo.setSeatCount(seatCart.getNumber());
                }
                if (StrUtil.isNotBlank(createOrderDTO.getVipUserId())) {
                    orderInfo.setUserId(createOrderDTO.getVipUserId());
                }
                orderInfo.setSendType(shopEatTypeInfoDTO.getSendType());
                // 存在新添加的商品，增加下单次数
                if (hasNewInfo) {
                    orderInfo.setPlaceNum(placeNum);
                }
                orderInfoMapper.updateById(orderInfo);
            } else {
                isFirst = true;
                String orderNo = generateOrderNumber(null);
                orderInfo = new TbOrderInfo();
                orderInfo.setOrderNo(orderNo);
                orderInfo.setAmount(totalAmount);
                orderInfo.setPayAmount(BigDecimal.ZERO);
                orderInfo.setPackFee(packAMount);
                orderInfo.setSettlementAmount(totalAmount);
                orderInfo.setOriginAmount(totalAmount);
                orderInfo.setProductAmount(saleAmount);
                orderInfo.setOrderAmount(totalAmount);
                orderInfo.setFreightAmount(feeAmount);
                orderInfo.setTableId(createOrderDTO.getTableId());
                orderInfo.setSendType("table");
                orderInfo.setOrderType("cash");
                orderInfo.setShopId(createOrderDTO.getShopId().toString());
                orderInfo.setRefundAble(1);
                orderInfo.setTradeDay(cn.ysk.cashier.utils.DateUtils.getDay());
                orderInfo.setMasterId(createOrderDTO.getMasterId());
                orderInfo.setOutNumber(createOrderDTO.getMasterId());
                orderInfo.setRemark(createOrderDTO.getNote());
                orderInfo.setCreatedAt(DateUtil.current());
                orderInfo.setTableName(tbShopTable != null ? tbShopTable.getName() : null);
                orderInfo.setPlaceNum(placeNum);
                orderInfo.setUseType(createOrderDTO.getUseType());
                if (StrUtil.isNotBlank(createOrderDTO.getVipUserId())) {
                    orderInfo.setUserId(createOrderDTO.getVipUserId());
                }
                if (seatCart != null) {
                    orderInfo.setSeatAmount(seatCart.getTotalAmount());
                    orderInfo.setSeatCount(seatCart.getNumber());
                }
                orderInfo.setSendType(shopEatTypeInfoDTO.getSendType());
                TbMerchantAccount merchantAccount = merchantAccountMapper.selectOne(new LambdaQueryWrapper<TbMerchantAccount>()
                        .eq(TbMerchantAccount::getShopId, createOrderDTO.getShopId())
                        .eq(TbMerchantAccount::getStatus, 1));
                if (merchantAccount == null) {
                    throw new BadRequestException("商户信息不存在");
                }
                orderInfo.setMerchantId(merchantAccount.getId().toString());
                orderInfoMapper.insert(orderInfo);
            }

            // 添加订单详细数据
            orderId = orderInfo.getId();
            for (TbOrderDetail orderDetail : orderDetails) {
                orderDetail.setOrderId(orderId);
                if (orderDetail.getPlaceNum() == null) {
                    orderDetail.setPlaceNum(placeNum);
                }
            }
            // 删除已经移除购物车的订单 修改并保存数据
            if (!orderDetails.isEmpty()) {
                mpOrderDetailService.saveOrUpdateBatch(orderDetails);
            }

            // 更新购物车记录的orderId
            for (TbCashierCart cashierCart : cashierCarts) {
                if (!"-999".equals(cashierCart.getProductId())) {
                    TbProduct product = productMapper.selectById(cashierCart.getProductId());
                    TbProductSku productSku = productSkuRepository.findById(Integer.valueOf(cashierCart.getSkuId())).orElse(null);

                    log.info("下单,开始校验库存预警,购物车id:{}", cashierCart.getId());
                    CompletableFuture.runAsync(() -> checkWarnLineAndSendMsg(productSku, product, Integer.valueOf(cashierCart.getShopId()), cashierCart.getNumber()));
                    // 已经添加的商品，修改数量
                    updateStock(cashierCart);
                }

                cashierCart.setOrderId(orderId);
                cashierCart.setUpdatedAt(System.currentTimeMillis());
                cashierCart.setStatus("pending".equals(orderInfo.getStatus()) ? "refund" : cashierCart.getStatus());
                if (cashierCart.getPlaceNum() == null) {
                    cashierCart.setPlaceNum(placeNum);
                }

                // 先付费模式，结束购物车状态
                if (shopEatTypeInfoDTO.isDineInBefore() || shopEatTypeInfoDTO.isTakeout() || StrUtil.isBlank(createOrderDTO.getTableId())) {
                    cashierCart.setStatus("final");
                }
                cashierCartMapper.updateById(cashierCart);
            }

            // 菜品票
            if (!addOrderDetails.isEmpty() && shopEatTypeInfoDTO.isDineInAfter()) {
                rabbitMsgUtils.printDishesTicket(orderInfo.getId(), false, addOrderDetails.toArray(new TbOrderDetail[0]));
            }

            if (!removeOrderDetailIds.isEmpty()) {
                // 退单票
                orderDetailMapper.deleteBatchIds(removeOrderDetailIds);
                if (shopEatTypeInfoDTO.isDineInAfter()) {
                    rabbitMsgUtils.printDishesTicket(orderInfo.getId(), true, removeOrderDetailList.toArray(new TbOrderDetail[0]));
                }
            }

            if (isFirst) {
                // 后付费，不增加当前台桌取餐号
                if (!shopEatTypeInfoDTO.isTakeout()) {
                    addGlobalCode(cn.ysk.cashier.utils.DateUtils.getDay(), "pc", String.valueOf(createOrderDTO.getShopId()));
                }

                if (!shopEatTypeInfoDTO.isTakeout() || (addMaterId && "pending".equals(orderInfo.getStatus()))) {
                    String key = "SHOP:CODE:USER:pc" + ":" + createOrderDTO.getShopId() + ":" + cn.ysk.cashier.utils.DateUtils.getDay() + ":" + orderInfo.getTableId();
                    redisTemplate.delete(key);
                }
            }

            // 推送耗材信息
            pushConsMsg(orderInfo, cashierCarts);

            if (!shopEatTypeInfoDTO.isTakeout() && StrUtil.isNotBlank(createOrderDTO.getTableId())) {
                // 清空台桌信息
                if (shopEatTypeInfoDTO.isDineInBefore()) {
                    mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
                            .eq(TbShopTable::getShopId, createOrderDTO.getShopId())
                            .eq(TbShopTable::getQrcode, createOrderDTO.getTableId())
                            .set(TbShopTable::getProductNum, 0)
                            .set(TbShopTable::getTotalAmount, 0)
                            .set(TbShopTable::getRealAmount, 0)
                            .set(TbShopTable::getUseNum, 0)
                            .set(TbShopTable::getStatus, TableStateEnum.IDLE.getState()));
                    // 设置台桌信息
                } else {
                    LambdaUpdateWrapper<TbShopTable> wrapper = new LambdaUpdateWrapper<TbShopTable>()
                            .eq(TbShopTable::getShopId, createOrderDTO.getShopId())
                            .eq(TbShopTable::getQrcode, createOrderDTO.getTableId())
                            .set(TbShopTable::getProductNum, cashierCarts.size())
                            .set(TbShopTable::getTotalAmount, orderInfo.getOrderAmount())
                            .set(TbShopTable::getRealAmount, orderInfo.getOrderAmount())
                            .set(TbShopTable::getUseNum, seatCart != null ? seatCart.getNumber() : null)
                            .set(TbShopTable::getStatus, TableStateEnum.USING.getState());
                    if (isFirst) {
                        wrapper.set(TbShopTable::getUseTime, DateUtil.date());
                    }
                    mpShopTableMapper.update(null, wrapper);

                }
            }


            if (StrUtil.isNotBlank(createOrderDTO.getTableId())) {
                String tableCartKey = RedisConstant.getTableCartKey(createOrderDTO.getTableId(), createOrderDTO.getShopId().toString());
                redisTemplate.delete(tableCartKey);
            }
            return orderInfo;
        }, stringRedisTemplate, RedisConstant.getLockKey(RedisConstant.CREATE_ORDER, createOrderDTO.getShopId(),
                createOrderDTO.getTableId(), createOrderDTO.getMasterId(), createOrderDTO.getUseType()));

    }


    @Override
    public Object pending(PendingDTO pendingDTO) {

        if (pendingDTO.getOrderId() != null && pendingDTO.getIsPending()) {
            TbOrderInfo tbOrderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<TbOrderInfo>()
                    .eq(TbOrderInfo::getId, pendingDTO.getOrderId())
                    .eq(TbOrderInfo::getStatus, "pending"));
            if (tbOrderInfo != null) {
                return tbOrderInfo;
            }
        }

        LambdaQueryWrapper<TbCashierCart> queryWrapper = new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getUseType, pendingDTO.getUseType())
                .eq(TbCashierCart::getShopId, pendingDTO.getShopId());

        if (pendingDTO.getIsPending()) {
            queryWrapper
                    .eq(TbCashierCart::getStatus, "create")
                    .and(query2 -> query2.or(query3 -> query3.eq(TbCashierCart::getTradeDay, DateUtils.getDay())
                                    .eq(TbCashierCart::getMasterId, pendingDTO.getMasterId()))
                            .or((query4 -> query4.isNull(TbCashierCart::getTradeDay)
                                    .eq(TbCashierCart::getMasterId, ""))));

            if (StrUtil.isNotBlank(pendingDTO.getTableId())) {
                queryWrapper.eq(TbCashierCart::getTableId, pendingDTO.getTableId());
            }
        } else {
            if (pendingDTO.getOrderId() == null) {
                throw new BadRequestException("取消挂起订单id不为空");
            }
            queryWrapper.eq(TbCashierCart::getOrderId, pendingDTO.getOrderId());
        }
        List<TbCashierCart> cashierCarts = cashierCartMapper
                .selectList(queryWrapper);

        Integer orderId = null;
        for (TbCashierCart item : cashierCarts) {
            if (item.getOrderId() == null) {
                orderId = null;
                break;
            }

            orderId = item.getOrderId();
        }

        if (orderId == null) {
            CreateOrderDTO createOrderDTO = new CreateOrderDTO();
            createOrderDTO.setTableId(pendingDTO.getTableId());
            createOrderDTO.setShopId(pendingDTO.getShopId());
            createOrderDTO.setMasterId(pendingDTO.getMasterId());
            createOrderDTO.setNote(pendingDTO.getNote());
            createOrderDTO.setUseType(pendingDTO.getUseType());
            orderId = createOrder(createOrderDTO, true, false).getId();

        }


        TbOrderDetail orderDetail = new TbOrderDetail();
        orderDetail.setStatus(pendingDTO.getIsPending() ? "pending" : "unpaid");
        orderDetailMapper.update(orderDetail, new LambdaUpdateWrapper<TbOrderDetail>()
                .eq(TbOrderDetail::getShopId, pendingDTO.getShopId())
                .eq(TbOrderDetail::getOrderId, orderId));

        TbOrderInfo orderInfo = new TbOrderInfo();
        orderInfo.setId(orderId);
        orderInfo.setStatus(pendingDTO.getIsPending() ? "pending" : "unpaid");
        orderInfoMapper.updateById(orderInfo);

        TbCashierCart cashierCart = new TbCashierCart();
        cashierCart.setStatus(pendingDTO.getIsPending() ? "refund" : "create");
        cashierCart.setPendingAt(pendingDTO.getIsPending() ? DateUtil.current() : cashierCart.getPendingAt() == null ? DateUtil.current() : cashierCart.getPendingAt());
        cashierCartMapper.update(cashierCart, new LambdaUpdateWrapper<TbCashierCart>()
                .eq(TbCashierCart::getOrderId, orderId));


        // 后付款订单，修改台桌状态并打票
        if (pendingDTO.getIsPending() && StrUtil.isNotBlank(pendingDTO.getTableId())) {
            mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
                    .eq(TbShopTable::getQrcode, cashierCart.getTableId())
                    .set(TbShopTable::getStatus, TableStateEnum.PENDING.getState()));
        }

        if (StrUtil.isNotBlank(pendingDTO.getTableId())) {
            redisTemplate.delete(RedisConstant.getCurrentOrderKey(pendingDTO.getTableId(), pendingDTO.getShopId().toString()));
        }

        return orderInfoMapper.selectById(orderId);
    }

    @Override
    public Object getCar(Integer shopId, String tableId, String useType) {
        ArrayList<Map<String, Object>> data = new ArrayList<>();

        List<PendingCountVO> pendingCountVOS = cashierCartMapper.countPending(shopId, tableId, DateUtils.getDay(), useType);
        LambdaQueryWrapper<TbCashierCart> queryWrapper = new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getShopId, shopId)
                .eq(TbCashierCart::getUseType, useType)
                .eq(TbCashierCart::getStatus, "refund");
        if (StrUtil.isNotBlank(tableId)) {
            queryWrapper.eq(TbCashierCart::getTableId, tableId);

        }
        List<TbCashierCart> tbCashierCarts = cashierCartMapper.selectList(queryWrapper);

        ArrayList<Integer> skuIds = new ArrayList<>();
        tbCashierCarts.forEach(item -> skuIds.add(Integer.valueOf(item.getSkuId())));

        List<TbProductSku> skuList = productSkuRepository.findAllById(skuIds);

        LinkedHashMap<String, TbProductSku> skuMap = new LinkedHashMap<>();
        skuList.forEach(item -> skuMap.put(item.getId().toString(), item));


        cashierCartMapper.selectPending(shopId);

        HashMap<String, List<Map<String, Object>>> cashierMap = new HashMap<>();
        for (TbCashierCart tbCashierCart : tbCashierCarts) {
            TbProductSku productSku = skuMap.get(tbCashierCart.getSkuId());
            Map<String, Object> map = BeanUtil.beanToMap(tbCashierCart, false, false);
            map.put("specSnap", productSku == null ? "" : productSku.getSpecSnap());

            List<Map<String, Object>> list = cashierMap.computeIfAbsent(tbCashierCart.getOrderId().toString(), k -> new ArrayList<>());

            list.add(map);
        }

        pendingCountVOS.forEach(item -> {
            Map<String, Object> map = BeanUtil.beanToMap(item, false, false);
            map.put("carList", cashierMap.get(item.getOrderId().toString()));
            data.add(map);
        });
        return data;
    }

    @Override
    public Object getPayType(Integer shopId) {
        return payTypeRepository.findByShopId(String.valueOf(shopId));
    }

    @Override
    public Object pay(PayDTO payDTO) {
        return Utils.runFunAndCheckKey(() -> {
            long count = tbShopPayTypeMapper.selectCount(new LambdaQueryWrapper<TbShopPayType>()
                    .eq(TbShopPayType::getShopId, payDTO.getShopId())
                    .eq(TbShopPayType::getIsDisplay, 1)
                    .eq(TbShopPayType::getPayType, "deposit"));
            if (count < 1) {
                throw new BadRequestException("未知支付方式");
            }


            TbOrderInfo orderInfo = orderInfoMapper.selectById(payDTO.getOrderId());

            if (ObjectUtil.isEmpty(orderInfo)) {
                throw new BadRequestException("订单不存在");
            }

            if (!"paying".equals(orderInfo.getStatus()) &&
                    !"unpaid".equals(orderInfo.getStatus()) && !"pending".equals(orderInfo.getStatus())) {
                throw new BadRequestException("订单非未支付状态");
            }

            if (payDTO.getDiscount() == null) {
                payDTO.setDiscount((double) 1);
            }

            BigDecimal finalAmount = orderInfo.getOrderAmount().multiply(BigDecimal.valueOf(payDTO.getDiscount())).setScale(2, RoundingMode.HALF_UP);

            boolean isOnline = false;

            switch (payDTO.getPayType()) {
                case "vipPay":
                    if (payDTO.getVipUserId() != null) {
                        orderInfo.setUserId(String.valueOf(payDTO.getVipUserId()));
                    }
                    tbPayServiceImpl.vipPay(finalAmount, Integer.valueOf(orderInfo.getUserId()));
                    orderInfo.setPayOrderNo("vipPay".concat(SnowFlakeUtil.generateOrderNo()));
                    orderInfo.setPayType("deposit");
                    break;
                case "cash":
                    orderInfo.setPayType("cash");
                    orderInfo.setPayOrderNo("cash".concat(SnowFlakeUtil.generateOrderNo()));

//                    orderInfo = tbPayServiceImpl.cashPay(payDTO);
                    break;
                case "scanCode":
                    isOnline = true;
                    orderInfo = tbPayServiceImpl.scanPay(payDTO.getShopId(), payDTO.getCode(), Integer.valueOf(orderInfo.getMerchantId()),
                            StrUtil.isNotBlank(orderInfo.getMemberId()) ? Integer.valueOf(orderInfo.getMemberId()) : null, finalAmount, orderInfo);
                    break;
                case "deposit":
                    orderInfo = tbPayServiceImpl.memberAccountPay("", String.valueOf(payDTO.getShopId()), payDTO.getCode(), orderInfo, finalAmount);
                    break;
                default:
                    throw new BadRequestException("未知支付方式");
            }

            if (!isOnline) {
                orderInfo.setPaidTime(DateUtil.current());
                orderInfo.setPayAmount(finalAmount);
                orderInfo.setStatus("closed");
                orderInfo.setDiscountRatio(BigDecimal.valueOf(payDTO.getDiscount()));
                orderInfo.setDiscountAmount(orderInfo.getAmount().subtract(finalAmount));
                orderInfoMapper.updateById(orderInfo);

                //更新购物车状态
                //修改数据库中购物车数据
                int cartCount = tbCashierCartMapper.update(null, new LambdaUpdateWrapper<TbCashierCart>()
                        .eq(TbCashierCart::getOrderId, orderInfo.getId())
                        .eq(TbCashierCart::getUseType, orderInfo.getUseType())
                        .in(TbCashierCart::getStatus, "create", "closed")
                        .set(TbCashierCart::getStatus, "final"));
                log.info("更新购物车:{}", cartCount);

                //更新子单状态
                tbOrderDetailMapper.update(null, new LambdaUpdateWrapper<TbOrderDetail>()
                        .eq(TbOrderDetail::getOrderId, orderInfo.getId())
                        .eq(TbOrderDetail::getUseType, orderInfo.getUseType())
                        .eq(TbOrderDetail::getStatus, "unpaid")
                        .setSql(StrUtil.format("price_amount=price*num*{}", payDTO.getDiscount()))
                        .set(TbOrderDetail::getStatus, "closed"));

                JSONObject jsonObject = new JSONObject();
                jsonObject.put("token", payDTO.getToken());
                jsonObject.put("type", "create");
                jsonObject.put("orderId", orderInfo.getId());
                rabbitMsgUtils.sendOrderCollectMsg(jsonObject, 3);

                // 打印消息
                if (!OrderUseTypeEnum.DINE_IN_AFTER.getValue().equals(orderInfo.getUseType())) {
                    List<TbOrderDetail> detailList = orderDetailMapper.selectList(new LambdaQueryWrapper<TbOrderDetail>()
                            .eq(TbOrderDetail::getOrderId, orderInfo.getId())
                            .eq(TbOrderDetail::getStatus, "closed"));
                    rabbitMsgUtils.printDishesTicket(orderInfo.getId(), false, detailList.toArray(new TbOrderDetail[0]));
                }
                rabbitMsgUtils.printPlaceTicket(orderInfo.getId(), false);

                // 发送库存记录mq消息
                JSONObject mqData = new JSONObject();
                mqData.put("orderId", payDTO.getOrderId());
                mqData.put("type", "pc");
                rabbitTemplate.convertAndSend(RabbitConstants.EXCHANGE_STOCK_RECORD, RabbitConstants.ROUTING_STOCK_RECORD_SALE, mqData.toJSONString(), new CorrelationData(UUID.randomUUID().toString()));

                // 修改台桌状态
                if (!OrderUseTypeEnum.TAKEOUT.getValue().equals(orderInfo.getUseType()) && StrUtil.isNotBlank(orderInfo.getTableId())) {
                    TbShopTable tbShopTable = mpShopTableMapper.selectOne(new LambdaQueryWrapper<TbShopTable>()
                            .eq(TbShopTable::getShopId, payDTO.getShopId())
                            .eq(TbShopTable::getQrcode, orderInfo.getTableId()));
                    if (tbShopTable.getAutoClear().equals(1)) {
                        mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
                                .eq(TbShopTable::getQrcode, orderInfo.getTableId())
                                .set(TbShopTable::getEndTime, DateUtil.date())
                                .set(TbShopTable::getUseNum, 0)
                                .set(TbShopTable::getStatus, TableStateEnum.IDLE.getState()));
                    } else {
                        mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
                                .eq(TbShopTable::getQrcode, orderInfo.getTableId())
                                .set(TbShopTable::getEndTime, DateUtil.date())
                                .set(TbShopTable::getStatus, TableStateEnum.CLEANING.getState()));
                    }

                    String day = DateUtils.getDay();
                    String key = "SHOP:CODE:USER:pc" + ":" + payDTO.getShopId() + ":" + day + ":" + orderInfo.getTableId();
                    redisTemplate.delete(key);
                }

                String currentOrderKey = RedisConstant.CURRENT_TABLE_ORDER + payDTO.getShopId() + ":" + orderInfo.getTableId();
                redisTemplate.delete(currentOrderKey);

                // 小程序购物车缓存
                String tableCartKey = RedisConstant.getTableCartKey(orderInfo.getTableId(), orderInfo.getShopId());
                redisTemplate.delete(tableCartKey);
            }

            return "success";
        }, stringRedisTemplate, RedisConstant.getLockKey("", payDTO.getShopId(), payDTO.getOrderId()));


    }

    @Override
    public Object deleteOrder(DeleteOrderDTO deleteOrderDTO) {
        int count1 = orderInfoMapper.delete(new LambdaQueryWrapper<TbOrderInfo>()
                .eq(TbOrderInfo::getShopId, deleteOrderDTO.getShopId())
                .eq(TbOrderInfo::getId, deleteOrderDTO.getOrderId()));

        int count2 = orderDetailMapper.delete(new LambdaQueryWrapper<TbOrderDetail>()
                .eq(TbOrderDetail::getShopId, deleteOrderDTO.getShopId())
                .eq(TbOrderDetail::getOrderId, deleteOrderDTO.getOrderId()));

        int count3 = cashierCartMapper.delete(new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getShopId, deleteOrderDTO.getShopId())
                .eq(TbCashierCart::getOrderId, deleteOrderDTO.getOrderId()));
        return count1 > 0 && count2 > 0 && count3 > 0;
    }

    @Override
    public Object updateVip(UpdateVipDTO updateVipDTO) {
        ShopEatTypeInfoDTO shopEatTypeInfoDTO = checkEatModel(updateVipDTO.getShopId(), updateVipDTO.getTableId());
        LambdaQueryWrapper<TbCashierCart> queryWrapper = new LambdaQueryWrapper<TbCashierCart>()
                .in(TbCashierCart::getStatus, "create", "return")
                .eq(TbCashierCart::getShopId, updateVipDTO.getShopId())
                .eq(TbCashierCart::getUseType, shopEatTypeInfoDTO.getUseType())
                .and(q -> q.eq(TbCashierCart::getMasterId, updateVipDTO.getMasterId()).or().isNull(TbCashierCart::getMasterId));

        if (!shopEatTypeInfoDTO.isTakeout()) {
            queryWrapper.eq(TbCashierCart::getTableId, updateVipDTO.getTableId());
        } else {
            queryWrapper.and(q -> q.isNull(TbCashierCart::getTableId).or().eq(TbCashierCart::getTableId, ""))
                    .in(TbCashierCart::getPlatformType, OrderPlatformTypeEnum.PC.getValue(), OrderPlatformTypeEnum.CASH.getValue());
        }

        List<TbCashierCart> tbCashierCarts = cashierCartMapper.selectList(queryWrapper.isNotNull(TbCashierCart::getOrderId));
        if (!tbCashierCarts.isEmpty()) {
            Integer orderId = tbCashierCarts.get(0).getOrderId();
            if (updateVipDTO.getType() == 0) {
                return orderInfoMapper.update(null, new LambdaUpdateWrapper<TbOrderInfo>()
                        .eq(TbOrderInfo::getId, orderId)
                        .set(TbOrderInfo::getUserId, updateVipDTO.getVipUserId()));
            } else {
                return orderInfoMapper.update(null, new LambdaUpdateWrapper<TbOrderInfo>()
                        .eq(TbOrderInfo::getId, orderId)
                        .set(TbOrderInfo::getUserId, null));
            }
        }
        return true;
    }


    private void setCurrentOrderId(String tableId, Object shopId, String orderId) {
        String currentOrderKey = RedisConstant.getCurrentOrderKey(tableId, shopId.toString());
        redisTemplate.opsForValue().set(currentOrderKey, orderId);
    }

    private String removeCurrentOrderId(String tableId, Object shopId) {
        String currentOrderKey = RedisConstant.getCurrentOrderKey(tableId, shopId.toString());
        String orderId = redisTemplate.opsForValue().get(currentOrderKey);
        redisTemplate.delete(currentOrderKey);
        return orderId;
    }

    @Override
    public Object choseTable(ChoseTableDTO choseTableDTO) {
        TbShopTable shopTable = mpShopTableService.selectByStateAndTableId(TableStateEnum.IDLE, choseTableDTO.getTableId(), choseTableDTO.getShopId());
        if (shopTable == null) {
            throw new BadRequestException("台桌不存在或不处于空闲状态");
        }

        List<TbCashierCart> tbCashierCarts = mpCashierCartService.selectUnTableCart(choseTableDTO.getMasterId(), choseTableDTO.getShopId());
        if (tbCashierCarts.isEmpty()) {
            throw new BadRequestException("购物车为空");
        }


        ShopEatTypeInfoDTO shopEatTypeInfoDTO = checkEatModel(choseTableDTO.getShopId(), choseTableDTO.getTableId());

        // 清空原有桌台商品
        if (choseTableDTO.getIsClear()) {
            mpCashierCartService.clearCartByTableIdAndUseType(choseTableDTO.getTableId(), shopEatTypeInfoDTO.getUseType(), choseTableDTO.getShopId());
        }

        String masterId = getMasterId(choseTableDTO.getShopId(), choseTableDTO.getTableId(), "", null).getString("masterId");

        ArrayList<Integer> cartIds = new ArrayList<>();
        Integer orderId = null;
        for (TbCashierCart item : tbCashierCarts) {
            item.setTableId(choseTableDTO.getTableId());
            item.setUseType(shopEatTypeInfoDTO.getUseType());
            item.setMasterId(masterId);
            cartIds.add(item.getId());
            if (item.getOrderId() != null) {
                orderId = item.getOrderId();
            }
        }

        mpCashierCartService.updateBatchById(tbCashierCarts);
        // 修改detail的用餐类型
        mpOrderDetailService.updateUseTypeByCartIds(cartIds, shopEatTypeInfoDTO.getUseType());

        // 修改订单表台桌信息
        if (orderId != null) {
            mpOrderInfoService.updateTableIdAndUseTypeById(orderId, shopEatTypeInfoDTO.getUseType(), choseTableDTO.getTableId());
            mpShopTableService.updateStateByTableId(TableStateEnum.USING, choseTableDTO.getTableId());
            // 将台桌redis数据迁移
            setCurrentOrderId(choseTableDTO.getTableId(), choseTableDTO.getShopId().toString(), orderId.toString());
        }
        return true;

    }

    @Override
    public Object choseCount(ChoseCountDTO choseCountDTO) {
        return Utils.runFunAndCheckKey(() -> {
            TbShopInfo shopInfo = shopInfoRepository.findById(choseCountDTO.getShopId()).orElse(null);
            if (shopInfo == null) throw new BadRequestException("店铺信息不存在");

            if (shopInfo.getIsTableFee() != null && shopInfo.getIsTableFee() == 1) {
                throw new BadRequestException("当前店铺无需选择餐位费");
            }

            TbShopTable shopTable = mpShopTableService.lambdaQuery().eq(TbShopTable::getQrcode, choseCountDTO.getTableId()).one();
            if (shopTable == null) {
                throw new BadRequestException("台桌不存在");
            }

            if (shopTable.getMaxCapacity() < choseCountDTO.getNum()) {
                throw new BadRequestException("当前台桌最大人数为: " + shopTable.getMaxCapacity());
            }

            LambdaQueryWrapper<TbCashierCart> query = new LambdaQueryWrapper<TbCashierCart>()
                    .eq(TbCashierCart::getShopId, choseCountDTO.getShopId())
                    .and(q -> q.eq(TbCashierCart::getMasterId, choseCountDTO.getMasterId()).or().isNull(TbCashierCart::getMasterId).or().eq(TbCashierCart::getMasterId, ""))
                    .eq(TbCashierCart::getProductId, TableConstant.CART_SEAT_ID)
                    .eq(TbCashierCart::getUseType, choseCountDTO.getUseType())
                    .gt(TbCashierCart::getCreatedAt, DateUtil.offsetDay(DateUtil.date(), -1).getTime())
                    .in(TbCashierCart::getStatus, OrderStatusEnums.CREATE.getValue(), OrderStatusEnums.RETURN.getValue())
                    .eq(TbCashierCart::getTableId, choseCountDTO.getTableId());
            TbCashierCart tbCashierCart = cashierCartMapper.selectOne(query);

            if (tbCashierCart == null) {
                tbCashierCart = new TbCashierCart();
                tbCashierCart.setStatus("create");
                tbCashierCart.setCreatedAt(System.currentTimeMillis());
                tbCashierCart.setTableId(choseCountDTO.getTableId());
                tbCashierCart.setName("客座费");
                tbCashierCart.setSalePrice(shopInfo.getTableFee());
                tbCashierCart.setMasterId(choseCountDTO.getMasterId());
                tbCashierCart.setShopId(String.valueOf(choseCountDTO.getShopId()));
                tbCashierCart.setTradeDay(DateUtils.getDay());
                tbCashierCart.setStatus("create");
                tbCashierCart.setTotalAmount(new BigDecimal(choseCountDTO.getNum()).multiply(shopInfo.getTableFee()));
                tbCashierCart.setPlaceNum(1);
                tbCashierCart.setProductId("-999");
                tbCashierCart.setSkuId("-999");
                tbCashierCart.setPackFee(BigDecimal.ZERO);
                tbCashierCart.setNumber(choseCountDTO.getNum());
                tbCashierCart.setTotalNumber(choseCountDTO.getNum());
                tbCashierCart.setUseType(choseCountDTO.getUseType());
                tbCashierCartMapper.insert(tbCashierCart);
            } else {
                tbCashierCart.setTotalAmount(new BigDecimal(choseCountDTO.getNum()).multiply(shopInfo.getTableFee()));
                tbCashierCart.setNumber(choseCountDTO.getNum());
                tbCashierCart.setTotalNumber(choseCountDTO.getNum());
                tbCashierCart.setUseType(choseCountDTO.getUseType());
                tbCashierCartMapper.updateById(tbCashierCart);
            }

            return tbCashierCart;
        }, stringRedisTemplate, RedisConstant.getLockKey(RedisConstant.CHOSE_TABLE_COUNT, choseCountDTO.getShopId(), choseCountDTO.getTableId()));

    }

    @Override
    public Object printOrder(BaseTableDTO baseTableDTO) {
        ShopEatTypeInfoDTO shopEatTypeInfoDTO = checkEatModel(baseTableDTO.getShopId(), baseTableDTO.getTableId());

        Integer currentOrderId = getCurrentOrderId(shopEatTypeInfoDTO);
        if (currentOrderId == null) {
            throw new BadRequestException("当前台桌不存在订单");
        }
        TbOrderInfo orderInfo = orderInfoMapper.selectById(currentOrderId);
        rabbitMsgUtils.printPlaceTicket(orderInfo.getId(), false);
        return true;
    }

    @Override
    public Object generate(TableGenerateDTO generateDTO) {
        TbShopArea shopArea = mpShopAreaMapper.selectOne(new LambdaQueryWrapper<TbShopArea>()
                .eq(TbShopArea::getShopId, generateDTO.getShopId())
                .eq(TbShopArea::getId, generateDTO.getAreaId()));
        if (shopArea == null) {
            throw new BadRequestException("台桌区域不存在");
        }

        List<TbShopTable> tbShopTables = mpShopTableMapper.selectList(new LambdaQueryWrapper<TbShopTable>()
                .eq(TbShopTable::getShopId, generateDTO.getShopId())
                .select(TbShopTable::getName));

        HashMap<String, Object> tableMap = new HashMap<>();
        tbShopTables.forEach(item -> tableMap.put(item.getName(), item));

        StringBuilder msg = new StringBuilder();
        ArrayList<TbShopTable> tableArrayList = new ArrayList<>();
        for (int i = generateDTO.getStart(); i <= generateDTO.getEnd(); i++) {
            String name = generateDTO.getSign() + i;
            if (tableMap.get(name) != null) {
                msg.append(name).append(";");
                continue;
            }
            TbShopTable shopTable = new TbShopTable();
            shopTable.setShopId(generateDTO.getShopId());
            shopTable.setName(name);
            shopTable.setMaxCapacity(generateDTO.getCapacity());
            shopTable.setAreaId(generateDTO.getAreaId());
            shopTable.setCreatedAt(DateUtil.current());
            shopTable.setStatus(TableStateEnum.CLOSED.getState());
            tableArrayList.add(shopTable);
        }

        mpShopTableService.saveBatch(tableArrayList);
        if (StrUtil.isNotBlank(msg.toString())) {
            msg.append("台桌名已存在，未添加");
            throw new BadRequestException(msg.toString());
        }
        return true;
    }

    private TbShopTable checkTableState(String tableId) {
        TbShopTable tbShopTable = mpShopTableMapper.selectOne(new LambdaQueryWrapper<TbShopTable>()
                .eq(TbShopTable::getQrcode, tableId)
                .in(TbShopTable::getStatus, TableStateEnum.USING.getState(), TableStateEnum.PAYING.getState(), TableStateEnum.CLEANING.getState()));
        if (tbShopTable == null) {
            throw new BadRequestException("台桌不存在或当前台桌未开台");
        }
        return tbShopTable;
    }

    @Override
    public Object printDishes(BaseTableDTO baseTableDTO) {
        ShopEatTypeInfoDTO shopEatTypeInfoDTO = checkEatModel(baseTableDTO.getShopId(), baseTableDTO.getTableId());
        TbOrderInfo currentOrder = getCurrentOrder(shopEatTypeInfoDTO);
        if (currentOrder == null) {
            throw new BadRequestException("当前台桌不存在订单");
        }
        List<TbOrderDetail> detailList = orderDetailMapper.selectList(new LambdaQueryWrapper<TbOrderDetail>()
                .eq(TbOrderDetail::getOrderId, currentOrder.getId())
                .eq(TbOrderDetail::getStatus, "unpaid")
                .ne(TbOrderDetail::getProductId, -999)
                .select(TbOrderDetail::getId));
        if (detailList.isEmpty()) {
            throw new BadRequestException("当前台桌还未下单任何菜品, 请先下单");
        }

        rabbitMsgUtils.printDishesTicket(currentOrder.getId(), false, detailList.toArray(new TbOrderDetail[0]));
        return true;
    }

    @Override
    public Object choseModel(ChoseModelDTO choseModelDTO) {
//        if (!OrderUseTypeEnum.TAKEOUT.getValue().equals(choseModelDTO.getUseType()) && StrUtil.isBlank(choseModelDTO.getTableId())) {
//            throw new BadRequestException("堂食tableId必须传递");
//            // 外带设置餐位费
//        }

        List<TbCashierCart> cashierCarts = cashierCartMapper.selectList(new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getShopId, choseModelDTO.getShopId())
                .in(TbCashierCart::getId, choseModelDTO.getCartIds()));
        if (cashierCarts.isEmpty()) {
            return true;
        }
        if (OrderUseTypeEnum.TAKEOUT.getValue().equals(choseModelDTO.getUseType())) {
            ArrayList<Integer> productIds = new ArrayList<>();
            cashierCarts.forEach(item -> {
                productIds.add(Integer.valueOf(item.getProductId()));
            });
            List<TbProduct> productList = productMapper.selectBatchIds(productIds);
            Map<String, TbProduct> productMap = productList.stream()
                    .collect(Collectors.toMap(product -> String.valueOf(product.getId()), product -> product));
            cashierCarts.forEach(item -> {
                TbProduct product = productMap.get(item.getProductId());
                // 设置打包费
                item.setPackFee(product.getPackFee() != null ?
                        product.getPackFee().multiply(BigDecimal.valueOf(item.getNumber())) : BigDecimal.ZERO);
                item.setTableId("");
                item.setUseType(OrderUseTypeEnum.TAKEOUT.getValue());
                item.setIsPack("true");
            });

            List<TbOrderDetail> detailList = orderDetailMapper.selectList(new LambdaQueryWrapper<TbOrderDetail>()
                    .in(TbOrderDetail::getCartId, choseModelDTO.getCartIds())
                    .eq(TbOrderDetail::getShopId, choseModelDTO.getShopId()));
            detailList.forEach(item -> {
                item.setUseType(OrderUseTypeEnum.TAKEOUT.getValue());
                TbProduct product = productMap.get(item.getProductId().toString());
                // 设置打包费
                item.setPackAmount(product.getPackFee() != null ?
                        product.getPackFee().multiply(BigDecimal.valueOf(item.getNum())) : BigDecimal.ZERO);
            });

            if (!detailList.isEmpty()) {
                mpOrderDetailService.updateBatchById(detailList);
            }

            return mpCashierCartService.updateBatchById(cashierCarts);
        } else {
            cashierCartMapper.update(null, new LambdaUpdateWrapper<TbCashierCart>()
                    .in(TbCashierCart::getId, choseModelDTO.getCartIds())
                    .eq(TbCashierCart::getShopId, choseModelDTO.getShopId())
                    .set(TbCashierCart::getTableId, choseModelDTO.getTableId())
                    .set(TbCashierCart::getUseType, choseModelDTO.getUseType())
                    .set(TbCashierCart::getIsPack, "false")
                    .set(TbCashierCart::getPackFee, BigDecimal.ZERO));
            return orderDetailMapper.update(null, new LambdaUpdateWrapper<TbOrderDetail>()
                    .in(TbOrderDetail::getCartId, choseModelDTO.getCartIds())
                    .set(TbOrderDetail::getUseType, choseModelDTO.getUseType())
                    .set(TbOrderDetail::getPackAmount, BigDecimal.ZERO));
        }

    }

    private TbOrderInfo updateReturnOrderInfo(ReturnOrderDTO returnOrderDTO, TbOrderInfo oldOrderInfo, boolean isOnline) {
        ArrayList<Integer> detailIds = new ArrayList<>();
        HashMap<String, Integer> returnNumMap = new HashMap<>();
        returnOrderDTO.getOrderDetails().forEach(item -> {
            detailIds.add(item.getId());
            returnNumMap.put(item.getId().toString(), item.getNum());
        });
        List<TbOrderDetail> detailList = orderDetailMapper.selectList(new LambdaQueryWrapper<TbOrderDetail>()
                .eq(TbOrderDetail::getShopId, returnOrderDTO.getShopId())
                .eq(TbOrderDetail::getStatus, "closed")
                .eq(TbOrderDetail::getOrderId, returnOrderDTO.getOrderId())
                .in(TbOrderDetail::getId, detailIds));
        if (detailList.size() != returnOrderDTO.getOrderDetails().size()) {
            throw new BadRequestException("订单明细数量不一致");
        }

        BigDecimal returnAmount = BigDecimal.ZERO;
        BigDecimal packAMount = BigDecimal.ZERO;
        BigDecimal saleAmount = BigDecimal.ZERO;
        ArrayList<TbOrderDetail> remainOrderDetailList = new ArrayList<>();
        for (TbOrderDetail orderDetail : detailList) {
            Integer returnNum = returnNumMap.get(orderDetail.getId().toString());
            int remainNum = orderDetail.getNum() - returnNum;
            if (remainNum < 0) {
                throw new BadRequestException("{}最多可退数量为: {}", orderDetail.getProductName(), orderDetail.getNum());
            }

            // 将未退款的剩余订单详情重新生成记录
            BigDecimal packFee = orderDetail.getPackAmount().divide(BigDecimal.valueOf(orderDetail.getNum()), RoundingMode.HALF_UP);
            if (remainNum > 0) {
                // 单个打包费
                BigDecimal remainPackFee = packFee.multiply(BigDecimal.valueOf(remainNum));
                TbOrderDetail remainOrderDetail = BeanUtil.copyProperties(orderDetail, TbOrderDetail.class);
                remainOrderDetail.setNum(remainNum);
                remainOrderDetail.setPriceAmount(BigDecimal.valueOf(remainNum).multiply(orderDetail.getPrice())
                        .add(remainPackFee));
                remainOrderDetail.setPackAmount(remainPackFee);
                remainOrderDetail.setReturnNum("0");
                remainOrderDetail.setId(null);
                remainOrderDetailList.add(remainOrderDetail);
            }

            returnAmount = returnAmount.add(orderDetail.getPriceAmount()
                    .divide(new BigDecimal(orderDetail.getNum()), 2, RoundingMode.DOWN)
                    .multiply(BigDecimal.valueOf(returnNum)));
            saleAmount = saleAmount.add(orderDetail.getPrice());
            packAMount = packAMount.add(orderDetail.getPackAmount()
                    .divide(new BigDecimal(orderDetail.getNum()), 2, RoundingMode.DOWN)
                    .multiply(BigDecimal.valueOf(returnNum)));

            BigDecimal returnPackFee = packFee.multiply(BigDecimal.valueOf(returnNum));
            orderDetail.setNum(returnNum);
            orderDetail.setPriceAmount(BigDecimal.valueOf(returnNum).multiply(orderDetail.getPrice())
                    .add(returnPackFee));
            orderDetail.setPackAmount(returnPackFee);
            orderDetail.setRefundNumber(returnNum);
            orderDetail.setStatus("refunding");
        }

        if (returnAmount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new BadRequestException("退款金额必须大于0");
        }

        // 保存剩余未退款的订单详情
        if (!remainOrderDetailList.isEmpty()) {
            mpOrderDetailService.saveBatch(remainOrderDetailList);
        }

        TbOrderInfo returnOrder = mpOrderInfoService.selectReturnOrderByOrderId(returnOrderDTO.getOrderId());
        if (returnOrder == null) {
            String orderNo = generateOrderNumber(isOnline ? "OLRO" : "RO");
            returnOrder = BeanUtil.copyProperties(oldOrderInfo, TbOrderInfo.class);
            returnOrder.setId(null);
            returnOrder.setOrderNo(orderNo);
            returnOrder.setRefundAmount(returnAmount);
            returnOrder.setOrderType("return");
            returnOrder.setStatus("refunding");
            returnOrder.setUpdatedAt(null);
            returnOrder.setSystemTime(DateUtil.current());
            returnOrder.setCreatedAt(DateUtil.current());
            returnOrder.setPayOrderNo(null);
            returnOrder.setSource(oldOrderInfo.getId());
            returnOrder.setRefundRemark(returnOrderDTO.getNote());
            orderInfoMapper.insert(returnOrder);
        } else {
            returnOrder.setUpdatedAt(DateUtil.current());
            returnOrder.setRefundRemark(returnOrderDTO.getNote());
            returnOrder.setRefundAmount(returnOrder.getRefundAmount().add(returnAmount));
            orderInfoMapper.updateById(returnOrder);
        }


        updateStockAndRecord(detailList);
        mpOrderDetailService.updateBatchById(detailList);
        return returnOrder;
    }

    private void updateStockAndRecord(List<TbOrderDetail> orderDetailList) {
        // 更新商品库存
        for (TbOrderDetail detail : orderDetailList) {
            TbProductSku productSku = producSkutMapper.selectById(detail.getProductSkuId());
            TbProduct product = productMapper.selectById(detail.getProductId());

            TbProductStockDetail tbProductStockDetail = new TbProductStockDetail();
            tbProductStockDetail.setCreatedAt(System.currentTimeMillis());
            tbProductStockDetail.setUpdatedAt(System.currentTimeMillis());
            tbProductStockDetail.setShopId(detail.getShopId().toString());
            tbProductStockDetail.setSourcePath("NORMAL");
            tbProductStockDetail.setType("退单");
            tbProductStockDetail.setSubType(1);
            tbProductStockDetail.setRemark("退单: " + detail.getOrderId());
            tbProductStockDetail.setOrderId(String.valueOf(detail.getOrderId()));

            if (ObjectUtil.isNotEmpty(product)) {
                TbShopUnit shopUnit = mpShopUnitMapper.selectById(product.getUnitId());
                tbProductStockDetail.setProductName(product.getName());
                tbProductStockDetail.setIsStock(product.getIsStock());
                tbProductStockDetail.setStockSnap(product.getSelectSpec());
                tbProductStockDetail.setUnitName(shopUnit.getName());
                tbProductStockDetail.setProductId(product.getId().toString());

                productMapper.incrStock(product.getId(), detail.getNum());
                tbProductStockDetail.setLeftNumber(product.getStockNumber());
                tbProductStockDetail.setStockNumber(Double.valueOf(detail.getNum()));

                producSkutMapper.decrRealSalesNumber(productSku.getId(), detail.getNum());
            }

            mpProductStockDetailMapper.insert(tbProductStockDetail);
        }
    }


    @Override
    @Transactional
    public Object returnOrder(ReturnOrderDTO returnOrderDTO) {
        TbOrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<TbOrderInfo>()
                        .eq(TbOrderInfo::getId, returnOrderDTO.getOrderId())
                        .in(TbOrderInfo::getStatus, OrderStatusEnums.REFUND.getValue(), OrderStatusEnums.CLOSED.getValue())
//                .eq(TbOrderInfo::getStatus, "closed")
        );
        if (orderInfo == null) {
            throw new BadRequestException("订单不处于可退款状态");
        }

        TbShopInfo shopInfo = mpShopInfoMapper.selectById(orderInfo.getShopId());
        if (shopInfo == null) {
            throw new BadRequestException("店铺信息不存在");
        }

        if ("1".equals(shopInfo.getIsReturn())) {
            // TODO 密码校验
        }

        TbOrderInfo returnOrderInfo = updateReturnOrderInfo(returnOrderDTO, orderInfo, true);
        String shopId = orderInfo.getShopId();
        String payType = orderInfo.getPayType();

//        // 线上退款
        orderInfo.setRefundAmount(returnOrderInfo.getRefundAmount());
        orderInfo.setRefundRemark(returnOrderDTO.getNote());
        if ("scanCode".equals(payType) || "wx_lite".equals(payType)) {
            payService.returnOrder(Integer.valueOf(shopId), orderInfo, returnOrderInfo);
            orderInfo.setStatus("refund");
            // 储值卡支付退款
        } else if ("deposit".equals(payType)) {
            orderInfoService.depositReturn(Integer.valueOf(orderInfo.getUserId()), Integer.valueOf(orderInfo.getShopId()), returnOrderInfo.getRefundAmount());
            orderInfo.setStatus("refund");
            mpOrderDetailService.updateStatusByOrderIdAndIds(OrderStatusEnums.REFUNDING, OrderStatusEnums.REFUND,
                    returnOrderDTO.getOrderId(), returnOrderDTO.getOrderDetails().stream().map(ReturnOrderDTO.OrderDetail::getId).collect(Collectors.toList()));
        } else if ("cash".equals(payType)) {
            orderInfo.setStatus("refund");
            mpOrderDetailService.updateStatusByOrderIdAndIds(OrderStatusEnums.REFUNDING, OrderStatusEnums.REFUND,
                    returnOrderDTO.getOrderId(), returnOrderDTO.getOrderDetails().stream().map(ReturnOrderDTO.OrderDetail::getId).collect(Collectors.toList()));
        }
        orderInfoMapper.updateById(orderInfo);
        // 打印退款小票
//        producer.printMechine(newOrderInfo.getId().toString());

        //修改耗材数据
        JSONObject jsonObject1 = new JSONObject();
        jsonObject1.put("orderId", returnOrderInfo.getId());
        jsonObject1.put("type", "delete");
        rabbitMsgUtils.updateCons(jsonObject1);
        return true;
    }

    @Override
    public Object getShopState(Integer shopId, String tableId) {
        TbShopTable tbShopTable = mpShopTableService.selectByTableId(tableId, shopId);
        if (tbShopTable != null) {
            long cartCount = mpCashierCartService.countByTableId(shopId, tableId, OrderStatusEnums.CREATE, OrderStatusEnums.RETURN);
            Map<String, Object> map = BeanUtil.beanToMap(tbShopTable, false, false);
            map.put("cartCount", cartCount);
            return map;
        }
        return null;
    }

    @Override
    public Object bindQrcode(BindTableQrCodeDTO bindTableQrCodeDTO) {
        TbShopTable shopTable = mpShopTableService.selectById(bindTableQrCodeDTO.getId(), bindTableQrCodeDTO.getShopId());
        if (shopTable == null) {
            throw new BadRequestException("台桌不存在");
        }

        shopTable.setQrcode(bindTableQrCodeDTO.getCode());
        if (!mpShopTableService.updateById(shopTable)) {
            throw new BadRequestException("绑定失败");
        }
        return true;

    }
}
