package com.czg.service.account.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.czg.account.dto.calltable.*;
import com.czg.account.entity.*;
import com.czg.account.service.*;
import com.czg.account.vo.CallQueueInfoVO;
import com.czg.config.RabbitPublisher;
import com.czg.config.RedisCst;
import com.czg.constants.ParamCodeCst;
import com.czg.exception.CzgException;
import com.czg.print.CallTablePrintDTO;
import com.czg.resp.CzgResult;
import com.czg.service.RedisService;
import com.czg.service.account.mapper.CallQueueMapper;
import com.czg.service.account.mapper.CallTableMapper;
import com.czg.service.account.util.WechatMiniMsgUtil;
import com.czg.system.dto.SysParamsDTO;
import com.czg.system.service.SysParamsService;
import com.czg.utils.JoinQueryWrapper;
import com.czg.utils.PageUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.io.ByteArrayOutputStream;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 叫号桌型表 服务层实现。
 *
 * @author zs
 * @since 2025-02-21
 */
@DubboService
public class CallTableServiceImpl extends ServiceImpl<CallTableMapper, CallTable> implements CallTableService {
    @DubboReference
    private SysParamsService sysParamsService;

    @Resource
    private CallQueueService callQueueService;
    @Resource
    private ShopUserService shopUserService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RedisService redisService;
    @Resource
    private ShopInfoService shopInfoService;
    @Resource
    private CallConfigService callConfigService;
    @Resource
    private CallQueueMapper callQueueMapper;
    @Resource
    private RabbitPublisher rabbitPublisher;
    @Resource
    private WechatMiniMsgUtil miniMsgUtil;

    @Override
    public CallTablePage get(Long shopId, Long callTableId, Integer state) {
        QueryWrapper query = new QueryWrapper()
                .eq(CallTable::getShopId, shopId)
                .eq(CallTable::getState, 1);

        if (callTableId != null) {
            query.eq(CallTable::getId, callTableId);
        }

        if (state != null) {
            query.eq(CallTable::getState, state);
        }
        Page<CallTable> pageInfo = page(PageUtil.buildPage(), query);
        List<CallTable> info = new ArrayList<>();
        long totalCount = 0L;
        for (CallTable item : pageInfo.getRecords()) {
            long count = callQueueService.queryChain()
                    .eq(CallQueue::getCallTableId, item.getId())
                    .eq(CallQueue::getShopId, shopId)
                    .eq(CallQueue::getCreateDay, DateUtil.today())
                    .in(CallQueue::getState, 0, 1).count();

            totalCount += count;

            CallTableInfoDTO callTableInfoDTO = BeanUtil.copyProperties(item, CallTableInfoDTO.class);
            callTableInfoDTO.setTotalCount(count);
            info.add(callTableInfoDTO);
        }

        CallTablePage callTablePage = BeanUtil.copyProperties(pageInfo, CallTablePage.class);
        callTablePage.setRecords(info);
        callTablePage.setTotalCount(totalCount);
        return callTablePage;
    }

    @Override
    public boolean add(Long shopId, CallTableDTO addCallTableDTO) {
        long count = queryChain()
                .eq(CallTable::getShopId, shopId)
                .and(q -> {
                    q.eq(CallTable::getName, addCallTableDTO.getName()).or(q1 -> {
                        q1.eq(CallTable::getPrefix, addCallTableDTO.getPrefix());
                    });
                }).count();
        if (count > 0) {
            throw new CzgException("名称或前缀已存在");
        }

        CallTable callTable = BeanUtil.copyProperties(addCallTableDTO, CallTable.class);
        callTable.setShopId(shopId);
        return save(callTable);
    }

