/*
 *  Copyright 2019-2020 Zheng Jie
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
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.StrUtil;
import cn.ysk.cashier.config.security.security.TokenProvider;
import cn.ysk.cashier.cons.RedisConstant;
import cn.ysk.cashier.cons.rabbit.RabbitConstants;
import cn.ysk.cashier.dto.shoptable.*;
import cn.ysk.cashier.enums.ShopWxMsgTypeEnum;
import cn.ysk.cashier.enums.TableStateEnum;
import cn.ysk.cashier.exception.BadRequestException;
import cn.ysk.cashier.mybatis.entity.TbShopOpenId;
import cn.ysk.cashier.mybatis.mapper.*;
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.shop.TbMerchantAccount;
import cn.ysk.cashier.pojo.shop.TbShopInfo;
import cn.ysk.cashier.pojo.shop.TbShopTable;
import cn.ysk.cashier.repository.TbShopPayTypeRepository;
import cn.ysk.cashier.repository.order.TbCashierCartRepository;
import cn.ysk.cashier.repository.product.TbProductRepository;
import cn.ysk.cashier.repository.product.TbProductSkuRepository;
import cn.ysk.cashier.repository.shop.TbShopInfoRepository;
import cn.ysk.cashier.service.impl.TbPayServiceImpl;
import cn.ysk.cashier.utils.*;
import cn.ysk.cashier.vo.PendingCountVO;
import com.alibaba.fastjson.JSONArray;
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 cn.ysk.cashier.repository.shop.TbShopTableRepository;
import cn.ysk.cashier.service.shop.TbShopTableService;
import cn.ysk.cashier.dto.shop.TbShopTableDto;
import cn.ysk.cashier.dto.shop.TbShopTableQueryCriteria;
import cn.ysk.cashier.mapper.shop.TbShopTableMapper;
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.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

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

/**
 * @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 TbProductRepository productRepository;
    private final TbProductSkuRepository productSkuRepository;
    private final TbCashierCartRepository cashierCartRepository;
    private final TbProductMapper productMapper;
    private final TbCashierCartMapper cashierCartMapper;
    private final RedisUtils redisUtils;
    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 TbShopOpenIdMapper shopOpenIdMapper;
    private final WxAccountUtil wxAccountUtil;
    private final WxMsgUtils wxMsgUtils;
    private final TbShopPayTypeRepository payTypeRepository;
    private final MpShopTableMapper mpShopTableMapper;

    /**
     * 桌码前缀
     */
    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;

    @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> queryAllNoPage(TbShopTableQueryCriteria criteria) {
        if (null == criteria.getAreaId() || criteria.getAreaId() == 0) {
            criteria.setAreaId(null);
        }
        List<TbShopTable> tbShopTableList = tbShopTableRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder));
        ArrayList<Map<String, Object>> infoList = new ArrayList<>();
        for (TbShopTable date : tbShopTableList) {
            String orderId = redisTemplate.opsForValue().get(RedisConstant.CURRENT_TABLE_ORDER + date.getShopId() + ":" + date.getQrcode());
            if (StrUtil.isBlank(date.getQrcode())) {
                date.setStatus("closed");
            } else if (tbCashierCartMapper.selectCount(new LambdaQueryWrapper<TbCashierCart>()
                    .eq(TbCashierCart::getShopId, date.getShopId())
                    .eq(TbCashierCart::getTableId, date.getQrcode())
                            .eq(TbCashierCart::getTradeDay, cn.ysk.cashier.utils.DateUtils.getDay())
                    .eq(TbCashierCart::getStatus, "create")) < 1 || (orderId != null &&
                    tbOrderDetailMapper.selectCount(new LambdaQueryWrapper<TbOrderDetail>()
                            .eq(TbOrderDetail::getShopId, date.getShopId())
                            .eq(TbOrderDetail::getStatus, "unpaid")
                                    .ge(TbOrderDetail::getCreateTime, DateUtil.beginOfDay(new Date()))
                            .eq(TbOrderDetail::getOrderId, orderId)) < 1)
            ) {
                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 (!"".equals(date.getQrcode())) {
                itemMap.put("qrcode", QRCODE + date.getQrcode().trim());
                itemMap.put("tableId", date.getQrcode());
            }
            infoList.add(itemMap);
        }
        int i = tbShopTableRepository.countAllByShopId(criteria.getShopId());
        HashMap<String, Object> map = new HashMap<>();
        map.put("content", infoList);
        map.put("total", i);
        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);
        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 (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()));
        if (tbCashierCart.getIsPack().equals("false")) {
            tbCashierCart.setPackFee(BigDecimal.ZERO);
        } else {
            tbCashierCart.setPackFee(new BigDecimal(updateCartDTO.getNum()).multiply(product.getPackFee()));
            tbCashierCart.setTotalAmount(tbCashierCart.getTotalAmount().add(tbCashierCart.getPackFee()));
        }

        if (tbCashierCart.getIsGift().equals("true")) {
            tbCashierCart.setTotalAmount(BigDecimal.ZERO);
        }
        tbCashierCart.setTotalNumber(updateCartDTO.getNum());
        tbCashierCart.setNumber(updateCartDTO.getNum());
        tbCashierCart.setCategoryId(product.getCategoryId());
        cashierCartRepository.save(tbCashierCart);

        return tbCashierCart;
    }

    /**
     * 台桌状态校验
     *
     * @param tableId 桌码
     * @return z
     */
    private TbShopTable checkTableIsOpen(String tableId) {
        TbShopTable shopTable = tbShopTableRepository.findByQrcode(tableId);
        if (shopTable == null) {
            throw new BadRequestException("桌码不存在，桌码" + tableId);
        }
        if (!shopTable.getStatus().equals("using") && !shopTable.getStatus().equals("idle")
                && !shopTable.getStatus().equals("pending")) {
            throw new BadRequestException("当前台桌非开台状态");
        }
        return shopTable;
    }

    @Override
    public TbCashierCart addCartForUser(AddCartDTO addCartDTO) {
        checkTableIsOpen(addCartDTO.getTableId());

        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.getIsDistribute().equals(1) && product.getStockNumber() < 1)
                || (!product.getIsDistribute().equals(1) && productSku.getStockNumber() < 1)
        ) {
            throw new BadRequestException(product.getName() + "商品库存不足");
        }

        LambdaQueryWrapper<TbCashierCart> query = new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getShopId, addCartDTO.getShopId())
                .eq(TbCashierCart::getSkuId, addCartDTO.getSkuId())
                .eq(TbCashierCart::getProductId, addCartDTO.getProductId())
                .eq(TbCashierCart::getTableId, addCartDTO.getTableId())
                .in(TbCashierCart::getStatus, "create", "refund")
