package com.cpop.jambox.business.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.cpop.api.cloudDb.core.dto.CloudClassCardDto;
import com.cpop.api.cloudDb.handler.CloudClassCardHandler;
import com.cpop.api.tencent.wxWork.handler.WebHookSendHandler;
import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.bean.BeanUtils;
import com.cpop.common.utils.http.HttpUtils;
import com.cpop.common.utils.ip.IpUtils;
import com.cpop.core.base.entity.PageDomain;
import com.cpop.core.base.enums.OrderSource;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.service.RedisService;
import com.cpop.core.utils.SecurityUtils;
import com.cpop.core.utils.SpringUtils;
import com.cpop.core.utils.sql.SqlUtils;
import com.cpop.jambox.business.bo.*;
import com.cpop.jambox.business.dto.EasyLearnPageDto;
import com.cpop.jambox.business.entity.*;
import com.cpop.jambox.business.mapper.EasyLearnOrderMapper;
import com.cpop.jambox.business.service.*;
import com.cpop.jambox.business.vo.EasyLearnPageVo;
import com.cpop.jambox.framework.constant.JamboxCloudUrl;
import com.cpop.jambox.framework.constant.JamboxRedisConstant;
import com.cpop.jambox.framework.enums.EasyLearnPayPayEnum;
import com.cpop.pay.framewok.config.wxPay.WxPayConfiguration;
import com.cpop.pay.framewok.core.request.ecpp.EcppSmsCodeRequest;
import com.cpop.pay.framewok.core.response.ecpp.EcppSmsCodeResponse;
import com.cpop.pay.framewok.handler.ecpp.EcppHandler;
import com.cpop.pay.framewok.handler.wxPay.WxPayHandler;
import com.cpop.pay.framewok.handler.wxPay.WxPayNormalHandler;
import com.cpop.pay.framewok.handler.wxPay.WxUtils;
import com.cpop.pay.framewok.task.WxPayAsyncTask;
import com.cpop.system.business.entity.Store;
import com.cpop.system.business.entity.WxPayScore;
import com.cpop.system.business.entity.WxPayScoreDetail;
import com.cpop.system.business.service.StoreService;
import com.cpop.system.business.service.WxPayScoreDetailService;
import com.cpop.system.business.service.WxPayScoreService;
import com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.payscore.*;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.PartnerPayScoreSignPlanService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.datasource.DataSourceKey;
import com.mybatisflex.core.datasource.FlexDataSource;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.Db;
import com.mybatisflex.core.row.DbChain;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.row.RowUtil;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.locks.Lock;

import static com.cpop.jambox.business.entity.table.BrandExtendTableDef.BRAND_EXTEND;
import static com.cpop.jambox.business.entity.table.EasyLearnOrderDetailTableDef.EASY_LEARN_ORDER_DETAIL;
import static com.cpop.jambox.business.entity.table.EasyLearnOrderExtendTableDef.EASY_LEARN_ORDER_EXTEND;
import static com.cpop.jambox.business.entity.table.EasyLearnOrderTableDef.EASY_LEARN_ORDER;
import static com.cpop.jambox.business.entity.table.StoreExtendTableDef.STORE_EXTEND;
import static com.cpop.system.business.entity.table.StoreTableDef.STORE;
import static com.cpop.system.business.entity.table.WxPayScoreDetailTableDef.WX_PAY_SCORE_DETAIL;
import static com.cpop.system.business.entity.table.WxPayScoreTableDef.WX_PAY_SCORE;

/**
 * 果酱-放心学订单表 服务层实现。
 *
 * @author DB
 * @since 2023-12-14
 */
@Service("easyLearnOrderService")
@Slf4j
public class EasyLearnOrderServiceImpl extends ServiceImpl<EasyLearnOrderMapper, EasyLearnOrder> implements EasyLearnOrderService {

    @Autowired
    private WxPayHandler wxPayHandler;

    @Autowired
    private WxPayAsyncTask wxPayAsyncTask;

    @Autowired
    private WxPayConfiguration wxPayConfiguration;

    /**
     * 一次性支付回调地址（本地内网穿透）
     */
    private final String ONCE_PAY_DEV_NOTIFY_URL = "https://frp-bid.top:60778/Cpop-Oam/callback/easyLearn/notify/oncePay";

    /**
     * 一次性支付回调地址（测试地址）
     */
    private final String ONCE_PAY_TEST_NOTIFY_URL = "https://test.cpopsz.com/Cpop-Oam/callback/easyLearn/notify/oncePay";

    /**
     * 获取放心学分页
     *
     * @param bo 查询参数
     * @return Page<EasyLearnPageVo>
     * @author DB
     * @since 2023/12/15
     */
    @Override
    public Page<EasyLearnPageVo> getEasyLearnPage(EasyLearnPageBo bo) {
        //TODO: 后续数据录入后，应该查询新版中的数据
        return getOldEasyLearnPage(bo);
    }

    /**
     * 导出放心学数据
     *
     * @param bo 请求参数
     * @return List<EasyLearnPageDto>
     * @author DB
     * @since 2023/12/15
     */
    @Override
    public List<EasyLearnPageDto> getEasyLearnXml(EasyLearnPageBo bo) {
        //TODO: 后续数据录入后，应该查询新版中的数据
        return getOldEasyLearnXml(bo);
    }

    /**
     * 获取旧数据
     *
     * @param bo 请求参数
     * @return List<EasyLearnPageDto>
     * @author DB
     * @since 2023/12/15
     */
    private List<EasyLearnPageDto> getOldEasyLearnXml(EasyLearnPageBo bo) {
        //TODO: 暂时不处理顾问
        try {
            getJamboxProdDataSource();
            switch (bo.getOrderType()) {
                // 先学后付
                case 0:
                    try {
                        DataSourceKey.use("jamboxProd");
                        return RowUtil.toEntityList(Db.selectListByQuery(getLearnNowPayLaterData(false, bo)), EasyLearnPageDto.class);
                    } finally {
                        DataSourceKey.clear();
                    }
                case 1:
                    try {
                        DataSourceKey.use("jamboxProd");
                        return RowUtil.toEntityList(Db.selectListByQuery(getLearnNowPayLaterData(true, bo)), EasyLearnPageDto.class);
                    } finally {
                        DataSourceKey.clear();
                    }
                case 2:
                    return new ArrayList<EasyLearnPageDto>();
                // 数币分页
                case 3:
                    try {
                        DataSourceKey.use("jamboxProd");
                        return RowUtil.toEntityList(Db.selectListByQuery(getEcppData(bo)), EasyLearnPageDto.class);
                    } finally {
                        DataSourceKey.clear();
                    }
                case 4:
                    try {
                        //使用旧库
                        DataSourceKey.use("jamboxProd");
                        return RowUtil.toEntityList(Db.selectListByQuery(getMemberData(bo)), EasyLearnPageDto.class);
                    } finally {
                        DataSourceKey.clear();
                    }
                default:
                    return null;
            }
        } finally {
            removeJamboxProdDataSource();
        }
    }