    @Override
    public boolean update(Long shopId, UpdateCallTableDTO callTableDTO) {
        CallTable callTable = queryChain()
                .eq(CallTable::getShopId, shopId)
                .eq(CallTable::getId, callTableDTO.getCallTableId()).one();
        if (StrUtil.isNotBlank(callTableDTO.getName())) {
            long count = queryChain()
                    .eq(CallTable::getShopId, shopId)
                    .ne(CallTable::getId, callTableDTO.getCallTableId())
                    .eq(CallTable::getName, callTableDTO.getName()).count();
            if (count > 0) {
                throw new CzgException("名称已存在");
            }
        }

        if (StrUtil.isNotBlank(callTableDTO.getPrefix())) {
            long count = queryChain()
                    .eq(CallTable::getShopId, shopId)
                    .ne(CallTable::getId, callTableDTO.getCallTableId())
                    .eq(CallTable::getPrefix, callTableDTO.getPrefix()).count();
            if (count > 0) {
                throw new CzgException("前缀已存在");
            }
        }

        if (callTable == null) {
            throw new CzgException("桌型不存在");
        }


        CallTable newInfo = BeanUtil.copyProperties(callTableDTO, CallTable.class);
        newInfo.setId(callTable.getId());

        return updateById(newInfo);
    }

    @Override
    public CallTableNumDTO takeNumber(Long shopId, TakeNumberDTO takeNumberDTO) {
        ShopInfo shopInfo = shopInfoService.getById(shopId);

        CallTable callTable = queryChain()
                .eq(CallTable::getShopId, shopId)
                .eq(CallTable::getId, takeNumberDTO.getCallTableId()).one();
        if (callTable == null) {
            throw new CzgException("桌型不存在");
        }

        // 拿取系统内部用户信息
        CallQueue callQueue;
        if (takeNumberDTO.getUserId() != null) {
            ShopUser shopUser = shopUserService.queryChain()
                    .eq(ShopUser::getStatus, 1)
                    .eq(ShopUser::getSourceShopId, shopId)
                    .eq(ShopUser::getId, takeNumberDTO.getUserId()).one();
            if (shopUser == null) {
                throw new CzgException("用户不存在");
            }

            callQueue = callQueueService.queryChain()
                    .eq(CallQueue::getUserId, shopUser.getId())
                    .eq(CallQueue::getShopId, shopId)
                    .in(CallQueue::getState, 0, 1)
                    .eq(CallQueue::getCreateDay, DateUtil.today())
                    .ne(CallQueue::getIsPostpone, 2)
                    .eq(CallQueue::getCallTableId, takeNumberDTO.getCallTableId()).one();
            if (callQueue != null) {
                throw new CzgException("当前用户已取号");
            }

            callQueue = BeanUtil.copyProperties(takeNumberDTO, CallQueue.class);
            callQueue.setPhone(StrUtil.isBlank(takeNumberDTO.getPhone()) ? shopUser.getPhone() : takeNumberDTO.getPhone());
//            callQueue.setOpenId(shopUser.getMiniOpenId());
        } else {
//            if (StrUtil.isBlank(takeNumberDTO.getPhone()) || StrUtil.isBlank(takeNumberDTO.getOpenId())) {
//                throw new CzgException("手机号或openId不能为空");
//            }

            callQueue = callQueueService.queryChain()
                    .eq(CallQueue::getPhone, takeNumberDTO.getPhone())
                    .eq(CallQueue::getShopId, shopId)
                    .eq(CallQueue::getCreateDay, DateUtil.today())
                    .in(CallQueue::getState, 0, 1)
                    .ne(CallQueue::getIsPostpone, 2)
                    .eq(CallQueue::getCallTableId, takeNumberDTO.getCallTableId()).one();
            if (callQueue != null) {
                throw new CzgException("当前用户已取号");
            }

            callQueue = BeanUtil.copyProperties(takeNumberDTO, CallQueue.class);
            callQueue.setPhone(takeNumberDTO.getPhone());
//            callQueue.setOpenId(takeNumberDTO.getOpenId());
            callQueue.setSubState(0);
        }

        callQueue.setCreateDay(DateUtil.today());
        callQueue.setCallNum(getCallNumber(shopId, callTable));
        callQueue.setShopId(shopId);
        callQueue.setShopName(shopInfo.getShopName());
        callQueue.setCreateTime(LocalDateTime.now());
        callQueueService.save(callQueue);

        CallTablePrintDTO callTablePrintDTO = new CallTablePrintDTO();
        callTablePrintDTO.setCallQueue(callQueue);
        callTablePrintDTO.setCallTable(callTable);
        String callUrl = sysParamsService.getSysParamValue(ParamCodeCst.System.CALL_PAGE_URL);
        if (StrUtil.isNotBlank(callUrl)) {
            callTablePrintDTO.setCallUrl(StrUtil.format(callUrl, callQueue.getShopId(), callQueue.getId()));
        } else {
            callTablePrintDTO.setCallUrl("未配置页面");
        }
        long count = callQueueService.count(new QueryWrapper()
                .eq(CallQueue::getShopId, callQueue.getShopId())
                .eq(CallQueue::getCallTableId, callQueue.getCallTableId())
                .lt(CallQueue::getId, callQueue.getId())
                .in(CallQueue::getState, 0, 1));
        callTablePrintDTO.setPreNum(count);
        // 打印排号票信息
        rabbitPublisher.sendOtherPrintMsg(callQueue.getShopId(), callTablePrintDTO, "CALL");

        return new CallTableNumDTO().setTableName(callTable.getName()).setTableNote(callTable.getNote())
                .setCallNum(callQueue.getCallNum()).setQueueId(callQueue.getId());
    }