//                .and(q -> {
//                    q.eq(TbCashierCart::getTradeDay, DateUtils.getDay())
//                            .or()
//                            .isNull(TbCashierCart::getTradeDay);
//
//                });
                .and(query2 -> {
                    query2.and(query3 -> {
                                query3.eq(TbCashierCart::getTradeDay, DateUtils.getDay())
                                        .eq(TbCashierCart::getMasterId, addCartDTO.getMasterId());
                            })
                            .or((query4 -> {
                                query4.isNull(TbCashierCart::getTradeDay)
                                        .eq(TbCashierCart::getMasterId, "");
                            }));
                });

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

        TbCashierCart tbCashierCart = cashierCartMapper.selectOne(query);
        // 增加redis购物车数据
        String tableCartKey = RedisConstant.getTableCartKey(addCartDTO.getTableId(), addCartDTO.getShopId().toString());
        String tableCart = redisTemplate.opsForValue().get(tableCartKey);
        // 首次加入
        if (tbCashierCart == null) {
            tbCashierCart = new TbCashierCart();
            tbCashierCart.setUserId(addCartDTO.getVipUserId());
            tbCashierCart.setCoverImg(product.getCoverImg());
            tbCashierCart.setCreatedAt(System.currentTimeMillis());
            tbCashierCart.setIsSku(product.getTypeEnum());
            tbCashierCart.setTableId(Long.valueOf(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()) {
                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.setTotalNumber(addCartDTO.getNum());
            tbCashierCart.setNumber(addCartDTO.getNum());
            tbCashierCart.setCategoryId(product.getCategoryId());
            cashierCartRepository.save(tbCashierCart);

        } else {
            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());
            // 数量0删除
            if (tbCashierCart.getNumber() == 0) {
                cashierCartRepository.deleteById(tbCashierCart.getId());
                return null;
            }
            tbCashierCart.setUpdatedAt(DateUtil.current());
            cashierCartRepository.save(tbCashierCart);
        }

        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::getId, removeCartDTO.getCartId()));
        if (cashierCart == 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::getOrderId, cashierCart.getOrderId()));
        }
        cashierCartMapper.delete(new LambdaQueryWrapper<TbCashierCart>().eq(TbCashierCart::getShopId, removeCartDTO.getShopId())
                .eq(TbCashierCart::getId, removeCartDTO.getCartId()));


        // 清空购物车 出票
        long carCount = countCar(cashierCart.getTableId(), cashierCart.getShopId(), cashierCart.getMasterId());

        log.info("购物车数量: {}", carCount);
        if (cashierCart.getOrderId() != null && carCount < 1) {
            rabbitMsgUtils.printTicket(String.valueOf(cashierCart.getOrderId()));
        }

        setRedisTableCartInfo(removeCartDTO.getTableId().toString(), removeCartDTO.getShopId().toString(), Collections.singletonList(cashierCart), false);

    }

    @Override
    public void clearCart(ClearCartDTO clearCartDTO) {
        String orderId = redisTemplate.opsForValue().get(RedisConstant.getCurrentOrderKey(clearCartDTO.getTableId().toString(), clearCartDTO.getShopId()));

        cashierCartMapper.delete(new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getShopId, clearCartDTO.getShopId())
                .eq(TbCashierCart::getTableId, clearCartDTO.getTableId())
                .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);
                })
        );

