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

import com.alibaba.fastjson.JSONObject;
import com.cpop.common.utils.DateUtils;
import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.bean.BeanUtils;
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.enums.UserType;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.base.table.SysUser;
import com.cpop.core.service.RedisService;
import com.cpop.core.utils.QuartzUtils;
import com.cpop.core.utils.SecurityUtils;
import com.cpop.core.utils.SpringUtils;
import com.cpop.core.utils.sql.SqlUtils;
import com.cpop.jambox.business.entity.BrandExtend;
import com.cpop.jambox.business.service.BrandExtendService;
import com.cpop.mall.business.bo.*;
import com.cpop.mall.business.dto.UserPointDto;
import com.cpop.pay.framewok.config.wxPay.WxPayConfiguration;
import com.cpop.pay.framewok.core.dto.easyLearn.WxPayGoodsDetailDto;
import com.cpop.mall.business.entity.*;
import com.cpop.mall.business.mapper.OrderMapper;
import com.cpop.mall.business.service.*;
import com.cpop.mall.business.vo.OrderDetailVo;
import com.cpop.mall.business.vo.OrderInfoVo;
import com.cpop.mall.business.vo.OrderPageVo;
import com.cpop.mall.framework.constant.MallRedisConstant;
import com.cpop.mall.framework.enums.BuyRestrictEnums;
import com.cpop.mall.framework.enums.QuartzEnums;
import com.cpop.mall.framework.task.OrderDetailAsyncTask;
import com.cpop.mall.framework.task.OrderOverTimeUnPayTask;
import com.cpop.mall.framework.task.ProductRecordSyncStockTask;
import com.cpop.pay.framewok.task.WxPayAsyncTask;
import com.cpop.pay.framewok.handler.wxPay.WxPayHandler;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.core.datasource.DataSourceKey;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.row.DbChain;
import com.mybatisflex.core.row.Row;
import com.mybatisflex.core.row.RowKey;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.stream.Collectors;

import static com.cpop.core.base.table.table.SysUserTableDef.SYS_USER;
import static com.cpop.jambox.business.entity.table.BrandExtendTableDef.BRAND_EXTEND;
import static com.cpop.mall.business.entity.table.OrderDetailTableDef.ORDER_DETAIL;
import static com.cpop.mall.business.entity.table.OrderRefundTableDef.ORDER_REFUND;
import static com.cpop.mall.business.entity.table.OrderTableDef.ORDER;
import static com.cpop.mall.business.entity.table.ProductRecordTableDef.PRODUCT_RECORD;
import static com.cpop.mall.business.entity.table.ProductTableDef.PRODUCT;
import static com.cpop.system.business.entity.table.StoreTableDef.STORE;
import static com.mybatisflex.core.query.QueryMethods.sum;


/**
 * 商城订单表 服务层实现。
 *
 * @author DB
 * @since 2023-10-24
 */