    private String getCallNumber(Long shopId, CallTable callTable) {
        return redisService.runFunAndCheckKey(() -> {
            String callNumKey = RedisCst.getTableCallNumKey(shopId, callTable.getId());
            String value = stringRedisTemplate.opsForValue().get(callNumKey);
            AtomicReference<String> newVal = new AtomicReference<>("");
            // 初始化
            if (StrUtil.isBlank(value)) {
                Boolean setFlag = RedisService.runFunAndRetry(() -> stringRedisTemplate.opsForValue().setIfAbsent(callNumKey, callTable.getStart().toString()), flag -> !flag,
                        _ -> newVal.set(stringRedisTemplate.opsForValue().get(callNumKey)));

                if (setFlag) {
                    return callTable.getPrefix() + callTable.getStart();
                } else if (StrUtil.isNotBlank(newVal.get())) {
                    value = String.valueOf((Integer.parseInt(newVal.get()) + 1));
                    stringRedisTemplate.opsForValue().set(callNumKey, value);
                    return callTable.getPrefix() + value;
                } else {
                    throw new CzgException("生成排队号码失败");
                }

            } else {
                value = String.valueOf((Integer.parseInt(value) + 1));
                stringRedisTemplate.opsForValue().set(callNumKey, value);
                return callTable.getPrefix() + value;
            }
        }, RedisCst.getLockKey("UPDATE_TABLE", shopId, callTable.getId()));

    }

    @Override
    public String takeNumberCode(Long shopId, Integer callTableId) {
        // 创建二维码配置对象，设置宽度和高度为400
        QrConfig config = new QrConfig(400, 400);

        // 使用字节数组输出流来存储二维码图片
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        // 生成二维码图片，输出到字节数组输出流
        CzgResult<SysParamsDTO> paramsByCode = sysParamsService.getParamsByCode(ParamCodeCst.System.CALL_PAGE_URL);
        if (paramsByCode.getData() == null) {
            throw new CzgException("页面路径未配置");
        }
        QrCodeUtil.generate(StrUtil.format(paramsByCode.getData().getParamValue(), shopId, ""), config, "png", outputStream);

        // 将图片转换为 Base64 字符串
        String base64 = Base64.getEncoder().encodeToString(outputStream.toByteArray());

        // 返回Base64格式的图片字符串
        return "data:image/png;base64," + base64;
    }