//        tbShopTableRepository.deleteByTableIdAndShopId(clearCartDTO.getTableId(), clearCartDTO.getShopId());

        if (StrUtil.isNotBlank(orderId)) {
            orderDetailMapper.delete(new LambdaQueryWrapper<TbOrderDetail>().eq(TbOrderDetail::getShopId, clearCartDTO.getShopId())
                    .eq(TbOrderDetail::getOrderId, orderId));
            rabbitMsgUtils.printTicket(orderId);
        }

    }

    private long countCar(Long tableId, String shopId, String masterId) {
        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 com.baomidou.mybatisplus.extension.plugins.pagination.Page<TbCashierCart> getCart(Long tableId, Integer page,
                                                                                             Integer size, Integer shopId, Integer vipUserId, String masterId) {
        LambdaQueryWrapper<TbCashierCart> queryWrapper = new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getTableId, tableId)
                .in(TbCashierCart::getStatus, "create", "refund")
                .eq(TbCashierCart::getShopId, shopId)
                .and(query2 -> {
                    query2.or(query3 -> {
                                query3.eq(TbCashierCart::getTradeDay, DateUtils.getDay())
                                        .eq(TbCashierCart::getMasterId, masterId);
                            })
                            .or((query4 -> {
                                query4.isNull(TbCashierCart::getTradeDay)
                                        .isNull(TbCashierCart::getMasterId);
                            }));
                });

//        if (vipUserId != null) {
//            queryWrapper.eq(TbCashierCart::getUserId, vipUserId);
//        }else {
//            queryWrapper.eq(TbCashierCart::getMasterId, masterId);
//            queryWrapper.isNull(TbCashierCart::getUserId);
//        }

        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()));
        });

        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 -> {
                Map<String, Object> map = BeanUtil.beanToMap(item, false, false);
                TbProductSku tbProductSku = skuMap.get(item.getSkuId());
                map.put("specSnap", tbProductSku != null ? tbProductSku.getSpecSnap() : null);
                infos.add(map);
            });

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

    @Override
    public void pack(PackCartDTO packCartDTO) {
        LambdaQueryWrapper<TbCashierCart> queryWrapper = new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getTableId, packCartDTO.getTableId())
                .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 (packCartDTO.getVipUserId() != null) {
//            queryWrapper.eq(TbCashierCart::getUserId, packCartDTO.getVipUserId());
//        } else {
//            queryWrapper.isNull(TbCashierCart::getUserId);
//        }

        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) {
        TbProduct product = productMapper.selectById(productId);
        if (product.getIsDistribute() == 1) {
            productMapper.incrStock(product.getId(), addNum);
        } else {
            producSkutMapper.incrStock(skuId, addNum);
        }
    }

    /**
     * 减少库存
     *
     * @param productId 商品数据
     * @param skuId     sku
     * @param decrNum   减少的数量
     */
    public void decrStock(Integer productId, String skuId, int decrNum) {
        TbProduct product = productMapper.selectById(productId);
        if (product.getIsDistribute() == 1) {
            if (product.getIsStock() == 1) {
                if (productMapper.decrStock(productId, decrNum) < 1) {
                    throw new BadRequestException(product.getName() + "库存不足，下单失败");
                }
            } else {
                productMapper.decrStockUnCheck(productId, decrNum);
            }
        } else {
            if (product.getIsStock() == 1) {
                if (producSkutMapper.decrStock(String.valueOf(skuId), decrNum) < 1) {
                    throw new BadRequestException(product.getName() + "库存不足，下单失败");
                }
            } else {
                producSkutMapper.decrStockUnCheck(String.valueOf(skuId), 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 (productSku.getWarnLine() == null) {
            return;
        }

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

        if (productSku.getStockNumber() == null) {
            productSku.setStockNumber((double) 0);
        }

        if (product.getStockNumber() == null) {
            product.setStockNumber(0);
        }
        if (
                (product.getIsDistribute() == 1 && product.getStockNumber() - num <= productSku.getWarnLine())
                        || (product.getIsDistribute() != 1) && productSku.getStockNumber() - num <= productSku.getWarnLine()
        ) {

            List<TbShopOpenId> openIdList = shopOpenIdMapper.selectList(new LambdaQueryWrapper<TbShopOpenId>()
                    .eq(TbShopOpenId::getShopId, shopId)
                    .eq(TbShopOpenId::getStatus, 1)
                    .and((queryWrapper) -> queryWrapper.eq(TbShopOpenId::getType, ShopWxMsgTypeEnum.ALL_MSG.getType())
                            .or().eq(TbShopOpenId::getType, ShopWxMsgTypeEnum.STOCK_MSG.getType()))
                    .groupBy(TbShopOpenId::getOpenId)
            );

            wxMsgUtils.aboardStockMsg(shopInfo.getShopName(), shopId, product.getName(),
                    product.getIsDistribute() == 1 ? product.getStockNumber() - num : (int) (productSku.getStockNumber() - num));
        }
    }

    public String generateOrderNumber() {
        String date = DateUtils.getSdfTimes();
        Random random = new Random();
        int randomNum = random.nextInt(900) + 100;
        return "DDPL" + 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 Object getMasterId(Integer shopId, Long tableId, Integer vipUserId) {
        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;
        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
    public TbOrderInfo createOrder(CreateOrderDTO createOrderDTO, boolean addMaterId, boolean isPrint) {
        return Utils.runFunAndCheckKey(() -> {
            String currentOrderKey = RedisConstant.getCurrentOrderKey(createOrderDTO.getTableId(),
                    createOrderDTO.getShopId().toString());
            String orderIdValue = redisTemplate.opsForValue().get(currentOrderKey);
            Integer orderId = orderIdValue == null ? null : Integer.parseInt(orderIdValue);
            orderId = createOrderDTO.getOrderId() != null ? createOrderDTO.getOrderId() : orderId;

            String day = DateUtils.getDay();
            LambdaQueryWrapper<TbCashierCart> queryWrapper = new LambdaQueryWrapper<TbCashierCart>()
                    .eq(TbCashierCart::getShopId, createOrderDTO.getShopId())
                    .eq(TbCashierCart::getTableId, createOrderDTO.getTableId())
                    .in(TbCashierCart::getStatus, "create", "refund")
                    .and(query2 -> {
                        query2.or(query3 -> {
                                    query3.eq(TbCashierCart::getTradeDay, DateUtils.getDay())
                                            .eq(TbCashierCart::getMasterId, createOrderDTO.getMasterId());
                                })
                                .or((query4 -> {
                                    query4.isNull(TbCashierCart::getTradeDay)
                                            .isNull(TbCashierCart::getMasterId);
                                }));
                    });
//        if (createOrderDTO.getVipUserId() != null) {
//            queryWrapper.eq(TbCashierCart::getUserId, createOrderDTO.getVipUserId());
//        }else {
//            queryWrapper.eq(TbCashierCart::getMasterId, createOrderDTO.getMasterId())
//                    .isNull(TbCashierCart::getUserId);
//        }
            List<TbCashierCart> cashierCarts = cashierCartMapper
                    .selectList(queryWrapper);
            if (cashierCarts.isEmpty()) {
                throw new BadRequestException("购物车为空，请先添加商品");
            }

            TbShopTable tbShopTable = mpShopTableMapper.selectOne(new LambdaQueryWrapper<TbShopTable>()
                    .eq(TbShopTable::getQrcode, createOrderDTO.getTableId())
                    .in(TbShopTable::getStatus, "idle", "using"));

            if (tbShopTable == null) {
                throw new BadRequestException("台桌未开台或不存在");
            }

            BigDecimal totalAmount = BigDecimal.ZERO;
            BigDecimal packAMount = BigDecimal.ZERO;
            BigDecimal feeAmount = BigDecimal.ZERO;
            BigDecimal saleAmount = BigDecimal.ZERO;
            List<TbOrderDetail> orderDetails = new ArrayList<>();

            for (TbCashierCart cashierCart : cashierCarts) {
                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 = new TbOrderDetail();

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

                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.setProductImg(cashierCart.getCoverImg());
                orderDetails.add(orderDetail);
                if (cashierCart.getOrderId() != null) {
                    orderId = cashierCart.getOrderId();
                }
                orderDetail.setOrderId(orderId);
            }

            TbOrderInfo orderInfo = null;
            if (orderId != null) {
                orderInfo = orderInfoMapper.selectById(orderId);

                if (orderInfo == null || !"unpaid".equals(orderInfo.getStatus())) {
                    redisTemplate.delete(currentOrderKey);
                }
            }

            // 修改订单信息
            if (orderInfo != null) {
                // 删除历史订单
                // 更新取餐号
                orderInfo.setOutNumber(updateOutNumber(String.valueOf(createOrderDTO.getShopId())).toString());
                orderDetailMapper.delete(new LambdaQueryWrapper<TbOrderDetail>().eq(TbOrderDetail::getOrderId, orderId));
                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.isPostPay() ? "postPay" : "afterPay");
                orderInfo.setUserId(createOrderDTO.getVipUserId() == null ? null : String.valueOf(createOrderDTO.getVipUserId()));
                orderInfo.setCreatedAt(DateUtil.current());
                orderInfoMapper.updateById(orderInfo);
            } else {
                String orderNo = generateOrderNumber();
                orderInfo = new TbOrderInfo();
                orderInfo.setOrderNo(orderNo);
                orderInfo.setUseType(createOrderDTO.isPostPay() ? "postPay" : "afterPay");
                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(String.valueOf(createOrderDTO.getTableId()));
                orderInfo.setSendType("table");
                orderInfo.setOrderType("cash");
                orderInfo.setShopId(createOrderDTO.getShopId().toString());
                orderInfo.setRefundAble(1);
                orderInfo.setTradeDay(day);
                orderInfo.setMasterId(createOrderDTO.getMasterId());
                orderInfo.setOutNumber(createOrderDTO.getMasterId());
                orderInfo.setRemark(createOrderDTO.getNote());
                orderInfo.setUserId(createOrderDTO.getVipUserId() == null ? null : String.valueOf(createOrderDTO.getVipUserId()));
                orderInfo.setCreatedAt(DateUtil.current());
                orderInfo.setTableName(tbShopTable.getName());
                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);

                redisTemplate.opsForValue().set(currentOrderKey, orderInfo.getId().toString());
            }


            // 添加订单详细数据
            orderId = orderInfo.getId();
            for (TbOrderDetail orderDetail : orderDetails) {
                orderDetail.setOrderId(orderId);
                orderDetailMapper.insert(orderDetail);
            }

            // 更新购物车记录的orderId
            // 是否是第一次添加的商品
            boolean isFirst = true;
            for (TbCashierCart cashierCart : cashierCarts) {
                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()));

                // 已经添加的商品，修改数量
                isFirst = updateStock(cashierCart);
                cashierCart.setOrderId(orderId);
                cashierCart.setUpdatedAt(System.currentTimeMillis());
                cashierCart.setStatus("pending".equals(orderInfo.getStatus()) ? "refund" : cashierCart.getStatus());
                cashierCartMapper.updateById(cashierCart);
            }
            if (isFirst) {
                // 后付费，不增加当前台桌取餐号
                if (createOrderDTO.isPostPay()) {
                    addGlobalCode(day, "pc", String.valueOf(createOrderDTO.getShopId()));
//                String key = "SHOP:CODE:USER:pc" + ":" + createOrderDTO.getShopId() + ":" + day + ":" + orderInfo.getTableId();
//                redisTemplate.delete(key);
                }

                if (!createOrderDTO.isPostPay() || (addMaterId && "pending".equals(orderInfo.getStatus()))) {
                    String key = "SHOP:CODE:USER:pc" + ":" + createOrderDTO.getShopId() + ":" + day + ":" + orderInfo.getTableId();
                    redisTemplate.delete(key);
                }

            }

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



            if (createOrderDTO.isPostPay() && isPrint) {
                Long count = orderInfoMapper.selectCount(new LambdaQueryWrapper<TbOrderInfo>()
                        .eq(TbOrderInfo::getStatus, "unpaid")
                        .eq(TbOrderInfo::getId, orderId));
                if (count != 0) {
                    rabbitMsgUtils.printTicket(String.valueOf(orderId));
                }
            }

            mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
                    .eq(TbShopTable::getShopId, createOrderDTO.getShopId())
                    .eq(TbShopTable::getQrcode, createOrderDTO.getTableId())
                    .set(TbShopTable::getStatus, TableStateEnum.USING.getState()));

            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.isPostPay()));

    }


    @Override
    public Object pending(PendingDTO pendingDTO) {

        if (pendingDTO.getOrderId() != null) {
            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::getShopId, pendingDTO.getShopId());

        if (!pendingDTO.getIsPending()) {
            if (pendingDTO.getOrderId() == null) {
                throw new BadRequestException("取消挂起订单id不为空");
            }
            queryWrapper.eq(TbCashierCart::getOrderId, pendingDTO.getOrderId());

        } else {
            queryWrapper.eq(TbCashierCart::getTableId, pendingDTO.getTableId())
                    .eq(TbCashierCart::getStatus, "create")
                    .eq(TbCashierCart::getTableId, pendingDTO.getTableId())
                    .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 (pendingDTO.getVipUserId() != null) {
//                queryWrapper.eq(TbCashierCart::getUserId, pendingDTO.getVipUserId());
//            }else {
//                queryWrapper.eq(TbCashierCart::getMasterId, pendingDTO.getMasterId())
//                        .isNull(TbCashierCart::getUserId);
//            }

        }
        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.setVipUserId(pendingDTO.getVipUserId());
            createOrderDTO.setNote(pendingDTO.getNote());
            orderId = createOrder(createOrderDTO, true, false).getId();

        }


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

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

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


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

        redisTemplate.delete(RedisConstant.getCurrentOrderKey(pendingDTO.getTableId(), pendingDTO.getShopId().toString()));

        return orderInfoMapper.selectById(orderId);
    }

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

        List<PendingCountVO> pendingCountVOS = cashierCartMapper.countPending(shopId, tableId, DateUtils.getDay());
        List<TbCashierCart> tbCashierCarts = cashierCartMapper.selectList(new LambdaQueryWrapper<TbCashierCart>()
                .eq(TbCashierCart::getShopId, shopId)
                .eq(TbCashierCart::getTableId, tableId)
                .eq(TbCashierCart::getStatus, "refund"));

        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(() -> {
            TbOrderInfo orderInfo = null;
            switch (payDTO.getPayType()) {
                case "vipPay":
                    orderInfo = tbPayServiceImpl.vipPay(payDTO.getShopId(), payDTO.getOrderId(), payDTO.getDiscount(), payDTO.getVipUserId());
                    break;
                case "cash":
                    orderInfo = tbPayServiceImpl.cashPay(payDTO);
                    break;
                case "scanCode":
                    orderInfo = tbPayServiceImpl.scanPay(payDTO);
                    break;
                case "deposit":
                    orderInfo = tbPayServiceImpl.memberAccountPay("", String.valueOf(payDTO.getShopId()), payDTO.getCode(), payDTO.getOrderId(), payDTO.getDiscount());
                    break;
                default:
                    throw new BadRequestException("未知支付方式");
            }

            log.info("更新购物车");

            JSONObject jsonObject = new JSONObject();
            jsonObject.put("token", null);
            jsonObject.put("type", "create");
            jsonObject.put("orderId", orderInfo.getId());

            // 打印消息
            rabbitMsgUtils.sendOrderCollectMsg(jsonObject);
            rabbitMsgUtils.printTicket(String.valueOf(orderInfo.getId()));

            // 发送库存记录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()));

            // 修改台桌状态
            mpShopTableMapper.update(null, new LambdaUpdateWrapper<TbShopTable>()
                    .eq(TbShopTable::getQrcode, orderInfo.getTableId())
                    .set(TbShopTable::getStatus, TableStateEnum.IDLE.getState()));

            if ("postPay".equals(orderInfo.getUseType())) {
                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 null;
        }, 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) {
        LambdaUpdateWrapper<TbCashierCart> queryWrapper = new LambdaUpdateWrapper<>();
        queryWrapper.eq(TbCashierCart::getTableId, updateVipDTO.getTableId())
                .eq(TbCashierCart::getMasterId, updateVipDTO.getMasterId())
                .eq(TbCashierCart::getShopId, updateVipDTO.getShopId());

        if (updateVipDTO.getType().equals(0)) {
            queryWrapper.set(TbCashierCart::getUserId, null);
            queryWrapper.eq(TbCashierCart::getUserId, updateVipDTO.getVipUserId());
        } else {
            queryWrapper.set(TbCashierCart::getUserId, updateVipDTO.getVipUserId());
        }
        return cashierCartMapper.update(null, queryWrapper);
    }
}