@Service("orderService")
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private WxPayHandler wxPayHandler;

    @Autowired
    private Scheduler scheduler;

    @Autowired
    private WxPayAsyncTask wxPayAsyncTask;

    /**
     * @descriptions 分页查询商城-订单
     * @author DB
     * @date 2023/10/24 15:19
     * @param bo 请求参数
     * @return: com.mybatisflex.core.paginate.Page<com.cpop.mall.business.vo.OrderPageVo>
     */
    @Override
    public Page<OrderPageVo> getOrderPage(OrderPageBo bo) {
        //获取当前登陆用户信息
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        //如果是商城员工
        QueryWrapper queryWrapper = QueryWrapper.create();
        UserType userType = UserType.valueOf(loginUserInfo.getString("userType"));
        if (userType == UserType.MALL_USER){
            //只能查询当前用户品牌下所有订单
            queryWrapper.where(ORDER.BRAND_ID.eq(loginUserInfo.getString("brandId")));
        }
        //如果是小程序用户
        if (userType == UserType.MINI_USER) {
            //查询此用户订单
            queryWrapper.where(ORDER.PAY_USER_ID.eq(loginUserInfo.getString("userId")));
        }
        //订单状态
        if (null != bo.getOrderStatus()) {
            queryWrapper.and(ORDER.ORDER_STATUS.eq(bo.getOrderStatus()));
        } else {
            queryWrapper.and(ORDER.ORDER_STATUS.ne(6));
        }
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        return this.mapper.paginateAs(Page.of(pageDomain.getPageNum(),pageDomain.getPageSize()),
                queryWrapper
                        .select(ORDER.ALL_COLUMNS)
                        .from(ORDER)
                        .leftJoin(SYS_USER).on(SYS_USER.ID.eq(ORDER.PAY_USER_ID))
                        .and(ORDER.RECEIVE_NAME.like(bo.getReceiveName()))
                        .and(ORDER.RECEIVE_PHONE.like(bo.getReceivePhone()))
                        .and(ORDER.PRODUCT_NAMES.like(bo.getProductName()))
                        .and(SYS_USER.USER_TYPE.eq("MINI_USER"))
                        .orderBy(ORDER.CREATE_TIME.desc()),
                OrderPageVo.class,
                //子查询
                item -> item.field(OrderPageVo::getDetailList)
                        .queryWrapper(pageVo -> queryChain()
                                //产品
                                .select(PRODUCT.PRODUCT_NAME, PRODUCT.PIC_URL, PRODUCT.PIC_DETAIL_URL)
                                //订单详情
                                .select(ORDER_DETAIL.ORDER_ID, ORDER_DETAIL.PRODUCT_RECORD_ID, ORDER_DETAIL.AMOUNT, ORDER_DETAIL.POINT, ORDER_DETAIL.STORE_ID)
                                //规格
                                .select(PRODUCT_RECORD.RECORD_NAMES)
                                //校区
                                .select(STORE.STORE_NAME)
                                .from(ORDER_DETAIL)
                                .leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.ID.eq(ORDER_DETAIL.PRODUCT_RECORD_ID))
                                .leftJoin(PRODUCT).on(PRODUCT.ID.eq(PRODUCT_RECORD.PRODUCT_ID))
                                .leftJoin(STORE).on(STORE.ID.eq(ORDER_DETAIL.STORE_ID))
                                .where(ORDER_DETAIL.ORDER_ID.eq(pageVo.getId()))),
                //查询退款状态
                item -> item.field(OrderPageVo::getOrderRefundVo)
                        .queryWrapper(refundVo -> queryChain()
                                .select(ORDER_REFUND.REFUND_STATUS, ORDER_REFUND.REFUND_REASON, ORDER_REFUND.ID, ORDER_REFUND.ORDER_ID, ORDER_REFUND.CREATE_TIME)
                                .from(ORDER_REFUND)
                                .where(ORDER_REFUND.ORDER_ID.eq(refundVo.getId()))
                                .orderBy(ORDER_REFUND.CREATE_TIME.desc())
                                .limit(1)));
    }

    /**
     * @descriptions 生成预订单
     * @author DB
     * @date 2023/11/06 11:17
     * @param bo 请求参数
     * @return: java.lang.Object
     */
    @Override
    public String createAdvanceOrder(AdvanceOrderBo bo) {
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        //获取当前下单用户信息
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        //分布式锁进行幂等处理
        Lock userIdLock = redisService.distributedLock(MallRedisConstant.IDEMPOTENT_LOCK_CREATE_ADVANCE_ORDER + loginUserInfo.getString("userId"));
        if (userIdLock.tryLock()) {
            //检查库存
            Map<String, Integer> recordNumIsEnough = null;
            try {
                //如果是积分商品,先检查积分
                if (bo.getPayType() == 1) {
                    UserPointDto userPoint = getUserPoint(loginUserInfo);
                    if (userPoint.getTotalPoint() < bo.getTotalPoint()) {
                        throw new ServiceException("您的积分不足!");
                    }
                }
                Order order = BeanUtils.mapToClass(bo, Order.class);
                List<OrderDetail> orderDetails = BeanUtils.mapToList(bo.getOrderDetailList(), OrderDetail.class);
                //规格记录ids
                Set<String> recordIds = orderDetails.stream().map(OrderDetail::getProductRecordId).collect(Collectors.toSet());
                //获取涉及到的商品
                ProductService productService = SpringUtils.getBean(ProductService.class);
                List<Product> productList = productService.queryChain()
                        .select(PRODUCT.ID, PRODUCT.PRODUCT_NAME, PRODUCT.DESCRIPTION, PRODUCT.BUY_RESTRICT, PRODUCT.LIMIT_NUM)
                        .from(PRODUCT)
                        .leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.PRODUCT_ID.eq(PRODUCT.ID))
                        .where(PRODUCT_RECORD.ID.in(recordIds))
                        .list();
                //检查库存
                recordNumIsEnough= recordNumIsEnough(orderDetails);
                //购买限制
                buyRestrict(productList, recordNumIsEnough);
                //设置待付款
                order.setOrderStatus(0)
                        .setPreviousStatus(0)
                        //获取当前用户所属品牌
                        .setBrandId(loginUserInfo.getString("brandId"))
                        .setProductNames(productList.stream().map(Product::getProductName).collect(Collectors.joining(",")))
                        //当前用户id，根据用户类型查询不同用户列表
                        .setPayUserId(loginUserInfo.getString("userId"));
                if (StringUtils.isBlank(bo.getPayUserName())) {
                    order.setPayUserName(loginUserInfo.getString("nickName"));
                }
                this.save(order);
                // 通过JobBuilder构建JobDetail实例，JobDetail规定其job只能是实现Job接口的实例
                JobDetail jobDetail = JobBuilder.newJob(OrderOverTimeUnPayTask.class).withIdentity(QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getName() + order.getId(),
                                QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getGroup())
                        .usingJobData("orderId", order.getId())
                        .build();
                //添加定时修改订单状态任务
                QuartzUtils quartzUtils = SpringUtils.getBean(QuartzUtils.class);
                //设置15分钟后
                String cron = quartzUtils.convertToCron(DateUtils.addMinutes(new Date(), 15));
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
                // CronTrigger表达式触发器 继承于Trigger。TriggerBuilder 用于构建触发器实例
                CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getName() + order.getId(),
                                QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getGroup())
                        .withSchedule(cronScheduleBuilder).build();
                scheduler.scheduleJob(jobDetail, cronTrigger);
                //保存订单详情
                orderDetails.forEach(item -> {
                    item.setOrderId(order.getId());
                });
                SpringUtils.getBean(OrderDetailService.class).saveBatch(orderDetails);
                //移除购物车
                if (null != bo.getShoppingCartIds()&& !bo.getShoppingCartIds().isEmpty()){
                    SpringUtils.getBean(ShoppingCartService.class).removeByIds(bo.getShoppingCartIds());
                }
                return order.getId();
            } catch (Exception e) {
                //回滚库存
                if (recordNumIsEnough != null){
                    recordNumIsEnough.forEach((key, value) -> {
                        redisService.longIncrement(MallRedisConstant.STOCK_RECORD_NUM + key, value.longValue());
                    });
                }
                throw new ServiceException(e.getMessage());
            } finally {
                //释放锁
                userIdLock.unlock();
            }
        }else {
            //获取锁失败，直接返回空
            throw new ServiceException("请勿重复下单");
        }
    }

    private void buyRestrict(List<Product> productList, Map<String, Integer> recordNumIsEnough) {
        //过滤出存在购买限制的商品
        List<Product> filterProduct = productList.stream().filter(item -> item.getBuyRestrict() != null).collect(Collectors.toList());
        //过滤之后不为空，存在购买限制
        if (!filterProduct.isEmpty()){
            filterProduct.forEach(item->{
                BuyRestrictEnums.getByCode(item.getBuyRestrict()).getBuyRestrictStrategy().buyRestrict(item, recordNumIsEnough);
            });
        }
    }

    /**
     * @descriptions 商城-订单-下单
     * @author DB
     * @date 2023/10/25 9:32
     * @param bo 下单请求对象
     * @return: void
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Object placeOrder(PlaceOrderBo bo) {
        //获取当前下单用户信息
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        //分布式锁进行幂等处理
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        //分布式锁进行幂等处理
        Lock userIdLock = redisService.distributedLock(MallRedisConstant.IDEMPOTENT_LOCK_USER_PAY + loginUserInfo.getString("userId"));
        if (userIdLock.tryLock()){
            try {
                //获取订单信息
                Order order = this.getById(bo.getId());
                //获取预订单详情信息
                OrderDetailService orderDetailService = SpringUtils.getBean(OrderDetailService.class);
                List<OrderDetail> orderDetails = orderDetailService.queryChain().where(ORDER_DETAIL.ORDER_ID.eq(order.getId())).list();
                //获取涉及到的商品
                ProductService productService = SpringUtils.getBean(ProductService.class);
                List<Product> productList = productService.queryChain()
                        .select(PRODUCT.ID, PRODUCT.PRODUCT_NAME, PRODUCT.DESCRIPTION, PRODUCT.PRODUCT_TYPE, PRODUCT.CARD_TEMPLATE_ID)
                        .from(PRODUCT)
                        .leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.PRODUCT_ID.eq(PRODUCT.ID))
                        .where(PRODUCT_RECORD.ID.in(orderDetails.stream().map(OrderDetail::getProductRecordId).collect(Collectors.toSet())))
                        .list();
                Object result;
                //微信支付
                if (bo.getPayType() == 0) {
                    //微信支付统一下单
                    WxPayService wxPayService = wxPayHandler.getWxPayService();
                    WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
                    //需要分账
                    if (order.getTotalAmount().scaleByPowerOfTen(2).intValue() >= Math.ceil(1 / OrderSource.MALL.getRate())) {
                        //需要分账
                        orderRequest.setProfitSharing("Y");
                    }
                    String description = productList.stream().map(Product::getDescription).collect(Collectors.joining(","));
                    if (description.getBytes().length > 127) {
                        description = StringUtils.substringByte(description, 0, 126);
                    }
                    WxPayConfiguration configuration = SpringUtils.getBean(WxPayConfiguration.class);
                    orderRequest.setSpbillCreateIp(IpUtils.getHostIp())
                            .setOpenid(loginUserInfo.getString("openId"))
                            //商品描述
                            .setBody(description)
                            //商品详情,如果涉及到商品优惠需要重新涉及
                            .setDetail(JSONObject.toJSONString(BeanUtils.mapToList(orderDetails, WxPayGoodsDetailDto.class)))
                            .setOutTradeNo(order.getId())
                            //元转分
                            .setTotalFee(order.getTotalAmount().scaleByPowerOfTen(2).intValue())
                            .setTradeType("JSAPI");
                    result = wxPayService.createOrder(orderRequest);
                    //删除定时器任务
                    QuartzUtils quartzUtils = SpringUtils.getBean(QuartzUtils.class);
                    quartzUtils.deleteJob(QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getName() + order.getId(),
                            QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getGroup());
                } else {
                    //统计支付商品情况
                    Map<String, Integer> recordNumIsEnough = orderDetails.stream().collect(Collectors.toMap(OrderDetail::getProductRecordId, OrderDetail::getNumber));
                    result = pointPay(recordNumIsEnough, order, loginUserInfo);
                    //删除定时器任务
                    QuartzUtils quartzUtils = SpringUtils.getBean(QuartzUtils.class);
                    quartzUtils.deleteJob(QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getName() + order.getId(),
                            QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getGroup());
                }
                //更新订单
                this.updateChain().set(ORDER.RECEIVE_PHONE, bo.getReceivePhone())
                        .set(ORDER.RECEIVE_ADDRESS, bo.getReceiveAddress())
                        .set(ORDER.RECEIVE_NAME, bo.getReceiveName())
                        .where(ORDER.ID.eq(bo.getId()))
                        .update();
                return result;
            } catch (Exception e) {
                throw new ServiceException(e.getMessage());
            } finally {
                //释放锁
                userIdLock.unlock();
            }
        } else {
            //获取锁失败，直接返回空
            throw new ServiceException("请勿重复支付");
        }
    }

    /**
     * @Description: 积分支付
     * @Author DB
     * @Date: 2023/10/28 9:33
     */
    private Object pointPay(Map<String, Integer> recordNumIsEnough, Order order, JSONObject loginUserInfo) {
        UserPointDto userPoint = getUserPoint(loginUserInfo);
        if (userPoint.getTotalPoint() < order.getTotalPoint()) {
            throw new ServiceException("您的积分不足!");
        }
        //扣除积分
        try {
            LocalDateTime now = LocalDateTime.now();
            DataSourceKey.use("jambox");
            DbChain.table("t_point_record")
                    .setId(RowKey.of("record_id", KeyType.Auto))
                    .set("user_id", userPoint.getOldUserId())
                    .set("brand_id", userPoint.getBrandCloudId())
                    .set("point_behaviour", 9)
                    .set("behaviour_record", "商城购买商品")
                    .set("point_change", -order.getTotalPoint())
                    .set("point_time", now)
                    .set("cumulative", userPoint.getTotalPoint() - order.getTotalPoint())
                    .set("creation_by", userPoint.getOldUserId())
                    .set("last_modified", userPoint.getOldUserId())
                    .set("creation_time", now)
                    .save();
        } finally {
            DataSourceKey.clear();
        }
        ProductRecordService productRecordService = SpringUtils.getBean(ProductRecordService.class);
        List<ProductRecord> productRecords = productRecordService.listByIds(recordNumIsEnough.keySet());
        productRecords.forEach(item -> {
            item.setRecordNum(item.getRecordNum() - recordNumIsEnough.get(item.getId()));
        });
        productRecordService.updateBatch(productRecords);
        //修改订单状态
        return this.updateChain().set(ORDER.ORDER_STATUS, 1).where(ORDER.ID.eq(order.getId())).update();
    }

    /**
     * @descriptions 获取当前用户积分
     * @author DB
     * @date 2023/11/10 16:10
     * @param loginUserInfo 用户信息
     * @return: java.lang.Integer
     */
    @Override
    public UserPointDto getUserPoint(JSONObject loginUserInfo){
        //获取云品牌id
        BrandExtend brandExtend = SpringUtils.getBean(BrandExtendService.class)
                .getOne(QueryWrapper.create()
                        .where(BRAND_EXTEND.BRAND_ID.eq(loginUserInfo.getString("brandId"))));
        //修改用户积分
        try {
            DataSourceKey.use("jambox");
            //获取旧用户信息
            List<Row> rows = DbChain.table("t_user_info")
                    .select("user_id")
                    .where("open_id = ?", loginUserInfo.getString("openId"))
                    .list();
            List<String> userIds = rows.stream().map(item -> item.getString("userId")).collect(Collectors.toList());
            Row pointChange = DbChain.table("t_point_record")
                    .select(sum("point_change").as("pointChange"))
                    .in("user_id", userIds)
                    .and("brand_id = ?", brandExtend.getBrandCloudId())
                    .one();
            if (null == pointChange.getInt("pointChange")) {
                throw new ServiceException("您的积分不足!");
            }else {
                UserPointDto userPointDto = new UserPointDto();
                userPointDto.setTotalPoint(pointChange.getInt("pointChange"));
                userPointDto.setBrandCloudId(brandExtend.getBrandCloudId());
                userPointDto.setOldUserId(userIds.get(0));
                return userPointDto;
            }
        } finally {
            DataSourceKey.clear();
        }
    }

    /**
     * @descriptions 库存是否足够
     * @author DB
     * @date 2023/10/25 10:18
     * @param list 订单详情
     * @return: Map<String, Integer> 预库存记录
     */
    private Map<String, Integer> recordNumIsEnough(List<OrderDetail> list) throws ServiceException {
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        Map<String, Integer> stockNumMap = new HashMap<>();
        //遍历库存
        list.forEach(item -> {
            Integer stockNum = redisService.getCacheObject(MallRedisConstant.STOCK_RECORD_NUM + item.getProductRecordId());
            ProductRecord record = null;
            //从数据库中获取库存信息然后放入缓存
            if (stockNum == null) {
                record = SpringUtils.getBean(ProductRecordService.class).getById(item.getProductRecordId());
                stockNum = record.getRecordNum();
                redisService.setCacheObject(MallRedisConstant.STOCK_RECORD_NUM + item.getProductRecordId(), stockNum);
            }
            //剩余库存
            Long surplusStock = redisService.longIncrement(MallRedisConstant.STOCK_RECORD_NUM + item.getProductRecordId(), -item.getNumber().longValue());
            boolean isEnough = surplusStock >= 0;
            if (!isEnough) {
                if (record == null) {
                    record = SpringUtils.getBean(ProductRecordService.class).getById(item.getProductRecordId());
                }
                //回滚库存
                if (!stockNumMap.isEmpty()){
                    stockNumMap.forEach((key, value) -> {
                        redisService.longIncrement(MallRedisConstant.STOCK_RECORD_NUM + key, value.longValue());
                    });
                }
                redisService.longIncrement(MallRedisConstant.STOCK_RECORD_NUM + item.getProductRecordId(), item.getNumber().longValue());
                throw new ServiceException("您购买的商品" + record.getRecordNames() + "库存不足,请联系客服并重新下单!");
            } else {
                //此处录入预库存信息
                stockNumMap.put(item.getProductRecordId(), item.getNumber());
            }
        });
        return stockNumMap;
    }

    /**
     * @descriptions 申请退款
     * @author DB
     * @date 2023/10/26 17:24
     * @param bo 申请退款对象
     * @return: void
     */
    @Override
    public void applyRefund(OrderApplyRefundBo bo) {
        //查询是否有进行中的退款申请
        OrderRefundService orderRefundService = SpringUtils.getBean(OrderRefundService.class);
        long applying = orderRefundService.queryChain().where(ORDER_REFUND.ORDER_ID.eq(bo.getOrderId()))
                .and(ORDER_REFUND.REFUND_STATUS.eq(0)).count();
        if (applying > 0) {
            throw new ServiceException("你已申请此订单退款,现在暂在处理中,如需跟多信息请和客服联系");
        }
        OrderRefund orderRefund = BeanUtils.mapToClass(bo, OrderRefund.class);
        orderRefund.setRefundStatus(0);
        orderRefundService.save(orderRefund);
        //修改订单状态
        this.updateChain().setRaw(ORDER.PREVIOUS_STATUS, ORDER.ORDER_STATUS)
                .set(ORDER.ORDER_STATUS, 4).where(ORDER.ID.eq(bo.getOrderId())).update();
    }

    /**
     * @descriptions 微信支付订单通知
     * @author DB
     * @date 2023/10/27 16:04
     * @param xmlData 加密数据
     * @return: void
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void wxPayNotifyOrder(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();
            //修改订单状态
            this.updateChain().set(ORDER.OUT_ORDER_NO, notifyResult.getTransactionId())
                    .set(ORDER.ORDER_STATUS, 1).where(ORDER.ID.eq(orderId)).update();
            //支付成功减库存
            List<OrderDetail> orderDetails = SpringUtils.getBean(OrderDetailService.class).queryChain()
                    .where(ORDER_DETAIL.ORDER_ID.eq(orderId)).list();
            //订单数量集合
            Map<String, Integer> orderNumMap = orderDetails.stream().collect(Collectors.toMap(OrderDetail::getProductRecordId, OrderDetail::getNumber));
            //需要分账
            if (notifyResult.getTotalFee() >= Math.ceil(1 / OrderSource.MALL.getRate())) {
                wxPayAsyncTask.asyncWxPayProfitSharing(orderId, notifyResult, wxPayService, OrderSource.MALL);
            }
            //异步更新
            SpringUtils.getBean(ProductRecordSyncStockTask.class).asyncUpdateRecords(orderNumMap);
        } catch (WxPayException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * @descriptions 取消订单
     * @author DB
     * @date 2023/10/27 17:17
     * @param orderId 订单id
     * @return: void
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrder(String orderId) {
        this.updateChain().setRaw(ORDER.PREVIOUS_STATUS, ORDER.ORDER_STATUS)
                .set(ORDER.ORDER_STATUS, 5).where(ORDER.ID.eq(orderId)).update();
        //获取订单详情
        List<OrderDetail> orderDetails = SpringUtils.getBean(OrderDetailService.class).queryChain().where(ORDER_DETAIL.ORDER_ID.eq(orderId)).list();
        Map<String, Integer> orderReturnMap = orderDetails.stream().collect(Collectors.toMap(OrderDetail::getProductRecordId, OrderDetail::getNumber));
        //redis库存回滚
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        orderReturnMap.forEach((key, value) -> {
            redisService.longIncrement(MallRedisConstant.STOCK_RECORD_NUM + key, value.longValue());
        });
    }

    /**
     * @Description: 输入物流订单
     * @param bo 请求参数
     * @Author DB
     * @Date: 2023/10/28 15:27
     */
    @Override
    public void inputLogisticsOrder(LogisticsOrderBo bo) {
        this.updateChain().setRaw(ORDER.PREVIOUS_STATUS, ORDER.ORDER_STATUS)
                .set(ORDER.ORDER_STATUS, 2)
                .set(ORDER.LOGISTICS_ORDER, bo.getLogisticsOrder())
                .where(ORDER.ID.eq(bo.getId())).update();
    }

    /**
     * @descriptions 小程序-重新支付订单
     * @author DB
     * @date 2023/11/03 16:14
     * @param orderId 订单id
     * @return: java.lang.Object
     */
    @Override
    public Object rePayOrder(String orderId) {
        Order order = this.getById(orderId);
        //获取订单详情
        List<OrderDetail> orderDetails = SpringUtils.getBean(OrderDetailService.class).queryChain().where(ORDER_DETAIL.ORDER_ID.eq(orderId)).list();
        //获取当前下单用户信息
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        List<Product> productList = SpringUtils.getBean(ProductService.class).queryChain()
                .select(PRODUCT.ID, PRODUCT.PRODUCT_NAME, PRODUCT.DESCRIPTION)
                .from(PRODUCT)
                .leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.PRODUCT_ID.eq(PRODUCT.ID))
                .where(PRODUCT_RECORD.ID.in(orderDetails.stream().map(OrderDetail::getProductRecordId).collect(Collectors.toSet())))
                .list();
        //分布式锁进行幂等处理
        RedisService redisService = SpringUtils.getBean(RedisService.class);
        Lock userIdLock = redisService.distributedLock(MallRedisConstant.IDEMPOTENT_LOCK_USER_PAY + loginUserInfo.getString("userId"));
        if (userIdLock.tryLock()) {
            //检查库存
            Map<String, Integer> recordNumIsEnough = null;
            try {
                recordNumIsEnough= recordNumIsEnough(orderDetails);
                //微信支付
                if (order.getPayType() == 0) {
                    //微信支付统一下单
                    WxPayService wxPayService = wxPayHandler.getWxPayService();
                    WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
                    //查询是否是服务商系品牌
                    String brandId = loginUserInfo.getString("brandId");
                    //需要分账
                    if (order.getTotalAmount().scaleByPowerOfTen(2).intValue() >= 10) {
                        //需要分账
                        orderRequest.setProfitSharing("Y");
                    }
                    orderRequest.setSpbillCreateIp(IpUtils.getHostIp())
                            .setOpenid(loginUserInfo.getString("openId"))
                            //商品描述
                            .setBody(productList.stream().map(Product::getDescription).collect(Collectors.joining(",")))
                            //商品详情,如果涉及到商品优惠需要重新涉及
                            .setDetail(JSONObject.toJSONString(BeanUtils.mapToList(orderDetails, WxPayGoodsDetailDto.class)))
                            .setOutTradeNo(order.getId())
                            //元转分
                            .setTotalFee(order.getTotalAmount().scaleByPowerOfTen(2).intValue())
                            .setTradeType("JSAPI");
                    return wxPayService.createOrder(orderRequest);
                } else {
                    return pointPay(recordNumIsEnough, order, loginUserInfo);
                }
            } catch (Exception e){
                //回滚库存
                if (recordNumIsEnough != null){
                    recordNumIsEnough.forEach((key, value) -> {
                        redisService.longIncrement(MallRedisConstant.STOCK_RECORD_NUM + key, value.longValue());
                    });
                }
                throw new ServiceException(e.getMessage());
            }finally {
                userIdLock.unlock();
            }
        }else {
            //获取锁失败，直接返回空
            throw new ServiceException("请勿重复下单");
        }
    }

    /**
     * @descriptions 小程序-获取订单详情
     * @author DB
     * @date 2023/11/06 14:26
     * @param orderId 订单id
     * @return: com.cpop.mall.business.vo.OrderInfoVo
     */
    @Override
    public OrderInfoVo getOrderInfo(String orderId) {
        OrderInfoVo orderInfoVo = this.mapper.selectOneWithRelationsByIdAs(orderId, OrderInfoVo.class);
        //获取订单详情
        List<OrderDetailVo> orderDetailVos = SpringUtils.getBean(OrderDetailService.class).listAs(QueryWrapper.create()
                .select(PRODUCT.PRODUCT_NAME, PRODUCT.PIC_URL, PRODUCT.PIC_DETAIL_URL)
                //订单详情
                .select(ORDER_DETAIL.ORDER_ID, ORDER_DETAIL.PRODUCT_RECORD_ID, ORDER_DETAIL.AMOUNT, ORDER_DETAIL.POINT, ORDER_DETAIL.STORE_ID)
                //规格
                .select(PRODUCT_RECORD.RECORD_NAMES)
                //校区
                .select(STORE.STORE_NAME)
                .from(ORDER_DETAIL)
                .leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.ID.eq(ORDER_DETAIL.PRODUCT_RECORD_ID))
                .leftJoin(PRODUCT).on(PRODUCT.ID.eq(PRODUCT_RECORD.PRODUCT_ID))
                .leftJoin(STORE).on(STORE.ID.eq(ORDER_DETAIL.STORE_ID))
                .where(ORDER_DETAIL.ORDER_ID.eq(orderId)),
                OrderDetailVo.class);
        orderInfoVo.setDetailList(orderDetailVos);
        return orderInfoVo;
    }

    /**
     * @descriptions 后台确定订单完成
     * @author DB
     * @date 2023/11/13 17:42
     * @param id 主键
     * @return: void
     */
    @Override
    public void orderFinish(String id) {
        //获取订单信息
        Order order = this.getById(id);
        //获取预订单详情信息
        OrderDetailService orderDetailService = SpringUtils.getBean(OrderDetailService.class);
        List<OrderDetail> orderDetails = orderDetailService.queryChain().where(ORDER_DETAIL.ORDER_ID.eq(order.getId())).list();
        //获取涉及到的商品
        ProductService productService = SpringUtils.getBean(ProductService.class);
        List<Product> productList = productService.queryChain()
                .select(PRODUCT.ID, PRODUCT.PRODUCT_NAME, PRODUCT.DESCRIPTION, PRODUCT.PRODUCT_TYPE, PRODUCT.CARD_TEMPLATE_ID)
                .from(PRODUCT)
                .leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.PRODUCT_ID.eq(PRODUCT.ID))
                .where(PRODUCT_RECORD.ID.in(orderDetails.stream().map(OrderDetail::getProductRecordId).collect(Collectors.toSet())))
                .list();
        this.updateChain()
                .setRaw(ORDER.PREVIOUS_STATUS, ORDER.ORDER_STATUS)
                .set(ORDER.ORDER_STATUS, 3)
                .where(ORDER.ID.eq(id)).update();
        //异步处理订单详情
        SysUser sysUser = DbChain.table(SYS_USER).where(SYS_USER.ID.eq(order.getPayUserId())).oneAs(SysUser.class);
        SpringUtils.getBean(OrderDetailAsyncTask.class).asyncProcessingOrderDetails(order, sysUser.getPhoneNumber(), sysUser.getNickName(), orderDetails, productList);
    }

}