    @Override
    public Integer call(Long shopId, CallQueueDTO callQueueDTO) {
        CallQueue callQueue = callQueueService.queryChain()
                .notIn(CallQueue::getState, -1, 2)
                .eq(CallQueue::getCreateDay, DateUtil.today())
                .eq(CallQueue::getId, callQueueDTO.getCallQueueId())
                .eq(CallQueue::getShopId, shopId)
                .one();

        if (callQueue == null) {
            throw new CzgException("叫号用户不存在");
        }

        callQueue.setState(1);
        callQueue.setCallCount(callQueue.getCallCount() + 1);
        callQueue.setCallTime(DateUtil.date().toLocalDateTime());
        callQueueService.updateById(callQueue);

        if (callQueue.getSubState().equals(0)) {
            return -1;
        }

        // 发送模板消息通知用户

        ShopInfo shopInfo = shopInfoService.getById(shopId);
        if (shopInfo == null) {
            throw new CzgException("店铺信息不存在");
        }

        List<CallQueue> current = callQueueService.queryChain()
                .eq(CallQueue::getCallTableId, callQueue.getCallTableId())
                .eq(CallQueue::getCreateDay, DateUtil.today())
                .and(r -> {
                    r.eq(CallQueue::getState, 1)
                            .or(r1 -> {
                                r1.eq(CallQueue::getState, 0);
                            });
                })
                .orderBy(CallQueue::getCreateTime, true)
                .page(new Page<>(1, 1)).getRecords();


        if (StrUtil.isBlank(callQueue.getOpenId())) {
            return -1;
        }

        miniMsgUtil.sendCurrentOrNearCallMsg(shopInfo.getShopName(), getStrByState(callQueue.getState()),
                callQueue.getCallNum(), current.isEmpty() ? "" : current.getFirst().getCallNum(), "排号信息", callQueue.getOpenId(), false);

        CallConfig config = getConfig(shopId);
        // 临近用户提醒
        List<CallQueue> nearList = callQueueService.queryChain()
                .eq(CallQueue::getCallTableId, callQueue.getCallTableId())
                .eq(CallQueue::getCreateDay, DateUtil.today())
                .gt(CallQueue::getId, current.getFirst().getId())
                .page(new Page<>(config.getNearNum(), 1)).getRecords();
        if (!nearList.isEmpty()) {
            CallQueue nearQueue = nearList.getFirst();
            miniMsgUtil.sendCurrentOrNearCallMsg(shopInfo.getShopName(), getStrByState(nearQueue.getState()),
                    nearQueue.getCallNum(), current.isEmpty() ? "" : current.getFirst().getCallNum(), "排号信息", nearQueue.getOpenId(), true);
        }
        return 1;
    }

    private String getStrByState(Integer state) {
        return switch (state) {
            case -1 -> "已取消";
            case 0 -> "排队中";
            case 1 -> "已到号";
            case 3 -> "已过号";
            default -> "";
        };
    }