    /**
     * 获取旧数据分页
     *
     * @param bo 请求参数
     * @return Page<EasyLearnPageVo>
     * @author DB
     * @since 2023/12/15
     */
    private Page<EasyLearnPageVo> getOldEasyLearnPage(EasyLearnPageBo bo) {
        //TODO: 暂时不处理顾问
        try {
            getJamboxProdDataSource();
            PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
            Page<Row> page = Page.of(pageDomain.getPageNum(), pageDomain.getPageSize());
            page.setOptimizeCountQuery(false);
            switch (bo.getOrderType()) {
                // 先学后付
                case 0:
                    try {
                        DataSourceKey.use("jamboxProd");
                        return Db.paginate("j_commodity_ticket", page, getLearnNowPayLaterData(false, bo)).map(item -> item.toEntity(EasyLearnPageVo.class));
                    } finally {
                        DataSourceKey.clear();
                    }
                case 1:
                    try {
                        DataSourceKey.use("jamboxProd");
                        return Db.paginate("j_commodity_ticket", page, getLearnNowPayLaterData(true, bo)).map(item -> item.toEntity(EasyLearnPageVo.class));
                    } finally {
                        DataSourceKey.clear();
                    }
                case 2:
                    return Page.of(1, 10, 0);
                // 数币分页
                case 3:
                    try {
                        DataSourceKey.use("jamboxProd");
                        return Db.paginate("j_commodity_ticket", page, getEcppData(bo)).map(item -> item.toEntity(EasyLearnPageVo.class));
                    } finally {
                        DataSourceKey.clear();
                    }
                case 4:
                    try {
                        //使用旧库
                        DataSourceKey.use("jamboxProd");
                        return Db.paginate("j_commodity_ticket", page, getMemberData(bo)).map(item -> item.toEntity(EasyLearnPageVo.class));
                    } finally {
                        DataSourceKey.clear();
                    }
                default:
                    return null;
            }
        } finally {
            removeJamboxProdDataSource();
        }
    }

    private QueryWrapper getMemberData(EasyLearnPageBo bo) {
        QueryWrapper queryWrapper = QueryWrapper.create();
        queryWrapper
                //查询品牌校区
                .select("tbi.name as brandName")
                .select("jmt.ticket_id as id", "jmt.wx_order_id as outOrderNo", "jmt.name as customerName", "jmt.price as totalAmount", "jmt.price as totalPayAmount", "jmt.phone as customerPhone", "jmt.creation_time as createTime")
                .from("j_membership_ticket").as("jmt")
                //品牌
                .leftJoin("t_brand_info").as("tbi").on("tbi.id = jmt.brand_id")
                .where("jmt.deleted = 1")
                .and("jmt.pay_status = 'PAY'")
                .groupBy("jmt.ticket_id")
                .orderBy("jmt.creation_time desc");
        //开始时间结束时间
        if (bo.getStartDate() != null && bo.getEndDate() != null) {
            queryWrapper.and("jmt.creation_time between ? and ?", bo.getStartDate(), bo.getEndDate());
        }
        //品牌校区模糊查询
        if (StringUtils.isNotBlank(bo.getBrandOrStore())) {
            queryWrapper.and("tbi.name like ?", "%" + bo.getBrandOrStore() + "%");
        }
        return queryWrapper;
    }


    /**
     * 获取数币数据
     *
     * @return Page<EasyLearnPageVo>
     * @author DB
     * @since 2023/12/15
     */
    private QueryWrapper getEcppData(EasyLearnPageBo bo) {
        QueryWrapper queryWrapper = QueryWrapper.create();
        queryWrapper
                //查询品牌校区
                .select("tbi.name as brandName", "tmi.mechanism as storeName")
                .select("jct.ticket_id as id", "jct.wx_order_id as outOrderNo", "jct.name as customerName", "jct.price as totalAmount", "jct.price as totalPayAmount", "jct.phone as customerPhone", "jct.creation_time as createTime")
                .select("tct.name as productName")
                .from("j_commodity_ticket").as("jct")
                //品牌
                .leftJoin("t_brand_info").as("tbi").on("tbi.id = jct.brand_id")
                //校区
                .leftJoin("t_mechanism_info").as("tmi").on("tmi.store_id = jct.store_id")
                //模板
                .leftJoin("t_card_template").as("tct").on("tct.template_id = jct.commodity_id")
                .where("jct.deleted = 1")
                .and("jct.pay_status = 'PAY'")
                .and("jct.is_suda = 1")
                .groupBy("jct.ticket_id")
                .orderBy("jct.creation_time desc");
        //开始时间结束时间
        if (bo.getStartDate() != null && bo.getEndDate() != null) {
            queryWrapper.and("jct.creation_time between ? and ?", bo.getStartDate(), bo.getEndDate());
        }
        //品牌校区模糊查询
        if (StringUtils.isNotBlank(bo.getBrandOrStore())) {
            queryWrapper.and("tbi.name like ?", "%" + bo.getBrandOrStore() + "%").or("tmi.mechanism like ?", "%" + bo.getBrandOrStore() + "%");
        }
        if (StringUtils.isNotBlank(bo.getStaffName())) {
            queryWrapper.and("jct.staff_name like ?", "%" + bo.getStaffName() + "%");
        }
        return queryWrapper;
    }

    /**
     * 获取先学后付数据
     *
     * @param isMonth 是否是月付
     * @return Page<EasyLearnPageVo>
     * @author DB
     * @since 2023/12/15
     */
    private QueryWrapper getLearnNowPayLaterData(Boolean isMonth, EasyLearnPageBo bo) {
        QueryWrapper queryWrapper = QueryWrapper.create()
                //查询品牌校区
                .select("tbi.name as brandName", "tmi.mechanism as storeName")
                .select("jct.ticket_id as id", "jct.wx_order_id as outOrderNo", "jct.name as customerName", "jct.price as totalAmount", "jct.price as totalPayAmount", "jct.phone as customerPhone", "jct.creation_time as createTime")
                .select("tct.name as productName")
                .from("j_commodity_ticket").as("jct")
                //品牌
                .leftJoin("t_brand_info").as("tbi").on("tbi.id = jct.brand_id")
                //校区
                .leftJoin("t_mechanism_info").as("tmi").on("tmi.store_id = jct.store_id")
                //模板
                .leftJoin("t_card_template").as("tct").on("tct.template_id = jct.commodity_id")
                //微信支付
                .leftJoin("j_wechat_pay_plan").as("jwpp").on("jwpp.template_id = jct.commodity_id")
                .where("jct.deleted = 1")
                .and("jct.pay_status = 'PAY'")
                .and("jct.ticket_type = 1")
                .and("jwpp.is_month = ?", isMonth)
                .groupBy("jct.ticket_id")
                .orderBy("jct.creation_time desc");
        //开始时间结束时间
        if (bo.getStartDate() != null && bo.getEndDate() != null) {
            queryWrapper.and("jct.creation_time between ? and ?", bo.getStartDate(), bo.getEndDate());
        }
        //品牌校区模糊查询
        if (StringUtils.isNotBlank(bo.getBrandOrStore())) {
            queryWrapper.and("tbi.name like ?", "%" + bo.getBrandOrStore() + "%").or("tmi.mechanism like ?", "%" + bo.getBrandOrStore() + "%");
        }
        if (StringUtils.isNotBlank(bo.getStaffName())) {
            queryWrapper.and("jct.staff_name like ?", "%" + bo.getStaffName() + "%");
        }
        return queryWrapper;
    }

