package com.iformall.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.iformall.common.ErrorCode;
import com.iformall.common.IdWorker;
import com.iformall.common.Result;
import com.iformall.common.ResultData;
import com.iformall.domain.dto.WxSharingOrderDto;
import com.iformall.domain.po.*;
import com.iformall.domain.po.msg.AppUniformMsg;
import com.iformall.domain.po.msg.FmInsideCouponVerifyMsg;
import com.iformall.domain.po.msg.MpAppMsg;
import com.iformall.domain.po.msg.SmartAppMsg;
import com.iformall.domain.vo.*;
import com.iformall.enums.*;
import com.iformall.exception.MallinkException;
import com.iformall.mapper.*;
import com.iformall.mq.MqBaseProducer;
import com.iformall.service.*;
import com.iformall.utils.DateUtils;
import com.iformall.utils.PayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class WxCouponOrderServiceImpl implements WxCouponOrderService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    WxCouponOrderMapper wxCouponOrderMapper;

    @Autowired
    WxOrderMapper wxOrderMapper;

    @Autowired
    WxMerchantMapper wxMerchantMapper;

    @Autowired
    WxCouponMerchantMapper wxCouponMerchantMapper;

    @Autowired
    WxMerchantBUserMapper wxMerchantBUserMapper;

    @Autowired
    WxCUserMapper wxCUserMapper;

    @Autowired
    WxOrderService wxOrderService;

    @Autowired
    WxCouponSendService wxCouponSendService;

    @Autowired
    WxProfitSharingOrderService wxProfitSharingOrderService;

    @Autowired
    WxPayOrderMapper wxPayOrderMapper;

    @Autowired
    WxMallMapper wxMallMapper;

    @Autowired
    WxTemplateMsgMapper wxTemplateMsgMapper;

    @Autowired
    WxCouponMapper wxCouponMapper;

    @Autowired
    WxAppinfoService wxAppinfoService;

    @Autowired
    WxMerchantSubsidyMapper wxMerchantSubsidyMapper;

    @Autowired
    WxPayAccountMapper wxPayAccountMapper;

    @Autowired
    WxCardInfoMapper wxCardInfoMapper;

    @Autowired
    WxCardTransferInfoMapper wxCardTransferInfoMapper;

    @Autowired
    WxCouponChannelMapper wxCouponChannelMapper;

    @Autowired
    MqBaseProducer mqBaseProducer;

    @Autowired
    WxAuthorizerInfoMapper authorizerInfoMapper;

    @Autowired
    ExcelService excelService;


    @Autowired
    WxCreditHistoryService wxCreditHistoryService;

    @Autowired
    WxScoreRulesService wxScoreRulesService;

    @Autowired
    WxMsgLimitService wxMsgLimitService;

    @Autowired
    PosCouponOrderVerifyMapper posCouponOrderVerifyMapper;

    @Autowired
    WxPayOrderMapper payOrderMapper;

    @Override
    public PageInfo<WxCouponOrder> listAsPage(WxCouponOrder record, Integer pageIndex, Integer pageSize) {
        return PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxCouponOrderMapper.findList(record));
    }

    @Override
    public WxCouponOrder getById(Long id) {
        return wxCouponOrderMapper.selectById(id);
    }

    @Override
    public void saveOrUpdate(WxCouponOrder record) {
        if (record.getId() == null) {
            //record.setId(UUID.randomUUID().toString().replaceAll("-", ""));
            final IdWorker idWorker = IdWorker.get();
            record.setId(idWorker.nextId());
            wxCouponOrderMapper.insert(record);
        } else {
            wxCouponOrderMapper.updateById(record);
        }
    }

    @Override
    public void deleteById(Long id) {
        wxCouponOrderMapper.deleteById(id);
    }


    @Override
    public ResultData listOnDateAsPage(Long bUserId, String date, Integer pageIndex, Integer pageSize, boolean isVerified) {

        WxMerchantBUser wxMerchantBUser = wxMerchantBUserMapper.selectById(bUserId.longValue());
        if (wxMerchantBUser == null) {
            logger.error("核销员不存在:" + bUserId);
            throw new MallinkException(ErrorCode.MERCHANT_INFO_NOT_FOUND);
        }

        WxMerchant wxMerchant = wxMerchantMapper.selectById(wxMerchantBUser.getMerchantId());
        if (wxMerchant == null) {
            logger.error("商户不存在:" + bUserId);
            throw new MallinkException(ErrorCode.MERCHANT_INFO_NOT_FOUND);
        }

        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
        String dateString;
        Date startDate;
        Date endDate;

        if (date == null) {
            endDate = new Date();
            dateString = fmt.format(new Date());
            try {
                startDate = fmt.parse(dateString);
            } catch (ParseException e) {
                return new ResultData(ErrorCode.SYS_PARAMETER_CAST_ERROR);
            }
        } else {
            try {
                startDate = fmt.parse(date);
            } catch (ParseException e) {
                return new ResultData(ErrorCode.SYS_PARAMETER_CAST_ERROR);
            }
            Calendar cal = Calendar.getInstance();
            cal.setTime(startDate);
            cal.add(Calendar.DATE, 1);
            endDate = cal.getTime();
        }

        Map dateMap = new HashMap();
        dateMap.put("startDate", startDate);
        dateMap.put("endDate", endDate);
        dateMap.put("merchantId", wxMerchant.getId());

        Map resultMap = new HashMap();

        if (pageIndex == null || pageSize == null) {
            List<WxCouponOrder> list;
            if (isVerified)
                list = wxCouponOrderMapper.findListOfVerifiedByDate(dateMap);
            else
                list = wxCouponOrderMapper.findListOfOrderedByDate(dateMap);
            //logger.info("find " + list.size() + " coupon order from " + /startDate + " to " + new Date());
            resultMap.put("amount", list.stream().mapToInt(p -> p.getCouponPrice()).sum());
            resultMap.put("orderCount", list.size());
        } else {
            if (isVerified)
                resultMap.put("list", PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxCouponOrderMapper.findListOfVerifiedByDateForBUser(dateMap)));
            else
                resultMap.put("list", PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxCouponOrderMapper.findListOfOrderedByDateForBUser(dateMap)));
        }
        return new ResultData(resultMap);
    }

    @Override
    public PageInfo<WxCouponOrderCVo> listCUserVoAsPage(WxCouponOrder record, Integer pageIndex, Integer pageSize) {

        return PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxCouponOrderMapper.findListOfCUser(record));
    }

    @Override
    public WxCouponOrderCVo detailCUserVo(String couponOrderId) {

        WxCouponOrderCVo wxCouponOrderCVo = wxCouponOrderMapper.findDetailOfCUser(Long.valueOf(couponOrderId));
        if (wxCouponOrderCVo == null)
            throw new MallinkException(ErrorCode.COUPON_ORDER_IS_NULL);
        return wxCouponOrderCVo;
    }


    @Override
    public ResultData carListCUserVoAsPage(WxCouponOrder record, Integer pageIndex, Integer pageSize) {
        return new ResultData(PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxCouponOrderMapper.findCarListOfCUser(record)));
    }

    @Override
    public List<WxCouponOrderCarCVo> carListCUserVo(WxCouponOrder record) {
        return wxCouponOrderMapper.findCarListOfCUser(record);
    }

    @Override
    public ResultData detailBUserVo(Long bUserId, String couponOrderId) {
        WxMerchantBUser wxMerchantBUser = wxMerchantBUserMapper.selectById(bUserId);
        if (wxMerchantBUser == null) {
            logger.error("用户不存在:" + bUserId);
            throw new MallinkException(ErrorCode.USER_IS_EMPTY);
        }

        WxMerchant wxMerchant = wxMerchantMapper.selectById(wxMerchantBUser.getMerchantId());
        if (wxMerchant == null) {
            logger.error("商户不存在:" + bUserId);
            throw new MallinkException(ErrorCode.MERCHANT_INFO_NOT_FOUND);
        }
        WxCouponOrderCVo wxCouponOrderCVo = null;
        try {
            wxCouponOrderCVo = wxCouponOrderMapper.findDetailOfCUser(Long.valueOf(couponOrderId));
        } catch (Exception e) {
            return new ResultData(ErrorCode.COUPON_ORDER_IS_NULL);
        }
        if (wxCouponOrderCVo == null)
            return new ResultData(ErrorCode.COUPON_ORDER_IS_NULL);
        return new ResultData(wxCouponOrderCVo);
    }

    @Override
    public ResultData listAdminAsPage(WxCouponOrderBVo wxCouponOrderBVo, Integer pageIndex, Integer pageSize) {
        PageInfo<WxCouponOrderBVo> pageInfo = PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxCouponOrderMapper.findListOfAdmin(wxCouponOrderBVo));
        return new ResultData(pageInfo);
    }

    @Override
    public ResultData detailAdminVo(Long couponOrderId) {

        WxCouponOrderBVo wxCouponOrderBVo = wxCouponOrderMapper.findDetailOfAdmin(couponOrderId);
        if (wxCouponOrderBVo == null)
            return new ResultData(ErrorCode.COUPON_ORDER_IS_NULL);
        return new ResultData(wxCouponOrderBVo);
    }

    @Override
    public void exportData(WxCouponOrderBVo wxCouponOrder, HttpServletRequest request, HttpServletResponse response) {
        String filename = "券订单";
        List<WxCouponOrderBVo> list = wxCouponOrderMapper.findListOfAdmin(wxCouponOrder);
        list.parallelStream().forEach(c -> {
            if (c.getCouponOrderStatus().equals(EnumCouponOrderStatus.COUPON_ORDER_USE_WAIT.getCode())) {
                c.setUpdateDate(null);
            }
            if (!c.getMerchantId().equals(0L)) {
                List<WxMerchantVo> merchantVoList = c.getMerchantVoList();
                if (!merchantVoList.isEmpty()) {
                    StringBuffer shopNumber = new StringBuffer();
                    StringBuffer building = new StringBuffer();
                    WxMerchantVo wxMerchantVo = merchantVoList.get(0);
                    List<WxShopVo> shopVoList = wxMerchantVo.getShopVoList();
                    for (int j = 0, s_size = shopVoList.size(); j < s_size; j++) {
                        WxShopVo wxShopVo = shopVoList.get(j);
                        shopNumber.append(wxShopVo.getShopNumber()).append(";");
                        building.append(wxShopVo.getBuildingName()).append(wxShopVo.getFloorName()).append(";");
                    }
                    c.setShopNumber(shopNumber.toString());
                    c.setBuildingStr(building.toString());
                }
            }
        });
        excelService.exportExcel(list, null, filename, WxCouponOrderBVo.class, filename + ".xlsx", response, false);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public WxCouponOrder verify(WxCouponOrder couponOrder, WxMerchantBUser bUser) {

        // 1. 获取券信息
        WxCoupon wxCoupon = wxCouponMapper.selectById(couponOrder.getCouponId());
        if (wxCoupon == null) {
            logger.error("券: coupon-" + wxCoupon.getId() + "不存在" + "couponOrder-" + couponOrder.getId());
            throw new MallinkException(ErrorCode.COUPON_ORDER_IS_NULL);
        }

        // 2. 检查适用门店
        WxCouponMerchant wxCouponMerchant = new WxCouponMerchant();
        wxCouponMerchant.setProductId(wxCoupon.getId());
        wxCouponMerchant.setStatus(EnumCouponMerchantStatus.COUPON_MERCHANT_STATUS_VALID.getCode());
        List<WxCouponMerchant> couponMerchantList = wxCouponMerchantMapper.findList(wxCouponMerchant);
        if (!couponMerchantList.stream().anyMatch(cm -> cm.getMerchantId().equals(bUser.getMerchantId()))) {
            logger.error("券: coupon-" + wxCoupon.getId() + "核销: couponMerchantId-" + bUser.getMerchantId());
            throw new MallinkException(ErrorCode.VERIFY_COUPON_ORDER_MERCHANT_IS_NULL);
        }

        // 3. 券包状态检查
        if (couponOrder.getCouponOrderStatus() == EnumCouponOrderStatus.COUPON_ORDER_OVER_TIME.getCode()) {
            logger.error("已过期: couponOrder-" + couponOrder.getId());
            throw new MallinkException(ErrorCode.COUPON_ORDER_IS_OVER_TIME);
        }
        if (couponOrder.getCouponOrderStatus() == EnumCouponOrderStatus.COUPON_ORDER_INVALID.getCode()) {
            logger.error("已退款: couponOrder-" + couponOrder.getId());
            throw new MallinkException(ErrorCode.COUPON_ORDER_IS_INVALID);
        }
        if (couponOrder.getCouponOrderStatus() == EnumCouponOrderStatus.COUPON_ORDER_USED.getCode()) {
            logger.error("已经核销过的券: couponOrder-" + couponOrder.getId());
            throw new MallinkException(ErrorCode.COUPON_ORDER_IS_USED);
        }
        if (couponOrder.getCouponOrderStatus() == EnumCouponOrderStatus.POS_PRE_VERIFY.getCode()) {
            logger.error("券已被预核销: couponOrder-" + couponOrder.getId());
            throw new MallinkException(ErrorCode.COUPON_ORDER_IS_PRE_VERIFY);
        }
        int num = 0;
        try {
            couponOrder.setCouponOrderStatus(EnumCouponOrderStatus.COUPON_ORDER_USED.getCode()); //1核销
            couponOrder.setBUserId(bUser.getId());
            couponOrder.setVerifyType(EnumCouponVerifyType.VERIFY_B.getCode());
            couponOrder.setUpdateDate(new Date());
            num = wxCouponOrderMapper.updateVerifyCouponOrder(couponOrder);
        } catch (Exception e) {
            logger.error("db failed: couponOrder-" + couponOrder.getId() + ", e:" + e.getMessage());
            throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "DB FAILD " + e.getMessage());
        }
        if (num != 1) {
            logger.error("核销异常：updatete num " + num);
            throw new MallinkException(ErrorCode.VERIFY_ERROR.getCode(), "updatete num " + num);
        }

        // 成长值
        try {
            WxOrder order = wxOrderMapper.selectById(couponOrder.getOrderId());
            wxScoreRulesService.addScore2(EnumScoreType.CONSUMPTION, order, wxCoupon.getBusiness());
        } catch (Exception e) {
            logger.error("成长值:" + e.getMessage());
        }

        // 积分
        try {
            //-------此处为【现金支付】记录增加积分操作-------
            WxCreditHistory creditHistory = new WxCreditHistory();
            creditHistory.setOperatorType(EnumUserType.BUSER.getCode());
            creditHistory.setOperatorId(bUser.getId());
            creditHistory.setCUserId(couponOrder.getcUserId());
            creditHistory.setCreateDate(new Date());
            creditHistory.setTenantId(couponOrder.getTenantId());
            creditHistory.setCreditType(EnumScoreType.CONSUMPTION.getCode());
            creditHistory.setCouponId(wxCoupon.getId());
            creditHistory.setBusinessId(wxCoupon.getBusiness());
            creditHistory.setSpend(couponOrder.getCouponPrice());
            //如果券与商户一对一 则直接将消费商户更新为此商户 若一对多 则消费商户显示多商户
            creditHistory.setMerchantId(bUser.getMerchantId());
            creditHistory.setBuserId(bUser.getBuserId());
            wxCreditHistoryService.saveOrUpdate(creditHistory);
        } catch (Exception e) {
            logger.error("积分值:" + e.getMessage());
        }

        return couponOrder;
    }

    @Override
    public void sendInsideCouponVerifyMsg(String tenantId, Long couponOrderId, Long buUserId) {
        FmInsideCouponVerifyMsg verifyMsg = new FmInsideCouponVerifyMsg();
        verifyMsg.setMsgType(EnumMsgRecordType.INSIDE_COUPON_VERIFY.getCode());
        verifyMsg.setTenantId(tenantId);
        verifyMsg.setCouponOrderId(couponOrderId);
        verifyMsg.setBUserId(buUserId);
        mqBaseProducer.sendMessage(verifyMsg, EnumMsgMqTopic.DEFAULT.getCode(), EnumMsgMqTag.DEFAULT.getCode(), EnumMsgMqKey.DEFAULT.getCode());
    }

    @Override
    public void actionAfterVerify(WxCouponOrder couponOrder, WxMerchantBUser bUser) {

        WxPayAccount wxPayAccountQuery = new WxPayAccount();
        wxPayAccountQuery.setTenantId(couponOrder.getTenantId());
        WxPayAccount wxPayAccount = wxPayAccountMapper.selectOne(new QueryWrapper<>(wxPayAccountQuery));
        if (wxPayAccount != null && wxPayAccount.getShare().equals(EnumPayShare.NO.getCode())) {
            try {
                // 核销记账
                recordAfterVerified(couponOrder, bUser.getMerchantId());
            } catch (Exception e) {
                logger.info("核销记账失败");
                logger.error(e.getMessage());
            }
        } else {
            try {
                // 核销分账
                shareAfterVerify(couponOrder, bUser.getMerchantId());
            } catch (Exception e) {
                logger.error(e.getMessage());
            }
        }

        try {
            // 核销补贴
            subsidyAfterVerify(couponOrder, bUser.getMerchantId());
        } catch (Exception e) {
            logger.error(e.getMessage());
        }

        boolean bSentCoupon = false;

        // 核销发券
        try {
            bSentCoupon = sendCouponAfterVerify(couponOrder, bUser);
        } catch (Exception e) {
            logger.error(e.getMessage());
        }

        // 核销发模板消息
        try {
            sendVerifyMsg(couponOrder, bUser, bSentCoupon);
        } catch (Exception e) {
            logger.error("核销发送消息发生错误:" + e.getMessage());
        }
    }

    public void recordAfterVerified(WxCouponOrder couponOrder, Long merchantId) {
        WxCoupon wxCoupon = wxCouponMapper.selectById(couponOrder.getCouponId());
        WxMerchantSubsidy merchantSubsidy = new WxMerchantSubsidy();
        if (couponOrder.getCouponType().equals(EnumCouponType.COUPON_PRESS.getCode())
                && couponOrder.getCouponPrice().equals(wxCoupon.getSalePrice())) {
            //砍价
            merchantSubsidy.setSource(EnumMerchantSubsidySource.PRESS.getCode());
        } else if (couponOrder.getCouponType().equals(EnumCouponType.COUPON_GROUP.getCode())
                && couponOrder.getCouponPrice().equals(wxCoupon.getSalePrice())) {
            //拼团
            merchantSubsidy.setSource(EnumMerchantSubsidySource.GROUP.getCode());
        } else {
            //券
            merchantSubsidy.setSource(EnumMerchantSubsidySource.COUPON.getCode());
        }
        final IdWorker idWorker = IdWorker.get();
        Date curDate = new Date();
        merchantSubsidy.setId(idWorker.nextId());
        merchantSubsidy.setTenantId(couponOrder.getTenantId());
        merchantSubsidy.setOrderId(couponOrder.getOrderId());
        merchantSubsidy.setOrderType(EnumOrderType.COUPON.getCode());
        merchantSubsidy.setMerchantId(merchantId);
        merchantSubsidy.setCouponOrderId(couponOrder.getId());
        merchantSubsidy.setCouponType(couponOrder.getCouponType());
        merchantSubsidy.setOrderPayment(couponOrder.getSalePrice());
        merchantSubsidy.setReceiverPayment(couponOrder.getSalePrice());
        merchantSubsidy.setRealPayment(couponOrder.getSalePrice());
        merchantSubsidy.setSubsidy(couponOrder.getSalePrice());
        merchantSubsidy.setRealSubsidy(couponOrder.getSalePrice());
        merchantSubsidy.setType(EnumMerchantSubsidyType.WECHAT.getCode());
        merchantSubsidy.setCreateDate(curDate);
        merchantSubsidy.setUpdateDate(curDate);
        wxMerchantSubsidyMapper.insert(merchantSubsidy);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public void shareAfterVerify(WxCouponOrder couponOrder, Long merchantId) {
        // 微信分账
        try {
            WxPayOrder wxPayOrder = new WxPayOrder();
            wxPayOrder.setTenantId(couponOrder.getTenantId());
            wxPayOrder.setOrderId(couponOrder.getOrderId());
            wxPayOrder.setPayOrderStatus(EnumPayStatus.PAY_STATUS_SUCCESS.getCode());
            wxPayOrder = wxPayOrderMapper.selectOne(new QueryWrapper(wxPayOrder));
            if (wxPayOrder != null && wxPayOrder.getShare().equals(EnumPayShare.YES.getCode())) {
                WxSharingOrderDto wxSharingOrderDto = new WxSharingOrderDto();
                wxSharingOrderDto.setCUserId(wxPayOrder.getCUserId());
                wxSharingOrderDto.setMerchantId(merchantId);
                wxSharingOrderDto.setType(EnumProfitSharingOrderType.PROFIT_SHARING_SINGLE.getCode());
                wxSharingOrderDto.setPayAmount(wxPayOrder.getPayAmount());
                wxSharingOrderDto.setRateAmount(wxPayOrder.getRateAmount());
                wxSharingOrderDto.setOrderId(wxPayOrder.getId());
                wxSharingOrderDto.setPayTimeStart(wxPayOrder.getPayTimeStart());
                wxSharingOrderDto.setPayTimeEnd(wxPayOrder.getPayTimeEnd());
                wxSharingOrderDto.setTenantId(wxPayOrder.getTenantId());
                wxSharingOrderDto.setTransactionId(wxPayOrder.getTransactionId());
                wxSharingOrderDto.setShareAmount(wxPayOrder.getShareAmount());
                wxProfitSharingOrderService.createSharingOrder(wxSharingOrderDto);
            }
        } catch (Exception e) {
            logger.error("微信分账: " + e.getMessage());
        }
    }


    // 补贴
    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public void subsidyAfterVerify(WxCouponOrder couponOrder, Long merchantId) {
        final IdWorker idWorker = IdWorker.get();

        WxCouponMerchant couponMerchantQ = new WxCouponMerchant();

        couponMerchantQ.setProductId(couponOrder.getCouponId());
        couponMerchantQ.setMerchantId(merchantId);
        WxCouponMerchant couponMerchant;
        try {
            couponMerchant = wxCouponMerchantMapper.selectOne(new QueryWrapper(couponMerchantQ));
        } catch (Exception e) {
            logger.error("查找所属卡券商户错误: " + "CouponId:" + couponMerchantQ.getProductId(), " merchantId:" + merchantId);
            throw new MallinkException(ErrorCode.SYS_PARAMETER_ERROR);
        }

        if (couponMerchant == null) {
            logger.error("未找到所属卡券商户: " + "CouponId:" + couponMerchantQ.getProductId(), " merchantId:" + merchantId);
            throw new MallinkException(ErrorCode.SYS_PARAMETER_ERROR);
        }

        // coupon
        WxCoupon coupon;
        try {
            coupon = wxCouponMapper.selectById(couponOrder.getCouponId());
        } catch (Exception e) {
            logger.error("查找卡券错误: " + "CouponId:" + couponMerchantQ.getProductId());
            throw new MallinkException(ErrorCode.SYS_PARAMETER_ERROR);
        }

        WxAppinfo wxAppinfo = wxAppinfoService.getCAppInfo(couponOrder.getTenantId());
        WxPayAccount payAccount = wxPayAccountMapper.selectById(wxAppinfo.getPayId());

        Integer subsidyNum = coupon.getSubsidyNum();
        if (subsidyNum == null || subsidyNum.intValue() == 0) {
            String parameter = couponMerchant.getParameter();
            try {
                JSONObject jParameter = JSONObject.parseObject(parameter);
                subsidyNum = new BigDecimal(jParameter.getString("subsidyNumStr")).multiply(new BigDecimal(100)).intValue();
            } catch (Exception e) {
                subsidyNum = null;
            }
        }
        if (subsidyNum != null && subsidyNum.intValue() > 0) {
            // 有补贴时入库
            int iSubChargeFee = PayUtils.getPayRate(subsidyNum, payAccount.getRate(), false);
            Integer realSubsidyFee = subsidyNum - iSubChargeFee;

            int iPriceChargeFee = PayUtils.getPayRate(coupon.getSalePrice(), payAccount.getRate(), false);
            Integer realPayment = coupon.getSalePrice() - iPriceChargeFee;

            WxMerchantSubsidy merchantSubsidy = new WxMerchantSubsidy();
            merchantSubsidy.setId(idWorker.nextId());
            merchantSubsidy.setTenantId(couponOrder.getTenantId());
            merchantSubsidy.setOrderId(couponOrder.getOrderId());
            merchantSubsidy.setOrderType(EnumOrderType.COUPON.getCode());
            merchantSubsidy.setMerchantId(merchantId);
            merchantSubsidy.setCouponOrderId(couponOrder.getId());
            merchantSubsidy.setCouponType(couponOrder.getCouponType());
            merchantSubsidy.setOrderPayment(coupon.getPrice());
            merchantSubsidy.setReceiverPayment(coupon.getSalePrice());
            merchantSubsidy.setRealPayment(realPayment);
            merchantSubsidy.setSubsidy(subsidyNum);
            merchantSubsidy.setRealSubsidy(realSubsidyFee);
            merchantSubsidy.setStatus(EnumMerchantSubsidyStatus.NOT_SUBSIDY.getCode());
            merchantSubsidy.setCreateDate(new Date());
            merchantSubsidy.setUpdateDate(new Date());
            merchantSubsidy.setType(EnumMerchantSubsidyType.MANUAL.getCode());
            merchantSubsidy.setSource(EnumMerchantSubsidySource.SUBSIDY.getCode());
            try {
                wxMerchantSubsidyMapper.insert(merchantSubsidy);
            } catch (Exception e) {
                logger.error("数据库插入错误:" + e.getMessage());
                throw new MallinkException(ErrorCode.DB_FAIL);
            }
        }
    }

    @Override
    public boolean sendCouponAfterVerify(WxCouponOrder couponOrder, WxMerchantBUser bUser) {
        // 核销发券
        WxOrder wxOrder = wxOrderMapper.selectById(couponOrder.getOrderId());
        if (wxOrder != null) {
            try {
                return wxCouponSendService.sendCouponToUser(EnumCouponSendSendType.COUPON_VERIFY, wxOrder);
            } catch (Exception e) {
                logger.error("核销发券: " + e.getMessage());
            }
        }
        return false;
    }

    @Override
    public boolean sendCouponAfterMicroPay(String tenantId, Long orderId, Long cUserId) {
        // B端刷卡支付发券
        WxOrder wxOrder = wxOrderMapper.selectById(orderId);
        if (wxOrder != null) {
            try {
                return wxCouponSendService.sendCouponToUser(EnumCouponSendSendType.B_MICROPAY, wxOrder);
            } catch (Exception e) {
                logger.error("B端刷卡支付发券: " + e.getMessage());
            }
        }
        return false;
    }

    /**
     * 微信模板消息
     *
     * @param couponOrder, bUser
     */
    @Override
    public void sendVerifyMsg(WxCouponOrder couponOrder, WxMerchantBUser bUser, boolean hadSentCoupon) {
        // 1. get order info
        WxOrder order = wxOrderMapper.selectById(couponOrder.getOrderId());
        if (order == null) {
            logger.error("找不到订单信息");
            throw new MallinkException(ErrorCode.ORDER_IS_NOT_FIND);
        }
        // 2. get coupon info
        WxCoupon coupon = wxCouponMapper.selectById(couponOrder.getCouponId());
        if (coupon == null) {
            logger.error("找不到券信息");
            throw new MallinkException(ErrorCode.COUPON_IS_EMPTY);
        }
        // 3. merchant info
        WxMerchant merchant = wxMerchantMapper.selectById(bUser.getMerchantId());
        if (merchant == null) {
            logger.error("找不到商户信息");
            throw new MallinkException(ErrorCode.MERCHANT_INFO_NOT_FOUND);
        }
        // 4. get user info
        WxCUser user = wxCUserMapper.selectById(order.getCUserId());
        if (user == null) {
            logger.error("找不到C端用户");
            throw new MallinkException(ErrorCode.USER_IS_EMPTY);
        }

        // 5. get app info
//        WxAppinfo appInfo = wxAppinfoService.getByAppId(user.getAppId());

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        String couponDetail = "";
        if (coupon.getType() == EnumCouponType.COUPON_MANJIAN.getCode()) {
            couponDetail = "满" + coupon.getUsePriceStr() + "减" + coupon.getPriceStr();
        }

        boolean bUsed = false;
        String gotopage = "pages/index/index";
        if (couponOrder.getCouponOrderStatus() == EnumCouponOrderStatus.COUPON_ORDER_USED.getCode()) {
            if (hadSentCoupon) {
                gotopage = "pages/index/index?type=mc";
            }
            bUsed = true;
        }

        // 7. 公众号消息
        if (StringUtils.isNotBlank(user.getMpAppId()) &&
                StringUtils.isNotBlank(user.getMpOpenId()) &&
                user.getMpSubscribe().equals(EnumUserIsSubscribe.YES.getCode())) {
            // 已关注公众号
            doVerifyMpMsg(couponOrder, coupon, merchant, user, dateFormat, gotopage);
            return;
        } else {
            // 可能未关注公众号, 用户可能收不到
            // 8. 微信小程序-统一消息
            //  获取小程序关联的服务号
            doVerifyAppUniformMsg(couponOrder, coupon, merchant, user, dateFormat, gotopage);
        }

        // 小程序 消息
        String formId = null;
        Date curDate = new Date();
        Date dateAfter7Day = DateUtils.getHourTimeAfter(7, order.getCreateDate());
        if (StringUtils.isNotBlank(order.getFormId()) && curDate.before(dateAfter7Day)
                && !order.getFormId().equalsIgnoreCase("undefined")) {
            formId = order.getFormId();
        }

        // 6. get payOrder info
        int formType = EnumMsgLimitType.WEAPP_FORMID.getCode();
        if (StringUtils.isBlank(order.getFormId()) || order.getFormId().equalsIgnoreCase("undefined")) {
            if (order.getPayment() > 0) {
                // formId为空，获取prepayId
                WxPayOrder payOrder = null;
                WxPayOrder payOrderQ = new WxPayOrder();
                payOrderQ.setTenantId(couponOrder.getTenantId());
                payOrderQ.setOrderId(order.getId());
                payOrderQ.setPayOrderStatus(EnumPayStatus.PAY_STATUS_SUCCESS.getCode());
                List<WxPayOrder> payList = wxPayOrderMapper.selectList(new QueryWrapper(payOrderQ));
                if (payList.size() > 0) {
                    payOrder = payList.get(0);
                }
                if (payOrder == null) {
                    logger.error("找不到payOrder信息");
                    throw new MallinkException(ErrorCode.PAY_ORDER_NOT_FOUND);
                }
                if (payOrder.getPrepayId() == null) {
                    logger.error("payOrder信息中，prepay_id为空");
                    throw new MallinkException(ErrorCode.PREPAY_ID_IS_EMPTY);
                }
                dateAfter7Day = DateUtils.getHourTimeAfter(7, payOrder.getCreateTime());
                if (curDate.before(dateAfter7Day)) {
                    formId = payOrder.getPrepayId();
                    formType = EnumMsgLimitType.WEAPP_PREPAYID.getCode();
                }
            }
        }

        if (StringUtils.isBlank(formId)) {
            logger.error("无可用formId, 不能发小程序模板消息");
            return;
        }

        // 9. 微信小程序-模板消息
        /**
         * 检查 小程序模板消息是否超限
         */
        WxMsgLimit msgLimit = new WxMsgLimit();
        msgLimit.setTenantId(user.getTenantId());
        msgLimit.setType(formType);
        msgLimit.setLimitId(formId);
        if (wxMsgLimitService.checkIsLimit(msgLimit)) {
            // 已达到限制条件，不发消息了
            logger.error("已达到限制条件，不发小程序模板消息了: " + user.getId());
            return;
        }
        if (bUsed) {
            //mq发送小程序消息 - 核销成功消息
            doVerifySuccessMsg(couponOrder, formId, coupon, merchant, user, dateFormat, couponDetail, gotopage);
        } else {
            // mq发送小程序消息 - 核销失败消息
            doVerifyFailMsg(couponOrder, formId, coupon, user, dateFormat, couponDetail, gotopage);
        }


    }

    private void doVerifyMpMsg(WxCouponOrder couponOrder, WxCoupon coupon, WxMerchant merchant, WxCUser user, SimpleDateFormat dateFormat, String gotopage) {
        /**
         * 卡券核销通知
         * {{first.DATA}}
         * 卡券类型：{{keyword1.DATA}}
         * 卡券号码：{{keyword2.DATA}}
         * 核销时间：{{keyword3.DATA}}
         * {{remark.DATA}}
         */
        MpAppMsg mpAppMsg = new MpAppMsg();
        mpAppMsg.setMsgType(EnumMsgRecordType.PUBLIC.getCode());
        mpAppMsg.setAppId(user.getMpAppId());
        mpAppMsg.setTo(user.getMpOpenId());
        mpAppMsg.setMiniAppId(user.getAppId());
        mpAppMsg.setMiniPagePath(gotopage);
        mpAppMsg.setTenantId(couponOrder.getTenantId());
        mpAppMsg.setTemplateType(EnumTemplateType.VERIFY_RESULT.getCode());
        mpAppMsg.setDataList(Lists.newArrayList(
                merchant.getName(),
                EnumCouponType.getEnum(coupon.getType()).getMessage(),
                couponOrder.getId().toString(),
                dateFormat.format(new Date()),
                user.getNickName()
        ));

        try {
            mqBaseProducer.sendMessage(mpAppMsg, EnumMsgMqTopic.DEFAULT.getCode(), EnumMsgMqTag.DEFAULT.getCode(), EnumMsgMqKey.DEFAULT.getCode());
        } catch (Exception e) {
            logger.error("核销-公众号-模板消息发送失败: " + e.getMessage());
            throw new MallinkException(ErrorCode.MP_TEMPLATE_SEND_FAILED);
        }
    }

    private void doVerifyAppUniformMsg(WxCouponOrder couponOrder, WxCoupon coupon, WxMerchant merchant, WxCUser user, SimpleDateFormat dateFormat, String gotopage) {
        WxAuthorizerInfo authQ = new WxAuthorizerInfo();
        authQ.setAuthorizerAppid(user.getAppId());
        authQ.setTenantId(user.getTenantId());
        WxAuthorizerInfo weappInfo = authorizerInfoMapper.findMp(authQ);
        if (weappInfo != null) {
            AppUniformMsg appUniformMsg = new AppUniformMsg();
            appUniformMsg.setMsgType(EnumMsgRecordType.SMART_APP_UNIFORM.getCode());
            appUniformMsg.setAppId(user.getAppId());
            appUniformMsg.setTo(user.getOpenId());
            appUniformMsg.setTemplateType(EnumTemplateType.VERIFY_RESULT.getCode());
            appUniformMsg.setPagePath(gotopage);
            appUniformMsg.setMpAppId(weappInfo.getAuthorizerAppid());
            appUniformMsg.setDataList(Lists.newArrayList(
                    merchant.getName(),
                    EnumCouponType.getEnum(coupon.getType()).getMessage(),
                    couponOrder.getId().toString(),
                    dateFormat.format(new Date()),
                    user.getNickName()
            ));
            try {
                mqBaseProducer.sendMessage(appUniformMsg, EnumMsgMqTopic.DEFAULT.getCode(), EnumMsgMqTag.DEFAULT.getCode(), EnumMsgMqKey.DEFAULT.getCode());
            } catch (Exception e) {
                logger.error("核销-公众号-模板消息发送失败: " + e.getMessage());
                throw new MallinkException(ErrorCode.UNIFORM_SEND_FAILED);
            }
        }
    }

    private void doVerifySuccessMsg(WxCouponOrder couponOrder, String formId, WxCoupon coupon, WxMerchant merchant, WxCUser user, SimpleDateFormat dateFormat, String couponDetail, String gotopage) {
        SmartAppMsg smartAppMsg;
        /**
         * 核销成功消息
         * 核销金额 {{keyword1.DATA}}
         * 商家名称 {{keyword2.DATA}}
         * 商品    {{keyword3.DATA}}
         * 商品描述 {{keyword4.DATA}}
         * 订单金额 {{keyword5.DATA}}
         * 核销时间 {{keyword6.DATA}}
         * 温馨提示 {{keyword7.DATA}}
         */
        smartAppMsg = new SmartAppMsg();
        smartAppMsg.setMsgType(EnumMsgRecordType.SMART_APP.getCode());
        smartAppMsg.setAppId(user.getAppId());
        smartAppMsg.setTenantId(couponOrder.getTenantId());
        smartAppMsg.setTemplateType(EnumTemplateType.VERIFY_SUCCESS.getCode());
        smartAppMsg.setGotopage(gotopage);
        smartAppMsg.setTo(user.getOpenId());
        smartAppMsg.setFormId(formId);
        smartAppMsg.setDataList(Lists.newArrayList(
                coupon.getPriceStr(),
                merchant.getName(),
                coupon.getTitle(),
                couponDetail,
                coupon.getSalePriceStr(),
                dateFormat.format(new Date()),
                user.getNickName()
        ));

        try {
            mqBaseProducer.sendMessage(smartAppMsg, EnumMsgMqTopic.DEFAULT.getCode(), EnumMsgMqTag.DEFAULT.getCode(), EnumMsgMqKey.DEFAULT.getCode());
        } catch (Exception e) {
            logger.error("支付通知发送失败: " + e.getMessage());
            throw new MallinkException(ErrorCode.TEMPLATE_SEND_FAILED);
        }
    }

    private void doVerifyFailMsg(WxCouponOrder couponOrder, String formId, WxCoupon coupon, WxCUser user, SimpleDateFormat dateFormat, String couponDetail, String gotopage) {

        SmartAppMsg smartAppMsg; /**
         * 核销失败消息
         * 商品名称 {{keyword1.DATA}}
         * 核销时间 {{keyword2.DATA}}
         * 失败原因 {{keyword3.DATA}}
         * 备注 {{keyword4.DATA}}
         */
        smartAppMsg = new SmartAppMsg();
        smartAppMsg.setMsgType(EnumMsgRecordType.SMART_APP.getCode());
        smartAppMsg.setAppId(user.getAppId());
        smartAppMsg.setTenantId(couponOrder.getTenantId());
        smartAppMsg.setTemplateType(EnumTemplateType.VERIFY_FAIL.getCode());
        smartAppMsg.setGotopage(gotopage);
        smartAppMsg.setTo(user.getOpenId());
        smartAppMsg.setReceiver(user.getOpenId());
        smartAppMsg.setFormId(formId);
        smartAppMsg.setDataList(Lists.newArrayList(
                coupon.getTitle(),
                dateFormat.format(new Date()),
                "核销失败",
                user.getNickName()
        ));

        try {
            mqBaseProducer.sendMessage(smartAppMsg, EnumMsgMqTopic.DEFAULT.getCode(), EnumMsgMqTag.DEFAULT.getCode(), EnumMsgMqKey.DEFAULT.getCode());
        } catch (Exception e) {
            logger.error("支付通知发送失败: " + e.getMessage());
            throw new MallinkException(ErrorCode.TEMPLATE_SEND_FAILED);
        }
    }


    @Override
    public List<CUserDateAmountVo> queryPriceTotalGroup(String tenantId, Date startTime, Date endTime) {
        return wxCouponOrderMapper.queryPriceTotalGroup(tenantId, startTime, endTime);
    }

    @Override
    public int queryPriceTotal(WxCouponOrder wxCouponOrder) {
        return wxCouponOrderMapper.queryPriceTotal(wxCouponOrder);
    }


    @Override
    public ResultData listCardVoAsPage(WxCardCVo record, Integer pageIndex, Integer pageSize) {
        return new ResultData(PageHelper.startPage(pageIndex, pageSize).doSelectPageInfo(() -> wxCouponOrderMapper.findCardListOfCUser(record)));
    }

    @Override
    public ResultData cardDetailCUserVo(Long ownerId, String couponOrderId) {
        WxCUser wxCuser = wxCUserMapper.selectById(ownerId);
        if (wxCuser == null) {
            logger.error("用户不存在:" + ownerId);
            throw new MallinkException(ErrorCode.USER_IS_EMPTY);
        }

        WxCardCVo cardCVo = wxCouponOrderMapper.findCardDetailOfCUser(Long.valueOf(couponOrderId));
        Long couponId = cardCVo.getCouponId();
        WxCouponChannel wxCouponChannel = new WxCouponChannel();
        wxCouponChannel.setCouponId(couponId);
        List<WxCouponChannel> list = wxCouponChannelMapper.findList(wxCouponChannel);
        if (!list.isEmpty()) {
            cardCVo.setCouponChannelId(list.get(0).getId());
        }
        if (cardCVo == null)
            return new ResultData(ErrorCode.COUPON_ORDER_IS_NULL);
        return new ResultData(cardCVo);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ResultData cardAccept(WxCouponOrder record) {
        //卡券是否存在
        WxCardCVo cardCVo = wxCouponOrderMapper.findCardDetailOfCUser(record.getId());
        if (cardCVo == null) {
            logger.info("卡券不存在" + record.getId());
            return new ResultData(ErrorCode.COUPON_ORDER_IS_NULL);
        }
        //转赠人是否存在
        Long cUserId = record.getCUserId();
        WxCUser wxCuser = wxCUserMapper.selectById(cUserId);
        if (wxCuser == null) {
            logger.info("转赠人不存在:" + cUserId);
            return new ResultData(ErrorCode.USER_IS_EMPTY.getCode(), "转赠人不存在");
        }

        //判断转赠人与目前卡所有者是否为同一个
        //数据表中当前卡所有者
        Long ownerId = cardCVo.getOwnerId();
        if (!ownerId.equals(cUserId)) {
            logger.info("转赠人与目前卡所有者不相同," + "转赠人:" + cUserId + ",当前所有者:" + ownerId);
            return new ResultData(ErrorCode.CARD_TRANSFERED);
        }

        if (!record.getUpdateDate().equals(cardCVo.getUpdateDate())) {
            return new ResultData(ErrorCode.CARD_TRANSFER_INVALID);
        }

        //判断卡是否已消费
        //面额与余额是否相同
        if (!cardCVo.getAmount().equals(cardCVo.getRemainingAmount())) {
            logger.info("面额与余额不相同");
            return new ResultData(ErrorCode.CARD_TRANSFER_INVALID);
        }
        //保存到转赠表中 id,coupon_order_id,tenant_id,title,amount,remain_amount,from_user,to_user,status,create_date,update_date
        try {
            saveCardTransfer(record, cardCVo);
        } catch (Exception e) {
            logger.info("保存卡转赠信息失败");
            throw new MallinkException(ErrorCode.CARD_TRANSFER_INVALID);
        }
        //更新卡拥有者
        try {
            record.setCUserId(null);
            record.setUpdateDate(new Date());
            wxCouponOrderMapper.updateById(record);
        } catch (Exception e) {
            logger.info("更新卡拥有者信息失败");
            throw new MallinkException(ErrorCode.CARD_TRANSFER_INVALID);
        }
        return new ResultData(Result.SUCCESS, "领取成功");
    }

    @Override
    public ResultData queryCardStatus(WxCouponOrder record) {
        //卡券是否存在
        WxCardCVo cardCVo = wxCouponOrderMapper.findCardDetailOfCUser(record.getId());
        if (cardCVo == null) {
            logger.info("卡券不存在" + record.getId());
            return new ResultData(ErrorCode.COUPON_ORDER_IS_NULL);
        }
        //转赠人是否存在
        Long cUserId = record.getCUserId();
        WxCUser wxCuser = wxCUserMapper.selectById(cUserId);
        if (wxCuser == null) {
            logger.info("转赠人不存在:" + cUserId);
            return new ResultData(ErrorCode.USER_IS_EMPTY.getCode(), "转赠人不存在");
        }
        //判断转赠人与目前卡所有者是否为同一个
        //数据表中当前卡所有者
        Long ownerId = cardCVo.getOwnerId();
        if (!ownerId.equals(cUserId)) {
            logger.info("转赠人与目前卡所有者不相同," + "转赠人:" + cUserId + ",当前所有者:" + ownerId);
            return new ResultData(ErrorCode.CARD_TRANSFERED);
        }

        if (!record.getUpdateDate().equals(cardCVo.getUpdateDate())) {
            return new ResultData(ErrorCode.CARD_TRANSFER_INVALID);
        }

        //判断卡是否已消费
        //面额与余额是否相同
        if (!cardCVo.getAmount().equals(cardCVo.getRemainingAmount())) {
            logger.info("面额与余额不相同");
            return new ResultData(ErrorCode.CARD_TRANSFER_INVALID);
        }
        return new ResultData(Result.SUCCESS, "查询成功");
    }

    public void saveCardTransfer(WxCouponOrder couponOrder, WxCardCVo cardCVo) {
        logger.info("保存卡转赠信息");
        WxCardTransferInfo wxCardTransferInfo = new WxCardTransferInfo();
        final IdWorker idWorker = IdWorker.get();
        wxCardTransferInfo.setId(idWorker.nextId());
        wxCardTransferInfo.setTenantId(cardCVo.getTenantId());
        wxCardTransferInfo.setCouponOrderId(couponOrder.getId());
        wxCardTransferInfo.setFromUser(couponOrder.getCUserId());
        wxCardTransferInfo.setToUser(couponOrder.getOwnerId());
        wxCardTransferInfo.setAmount(cardCVo.getAmount());
        wxCardTransferInfo.setRemainAmount(cardCVo.getRemainingAmount());
        wxCardTransferInfo.setStatus(EnumCouponOrderStatus.CARD_TRANSFER.getCode());
        wxCardTransferInfo.setTitle(cardCVo.getTitle());
        wxCardTransferInfo.setExpiredTime(cardCVo.getExpiredTime());
        Date date = new Date();
        wxCardTransferInfo.setCreateDate(date);
        wxCardTransferInfo.setUpdateDate(date);
        wxCardTransferInfoMapper.insert(wxCardTransferInfo);
    }

    @Override
    public List<WxCouponOrderCVo> queryMicroPayCouponOrder(WxMerchantBUser user, WxCUser cUser, Integer payPrice) {
        Map<String, Object> coQ = new HashMap<>();
        coQ.put("tenantId", user.getTenantId());
        coQ.put("cUserId", user.getId());
        coQ.put("merchantId", user.getMerchantId());
        List<WxCouponOrderCVo> avaCoList = wxCouponOrderMapper.findAvailCouponOrder(coQ);
        List<WxCouponOrderCVo> avaCouponOrderList = avaCoList.stream().filter(couponOrderCVo -> {
            if (couponOrderCVo.getType().equals(EnumCouponType.COUPON_MANJIAN.getCode())) {
                if (payPrice > couponOrderCVo.getUsePrice()) {
                    return true;
                }
            } else {
                return true;
            }
            return false;
        }).collect(Collectors.toList());
        return avaCouponOrderList;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class})
    public Integer microPayVerify(WxOrder microOrder, WxCouponOrder couponOrder, WxMerchantBUser bUser, Integer payPrice) {

        // 1. 获取券信息
        WxCoupon wxCoupon = wxCouponMapper.selectById(couponOrder.getCouponId());
        if (wxCoupon == null) {
            logger.error("券: coupon-" + wxCoupon.getId() + "不存在" + "couponOrder-" + couponOrder.getId());
            throw new MallinkException(ErrorCode.COUPON_ORDER_IS_NULL);
        }
        if (!(wxCoupon.getType().equals(EnumCouponType.COUPON_MANJIAN.getCode()) ||
                wxCoupon.getType().equals(EnumCouponType.COUPON_DAIJIN.getCode()) ||
                        wxCoupon.getType().equals(EnumCouponType.COUPON_MULTIMCH.getCode()))) {
            logger.error("收银台不支持此券类型: " + wxCoupon.getType());
            throw new MallinkException(ErrorCode.COUPON_ORDER_TYPE_NOT_SUPPORTED);
        }

        // 2. 检查适用门店
        WxCouponMerchant wxCouponMerchant = new WxCouponMerchant();
        wxCouponMerchant.setProductId(wxCoupon.getId());
        wxCouponMerchant.setStatus(EnumCouponMerchantStatus.COUPON_MERCHANT_STATUS_VALID.getCode());
        List<WxCouponMerchant> couponMerchantList = wxCouponMerchantMapper.findList(wxCouponMerchant);
        if (!couponMerchantList.stream().anyMatch(cm -> cm.getMerchantId().equals(bUser.getMerchantId()))) {
            logger.error("券: coupon-" + wxCoupon.getId() + "核销: couponMerchantId-" + bUser.getMerchantId());
            throw new MallinkException(ErrorCode.VERIFY_COUPON_ORDER_MERCHANT_IS_NULL);
        }

        // 3. 券包状态检查
        if (couponOrder.getCouponOrderStatus() == EnumCouponOrderStatus.COUPON_ORDER_OVER_TIME.getCode()) {
            logger.error("已过期: couponOrder-" + couponOrder.getId());
            throw new MallinkException(ErrorCode.COUPON_ORDER_IS_OVER_TIME);
        }
        if (couponOrder.getCouponOrderStatus() == EnumCouponOrderStatus.COUPON_ORDER_INVALID.getCode()) {
            logger.error("已退款: couponOrder-" + couponOrder.getId());
            throw new MallinkException(ErrorCode.COUPON_ORDER_IS_INVALID);
        }
        if (couponOrder.getCouponOrderStatus() == EnumCouponOrderStatus.COUPON_ORDER_USED.getCode()) {
            logger.error("已经核销过的券: couponOrder-" + couponOrder.getId());
            throw new MallinkException(ErrorCode.COUPON_ORDER_IS_USED);
        }
        if (couponOrder.getCouponOrderStatus() == EnumCouponOrderStatus.POS_PRE_VERIFY.getCode()) {
            logger.error("券已被预核销: couponOrder-" + couponOrder.getId());
            throw new MallinkException(ErrorCode.COUPON_ORDER_IS_PRE_VERIFY);
        }
        if (couponOrder.getCouponType().equals(EnumCouponType.COUPON_MANJIAN.getCode())) {
            if (payPrice < wxCoupon.getUsePrice()) {
                logger.error("券达不到满减条件: couponOrder-" + couponOrder.getId());
                throw new MallinkException(ErrorCode.COUPON_ORDER_IS_PRE_VERIFY);
            }
        }

        // 1. insert posOrderId
        PosCouponOrderVerify posCouponOrderVerify = new PosCouponOrderVerify();
        posCouponOrderVerify.setTenantId(couponOrder.getTenantId());
        posCouponOrderVerify.setCouponOrderId(couponOrder.getId());
        posCouponOrderVerify.setPosOrderId(microOrder.getId());
        posCouponOrderVerify.setOrderFrom(EnumOrderFrom.INSIDE.getCode());
        posCouponOrderVerify.setState(EnumEnableType.Enable.getCode());
        try {
            posCouponOrderVerifyMapper.insert(posCouponOrderVerify);
        } catch (Exception e) {
            logger.error("db failed: couponOrder-" + couponOrder.getId() + ", e:" + e.getMessage());
            throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "DB FAILD " + e.getMessage());
        }

        int num = 0;
        try {
            couponOrder.setCouponOrderStatus(EnumCouponOrderStatus.COUPON_ORDER_USED.getCode()); //1核销
            couponOrder.setBUserId(bUser.getId());
            couponOrder.setVerifyType(EnumCouponVerifyType.VERIFY_B.getCode());
            couponOrder.setUpdateDate(new Date());
            num = wxCouponOrderMapper.updateVerifyCouponOrder(couponOrder);
        } catch (Exception e) {
            logger.error("db failed: couponOrder-" + couponOrder.getId() + ", e:" + e.getMessage());
            throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "DB FAILD " + e.getMessage());
        }
        if (num != 1) {
            logger.error("核销异常：updatete num " + num);
            throw new MallinkException(ErrorCode.VERIFY_ERROR.getCode(), "updatete num " + num);
        }

        // 添加payOrder
        WxPayOrder payOrder = new WxPayOrder() {{
            Date curDate = new Date();
            final IdWorker idWorker = IdWorker.get();
            Long id = idWorker.nextId();
            setId(id);
            setTenantId(microOrder.getTenantId());
            setCUserId(microOrder.getCUserId());
            setOrderId(microOrder.getId());
            setCreateTime(curDate);
            setUpdateTime(curDate);
            setPayTimeStart(curDate);
            setPayTimeEnd(curDate);
            setPayOrderNo(String.valueOf(id));
            setPayAmount(wxCoupon.getPrice());
            setPayVendor(EnumPayWay.PAY_WAY_WEAPP.getCode());
            setPayOrderStatus(EnumPayStatus.PAY_STATUS_SUCCESS.getCode());
            setShare(EnumPayShare.NO.getCode());
            setPayFrom(EnumPayFrom.INSIDE_B.getCode());
        }};

        try {
            payOrderMapper.insert(payOrder);
        } catch (Exception e) {
            logger.error("payOrder error");
        }

        Integer remainPrice = payPrice - wxCoupon.getPrice();
        if (remainPrice < 0) {
            remainPrice = 0;
        }

        WxOrder updateOrder = new WxOrder();
        updateOrder.setId(microOrder.getId());
        updateOrder.setRealPayment(remainPrice);
        wxOrderMapper.updateById(updateOrder);

        // 成长值
        try {
            WxOrder orderByCoupon = wxOrderMapper.selectById(couponOrder.getOrderId());
            wxScoreRulesService.addScore2(EnumScoreType.CONSUMPTION, orderByCoupon, wxCoupon.getBusiness());
        } catch (Exception e) {
            logger.error("成长值:" + e.getMessage());
        }

        // 积分
        try {
            //-------此处为【现金支付】记录增加积分操作-------
            WxCreditHistory creditHistory = new WxCreditHistory();
            creditHistory.setOperatorType(EnumUserType.BUSER.getCode());
            creditHistory.setOperatorId(bUser.getId());
            creditHistory.setCUserId(couponOrder.getcUserId());
            creditHistory.setCreateDate(new Date());
            creditHistory.setTenantId(couponOrder.getTenantId());
            creditHistory.setCreditType(EnumScoreType.CONSUMPTION.getCode());
            creditHistory.setCouponId(wxCoupon.getId());
            creditHistory.setBusinessId(wxCoupon.getBusiness());
            creditHistory.setSpend(couponOrder.getCouponPrice());
            //如果券与商户一对一 则直接将消费商户更新为此商户 若一对多 则消费商户显示多商户
            creditHistory.setMerchantId(bUser.getMerchantId());
            creditHistory.setBuserId(bUser.getBuserId());
            wxCreditHistoryService.saveOrUpdate(creditHistory);
        } catch (Exception e) {
            logger.error("积分值:" + e.getMessage());
        }

        return remainPrice;
    }

}