    @Override
    public boolean updateInfo(Long shopId, UpdateCallQueueDTO updateCallQueueDTO) {
        CallQueue callQueue = callQueueService.queryChain()
                .eq(CallQueue::getShopId, shopId)
                .eq(CallQueue::getCreateDay, DateUtil.today())
                .eq(CallQueue::getId, updateCallQueueDTO.getCallQueueId()).one();
        if (callQueue == null) {
            throw new CzgException("叫号用户不存在");
        }

        switch (updateCallQueueDTO.getState()) {
            case -1:
                callQueue.setCancelTime(DateUtil.date().toLocalDateTime());
                break;
            case 0:
                callQueue.setState(0);
                break;
            case 1:
                if (callQueue.getSubState().equals(0)) {
                    throw new CzgException("当前用户未订阅微信提醒");
                }
                callQueue.setState(1);
                callQueue.setCallCount(callQueue.getCallCount() + 1);
                callQueue.setCallTime(DateUtil.date().toLocalDateTime());
                break;
            case 2:
                callQueue.setConfirmTime(DateUtil.date().toLocalDateTime());
                break;
            case 3:
                callQueue.setPassTime(DateUtil.date().toLocalDateTime());
                // 已经顺延
                callQueue.setIsPostpone(callQueue.getIsPostpone() == null ? 1 : callQueue.getIsPostpone() == 0 ? 1 : 2);
                ShopInfo shopInfo = shopInfoService.getById(callQueue.getShopId());
                if (shopInfo == null) {
                    throw new CzgException("店铺信息不存在");
                }

                if (StrUtil.isBlank(callQueue.getOpenId()) && callQueue.getSubState() != 1) {
                    break;
                }

                CallTable callTable = getById(callQueue.getCallTableId());
                Integer isPostpone = callTable.getIsPostpone();
                Integer postponeNum = callTable.getPostponeNum();

                // 判断是否需要顺延, 暂时注释
                if (false && callQueue.getIsPostpone() == 0 && isPostpone != null && isPostpone == 1 && postponeNum != null && postponeNum > 0) {
                    // 查询当前桌以及顺延桌数
                    List<CallQueue> current = callQueueService.queryChain()
                            .eq(CallQueue::getCallTableId, callQueue.getCallTableId())
                            .eq(CallQueue::getCreateDay, DateUtil.today())
                            .eq(CallQueue::getShopId, callTable.getShopId())
                            .ge(CallQueue::getId, callQueue.getId())
                            .orderBy(CallQueue::getCreateTime, true)
                            .page(new Page<>(1, postponeNum + 1)) // 获取当前桌和顺延的桌数
                            .getRecords();

                    // 确保有足够的桌可以顺延
                    if (current.size() > 1) {
                        // 获取当前桌以及顺延桌
                        CallQueue currentTable = BeanUtil.copyProperties(current.getFirst(), CallQueue.class);
                        // 顺延替换信息，将每一张顺延桌向前移动
                        for (int i = 0; i < current.size() - 1; i++) {
                            exchangeCallQueueInfo(current.get(i), current.get(i + 1));  // 当前桌替换为顺延桌
                        }

                        exchangeCallQueueInfo(current.getLast(), currentTable);
                        callQueue = current.getLast();

                        // 更新数据库中的桌号信息
                        callQueueService.updateBatch(current);
                    }
                }

//                List<CallQueue> current = callQueueService.queryChain()
//                        .eq(CallQueue::getCallTableId, callQueue.getCallTableId())
//                        .eq(CallQueue::getCreateDay, DateUtil.today())
//                        .and(r -> {
//                            r.eq(CallQueue::getState, 1)
//                                    .or(q -> {
//                                        q.eq(CallQueue::getState, 0);
//                                    });
//                        })
//                        .orderBy(CallQueue::getCreateTime, true)
//                        .page(new Page<>(1, 1)).getRecords();
//                wxMiniUtils.sendPassCallMsg(shopInfo.getShopName(), getStrByState(Integer.valueOf(updateCallQueueDTO.getState())),
//                        callQueue.getCallNum(), current.isEmpty() ? "" : current.get(0).getCallNum(), "即将过号", callQueue.getOpenId());
                break;
            default:
                throw new CzgException("错误类型");

        }

        callQueue.setState(updateCallQueueDTO.getState());

        return callQueueService.updateById(callQueue);
    }