    private static final String URL = "jdbc:mysql://sh-cynosdbmysql-grp-fggo83js.sql.tencentcdb.com:20965/jambox_association?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8";
    /**
     * 数据库用户名
     */
    private static final String USERNAME = "root";
    /**
     * 数据库密码
     */
    private static final String PASSWORD = "Customer0401";

    @Deprecated
    private void getJamboxProdDataSource() {
        FlexDataSource flexDataSource = FlexGlobalConfig.getDefaultConfig().getDataSource();
        //新的数据源
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl(URL);
        dataSource.setUsername(USERNAME);
        dataSource.setPassword(PASSWORD);
        flexDataSource.addDataSource("jamboxProd", dataSource);
    }

    @Deprecated
    private void removeJamboxProdDataSource() {
        FlexDataSource flexDataSource = FlexGlobalConfig.getDefaultConfig().getDataSource();
        flexDataSource.removeDatasource("jamboxProd");
    }

    /**
     * 放心学一次性订单通知
     * @author DB
     * @since 2023/12/21
     * @param xmlData 数据
     */
    @Override
    public void oncePayResult(String xmlData) {
        try {
            WxPayService wxPayService = wxPayHandler.getWxPayService();
            WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);
            if (!StringUtils.equals(notifyResult.getResultCode(), "SUCCESS")) {
                throw new ServiceException(notifyResult.getReturnMsg());
            }
            String orderId = notifyResult.getOutTradeNo();
            //获取订单信息
            EasyLearnOrder easyLearnOrder = this.getById(orderId);
            //需要分账
            String subMchId = wxPayHandler.getSubMchId(easyLearnOrder.getBrandId(), easyLearnOrder.getStoreId());
            if (notifyResult.getTotalFee() >= Math.ceil(1 / OrderSource.EASY_LEARN.getRate())) {
                //设置子商户
                WxPayConfig config = wxPayService.getConfig();
                config.setSubMchId(subMchId);
                wxPayAsyncTask.asyncWxPayProfitSharing(orderId, notifyResult, wxPayService, OrderSource.EASY_LEARN);
            }
            //课卡信息
            JSONObject jsonBody = new JSONObject();
            jsonBody.put("_type", "addPeriod");
            //办卡实收金额
            jsonBody.put("money", easyLearnOrder.getTotalPayAmount());
            //获取云校区id
            StoreExtend storeExtend = SpringUtils.getBean(StoreExtendService.class).getOne(QueryWrapper.create().where(STORE_EXTEND.STORE_ID.eq(easyLearnOrder.getStoreId())));
            //店铺/校区
            jsonBody.put("storeId", storeExtend.getStoreCloudId());
            //手机号
            jsonBody.put("phone", easyLearnOrder.getCustomerPhone());
            //客户名称
            jsonBody.put("customerName", easyLearnOrder.getCustomerName());
            //模板id
            jsonBody.put("templateId", easyLearnOrder.getOrderCloudId());
            //订单来源
            jsonBody.put("orderSource", OrderSource.EASY_LEARN.toString());
            //订单来源
            jsonBody.put("subMchId", subMchId);
            //获取课卡信息
            JSONObject result = SpringUtils.getBean(RestTemplate.class).postForObject(JamboxCloudUrl.COMMON_CARD_URL, jsonBody, JSONObject.class);
            if (result == null){
                throw new ServiceException("放心学一次性支付办卡失败!");
            }
            //更新订单
            this.updateChain().set(EASY_LEARN_ORDER.OUT_ORDER_NO, notifyResult.getTransactionId())
                    .set(EASY_LEARN_ORDER.PRODUCT_ID, result.getString("data"))
                    .set(EASY_LEARN_ORDER.ORDER_STATUS, 1)
                    .where(EASY_LEARN_ORDER.ID.eq(orderId)).update();
        } catch (WxPayException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 一次性支付下单
     * @author DB
     * @since  2023/10/23 12:15
     * @param bo 下单请求对象
     */
    @Override
    public Object oncePlaceOrder(OncePlaceOrderBo bo) {
        //分布式锁进行幂等处理
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        String openId;
        if (StringUtils.isBlank(bo.getOpenId())){
            //获取当前下单用户信息
            JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
            openId = loginUserInfo.getString("openId");
        }else {
            openId = bo.getOpenId();
        }
        //分布式锁进行幂等处理
        Lock userIdLock = redisService.distributedLock(JamboxRedisConstant.ONCE_PAY_LOCK_USER_PAY + bo.getCustomerPhone());
        if (userIdLock.tryLock()) {
            try {
                //获取品牌
                BrandExtend brandExtend = SpringUtils.getBean(BrandExtendService.class).queryChain().where(BRAND_EXTEND.BRAND_CLOUD_ID.eq(bo.getBrandCloudId())).one();
                //获取校区
                StoreExtend storeExtend = SpringUtils.getBean(StoreExtendService.class).queryChain().where(STORE_EXTEND.STORE_CLOUD_ID.eq(bo.getStoreCloudId())).one();
                EasyLearnOrder easyLearnOrder = this.getOne(QueryWrapper.create().where(EASY_LEARN_ORDER.ORDER_CLOUD_ID.eq(bo.getOrderCloudId())));
                if (easyLearnOrder == null) {
                    //创建订单
                    easyLearnOrder = BeanUtils.mapToClass(bo, EasyLearnOrder.class);
                    easyLearnOrder.setOrderType(2).setBrandId(brandExtend.getBrandId()).setStoreId(storeExtend.getStoreId()).setOrderContent(bo.getOrderContent());
                    //需要分账
                    if (bo.getTotalAmount().scaleByPowerOfTen(2).intValue() >= Math.ceil(1 / OrderSource.EASY_LEARN.getRate())) {
                        easyLearnOrder.setRate(OrderSource.EASY_LEARN.getRate());
                    }
                    this.save(easyLearnOrder);
                } else {
                    if (easyLearnOrder.getOrderStatus() == 1){
                        throw new ServiceException("当前订单已支付,请勿重复支付");
                    }
                }
                String payNotifyUrl;
                if (StringUtils.equals("prod", SpringUtils.getActiveProfile())) {
                    //一次性支付
                    payNotifyUrl = SpringUtils.getBean(WxPayConfiguration.class).getProperties().getEasyLearnOncePayNotifyUrl();
                } else if (StringUtils.equals("test", SpringUtils.getActiveProfile())){
                    payNotifyUrl = ONCE_PAY_TEST_NOTIFY_URL;
                } else {
                    payNotifyUrl = ONCE_PAY_DEV_NOTIFY_URL;
                }
                //获取商户信息
                WxPayService wxPayService = wxPayHandler.getWxPayService(null, wxPayHandler.getSubMchId(brandExtend.getBrandId(), storeExtend.getStoreId()), payNotifyUrl);
                WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
                if (easyLearnOrder.getRate() != null) {
                    //需要分账
                    orderRequest.setProfitSharing("Y");
                }
                orderRequest.setSpbillCreateIp(IpUtils.getHostIp())
                        .setOpenid(openId)
                        //商品描述
                        .setBody("一次性支付")
                        .setOutTradeNo(easyLearnOrder.getId())
                        //元转分
                        .setTotalFee(easyLearnOrder.getTotalAmount().scaleByPowerOfTen(2).intValue())
                        .setTradeType("JSAPI");
                return wxPayService.createOrder(orderRequest);
            } catch (Exception e) {
                throw new ServiceException(e.getMessage());
            } finally {
                //释放锁
                userIdLock.unlock();
            }
        } else {
            //获取锁失败，直接返回空
            throw new ServiceException("请勿重复支付");
        }
    }

    /**
     * 联合支付回调地址（本地内网穿透）
     */
    private final String UNION_PAY_DEV_NOTIFY_URL = "https://frp-bid.top:60778/Cpop-Oam/callback/easyLearn/notify/unionPay";

    /**
     * 联合支付回调地址（测试地址）
     */
    private final String UNION_PAY_TEST_NOTIFY_URL = "https://test.cpopsz.com/Cpop-Oam/callback/easyLearn/notify/unionPay";

    /**
     * 联合支付回调地址（本地内网穿透）
     */
    private final String UNION_PAY_DEV_NORMAL_NOTIFY_URL = "https://frp-bid.top:60778/Cpop-Oam/callback/easyLearn/notify/normalUnionPay";

    /**
     * 联合支付回调地址（测试地址）
     */
    private final String UNION_PAY_TEST_NORMAL_NOTIFY_URL = "https://test.cpopsz.com/Cpop-Oam/callback/easyLearn/notify/normalUnionPay";

    /**
     * 放心学统一支付
     * @author DB
     * @since 2023/12/28
     * @param bo 请求参数
     * @return R<Object>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object unionPay(EasyLearnUnionPayBo bo) {
        //分布式锁进行幂等处理
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        //分布式锁进行幂等处理
        Lock userIdLock = redisService.distributedLock(JamboxRedisConstant.ONCE_PAY_LOCK_USER_PAY + bo.getCustomerPhone());
        if (userIdLock.tryLock()) {
            WxPayService wxPayService = null;
            try {
                //创建订单
                EasyLearnOrder easyLearnOrder = BeanUtils.mapToClass(bo, EasyLearnOrder.class);
                //获取校区
                StoreExtend storeExtend = SpringUtils.getBean(StoreExtendService.class).queryChain().where(STORE_EXTEND.STORE_CLOUD_ID.eq(bo.getBrandOrStoreCloudId())).one();
                //获取品牌
                BrandExtend brandExtend;
                if (storeExtend == null) {
                    brandExtend = SpringUtils.getBean(BrandExtendService.class).queryChain().where(BRAND_EXTEND.BRAND_CLOUD_ID.eq(bo.getBrandOrStoreCloudId())).one();
                    easyLearnOrder.setBrandId(brandExtend.getBrandId());
                } else {
                    //获取品牌
                    Store store = SpringUtils.getBean(StoreService.class).getById(storeExtend.getStoreId());
                    easyLearnOrder.setBrandId(store.getBrandId());
                    easyLearnOrder.setStoreId(storeExtend.getStoreId());
                }
                EasyLearnPayPayEnum easyLearnPayPayEnum = EasyLearnPayPayEnum.valueOf(bo.getEasyLearnUnionPay());
                easyLearnOrder.setOrderType(easyLearnPayPayEnum.getOrderType()).setOrderContent(bo.getOrderContent());
                this.save(easyLearnOrder);
                String payNotifyUrl;
                if (StringUtils.equals("prod", SpringUtils.getActiveProfile())) {
                    //联合支付
                    payNotifyUrl = SpringUtils.getBean(WxPayConfiguration.class).getProperties().getEasyLearnUnionPayNotifyUrl();
                } else if (StringUtils.equals("test", SpringUtils.getActiveProfile())){
                    payNotifyUrl = UNION_PAY_TEST_NOTIFY_URL;
                } else {
                    payNotifyUrl = UNION_PAY_DEV_NOTIFY_URL;
                }
                //获取商户信息
                wxPayService = wxPayHandler.getWxPayService(null, wxPayHandler.getSubMchId(easyLearnOrder.getBrandId(), easyLearnOrder.getStoreId()), payNotifyUrl);
                if (StringUtils.isBlank(wxPayService.getConfig().getSubMchId())){
                    if (StringUtils.equals("prod", SpringUtils.getActiveProfile())) {
                        //联合支付
                        payNotifyUrl = SpringUtils.getBean(WxPayConfiguration.class).getProperties().getEasyLearnUnionPayNormalNotifyUrl();
                    } else if (StringUtils.equals("test", SpringUtils.getActiveProfile())){
                        payNotifyUrl = UNION_PAY_TEST_NORMAL_NOTIFY_URL;
                    } else {
                        payNotifyUrl = UNION_PAY_DEV_NORMAL_NOTIFY_URL;
                    }
                    return SpringUtils.getBean(WxPayNormalHandler.class).createOrder(easyLearnOrder.getId(),bo.getOpenId(), easyLearnOrder.getTotalPayAmount(), "联合支付", IpUtils.getHostIp(), payNotifyUrl);
                } else {
                    WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
                    //需要分账
                    if (easyLearnOrder.getRate() != null && easyLearnOrder.getTotalAmount().scaleByPowerOfTen(2).intValue() >= Math.ceil(1 / easyLearnOrder.getRate())) {
                        //需要分账
                        orderRequest.setProfitSharing("Y");
                    }
                    orderRequest.setSpbillCreateIp(IpUtils.getHostIp())
                            .setOpenid(bo.getOpenId())
                            //商品描述
                            .setBody("联合支付")
                            .setOutTradeNo(easyLearnOrder.getId())
                            //元转分
                            .setTotalFee(easyLearnOrder.getTotalAmount().scaleByPowerOfTen(2).intValue())
                            .setTradeType("JSAPI");
                    return wxPayService.createOrder(orderRequest);
                }
            } catch (Exception e) {
                throw new ServiceException(e.getMessage());
            } finally {
                //释放锁
                userIdLock.unlock();
            }
        } else {
            //获取锁失败，直接返回空
            throw new ServiceException("请勿重复支付");
        }
    }

    /**
     * 果酱通知地址（本地）
     */
    private final String UNION_PAY_DEV_JAMBOX_NOTIFY_URL = "http://localhost:8078/test/ass/jambox/contract/unionPayNotify";

    /**
     * 果酱通知地址（测试地址）
     */
    private final String UNION_PAY_TEST_JAMBOX_NOTIFY_URL = "https://test.cpopsz.com/test/ass/jambox/contract/unionPayNotify";

    /**
     * 果酱通知地址(线上)
     */
    private final String UNION_PAY_PROD_JAMBOX_NOTIFY_URL = "https://api.jamboxsys.com/jambox/ass/jambox/contract/unionPayNotify";

    /**
     * 微信支付联合支付订单通知
     * @param xmlData 数据
     */
    @Override
    public void unionPayResult(String xmlData) {
        try {
            WxPayService wxPayService = wxPayHandler.getWxPayService();
            WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);
            if (!StringUtils.equals(notifyResult.getResultCode(), "SUCCESS")) {
                throw new ServiceException(notifyResult.getReturnMsg());
            }
            String orderId = notifyResult.getOutTradeNo();
            //获取订单信息
            EasyLearnOrder easyLearnOrder = this.getById(orderId);
            if (easyLearnOrder.getRate() != null && easyLearnOrder.getTotalAmount().scaleByPowerOfTen(2).intValue() >= Math.ceil(1 / easyLearnOrder.getRate())) {
                //设置子商户
                WxPayConfig config = wxPayService.getConfig();
                //需要分账
                String subMchId = wxPayHandler.getSubMchId(easyLearnOrder.getBrandId(), easyLearnOrder.getStoreId());
                config.setSubMchId(subMchId);
                wxPayAsyncTask.asyncWxPayProfitSharing(orderId, notifyResult, wxPayService, OrderSource.EASY_LEARN, easyLearnOrder.getRate());
            }
            JSONObject jsonObject = new JSONObject();
            EasyLearnPayPayEnum easyLearnPayPayEnum = EasyLearnPayPayEnum.getEasyLearnPayPayEnum(easyLearnOrder.getOrderType());
            jsonObject.put("wxOrderId", easyLearnOrder.getId());
            jsonObject.put("unionPayOutOrderId", easyLearnOrder.getUnionPayOutOrderId());
            jsonObject.put("easyLearnUnionPay", easyLearnPayPayEnum.toString());
            String url;
            if (StringUtils.equals("prod", SpringUtils.getActiveProfile())) {
                url = UNION_PAY_PROD_JAMBOX_NOTIFY_URL;
            } else if (StringUtils.equals("test", SpringUtils.getActiveProfile())) {
                url = UNION_PAY_TEST_JAMBOX_NOTIFY_URL;
            } else {
                url = UNION_PAY_DEV_JAMBOX_NOTIFY_URL;
            }
            //通知晖哥
            Response response = HttpUtils.sendOkHttpPost(url, jsonObject.toJSONString());
            response.close();
            //更新订单
            this.updateChain().set(EASY_LEARN_ORDER.OUT_ORDER_NO, notifyResult.getTransactionId())
                    .set(EASY_LEARN_ORDER.ORDER_STATUS, 1)
                    .where(EASY_LEARN_ORDER.ID.eq(orderId)).update();
        } catch (WxPayException | IOException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 放心学统一支付
     * @author DB
     * @since 2024/1/9
     * @param bo 请求参数
     * @return Object
     */
    @Override
    public Object unifiedPay(EasyLearnUnifiedPayBo bo) {
        return null;
    }

    /**
     * 创建用户签约计划
     * @author DB
     * @since 2024/1/19
     * @param bo 请求参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public WxPartnerPayScoreUserSignPlanResult createUserSignPlans(LearnNowPayLaterUserSignPlansBo bo) {
        //分布式锁进行幂等处理
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        //分布式锁进行幂等处理
        Lock userIdLock = redisService.distributedLock(JamboxRedisConstant.LEARN_NOW_PAY_LATER_CREATE_USER_PLANS_LOCK_USER_PAY + bo.getId());
        if (userIdLock.tryLock()) {
            try {
                // 获取计划
                WxPayScore wxPayScore = SpringUtils.getBean(WxPayScoreService.class).getById(bo.getId());
                //获取计划详情
                List<WxPayScoreDetail> wxPayScoreDetails = SpringUtils.getBean(WxPayScoreDetailService.class).queryChain()
                        .where(WX_PAY_SCORE_DETAIL.SYS_WX_PAY_SCORE_ID.eq(bo.getId()))
                        .list();
                Row brandAndStore = Db.selectOneByQuery(QueryWrapper.create()
                        .select(STORE.ID, STORE.BRAND_ID)
                        .leftJoin(STORE_EXTEND).on(STORE_EXTEND.STORE_ID.eq(STORE.ID))
                        .where(STORE_EXTEND.STORE_CLOUD_ID.eq(bo.getStoreCloudId())));
                //创建用户签约计划
                String orderId = createEasyLearnUserSignPlans(bo, brandAndStore, wxPayScore);
                String subMchId = wxPayHandler.getSubMchId(brandAndStore.getString("brandId"), brandAndStore.getString("id"));
                WxPayService wxPayService = wxPayHandler.getWxPayService(null, subMchId);
                PartnerPayScoreSignPlanService partnerPayScoreSignPlanService = wxPayService.getPartnerPayScoreSignPlanService();
                List<PayScorePlanDetailRequest> payScorePlanDetailRequests = BeanUtils.mapToList(wxPayScoreDetails, PayScorePlanDetailRequest.class);
                WxPartnerPayScoreSignPlanRequest signPlanRequest = new WxPartnerPayScoreSignPlanRequest();
                signPlanRequest.setPlanId(wxPayScore.getOutPlanId()).setSubAppid(subMchId)
                        .setSubOpenid(bo.getSubOpenId())
                        .setMerchantSignPlanNo(orderId)
                        .setPlanDetailList(payScorePlanDetailRequests)
                        .setNotifyUrl(wxPayConfiguration.getProperties().getLearnNowPayLaterUserSignPlanNotifyUrl());
                try {
                    return partnerPayScoreSignPlanService.createUserSignPlans(signPlanRequest);
                } catch (WxPayException e) {
                    throw new ServiceException(e.getMessage());
                }
            } finally {
                //释放锁
                userIdLock.unlock();
            }
        } else {
            //获取锁失败，直接返回空
            throw new ServiceException("请勿重复提交");
        }

    }

    /**
     * 创建用户支付计划
     * @author DB
     * @since 2024/1/19
     * @param wxPayScore 支付分
     * @return String
     */
    private String createEasyLearnUserSignPlans(LearnNowPayLaterUserSignPlansBo bo, Row brandAndStore, WxPayScore wxPayScore) {
        EasyLearnOrder easyLearnOrder = new EasyLearnOrder();
        easyLearnOrder.setBrandId(brandAndStore.getString("brandId"))
                .setStoreId(brandAndStore.getString("id"))
                .setOrderContent(bo.getPlanName())
                .setOrderStatus(0)
                .setCustomerName(bo.getCustomerName())
                .setCustomerPhone(bo.getCustomerPhone())
                .setTotalAmount(new BigDecimal(wxPayScore.getTotalOriginalPrice()).divide(BigDecimal.valueOf(100)))
                .setTotalPayAmount(BigDecimal.ZERO)
                .setOrderType(0)
                .setRate(OrderSource.LEARN_NOW_PAY_LATER.getRate())
                .setTemplateId(bo.getTemplateId() == null ? bo.getOldTemplateId() : bo.getTemplateId())
                .setCounselor(bo.getCounselor());
        this.save(easyLearnOrder);
        return easyLearnOrder.getId();
    }

    private final String LOCAL_TEMPLATE_URL = "http://localhost:9420/Cpop-Oam/";

    private final String TEST_TEMPLATE_URL = "https://test.cpopsz.com/oam/Cpop-Oam/";

    private final String PROD_TEMPLATE_URL = "https://oam.jamboxsys.com/Cpop-Oam/";

    /**
     * 用户签约成功回调
     * @author DB
     * @since 2024/1/20
     * @param notifyData 响应数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void learnNowPayLaterUserSignPlanNotify(String notifyData) {
        try {
            PayScoreNotifyData payScoreNotifyData = JSONObject.parseObject(notifyData, PayScoreNotifyData.class);
            PartnerPayScoreSignPlanService partnerPayScoreSignPlanService = wxPayHandler.getWxPayService().getPartnerPayScoreSignPlanService();
            SignatureHeader signatureHeader = wxPayHandler.getSignatureHeader();
            PartnerUserSignPlanEntity result = partnerPayScoreSignPlanService.parseSignPlanNotifyResult(notifyData, signatureHeader);
            String orderId = result.getMerchantSignPlanNo();
            EasyLearnOrder easyLearnOrder = this.getById(orderId);
            //支付成功
            if (StringUtils.equals("PAYSCORE.USER_SIGN_PLAN", payScoreNotifyData.getEventType())) {
                //订单已支付
                if (easyLearnOrder.getOrderStatus() == 1){
                    return;
                }
                easyLearnOrder.setOrderStatus(1).setOutOrderNo(result.getSignPlanId());
                //添加支付分详情
                List<EasyLearnOrderDetail> easyLearnOrderDetails = new ArrayList<>();
                result.getSignedDetailList().forEach(item->{
                    EasyLearnOrderDetail easyLearnOrderDetail = new EasyLearnOrderDetail();
                    easyLearnOrderDetail.setOrderId(easyLearnOrder.getId())
                            .setPeriodNo(item.getPlanDetailNo())
                            .setAmount(new BigDecimal(item.getOriginalPrice()).divide(BigDecimal.valueOf(100)))
                            .setDetailDesc(item.getPlanDiscountDescription())
                            .setActualPrice(new BigDecimal(item.getActualPrice()).divide(BigDecimal.valueOf(100)))
                            .setDetailStatus(0)
                            .setOutOrderNo(item.getOrderId())
                            .setDetailName(item.getPlanDetailName());
                    easyLearnOrderDetails.add(easyLearnOrderDetail);
                });
                SpringUtils.getBean(EasyLearnOrderDetailService.class).saveBatch(easyLearnOrderDetails);
                //支付分拓展
                EasyLearnOrderExtend orderExtend = new EasyLearnOrderExtend();
                orderExtend.setOrderId(orderId)
                        .setPlanId(result.getPlanId())
                        .setSignPlanId(result.getSignPlanId())
                        .setSubMchId(result.getSubMchid())
                        .setExtendStatus(0)
                        .setGoingDetailNo(0);
                SpringUtils.getBean(EasyLearnOrderExtendService.class).save(orderExtend);
                //云办卡
                CloudClassCardDto dto = new CloudClassCardDto();
                if (StringUtils.equals("prod", SpringUtils.getActiveProfile())) {
                    dto.setTemplateUrl(PROD_TEMPLATE_URL);
                } else if (StringUtils.equals("test", SpringUtils.getActiveProfile())){
                    dto.setTemplateUrl(TEST_TEMPLATE_URL);
                } else {
                    dto.setTemplateUrl(LOCAL_TEMPLATE_URL);
                }
                // 办卡
                dto.setOpenId(result.getSubOpenid());
                dto.setMerchantSignPlanNo(result.getMerchantSignPlanNo());
                dto.setSignPlanId(result.getSignPlanId());
                dto.setOrderCloudId(easyLearnOrder.getTemplateId());
                StoreExtend storeExtend = SpringUtils.getBean(StoreExtendService.class).queryChain()
                        .where(STORE_EXTEND.STORE_ID.eq(easyLearnOrder.getStoreId()))
                        .one();
                dto.setStoreCloudId(storeExtend.getStoreCloudId());
                dto.setCustomerPhone(easyLearnOrder.getCustomerPhone());
                dto.setCustomerName(easyLearnOrder.getCustomerName());
                dto.setPlanId(result.getPlanId());
                dto.setSignPlanDetails(JSONArray.toJSONString(result.getSignedDetailList()));
                //获取计划
                Row payScorePlan = DbChain.table(WX_PAY_SCORE.getTableName()).select(WX_PAY_SCORE.CLASS_HOUR).where(WX_PAY_SCORE.OUT_PLAN_ID.eq(result.getPlanId())).one();
                //获取支付分
                if (payScorePlan != null && StringUtils.isNotBlank(payScorePlan.getString("classHour"))) {
                    dto.setClassHour(payScorePlan.getString("classHour"));
                }
                String cloudCardId = SpringUtils.getBean(CloudClassCardHandler.class).insertCloudClassCard(dto);
                easyLearnOrder.setProductId(cloudCardId);
                this.updateById(easyLearnOrder);
            } else if (StringUtils.equals("PAYSCORE.USER_CANCEL_SIGN_PLAN", payScoreNotifyData.getEventType())){
                //取消合约
                easyLearnOrder.setOrderStatus(2);
                this.updateById(easyLearnOrder);
                //批量修改订单详情状态
                SpringUtils.getBean(EasyLearnOrderDetailService.class).updateChain()
                        .set(EASY_LEARN_ORDER_DETAIL.DETAIL_STATUS, 3)
                        .where(EASY_LEARN_ORDER_DETAIL.ORDER_ID.eq(easyLearnOrder.getId()))
                        .and(EASY_LEARN_ORDER_DETAIL.DETAIL_STATUS.eq(0))
                        .update();
                //通知波哥取消课卡
                CloudClassCardDto cloudClassCardDto = new CloudClassCardDto();
                cloudClassCardDto.setSignPlanId(result.getSignPlanId());
                cloudClassCardDto.setMerchantSignPlanNo(result.getMerchantSignPlanNo());
                SpringUtils.getBean(CloudClassCardHandler.class).refundCloudClassCard(cloudClassCardDto);
            }
        } catch (WxPayException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 创建用户服务订单(核销)
     * @author DB
     * @since 2024/1/20
     * @param bo 请求参数
     * @return WxPartnerPayScoreSignPlanResult
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public WxPartnerPayScoreSignPlanResult createServiceOrder(LearnNowPayLaterServiceOrderBo bo) {
        //分布式锁进行幂等处理
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        //分布式锁进行幂等处理
        Lock userIdLock = redisService.distributedLock(JamboxRedisConstant.LEARN_NOW_PAY_LATER_SERVICE_ORDER_LOCK_USER_PAY + bo.getSignPlanId());
        if (userIdLock.tryLock()) {
            try {
                EasyLearnOrder easyLearnOrder = this.queryChain().where(EASY_LEARN_ORDER.OUT_ORDER_NO.eq(bo.getSignPlanId())).one();
                EasyLearnOrderDetailService easyLearnOrderDetailService = SpringUtils.getBean(EasyLearnOrderDetailService.class);
                EasyLearnOrderDetail easyLearnOrderDetail = easyLearnOrderDetailService.queryChain()
                        .where(EASY_LEARN_ORDER_DETAIL.ORDER_ID.eq(easyLearnOrder.getId()))
                        .and(EASY_LEARN_ORDER_DETAIL.PERIOD_NO.eq(bo.getPlanDetailNo()))
                        .one();
                //获取计划拓展
                EasyLearnOrderExtend orderExtend = SpringUtils.getBean(EasyLearnOrderExtendService.class).queryChain()
                        .where(EASY_LEARN_ORDER_EXTEND.ORDER_ID.eq(easyLearnOrder.getId()))
                        .one();
                WxPayService wxPayService = wxPayHandler.getWxPayService(null, orderExtend.getSubMchId());
                PartnerPayScoreSignPlanService partnerPayScoreSignPlanService = wxPayService.getPartnerPayScoreSignPlanService();
                WxPartnerPayScoreSignPlanRequest signPlanRequest = new WxPartnerPayScoreSignPlanRequest();
                signPlanRequest.setSignPlanId(easyLearnOrder.getOutOrderNo())
                        .setPlanDetailNo(easyLearnOrderDetail.getPeriodNo())
                        .setSubMchid(orderExtend.getSubMchId())
                        .setSubOpenid(bo.getOpenid())
                        .setNotifyUrl(wxPayConfiguration.getProperties().getLearnNowPayLaterServiceOrderNotifyUrl())
                        .setServiceIntroduction(easyLearnOrderDetail.getDetailDesc());
                TimeRange timeRange = new TimeRange();
                LocalDateTime now = LocalDateTime.now();
                DateTimeFormatter yyyyMMddHHmmss = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
                String startTime = now.plusSeconds(10).format(yyyyMMddHHmmss);
                timeRange.setStartTime(startTime);
                String endTime = now.plusSeconds(60).format(yyyyMMddHHmmss);
                timeRange.setEndTime(endTime);
                signPlanRequest.setTimeRange(timeRange).setOutOrderNo(easyLearnOrderDetail.getId());
                easyLearnOrderDetail.setDetailStatus(1);
                easyLearnOrderDetailService.updateById(easyLearnOrderDetail);
                try {
                    return partnerPayScoreSignPlanService.signPlanServiceOrder(signPlanRequest);
                } catch (WxPayException e) {
                    throw new ServiceException(e.getMessage());
                }
            } finally {
                //释放锁
                userIdLock.unlock();
            }
        } else {
            //获取锁失败，直接返回空
            throw new ServiceException("请勿重复提交");
        }
    }

    /**
     * 支付分用户核销成功回调
     * @author DB
     * @since 2024/1/20
     * @param notifyData 返回数据
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void userServiceOrderSuccess(String notifyData) {
        try {
            WxPayOrderNotifyResult notifyResult = wxPayHandler.getWxPayService().parseOrderNotifyResult(notifyData);
            EasyLearnOrderDetailService easyLearnOrderDetailService = SpringUtils.getBean(EasyLearnOrderDetailService.class);
            EasyLearnOrderDetail easyLearnOrderDetail = easyLearnOrderDetailService.queryChain()
                    .where(EASY_LEARN_ORDER_DETAIL.ORDER_ID.eq(notifyResult.getOutTradeNo()))
                    .and(EASY_LEARN_ORDER_DETAIL.OUT_ORDER_NO.eq(notifyResult.getTransactionId()))
                    .one();
            EasyLearnOrder easyLearnOrder = this.getById(easyLearnOrderDetail.getOrderId());
            //需要分账
            String subMchId = notifyResult.getSubMchId();
            if (notifyResult.getTotalFee() >= Math.ceil(1 / OrderSource.EASY_LEARN.getRate())) {
                //设置子商户
                WxPayService wxPayService = wxPayHandler.getWxPayService(null, subMchId);
                wxPayAsyncTask.asyncWxPayProfitSharing(notifyResult.getOutTradeNo(), notifyResult, wxPayService, OrderSource.LEARN_NOW_PAY_LATER);
            }
            BigDecimal payAmount = new BigDecimal(notifyResult.getTotalFee()).divide(BigDecimal.valueOf(100));
            easyLearnOrderDetail.setDetailStatus(2)
                    .setActualPrice(payAmount);
            easyLearnOrderDetailService.updateById(easyLearnOrderDetail);
            //修改订单实际支付金额
            easyLearnOrder.setTotalPayAmount(easyLearnOrder.getTotalPayAmount() == null ? payAmount : easyLearnOrder.getTotalPayAmount().add(payAmount));
            this.updateById(easyLearnOrder);
        } catch (WxPayException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 取消用户签约计划
     * @author DB
     * @since 2024/1/20
     * @param bo 请求参数
     */
    @Override
    public void stopUserSignPlans(LearnNowPayLaterStopUserSignPlansBo bo) {
        EasyLearnOrder easyLearnOrder = this.queryChain().where(EASY_LEARN_ORDER.OUT_ORDER_NO.eq(bo.getSignPlanId())).one();
        String subMchId = wxPayHandler.getSubMchId(easyLearnOrder.getBrandId(), easyLearnOrder.getStoreId());
        WxPayService wxPayService = wxPayHandler.getWxPayService(null, subMchId);
        try {
            wxPayService.getPartnerPayScoreSignPlanService().stopUserSignPlans(easyLearnOrder.getId(), subMchId, bo.getStopReason());
            //取消合约
            easyLearnOrder.setOrderStatus(2);
            this.updateById(easyLearnOrder);
            //批量修改订单详情状态
            SpringUtils.getBean(EasyLearnOrderDetailService.class).updateChain()
                    .set(EASY_LEARN_ORDER_DETAIL.DETAIL_STATUS, 3)
                    .where(EASY_LEARN_ORDER_DETAIL.ORDER_ID.eq(easyLearnOrder.getId()))
                    .and(EASY_LEARN_ORDER_DETAIL.DETAIL_STATUS.eq(0))
                    .update();
            //获取拓展信息
            EasyLearnOrderExtend easyLearnOrderExtend = SpringUtils.getBean(EasyLearnOrderExtendService.class).queryChain()
                    .where(EASY_LEARN_ORDER_EXTEND.ORDER_ID.eq(easyLearnOrder.getId()))
                    .one();
            //通知波哥取消课卡
            CloudClassCardDto cloudClassCardDto = new CloudClassCardDto();
            cloudClassCardDto.setSignPlanId(easyLearnOrderExtend.getSignPlanId());
            cloudClassCardDto.setMerchantSignPlanNo(easyLearnOrder.getId());
            SpringUtils.getBean(CloudClassCardHandler.class).refundCloudClassCard(cloudClassCardDto);
        } catch (WxPayException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 发送数币手机验证码
     * @author DB
     * @since 2024/1/29
     * @param phone 验证码
     */
    @Override
    public void sendEcppMsmCode(String phone) {
        EcppSmsCodeRequest request = new EcppSmsCodeRequest();
        request.setSmsTpCd("YGJ_SIGN");
        EcppSmsCodeResponse response = SpringUtils.getBean(EcppHandler.class).sendEcppMsmCode(request);


    }

    /**
     * 微信支付联合支付普通订单通知
     * @author DB
     * @since 2024/2/22
     * @return String
     */
    @Override
    public String normalUnionPay(HttpServletRequest request, HttpServletResponse response) {
        WxPayNormalHandler wxPayNormalHandler = SpringUtils.getBean(WxPayNormalHandler.class);
        // 预先设定返回的 response 类型为 xml
        response.setHeader("Content-type", "application/xml");
        WxUtils wxUtils = SpringUtils.getBean(WxUtils.class);
        // 读取参数，解析Xml为map
        Map<String, String> map = null;
        Response okResponse = null;
        try {
            map = wxUtils.transferXmlToMap(wxUtils.readRequest(request));
            // 转换为有序 map，判断签名是否正确
            boolean isSignSuccess = wxUtils.checkSign(new TreeMap<String, Object>(map), "JamBox20220329174000000000000002");
            if (isSignSuccess) {
                // 签名校验成功，说明是微信服务器发出的数据
                String orderId = map.get("out_trade_no");
                //获取订单信息
                EasyLearnOrder easyLearnOrder = this.getById(orderId);
                JSONObject jsonObject = new JSONObject();
                EasyLearnPayPayEnum easyLearnPayPayEnum = EasyLearnPayPayEnum.getEasyLearnPayPayEnum(easyLearnOrder.getOrderType());
                jsonObject.put("wxOrderId", easyLearnOrder.getId());
                jsonObject.put("unionPayOutOrderId", easyLearnOrder.getUnionPayOutOrderId());
                jsonObject.put("easyLearnUnionPay", easyLearnPayPayEnum.toString());
                String url;
                if (StringUtils.equals("prod", SpringUtils.getActiveProfile())) {
                    url = UNION_PAY_PROD_JAMBOX_NOTIFY_URL;
                } else if (StringUtils.equals("test", SpringUtils.getActiveProfile())) {
                    url = UNION_PAY_TEST_JAMBOX_NOTIFY_URL;
                } else {
                    url = UNION_PAY_DEV_JAMBOX_NOTIFY_URL;
                }
                //通知晖哥
                okResponse = HttpUtils.sendOkHttpPost(url, jsonObject.toJSONString());
                //更新订单
                this.updateChain().set(EASY_LEARN_ORDER.OUT_ORDER_NO, map.get("transaction_id"))
                        .set(EASY_LEARN_ORDER.ORDER_STATUS, 1)
                        .where(EASY_LEARN_ORDER.ID.eq(orderId)).update();
                //发送通知到财务群
                List<String> phoneList = new ArrayList<String>();
                // 获取审核管理员手机号
                RedisService redisService = SpringUtils.getBean(RedisService.class);
                String auditStaffPhone = redisService.getCacheObject("sysConfig:oam:OamCancelAfterVerificationStaffPhone");
                phoneList.add(auditStaffPhone);
                try {
                    //获取品牌和校区信息
                    Row brandAndStore = Db.selectOneByQuery("cp_sys_store", QueryWrapper.create()
                            .select("css.store_name")
                            .select("csb.brand_name")
                            .from("cp_sys_store").as("css")
                            .leftJoin("cp_sys_brand").as("csb").on("csb.id = css.brand_id")
                            .where("css.id= ?", easyLearnOrder.getStoreId()));
                    SpringUtils.getBean(WebHookSendHandler.class).webHookSendText("a885058b-2478-4df8-bdb7-2bb94a08c1f5", phoneList,
                            "==========用户订单到账==========" +
                                    "\n订单创建时间:" + easyLearnOrder.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) +
                                    "\n品牌:" + brandAndStore.getString("brandName") +
                                    "\n校区:" + brandAndStore.getString("storeName") +
                                    "\n订单类型:" + easyLearnOrder.getOrderType() +
                                    "\n受益人:" + easyLearnOrder.getCustomerName() +
                                    "\n电话:" + easyLearnOrder.getCustomerPhone() +
                                    "\n订单金额:" + easyLearnOrder.getTotalAmount() +
                                    "\n到账金额:" + easyLearnOrder.getTotalPayAmount() +
                                    "\n微信订单号:" + map.get("transaction_id") +
                                    "\n商户号:1618436087",
                            false);
                } catch (IOException e) {
                    throw new ServiceException("发送任务通知失败!");
                }
                return wxPayNormalHandler.success();
            } else {
                // 签名校验失败（可能不是微信服务器发出的数据）
                return wxPayNormalHandler.fail();
            }
        } catch (Exception e) {
            return wxPayNormalHandler.fail();
        } finally {
            assert okResponse != null;
            okResponse.close();
        }
    }
}