    private void exchangeCallQueueInfo(CallQueue setCallQueue, CallQueue copyCallQueue) {
        setCallQueue.setOpenId(copyCallQueue.getOpenId());
        setCallQueue.setState(copyCallQueue.getState());
        setCallQueue.setSubState(copyCallQueue.getSubState());
        setCallQueue.setCreateTime(copyCallQueue.getCreateTime());
        setCallQueue.setName(copyCallQueue.getName());
        setCallQueue.setNote(copyCallQueue.getNote());
        setCallQueue.setCallNum(copyCallQueue.getCallNum());
        setCallQueue.setCallTime(copyCallQueue.getCallTime());
        setCallQueue.setCallCount(copyCallQueue.getCallCount());
        setCallQueue.setPassTime(copyCallQueue.getPassTime());
        setCallQueue.setCancelTime(copyCallQueue.getCancelTime());
        setCallQueue.setUserId(copyCallQueue.getUserId());
        setCallQueue.setConfirmTime(copyCallQueue.getConfirmTime());
    }

    @Override
    public Page<CallQueue> getQueue(Long shopId, String openId, Long callTableId, Integer state) {
        List<Long> tableIds;
        if (callTableId != null) {
            tableIds = Collections.singletonList(callTableId);
        } else {
            List<CallTable> list = queryChain().eq(CallTable::getShopId, shopId).eq(CallTable::getState, 1).list();
            if (list.isEmpty()) {
                return PageUtil.emptyPage();
            }
            tableIds = list.stream()
                    .map(CallTable::getId)
                    .collect(Collectors.toList());
        }

        QueryWrapper query = callQueueService.queryChain()
                .eq(CallQueue::getShopId, shopId)
                .eq(CallQueue::getCreateDay, DateUtil.today())
                .in(CallQueue::getCallTableId, tableIds);
        if (state != null) {
            query.eq(CallQueue::getState, state);
        } else {
            query.in(CallQueue::getState, 0, 1);
        }

        if (StrUtil.isNotBlank(openId)) {
            query.eq(CallQueue::getOpenId, openId);
        }

        Page<CallQueue> pageInfo = callQueueService.page(PageUtil.buildPage(), query
                .orderBy(CallQueue::getCreateTime, true)
                .orderBy(CallQueue::getState, false));

        List<CallQueue> list1 = pageInfo.getRecords();

        // 创建返回的结果集
        List<CallQueue> resultList = new ArrayList<>();

        // 遍历每一个叫号中的记录，计算前面状态为"0" (排队中) 的人数
        for (CallQueue calling : list1) {
            // 计算前面等待的人数 (状态为"0"且在叫号记录创建时间之前的)
            long waitingCount = 0;
            if (calling.getState() == 0) {
                waitingCount = list1.stream()
                        .filter(item -> item.getState() == 0 || item.getState() == 1)  // 过滤出状态为"排队中"的
                        .filter(item -> item.getCreateTime().isBefore(calling.getCreateTime()))  // 时间在当前叫号之前
                        .count();
            }

            // 创建一个Map来保存叫号中的记录及其前面排队的人数
            CallQueueListDTO callQueueListDTO = BeanUtil.copyProperties(calling, CallQueueListDTO.class);
            callQueueListDTO.setWaitingCount(waitingCount);
            // 将该map加入结果集
            resultList.add(callQueueListDTO);
        }

        pageInfo.setRecords(resultList);
        // 返回结果列表
        return pageInfo;
    }

    @Override
    public Page<CallRecordVO> getCallRecord(Long shopId, Integer callTableId) {
        QueryWrapper queryWrapper = new JoinQueryWrapper().eq(CallQueue::getShopId, shopId).in(CallQueue::getState, 3, 2, 1).orderBy(CallQueue::getCreateTime, false);
        if (callTableId != null) {
            queryWrapper.eq(CallQueue::getCallTableId, callTableId);
        }
        return callQueueMapper.xmlPaginate("selectCallRecord", PageUtil.buildPage(), queryWrapper);
    }

    @Override
    public CallConfig getConfig(Long shopId) {
        CallConfig config = callConfigService.queryChain().eq(CallConfig::getShopId, shopId).one();
        if (config == null) {
            config = new CallConfig();
            config.setShopId(shopId);
            callConfigService.save(config);
            config = callConfigService.queryChain().eq(CallConfig::getShopId, shopId).one();
        }

        return config;
    }

    @Override
    public boolean updateConfig(Long shopId, UpdateConfigDTO configDTO) {
        CallConfig config = callConfigService.queryChain().eq(CallConfig::getShopId, shopId).one();
        if (config == null) {
            throw new CzgException("未查询到配置信息");
        }
        CallConfig tbCallConfig = BeanUtil.copyProperties(configDTO, CallConfig.class);
        tbCallConfig.setId(config.getId());
        return callConfigService.updateById(tbCallConfig);
    }

    @Override
    public boolean subMsg(CallSubMsgDTO subMsgDTO) {
        CallQueue queue = callQueueService.getOne(new QueryWrapper()
                .eq(CallQueue::getShopId, subMsgDTO.getShopId())
                .eq(CallQueue::getId, subMsgDTO.getQueueId()));
        if (queue == null) {
            throw new CzgException("您未排号请先排号");
        }

        if (queue.getOpenId() != null && queue.getOpenId().equals(subMsgDTO.getOpenId()) && queue.getSubState() == 1) {
            return true;
        }

        if (StrUtil.isNotBlank(queue.getOpenId()) && queue.getSubState() == 1) {
            throw new CzgException("此号码已被其他用户订阅");
        }

        if (!subMsgDTO.getOpenId().equals(queue.getOpenId()) && queue.getSubState() == 0) {
            long count = callQueueService.queryChain()
//                .eq(CallQueue::getPhone, takeNumberDTO.getPhone())
                    .eq(CallQueue::getOpenId, subMsgDTO.getOpenId())
                    .eq(CallQueue::getShopId, subMsgDTO.getShopId())
                    .eq(CallQueue::getCreateDay, DateUtil.date().toString("yyyy-MM-dd"))
                    .in(CallQueue::getState, 0, 1)
                    .ne(CallQueue::getIsPostpone, 2)
                    .eq(CallQueue::getCallTableId, queue.getCallTableId()).count();
            if (count > 0) {
                throw new CzgException("您已订阅其他号码，请勿重复订阅");
            }
        }

        queue.setSubState(1);
        queue.setOpenId(subMsgDTO.getOpenId());
        return callQueueService.updateById(queue);
    }

    @Override
    public Object getStatus(Long shopId, String openId, Long queueId) {
        List<CallQueueInfoVO> callQueueInfoVOS = callQueueMapper.selectInfoByOpenId(shopId, openId, DateUtil.date().toString("yyyy-MM-dd"), null);
        if (callQueueInfoVOS.isEmpty()) {
            callQueueInfoVOS = callQueueMapper.selectInfoByOpenId(shopId, openId, DateUtil.date().toString("yyyy-MM-dd"), queueId);
        }
        if (!callQueueInfoVOS.isEmpty()) {
            CallQueueInfoVO callQueueInfoVO = callQueueInfoVOS.getFirst();
            CallQueue callQueue = new CallQueue();
            callQueue.setOpenId(openId);
            callQueue.setId(callQueueInfoVO.getId());
            callQueueService.updateById(callQueue);
        }
        ShopInfo shopInfo = shopInfoService.getById(shopId);
        HashMap<String, Object> data = new HashMap<>();
        data.put("shopInfo", shopInfo);
        data.put("queueInfo", callQueueInfoVOS.isEmpty() ? null : callQueueInfoVOS.getFirst());
        return data;
    }

    @Override
    public Boolean cancel(Long shopId, Long queueId) {
        CallQueue queue = new CallQueue();
        queue.setCancelTime(DateUtil.date().toLocalDateTime());
        queue.setState(-1);
        return callQueueService.update(queue, new QueryWrapper().eq(CallQueue::getShopId, shopId).eq(CallQueue::getId, queueId));
    }
}
