package com.ylx.massage.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ylx.common.config.WechatAccountConfig;
import com.ylx.common.constant.MassageConstants;
import com.ylx.common.core.domain.R;
import com.ylx.common.exception.ServiceException;
import com.ylx.common.utils.SecurityUtils;
import com.ylx.massage.domain.*;
import com.ylx.massage.domain.vo.*;
import com.ylx.massage.enums.BillTypeEnum;
import com.ylx.massage.enums.DiscountTypeEnum;
import com.ylx.massage.enums.JsStatusEnum;
import com.ylx.massage.enums.OrderStatusEnum;
import com.ylx.massage.mapper.TOrderMapper;
import com.ylx.massage.service.*;
import com.ylx.massage.utils.*;
import com.ylx.point.service.IPointUserActivityTaskCompletionService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;


/**
 * 订单表 服务实现类
 */
@Service
@Slf4j
public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements TOrderService {

    @Resource
    private TOrderMapper orderMapper;

    @Resource
    private WechatAccountConfig wxPayProperties;

    @Resource
    private LocationUtil locationUtil;

    @Resource
    private TWxUserService wxUserService;

    @Resource
    private TRechargeService rechargeService;

    @Resource
    private TXiangmuService xiangmuService;

    @Resource
    private OrderNumberGenerator generator;

    @Resource
    private TJsService jsService;

    @Resource
    private TAddressService addressService;

    @Resource
    private TConsumptionLogService consumptionLogService;

    @Resource
    private MassageUtil massageUtil;

    @Resource
    private CouponReceiveService couponReceiveService;

    @Resource
    private CouponService couponService;

    @Resource
    private WeChatUtil weChatUtil;

    @Resource
    private RefundVoucherService refundVoucherService;

    @Resource
    private OrderValidationService orderValidationService;

    @Resource
    private OrderNotificationService orderNotificationService;

    @Resource
    private IPointUserActivityTaskCompletionService pointUserActivityTaskCompletionService;

    @Resource
    private OrderAllocationLogService allocationLogService;

    @Resource
    private CancelOrderApplicationService cancelOrderApplicationService;

    /**
     * 判断是否免车费
     * 时间段判断：
     * - 白天时段（7:30-20:00）：距离 ≤ 技师白天免车费里程 → 免费
     * - 夜间时段（20:00-7:30）：距离 ≤ 技师夜间免车费里程 → 免费
     *
     * @param js
     * @param distance
     * @return Boolean
     */
    public Boolean isFree(TJs js, BigDecimal distance) {
        Date date = new Date();
        //白天免车费(07.30-20.00)
        long current = Long.parseLong(DateTimeUtils.numTime(date));
        if (current >= MassageConstants.START_FREE && current <= MassageConstants.END_FREE) {
            if (js.getDaytimeMileage().compareTo(distance) >= 0) {
                //免车费
                return Boolean.TRUE;
            } else {
                return Boolean.FALSE;
            }
        } else {
            //夜间免车费(20.00-07.30)
            if (js.getNigthMileage().compareTo(distance) >= 0) {
                //免车费
                return Boolean.TRUE;
            } else {
                return Boolean.FALSE;
            }
        }
    }

    /**
     * 添加订单
     *
     * @param order
     * @return TOrder
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public TOrder addOrder(TOrder order) {
        String jsId = order.getcJsId();
        // 1. 基础参数校验
        if (StringUtils.isBlank(jsId)) {
            throw new ServiceException("请选择技师");
        }
        if (order.getcGoods().isEmpty()) {
            throw new ServiceException("请选择项目");
        }

        TJs js = jsService.getById(jsId);
        if (js == null) {
            throw new ServiceException("技师不存在");
        }
        Integer techType = js.getTechType();
        // 虚拟技师
        if(techType.equals(1)){
            //虚拟技师订单
            order.setVirtualOrderFlag(1);
            //虚拟技师订单未分配
            order.setVirtualOrderAllocation(1);
        }else{
            //真实订单
            order.setVirtualOrderFlag(0);
            order.setVirtualOrderAllocation(0);
        }

        // 2. 【新增】订单状态锁校验 - 检查技师是否可以接单
        // 确保技师没有进行中的订单，保证服务时间互斥
        log.info("开始校验技师 {} 是否可以接单，订单号：{}", order.getcJsId(), order.getOrderNo());
        orderValidationService.canAcceptOrder(order.getcJsId(), order);
        log.info("技师 {} 接单校验通过，继续创建订单", order.getcJsId());

        //优惠卷减免
//        List<CouponReceiveVo> coupons = couponReceiveService.getByOpenId(order.getcOpenId());
//        BigDecimal preferential = this.setCoupon(coupons);
//        order.setPreferential(preferential);
        // 生成订单号
        order.setOrderNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_ORDER));
        //订单价格
        List<TXiangmu> list = JSONObject.parseArray(order.getcGoods().toJSONString(), TXiangmu.class);
        BigDecimal sum = list.stream().map(TXiangmu::getSum).reduce(BigDecimal.ZERO, BigDecimal::add);
        //订单总金额
        order.setdTotalMoney(sum);

        //获取用户默认地址
        TAddress address = addressService.getByOpenId(order.getcOpenId());
        if (address == null) {
            throw new ServiceException("请先添加地址");
        }

        //添加技师位置信息
        locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), Double.parseDouble(js.getLongitude().toString()), Double.parseDouble(js.getLatitude().toString()));
        //添加用户位置信息
        locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, order.getcOpenId() + order.getOrderNo(), Double.parseDouble(address.getLongitude().toString()), Double.parseDouble(address.getLatitude().toString()));
        //计算距离
        double distance = locationUtil.getDistance(js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
        log.info("技师与用户之间的距离：{}", distance);
        locationUtil.remove(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
        order.setDistance(new BigDecimal(distance));

        //计算车费
        if (order.getDistance() != null && order.getDistance().compareTo(BigDecimal.ZERO) > 0 && StringUtils.isBlank(order.getParentNo())) {
            //判断是否可以免车费
            if (!this.isFree(js, order.getDistance())) {
                BigDecimal bigDecimal = massageUtil.calculateTaxiFare(order.getDistance(), js.getDeptId());
                order.setFare(bigDecimal.setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP));
            }
        }
        //总价 = 订单 + 车费
        order.setTotalPrice(sum.add(Optional.ofNullable(order.getFare()).orElse(BigDecimal.ZERO)));
        if (order.getParentNo() != null && order.getOrderType() == 2) {
            //升级订单 补差价
            TOrder partOrder = this.getByNo(order.getParentNo());
            order.setPriceDifference(order.getTotalPrice().subtract(partOrder.getTotalPrice()));
        }

        order.setAddress(address.getAddress());
        order.setName(address.getName());
        order.setLatitude(address.getLatitude());
        order.setLongitude(address.getLongitude());
        order.setcPhone(address.getPhone());
        //设置用户姓名
        order.setcName(address.getUserName());
        order.setAtlasAdd(address.getAtlasAdd());
        order.setDeptId(js.getDeptId());
        order.setDeptName(js.getCity());
        //设置订单状态：待支付
        order.setnStatus(OrderStatusEnum.WAIT_PAY.getCode());
        order.setDtCreateTime(LocalDateTime.now());
        Date date = DateTimeUtils.addMinute(new Date(), 10);
        order.setcTime(DateTimeUtils.formatDate(date, "yyyy-MM-dd HH:mm:ss"));
        save(order);
        return order;
    }

    private BigDecimal setCoupon(List<CouponReceiveVo> coupons) {
        //过滤过期的优惠券
        coupons = coupons.stream().filter(coupon -> coupon.getExpirationTime().after(new Date())).collect(Collectors.toList());
        //无门槛优惠券
        //List<CouponReceiveVo> collect = coupons.stream().filter(coupon -> coupon.getDiscountType().equals(DiscountTypeEnum.NO_THRESHOLD.getCode())).collect(Collectors.toList());
        //支付成功 后 删除优惠卷
//        couponReceiveService.removeCoupons(collect);
        //计算优惠金额
        //return collect.stream().map(CouponReceiveVo::getDiscountValue).reduce(BigDecimal.ZERO, BigDecimal::add);
        return BigDecimal.ZERO;
    }


    @Override
    public void payNotifyOrder(String outTradeNo) {
        //查询未支付的订单
        LambdaQueryWrapper<TOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TOrder::getOrderNo, outTradeNo).eq(TOrder::getnStatus, OrderStatusEnum.WAIT_PAY.getCode());
        TOrder orderNew = this.getOne(queryWrapper);
        if (orderNew == null) {
            log.error("订单 {} 未支付状态不存在", outTradeNo);
            return;
        }
        // 设置微信支付
        orderNew.setPayType(1);
        TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
        orderPayManage(user, orderNew);
    }

    @Override
    public Object updateAddressById(TOrder borrow) {
        TOrder order = this.getById(borrow.getcId());
        if (borrow.getLatitude() != null && borrow.getLatitude() != 0 && borrow.getLongitude() != null && borrow.getLongitude() != 0) {
            order.setAtlasAdd(borrow.getAtlasAdd());
            order.setcName(borrow.getcName());
            order.setcPhone(borrow.getcPhone());
            order.setName(borrow.getName());
            order.setAddress(borrow.getAddress());
            order.setLatitude(borrow.getLatitude());
            order.setLongitude(borrow.getLongitude());
            TJs js = jsService.getById(order.getcJsId());

            //添加技师位置信息
            locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), Double.parseDouble(js.getLongitude().toString()), Double.parseDouble(js.getLatitude().toString()));
            //添加用户位置信息
            locationUtil.geoAdd(LocationUtil.GEO_KEY_USER, order.getcOpenId() + order.getOrderNo(), Double.parseDouble(borrow.getLongitude().toString()), Double.parseDouble(borrow.getLatitude().toString()));
            double distance = locationUtil.getDistance(js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
            locationUtil.remove(LocationUtil.GEO_KEY_USER, js.getcOpenId() + order.getOrderNo(), order.getcOpenId() + order.getOrderNo());
            order.setDistance(new BigDecimal(distance));
            //计算车费
            if (order.getDistance() != null && order.getDistance().compareTo(BigDecimal.ZERO) > 0) {
                //判断是否可以免车费
                if (!this.isFree(js, order.getDistance())) {
                    BigDecimal bigDecimal = massageUtil.calculateTaxiFare(order.getDistance(), order.getDeptId());
                    order.setFare(bigDecimal.setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP));
                }
            }
            order.setTotalPrice(order.getdTotalMoney().add(Optional.ofNullable(order.getFare()).orElse(BigDecimal.ZERO)));
            this.updateById(order);
        }
        return order;
    }

    @Override
    public Object depart(TOrder order) {
        LambdaQueryWrapper<TOrder> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TOrder::getcId, order.getcId()).eq(TOrder::getnStatus, OrderStatusEnum.RECEIVED_ORDER.getCode());
        //设置订单状态：已出发
        order.setnStatus(OrderStatusEnum.DEPART.getCode());
        order.setDepartTime(new Date());
        order.setDepartLongitude(Optional.ofNullable(order.getDepartLongitude()).orElse(BigDecimal.ZERO));
        order.setDepartLatitude(Optional.ofNullable(order.getDepartLatitude()).orElse(BigDecimal.ZERO));
        return this.update(order, wrapper);
    }

    @Override
    public Integer getOrderNum(String jsid, Date startDate, Date endDate) {
        return orderMapper.getOrderNum(jsid, startDate, endDate);
    }

    @Override
    public Integer getAddNum(String jsid, Date startDate, Date endDate) {
        return orderMapper.getAddNum(jsid, startDate, endDate);
    }

    @Override
    public Integer getUpgradeNum(String jsid, Date startDate, Date endDate) {
        return orderMapper.getUpgradeNum(jsid, startDate, endDate);
    }

    @Override
    public BigDecimal getTurnover(String jsid, Date startDate, Date endDate) {
        return orderMapper.getTurnover(jsid, startDate, endDate);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TOrder transferOrder(TOrder order) {
        // ========== 第1步：参数校验 ==========
        if (StringUtils.isBlank(order.getcId())) {
            throw new ServiceException("订单id不能为空");
        }
        if (StringUtils.isBlank(order.getcJsId())) {
            throw new ServiceException("转单技师ID不能为空");
        }

        // 定义操作结果（默认为失败）
        Integer operationResult = 1; // 1-失败

        // 定义操作记录所需的变量
        String orderId = null;
        String orderNo = null;
        String oldTechnicianId = null;
        String oldTechnicianName = null;
        Integer oldTechnicianStatusBefore = null;
        Integer oldTechnicianStatusAfter = null;
        String newTechnicianId = null;
        String newTechnicianName = null;
        Integer newTechnicianStatusBefore = null;
        Integer newTechnicianStatusAfter = null;
        Integer orderStatusBefore = null;
        Integer orderStatusAfter = null;
        String operatorId = null;
        String operatorName = null;
        String operationReason = "虚拟订单分配";

        try {
            // ========== 第2步：查询原订单信息 ==========
            TOrder oldOrder = this.getById(order.getcId());
            if (oldOrder == null) {
                throw new ServiceException("订单不存在");
            }

            //原技师ID
            oldTechnicianId = oldOrder.getcJsId();
            //新技师ID
            newTechnicianId = order.getcJsId();

            // 记录订单操作前状态
            orderStatusBefore = oldOrder.getnStatus();
            orderId = oldOrder.getcId();
            orderNo = oldOrder.getOrderNo();

            log.info("开始转单操作 - 订单号：{}, 原技师ID：{}, 新技师ID：{}, 订单状态：{}", oldOrder.getOrderNo(), oldTechnicianId, newTechnicianId, orderStatusBefore);

            // ========== 第3步：查询原技师信息 ==========
            TJs oldTechnician = jsService.getById(oldTechnicianId);
            if (oldTechnician == null) {
                throw new ServiceException("原技师不存在");
            }
            oldTechnicianName = oldTechnician.getcName();
            oldTechnicianStatusBefore = oldTechnician.getnStatus();

            // ========== 第4步：查询新技师信息 ==========
            TJs newTechnician = jsService.getById(newTechnicianId);
            if (newTechnician == null) {
                throw new ServiceException("新技师不存在");
            }
            newTechnicianName = newTechnician.getcName();
            newTechnicianStatusBefore = newTechnician.getnStatus();

            // ========== 第5步：更新订单技师信息 ==========
            oldOrder.setOldJsId(oldTechnicianId);  // 保存原技师ID
            oldOrder.setcJsId(newTechnicianId);      // 更新为新技师ID

            log.info("更新订单技师 - 订单号：{}, 原技师：[ID:{}, 姓名:{}], 新技师：[ID:{}, 姓名:{}]", oldOrder.getOrderNo(), oldTechnicianId, oldTechnicianName, newTechnicianId, newTechnicianName);

            if (!this.updateById(oldOrder)) {
                throw new ServiceException("转单失败：更新订单技师信息失败");
            }

            // 记录订单操作后状态（转单后状态通常保持不变）
            orderStatusAfter = oldOrder.getnStatus();

            // ========== 第6步：更新新技师状态（可服务 → 服务中）==========
            TJs newJsUpdate = new TJs();
            newJsUpdate.setId(newTechnicianId);
            newJsUpdate.setnStatus(JsStatusEnum.JS_SERVICE.getCode());
            if (!jsService.updateById(newJsUpdate)) {
                throw new ServiceException("转单失败：更新新技师状态失败");
            }
            newTechnicianStatusAfter = JsStatusEnum.JS_SERVICE.getCode();

            // ========== 第7步：更新原技师状态（服务中 → 可服务）==========
            TJs oldJsUpdate = new TJs();
            oldJsUpdate.setId(oldTechnicianId);
            oldJsUpdate.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
            if (!jsService.updateById(oldJsUpdate)) {
                throw new ServiceException("转单失败：更新原技师状态失败");
            }
            oldTechnicianStatusAfter = JsStatusEnum.JS_SERVICEABLE.getCode();

            log.info("更新技师状态完成 - 新技师：{} {}→{}, 原技师：{} {}→{}",
                newTechnicianName, getStatusName(newTechnicianStatusBefore), getStatusName(newTechnicianStatusAfter),
                oldTechnicianName, getStatusName(oldTechnicianStatusBefore), getStatusName(oldTechnicianStatusAfter));

            // ========== 第8步：获取操作人信息 ==========
            operatorId = SecurityUtils.getUserId() != null ? SecurityUtils.getUserId().toString() : "ADMIN";
            operatorName = SecurityUtils.getUsername() != null ? SecurityUtils.getUsername() : "系统管理员";

            // ========== 第9步：转单成功，设置操作结果为成功 ==========
            operationResult = 0; // 0-成功
            log.info("转单操作完成 - 订单号：{}", oldOrder.getOrderNo());
            return oldOrder;
        } catch (ServiceException e) {
            // 业务异常，操作失败
            log.error("转单操作失败 - 订单号：{}, 错误信息：{}", orderNo, e.getMessage());
            operationResult = 1; // 1-失败
            throw e;

        } catch (Exception e) {
            // 系统异常，操作失败
            log.error("转单操作异常 - 订单号：{}, 异常信息：{}", orderNo, e.getMessage(), e);
            operationResult = 1; // 1-失败
            throw new ServiceException("转单操作异常：" + e.getMessage());
        } finally {
            // ========== 第10步：记录转单操作日志（无论成功或失败都记录）==========
            try {
                // 只有在获取到基本信息后才记录日志
                if (orderId != null && orderNo != null && oldTechnicianId != null && newTechnicianId != null) {
                    allocationLogService.recordTransferOrder(
                            orderId,                     // orderId
                            orderNo,                     // orderNo
                            oldTechnicianId,             // oldTechnicianId
                            oldTechnicianName,           // oldTechnicianName
                            oldTechnicianStatusBefore,   // oldTechnicianStatusBefore
                            oldTechnicianStatusAfter,    // oldTechnicianStatusAfter
                            newTechnicianId,             // newTechnicianId
                            newTechnicianName,           // newTechnicianName
                            newTechnicianStatusBefore,   // newTechnicianStatusBefore
                            newTechnicianStatusAfter,    // newTechnicianStatusAfter
                            orderStatusBefore,           // orderStatusBefore
                            orderStatusAfter,            // orderStatusAfter
                            operatorId,                  // operatorId
                            operatorName,                // operatorName
                            operationReason,             // operationReason
                            operationResult              // operationResult（0-成功，1-失败）
                    );

                    String resultDesc = operationResult == 0 ? "成功" : "失败";
                    log.info("转单操作记录已保存 - 订单号：{}, 操作结果：{}", orderNo, resultDesc);
                }
            } catch (Exception e) {
                // 记录日志失败不影响转单操作
                log.error("记录转单操作日志失败 - 订单号：{}, 错误信息：{}", orderNo, e.getMessage(), e);
            }
        }
    }

    /**
     * 获取技师状态名称
     *
     * @param status 状态码
     * @return String 状态名称
     */
    private String getStatusName(Integer status) {
        if (status == null) {
            return "未知";
        }
        switch (status) {
            case 0:
                return "可服务";
            case 1:
                return "服务中";
            case 2:
                return "不可服务";
            default:
                return "未知(" + status + ")";
        }
    }

    @Override
    public List<HomeBlock> getBlock(Date start, Date end, String deptId) {
        return orderMapper.getBlock(start, end, deptId);

    }

    @Override
    public OrderVerificationVo verification(TOrder order) {
        if (StringUtils.isBlank(order.getCouponReceiveId())) {
            throw new ServiceException("认领优惠券id为空");
        }
        if (StringUtils.isBlank(order.getcId())) {
            throw new ServiceException("订单id为空");
        }
        OrderVerificationVo orderVerificationVo = new OrderVerificationVo();
        TOrder tOrder = this.getById(order.getcId());
        orderVerificationVo.setCouponReceiveId(order.getCouponReceiveId());
        CouponReceive couponReceive = couponReceiveService.getById(order.getCouponReceiveId());
        Coupon coupon = couponService.getById(couponReceive.getCouponId());
        log.info("订单信息,{}", tOrder);
        log.info("优惠卷信息,{}", coupon);


        //折扣券
        if (coupon.getDiscountType() == 2) {
            //判断门槛金额
            if (tOrder.getTotalPrice().compareTo(coupon.getThresholdAmount()) >= 0) {
                //折扣值
                BigDecimal divide = coupon.getRebValue().divide(new BigDecimal(10));
                //优惠后的金额 = 订单总金额*折扣值
                BigDecimal bigDecimal = tOrder.getTotalPrice().multiply(divide).setScale(MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP);
                //优惠值
                orderVerificationVo.setPreferential(tOrder.getTotalPrice().subtract(bigDecimal));
                orderVerificationVo.setTotalPrice(bigDecimal);
            } else {
                throw new ServiceException("不满足优惠券门槛金额");
            }
        } else {
            if (tOrder.getTotalPrice().compareTo(coupon.getThresholdAmount()) >= 0) {
                //优惠值
                orderVerificationVo.setPreferential(coupon.getDiscountValue());
                orderVerificationVo.setTotalPrice(tOrder.getTotalPrice().subtract(coupon.getDiscountValue()));
            } else {
                throw new ServiceException("不满足优惠券门槛金额");
            }
        }
        if (orderVerificationVo.getTotalPrice().compareTo(BigDecimal.ZERO) < 0) {
            throw new ServiceException("当前项目不可用");
        }
        return orderVerificationVo;
    }

    private TOrder getOrder(TOrder tOrder) {
        if (updateById(tOrder)) {
            return tOrder;
        } else {
            throw new ServiceException("优惠券核销失败");
        }
    }


    /**
     * 支付订单
     *
     * @param order
     * @return R
     */
    @Override
    public R payOrder(TOrder order) throws Exception {
        // 根据订单ID查询订单信息
        TOrder orderNew = getById(order.getcId());
        if (!orderNew.getnStatus().equals(OrderStatusEnum.WAIT_PAY.getCode())) {
            throw new ServiceException("该订单已经支付或者超时被取消");
        }
        TJs js = jsService.getById(orderNew.getcJsId());
        if (StringUtils.isBlank(orderNew.getParentNo())) {
            if (null == js || js.getnStatus().equals(JsStatusEnum.JS_SERVICE.getCode())) {
                throw new ServiceException("该技师已在服务中请重新下单");
            }
        }

        orderNew.setPayType(order.getPayType());
        //优惠券核销
        if (StringUtils.isNotBlank(order.getCouponReceiveId())) {
            orderNew.setCouponReceiveId(order.getCouponReceiveId());
            orderNew.setPreferential(order.getPreferential());
            orderNew.setTotalPrice(order.getTotalPrice());
            if (!updateById(orderNew)) {
                throw new ServiceException("支付失败");
            }
        }
        //判断支付方式
        if (order.getPayType().equals(MassageConstants.INTEGER_ONE)) {
            //微信支付
            R resp = rechargeService.getPay(orderNew.getOrderNo(), orderNew.getTotalPrice(), orderNew.getcOpenId(), BillTypeEnum.WX_PAY.getInfo(), BillTypeEnum.WX_PAY.getCode().toString());

            //添加待接单消息通知（技师侧）这块的逻辑在回调接口中
            //orderNotificationService.sendPendingRemindNotification(orderNew);
            return resp;
        }

        TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
        if (null == user) {
            throw new ServiceException("用户不存在");
        }
        //现金支付
        if (order.getPayType().equals(MassageConstants.INTEGER_THREE)) {
            //现金支付
            orderPayManage(user, orderNew);
            //添加待接单消息通知（技师侧）
            orderNotificationService.sendPendingRemindNotification(orderNew);
            return R.ok();
        }
        if (user.getdBalance().compareTo(orderNew.getTotalPrice()) < MassageConstants.INTEGER_ZERO) {
            throw new ServiceException("账户金额不够请充值");
        } else {
            //余额支付
            orderPayManage(user, orderNew);
            //添加待接单消息通知（技师侧）
            orderNotificationService.sendPendingRemindNotification(orderNew);
            return R.ok();
        }
    }



    /**
     * 新订单通知
     *
     * @param order
     */
    public void newOrderNotification(TOrder order) {
        cn.hutool.json.JSONObject param = JSONUtil.createObj();
        //订单号
        param.set("character_string9", JSONUtil.createObj().set("value", order.getOrderNo()));
        //电话
        param.set("phone_number14", JSONUtil.createObj().set("value", order.getcPhone()));
        param.set("thing18", JSONUtil.createObj().set("value", order.getcName()));
        param.set("time6", JSONUtil.createObj().set("value", DateTimeUtils.formatDate(new Date(), DateTimeUtils.DATE_NUMBER_YEAR_MONTH_FORMAT)));
        param.set("thing27", JSONUtil.createObj().set("value", order.getName()));
        TJs js = jsService.getById(order.getcJsId());
        weChatUtil.notification(js.getcOpenId(), wxPayProperties.getTemplateId1(), param);

    }

    /**
     * 订单支付管理
     *
     * @param user
     * @param orderNew
     */
    @Transactional(rollbackFor = Exception.class)
    public void orderPayManage(TWxUser user, TOrder orderNew) {
        //更新优惠卷状态
        if (StringUtils.isNotBlank(orderNew.getCouponReceiveId())) {
            CouponReceive couponReceive = new CouponReceive();
            couponReceive.setId(orderNew.getCouponReceiveId());
            couponReceive.setCouponStatus(MassageConstants.INTEGER_TWO);
            if (!couponReceiveService.updateById(couponReceive)) {
                log.error("优惠券状态更新失败id:,{}", orderNew.getCouponReceiveId());
            }
        }

        // 更新用户金额 及下单此时
        TWxUser paramUser = new TWxUser();
        paramUser.setcOpenid(user.getcOpenid());
        // 余额支付
        if (orderNew.getPayType().equals(MassageConstants.INTEGER_TWO)) {
            paramUser.setdBalance(user.getdBalance().subtract(orderNew.getTotalPrice()));
        }
        paramUser.setdMoney(user.getdMoney().add(orderNew.getTotalPrice()));
        paramUser.setnNum(user.getnNum() + MassageConstants.INTEGER_ONE);
        paramUser.setId(user.getId());
        wxUserService.updateById(paramUser);
        //增加个人消费记录
        TConsumptionLog tConsumptionLog = new TConsumptionLog();
        tConsumptionLog.setAmount(orderNew.getTotalPrice().negate());
        tConsumptionLog.setBillNo(orderNew.getOrderNo());
        tConsumptionLog.setOpenId(orderNew.getcOpenId());
        // 余额支付
        if (orderNew.getPayType().equals(MassageConstants.INTEGER_TWO)) {
            tConsumptionLog.setBillType(BillTypeEnum.BALANCE_PAYMENT.getCode());
            tConsumptionLog.setNote("余额支付");
        } else if(orderNew.getPayType().equals(MassageConstants.INTEGER_ONE)){
            tConsumptionLog.setBillType(BillTypeEnum.WX_PAY.getCode());
            tConsumptionLog.setNote("微信支付");
        } else {
            tConsumptionLog.setBillType(BillTypeEnum.CASH_PAYMENT.getCode());
            tConsumptionLog.setNote("现金支付");
        }
        consumptionLogService.save(tConsumptionLog);

        // 更新项目数据
        JSONArray objects = orderNew.getcGoods();
        objects.forEach(item -> {
            UpdateWrapper<TXiangmu> wrapper = new UpdateWrapper<>();
            // 获取参数
            wrapper.lambda().eq(TXiangmu::getcId, ((JSONObject) item).getString("cId"));
            // 设置数量
            wrapper.setSql(" n_sale_number = n_sale_number + " + ((JSONObject) item).getInteger("number"));
            xiangmuService.update(wrapper);
        });
        TOrder orderParam = new TOrder();
        orderParam.setPayType(orderNew.getPayType());
        orderParam.setcId(orderNew.getcId());
        orderParam.setnStatus(OrderStatusEnum.WAIT_JD.getCode());
        orderParam.setPayTime(new Date());
        //加钟的订单支付完直接服务中
        if (StringUtils.isNotBlank(orderNew.getParentNo())) {
            orderParam.setnStatus(OrderStatusEnum.SERVICE.getCode());
        }
//            orderParam.setnStatus(OrderStatusEnum.SERVICE.getCode());
        //更新技师状态
        updateJs(orderNew);
        // 更新订单的信息
        updateById(orderParam);

        //添加待接单消息通知（技师侧）
        orderNotificationService.sendPendingRemindNotification(orderNew);
        //电话通知
        TJs js = jsService.getById(orderNew.getcJsId());
        Sendvoice.sendPhone(js.getcPhone());
    }

    /**
     * 拒绝订单
     *
     * @param order
     * @return Boolean 是否成功
     */
    @Override
    public Boolean jujue(TOrder order) {

        TOrder orderNew = getById(order.getcId());

        TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
        // 更新用户金额 及下单此时
        TWxUser paramUser = new TWxUser();
        paramUser.setcOpenid(user.getcOpenid());
        paramUser.setId(user.getId());
        // 余额记录
        TConsumptionLog tConsumptionLog = new TConsumptionLog();
        tConsumptionLog.setAmount(orderNew.getTotalPrice());
        tConsumptionLog.setBillNo(orderNew.getOrderNo());
        tConsumptionLog.setOpenId(orderNew.getcOpenId());
        if (orderNew.getPayType() == 2) {
            // 金额归还对应账户
            paramUser.setdBalance(user.getdBalance().add(orderNew.getTotalPrice()));
            tConsumptionLog.setBillType(BillTypeEnum.REFUSE_ACCEPT_REFUND.getCode());
            tConsumptionLog.setNote("拒绝接单退款到余额");

        } else {
            // 微信支付
            // 生成退款单退款
            RefundVoucher refundVoucher = new RefundVoucher();
            refundVoucher.setRefundNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_REFUND));
            refundVoucher.setOrderNo(orderNew.getOrderNo());
            refundVoucher.setMoney(orderNew.getTotalPrice());
            refundVoucher.setOpenId(orderNew.getcOpenId());
            refundVoucher.setReStatus(MassageConstants.INTEGER_ZERO);
            refundVoucher.setReason("技师拒绝接单");
            refundVoucherService.save(refundVoucher);
            tConsumptionLog.setBillType(BillTypeEnum.REFUSE_ACCEPT_REFUND.getCode());
            tConsumptionLog.setNote("拒绝接单退款到余额");
            // 微信退款原路返回
            rechargeService.refund(refundVoucher.getRefundNo(), null, orderNew.getOrderNo(), orderNew.getTotalPrice());
        }
        consumptionLogService.save(tConsumptionLog);
        //退优惠卷
        if (StringUtils.isNotBlank(orderNew.getCouponReceiveId())) {
            CouponReceive couponReceive = couponReceiveService.getById(orderNew.getCouponReceiveId());
            couponReceive.setCouponStatus(MassageConstants.INTEGER_ZERO);
            couponReceiveService.updateById(couponReceive);
        }
        log.info("余额支付退款user:{}", user);

        // 消费金额对应减少
        paramUser.setdMoney(user.getdMoney().subtract(orderNew.getTotalPrice()));
        // 下单次数减一
        paramUser.setnNum(user.getnNum() - MassageConstants.INTEGER_ONE);
        wxUserService.updateById(paramUser);

        // 更新项目数据
        JSONArray objects = orderNew.getcGoods();
        objects.forEach(item -> {
            UpdateWrapper<TXiangmu> wrapper = new UpdateWrapper<>();
            // 获取参数
            wrapper.lambda().eq(TXiangmu::getcId, ((JSONObject) item).getString("cId"));
            // 设置数量
            wrapper.setSql(" n_sale_number = n_sale_number - " + ((JSONObject) item).getInteger("number"));
            xiangmuService.update(wrapper);
        });
        TOrder orderParam = new TOrder();
        orderParam.setcId(orderNew.getcId());
        orderParam.setnStatus(OrderStatusEnum.REFUSE.getCode());
        orderParam.setReasonRefusal(order.getReasonRefusal());
        updateJs(orderNew);
        return updateById(orderParam);
    }

    /**
     * 确认服务完成
     *
     * @param order
     * @return Boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean confirm(TOrder order) {
        // 获取订单信息
        TOrder orderNew = getById(order.getcId());
        if (!orderNew.getnStatus().equals(OrderStatusEnum.SERVICE.getCode())) {
            throw new ServiceException("订单状态不是服务中");
        }
        // 更新技师信息
        TJs jsParam = new TJs();
        jsParam.setId(orderNew.getcJsId());
        jsParam.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
        //判断热度标识
        List<TOrder> list = list(new LambdaQueryWrapper<TOrder>().eq(TOrder::getcJsId, orderNew.getcJsId())
                .ge(TOrder::getDtCreateTime, DateTimeUtils.addDays(new Date(), -3))
                .ge(TOrder::getnStatus, OrderStatusEnum.WAIT_EVALUATE.getCode()));
        if (list.size() >= 2) {
            // 设置热度标识：1
            jsParam.setnB3(MassageConstants.INTEGER_ONE);
        }
        // 更新技师状态
        jsService.updateById(jsParam);
        // 更新技师钱包金额
        TJs jsById = jsService.getById(orderNew.getcJsId());
        // 获取技师抽成
        BigDecimal multiply = orderNew.getTotalPrice().multiply(new BigDecimal(jsById.getnBili()));
        multiply = multiply.divide(new BigDecimal(100), MassageConstants.INTEGER_TWO, RoundingMode.HALF_UP);
        // 获取技师所对应的用户
        TWxUser jsUser = wxUserService.getByOpenId(jsById.getcOpenId());
        // 更新余额
        jsUser.setdBalance(jsUser.getdBalance().add(multiply));
        // 更新总钱数
        jsUser.setdAllMoney(jsUser.getdAllMoney().add(multiply));
        wxUserService.updateById(jsUser);

        //增加消费记录
        TConsumptionLog tConsumptionLog = new TConsumptionLog();
        tConsumptionLog.setAmount(multiply);
        tConsumptionLog.setBillNo(orderNew.getOrderNo());
        tConsumptionLog.setOpenId(jsUser.getcOpenid());
        tConsumptionLog.setBillType(BillTypeEnum.INCOME.getCode());
        tConsumptionLog.setNote("技师收益");
        consumptionLogService.save(tConsumptionLog);

        // 如果该技师有推荐人员 一级
        if (StringUtils.isNotBlank(jsUser.getcUpUser())) {
            // 获取技师上级对应的用户
            TWxUser jsUp = wxUserService.getByOpenId(jsUser.getcUpUser());
            extracted(orderNew, jsUp);
            //二级
            if (StringUtils.isNotBlank(jsUp.getcUpUser())) {
                TWxUser jsUpTwo = wxUserService.getByOpenId(jsUp.getcUpUser());
                extracted(orderNew, jsUpTwo);
                //三级
                if (StringUtils.isNotBlank(jsUpTwo.getcUpUser())) {
                    TWxUser jsUpThree = wxUserService.getByOpenId(jsUpTwo.getcUpUser());
                    extracted(orderNew, jsUpThree);
                }
            }
        }
        // 更新订单
        // 订单状态：待评价
        orderNew.setnStatus(OrderStatusEnum.WAIT_EVALUATE.getCode());
        orderNew.setEndTime(LocalDateTime.now());
        updateById(orderNew);

        // 添加订单完成消息通知（用户侧）
        orderNotificationService.sendCompletedNotification(orderNew);
        
        // 完成积分任务（按照新手活动、每日活动、每月活动的优先级顺序）
        try {
            this.pointUserActivityTaskCompletionService.completeOrderTaskByPriority(orderNew.getcOpenId());
        } catch (Exception e) {
            log.error("完成积分任务失败 - 订单号：{}, 错误信息：{}", orderNew.getOrderNo(), e.getMessage(), e);
        }
        
        return true;
    }

    private void extracted(TOrder orderNew, TWxUser jsUp) {
        log.info("TOrderServiceImpl->extracted->jsUp,{}", JSONUtil.toJsonStr(jsUp));
        log.info("TOrderServiceImpl->extracted->orderNew,{}",JSONUtil.toJsonStr(orderNew));
        BigDecimal up = orderNew.getdTotalMoney().multiply(new BigDecimal("10"));
        up = up.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
        // 更新余额
        jsUp.setdBalance(jsUp.getdBalance().add(up));
        // 更新总钱数
        jsUp.setdAllMoney(jsUp.getdAllMoney().add(up));
        jsUp.setDistributionAmount(up);
        wxUserService.updateById(jsUp);

        //记录分销收益
        TConsumptionLog tConsumptionLog = new TConsumptionLog();
        tConsumptionLog.setAmount(up);
        tConsumptionLog.setBillNo(orderNew.getOrderNo());
        tConsumptionLog.setOpenId(jsUp.getcOpenid());
        tConsumptionLog.setBillType(BillTypeEnum.DISTRIBUTION.getCode());
        tConsumptionLog.setNote("分销收益");
        consumptionLogService.save(tConsumptionLog);
    }

    /**
     * 获取技师当天可预约时间
     *
     * @param technicianId 技师ID
     * @param dateStr 查询日期（格式：yyyy-MM-dd），为null则查询当天
     * @return TechnicianAvailabilityVo 技师当天可预约时间VO
     */
    @Override
    public TechnicianAvailabilityVo getTechnicianAvailability(String technicianId, String dateStr) {
        // 1. 参数校验
        if (StringUtils.isBlank(technicianId)) {
            throw new ServiceException("技师ID不能为空");
        }

        // 2. 查询技师信息
        TJs js = jsService.getById(technicianId);
        if (js == null) {
            throw new ServiceException("技师不存在");
        }

        // 3. 解析日期，默认为当天
        LocalDate queryDate;
        if (StringUtils.isBlank(dateStr)) {
            queryDate = LocalDate.now();
        } else {
            try {
                queryDate = LocalDate.parse(dateStr);
            } catch (Exception e) {
                throw new ServiceException("日期格式错误，请使用 yyyy-MM-dd 格式");
            }
        }

        // 4. 定义当天的所有时间段（以30分钟为间隔，从00:00到23:30）
        List<TimeSlotVo> timeSlots = new ArrayList<>();
        for (int hour = 0; hour < 24; hour++) {
            // 每小时生成两个时间段：xx:00 和 xx:30
            timeSlots.add(TimeSlotVo.builder()
                    .time(String.format("%02d:00", hour))
                    .available(true)
                    .build());
            timeSlots.add(TimeSlotVo.builder()
                    .time(String.format("%02d:30", hour))
                    .available(true)
                    .build());
        }

        // 5. 查询技师当天所有进行中的订单
        // 开始时间
        LocalDateTime startOfDay = queryDate.atStartOfDay();
        // 结束时间
        LocalDateTime endOfDay = queryDate.plusDays(1).atStartOfDay();
        log.info("开始时间：{}，结束时间：{}", startOfDay, endOfDay);

        LambdaQueryWrapper<TOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TOrder::getcJsId, technicianId)
                .in(TOrder::getnStatus, OrderStatusEnum.WAIT_JD.getCode(),
                        OrderStatusEnum.RECEIVED_ORDER.getCode(),
                        OrderStatusEnum.DEPART.getCode(),
                        OrderStatusEnum.ARRIVED.getCode(),
                        OrderStatusEnum.SERVICE.getCode())
                .ge(TOrder::getDtCreateTime, startOfDay)
                .lt(TOrder::getDtCreateTime, endOfDay)
                .eq(TOrder::getIsDelete, 0);

        List<TOrder> orders = this.list(queryWrapper);
        log.info("技师{}，在{}天共有 {} 个进行中的订单", technicianId, queryDate, orders.size());

        // 6. 标记不可预约的时间段
        LocalDateTime now = LocalDateTime.now();
        for (TOrder order : orders) {
            // 6.1 计算订单的开始时间和结束时间
            LocalDateTime orderStart = OrderTimeRangeUtils.estimateStartTime(order);
            LocalDateTime orderEnd = OrderTimeRangeUtils.estimateEndTime(order);

            if (orderStart == null || orderEnd == null) {
                log.warn("订单 {} 的时间信息不完整，跳过", order.getOrderNo());
                continue;
            }

            // 6.2 限制在查询日期范围内
            LocalDateTime effectiveStart = orderStart.isBefore(startOfDay) ? startOfDay : orderStart;
            LocalDateTime effectiveEnd = orderEnd.isAfter(endOfDay) ? endOfDay : orderEnd;
            // 6.3 标记不可预约的时间段
            markTimeSlotsUnavailable(timeSlots, effectiveStart, effectiveEnd, order.getOrderNo());
        }

        // 7. 根据查询日期判断是否可预约
        LocalDate today = LocalDate.now();
        if (queryDate.isBefore(today)) {
            // 查询日期是过去的日期，所有时间段都不可预约
            markAllTimeSlotsUnavailable(timeSlots, "日期已过期");
        } else if (queryDate.equals(today)) {
            // 查询日期是今天，标记过去的时间为不可预约
            markPastTimeSlotsUnavailable(timeSlots, now);
        }
        // 查询日期是未来的日期，所有时间段默认可预约，无需处理
        // 8. 构建返回结果
        return TechnicianAvailabilityVo.builder()
                .date(queryDate.toString())
                .technicianId(technicianId)
                .technicianName(js.getcName())
                .timeSlots(timeSlots)
                .build();
    }

    /**
     * 标记指定时间范围内的时间段为不可预约
     *
     * @param timeSlots 时间段列表
     * @param start 开始时间
     * @param end 结束时间
     * @param orderNo 订单号
     */
    private void markTimeSlotsUnavailable(List<TimeSlotVo> timeSlots, LocalDateTime start, LocalDateTime end, String orderNo) {
        LocalTime startTime = start.toLocalTime();
        LocalTime endTime = end.toLocalTime();

        for (TimeSlotVo slot : timeSlots) {
            LocalTime slotTime = LocalTime.parse(slot.getTime());

            // 判断时间段是否在订单时间范围内
            boolean isInRange = !slotTime.isBefore(startTime) && slotTime.isBefore(endTime);
            if (isInRange) {
                slot.setAvailable(false);
                slot.setReason("已有订单");
                slot.setOrderNo(orderNo);
            }
        }
    }

    /**
     * 标记所有时间段为不可预约
     *
     * @param timeSlots 时间段列表
     * @param reason 不可预约原因
     */
    private void markAllTimeSlotsUnavailable(List<TimeSlotVo> timeSlots, String reason) {
        for (TimeSlotVo slot : timeSlots) {
            slot.setAvailable(false);
            slot.setReason(reason);
            slot.setOrderNo(null);
        }
    }

    /**
     * 标记过去的时间段为不可预约
     *
     * @param timeSlots 时间段列表
     * @param now 当前时间
     */
    private void markPastTimeSlotsUnavailable(List<TimeSlotVo> timeSlots, LocalDateTime now) {
        LocalTime currentTime = now.toLocalTime();
        for (TimeSlotVo slot : timeSlots) {
            LocalTime slotTime = LocalTime.parse(slot.getTime());

            // 如果当前时间已经过了这个时间段，标记为不可预约
            if (slotTime.isBefore(currentTime)) {
                slot.setAvailable(false);
                slot.setReason("已过期");
                slot.setOrderNo(null);
            }
        }
    }

    /**
     * 取消订单
     *
     * @param order
     * @return Boolean
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean cancle(TOrder order) {
        // 获取订单信息
        // 根据orderid查询订单信息
        TOrder orderNew = getById(order.getcId());
        //待接单
        if (Objects.equals(orderNew.getnStatus(), OrderStatusEnum.WAIT_JD.getCode())) {
            TWxUser user = wxUserService.getByOpenId(orderNew.getcOpenId());
            // 更新用户金额 及下单此时
            TWxUser paramUser = new TWxUser();
            paramUser.setId(user.getId());
            paramUser.setcOpenid(user.getcOpenid());

            TConsumptionLog tConsumptionLog = new TConsumptionLog();
            tConsumptionLog.setAmount(orderNew.getTotalPrice());
            tConsumptionLog.setBillNo(orderNew.getOrderNo());
            tConsumptionLog.setOpenId(orderNew.getcOpenId());
            // 余额支付
            if (orderNew.getPayType() == 2) {
                // 金额归还对应账户
                paramUser.setdBalance(user.getdBalance().add(orderNew.getTotalPrice()));
                // 余额记录
                tConsumptionLog.setBillType(BillTypeEnum.CANCEL_ACCEPT_REFUND.getCode());
                tConsumptionLog.setNote("取消订单退款到余额");
                //自己取消的不退优惠卷
            } else {
                // 微信支付
                // 生成退款单退款
                RefundVoucher refundVoucher = new RefundVoucher();
                refundVoucher.setRefundNo(generator.generateNextOrderNumber(OrderNumberGenerator.KEY_PREFIX_REFUND));
                refundVoucher.setOrderNo(orderNew.getOrderNo());
                refundVoucher.setMoney(orderNew.getTotalPrice());
                refundVoucher.setOpenId(orderNew.getcOpenId());
                refundVoucher.setReStatus(MassageConstants.INTEGER_ZERO);
                refundVoucher.setReason("技师拒绝接单");
                refundVoucherService.save(refundVoucher);
                tConsumptionLog.setBillType(BillTypeEnum.CANCEL_WX_REFUND.getCode());
                tConsumptionLog.setNote("取消订单退款到微信");
                // 微信退款原路返回
                rechargeService.refund(refundVoucher.getRefundNo(), null, orderNew.getOrderNo(), orderNew.getTotalPrice());
            }
            consumptionLogService.save(tConsumptionLog);
            // 消费金额对应减少
            paramUser.setdMoney(user.getdMoney().subtract(orderNew.getTotalPrice()));
            // 下单次数减一
            paramUser.setnNum(user.getnNum() - MassageConstants.INTEGER_ONE);
            wxUserService.updateById(paramUser);

            // 更新项目数据
            JSONArray objects = orderNew.getcGoods();
            objects.forEach(item -> {
                UpdateWrapper<TXiangmu> wrapper = new UpdateWrapper<>();
                // 获取参数
                wrapper.lambda().eq(TXiangmu::getcId, ((JSONObject) item).getString("cId"));
                // 设置数量
                wrapper.setSql(" n_sale_number = n_sale_number - " + ((JSONObject) item).getInteger("number"));
                xiangmuService.update(wrapper);
            });
            TOrder orderParam = new TOrder();
            orderParam.setcId(orderNew.getcId());
            orderParam.setnStatus(OrderStatusEnum.CANCEL.getCode());
            //更新技师状态
            TJs tJs = new TJs();
            tJs.setId(orderNew.getcJsId());
            tJs.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
            jsService.updateById(tJs);
            updateById(orderParam);

            // 添加取消订单通知（用户侧）
            orderNotificationService.sendCancelledNotification(orderNew);

            // 添加取消订单通知（技师侧）
            orderNotificationService.sendTechnicianCancelledNotification(orderNew);
            return true;
        } else if (Objects.equals(orderNew.getnStatus(), OrderStatusEnum.WAIT_PAY.getCode())) {//待付款
            TOrder orderParam = new TOrder();
            orderParam.setcId(orderNew.getcId());
            orderParam.setnStatus(OrderStatusEnum.CANCEL.getCode());
            updateById(orderParam);
            // 添加取消订单通知
            orderNotificationService.sendCancelledNotification(orderNew);

            // 添加取消订单通知（技师侧）
            orderNotificationService.sendTechnicianCancelledNotification(orderNew);
            return true;
        } else {
            return false;
        }

    }

    @Override
    public TOrder getByNo(String orderNo) {
        LambdaQueryWrapper<TOrder> objectLambdaQueryWrapper = new LambdaQueryWrapper<>();
        return this.getOne(objectLambdaQueryWrapper.eq(TOrder::getOrderNo, orderNo));
    }

//    private TOrder gettOrder(TOrder order) {
//        LambdaUpdateWrapper<TOrder> objectLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
//        objectLambdaUpdateWrapper.eq(TOrder::getOrderNo, order.getOrderNo());
//        return this.getOne(objectLambdaUpdateWrapper);
//    }


    @Override
    public Page<TOrder> getAll(Page<TOrder> page, TOrder order) {
        Page<TOrder> orderPage = orderMapper.getAll(page, order);
        if (orderPage != null && CollectionUtil.isNotEmpty(orderPage.getRecords())) {

            ArrayList<TOrder> ordersList = Lists.newArrayList();
            orderPage.getRecords().forEach(orders -> {
                orders.setStatusName(OrderStatusEnum.getDescByCode(orders.getnStatus()));
                orders.setJsPhone(orders.getJs().getcPhone());
                orders.setJsName(orders.getJs().getcName());
                if (StringUtils.isEmpty(orders.getcTime())) {
                    orders.setRemainingTime(0L);
                }
                if (StringUtils.isNotBlank(orders.getcTime()) && DateTimeUtils.dateStringToStamp(orders.getcTime()) > DateTimeUtils.dateToStamp(new Date())) {
                    orders.setRemainingTime((DateTimeUtils.dateStringToStamp(orders.getcTime()) - DateTimeUtils.dateToStamp(new Date())) / 1000);
                }
                if (StringUtils.isNotBlank(orders.getcTime()) && DateTimeUtils.dateStringToStamp(orders.getcTime()) < DateTimeUtils.dateToStamp(new Date())) {
                    orders.setRemainingTime(0L);
                }
                if (StringUtils.isNotBlank(orders.getOldJsId())) {
                    orders.setOldJs(jsService.getById(orders.getOldJsId()));
                }
                // 计算已服务时长
                Long serviceDuration = calculateServiceDuration(orders);
                orders.setServiceDuration(String.valueOf(serviceDuration));
                ordersList.add(orders);

            });
            orderPage.setRecords(ordersList);
        }
        return orderPage;
    }


    /**
     * 计算已服务时长（分钟）
     *
     * @param order 订单对象
     * @return Long 已服务时长（分钟）
     */
    private Long calculateServiceDuration(TOrder order) {
        try {
            LocalDateTime startTime = order.getStartTime();
            LocalDateTime endTime = order.getEndTime();

            if (startTime != null && endTime != null) {
                long minutes = Duration.between(startTime, endTime).toMinutes();
                if (minutes > 0) {
                    return minutes;
                }
            } else if (startTime != null) {
                // 如果只有开始时间，计算到现在的时长
                long minutes = Duration.between(startTime, LocalDateTime.now()).toMinutes();
                if (minutes > 0) {
                    return minutes;
                }
            }
        } catch (Exception e) {
            log.warn("计算服务时长失败，订单ID：{}", order.getcId(), e);
        }
        return 0L;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void takingOrders(TOrder order) {
        String orderId = order.getcId();
        if (orderId == null || StringUtils.isBlank(orderId)) {
            throw new IllegalArgumentException("订单ID不能为空");
        }

        TOrder orderNew = this.getById(orderId);

        // 【新增】订单状态锁校验 - 检查技师是否可以接单
        log.info("开始校验技师 {} 是否可以接单，订单号：{}", orderNew.getcJsId(), orderNew.getOrderNo());
        orderValidationService.canAcceptOrder(orderNew.getcJsId(), orderNew);
        log.info("技师 {} 接单校验通过，继续接单流程", orderNew.getcJsId());

        // 检查订单对应的技师是否存在
//        updateJs (orderNew);
        TOrder orderParam = new TOrder();
        orderParam.setcId(orderId);
        //设置订单状态：已接单
        orderParam.setnStatus(OrderStatusEnum.RECEIVED_ORDER.getCode());
        orderParam.setAcceptanceTime(LocalDateTime.now());
        this.updateById(orderParam);

        // 已接单消息通知（用户侧）
        orderNotificationService.sendReceivedNotification(orderNew);
        // 已接单消息通知（技师侧）
        orderNotificationService.sendTechnicianReceivedNotification(orderNew);
    }

    /**
     * 更新技师状态
     * @param orderNew
     */
    private void updateJs(TOrder orderNew) {
        TJs js = jsService.getById(orderNew.getcJsId());
        if (js == null) {
            throw new IllegalStateException("无法找到对应的技师");
        }

        if (Objects.equals(js.getnStatus(), JsStatusEnum.JS_SERVICEABLE.getCode())) {
            // 更新技师状态 服务中
            js.setnStatus(JsStatusEnum.JS_SERVICE.getCode());
            // 确保js.getnNum()不为null，避免 NullPointerException
            int num = js.getnNum() == null ? 0 : js.getnNum();
            js.setnNum(num + MassageConstants.INTEGER_ONE);
        } else {
            // 更新技师状态 可服务
            js.setnStatus(JsStatusEnum.JS_SERVICEABLE.getCode());
            // 确保js.getnNum()不为null，避免 NullPointerException
            int num = js.getnNum() == null ? 0 : js.getnNum();
            js.setnNum(num - MassageConstants.INTEGER_ONE);
        }
        jsService.updateById(js);
    }

    /**
     * 申请取消订单（退单申请）
     *
     * 业务流程：
     * 1. 校验订单状态（仅进行中的订单可申请退单）
     * 2. 创建退单申请记录
     * 3. 更新订单状态为"退单待审核"
     *
     * @param order 订单对象
     * @return Boolean 申请结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean applyCancle(String cId, String cancelReason) {
        log.info("开始处理退单申请，订单ID：{}", cId);

        // 1. 参数校验
        if (StringUtils.isBlank(cId)) {
            throw new ServiceException("订单ID不能为空");
        }
        if (StringUtils.isBlank(cancelReason)) {
            throw new ServiceException("退单原因不能为空");
        }

        // 2. 根据订单ID查询订单信息
        TOrder existingOrder = this.getById(cId);
        if (existingOrder == null) {
            throw new ServiceException("订单不存在");
        }

        // 3. 创建退单申请记录（内部会校验订单状态和其他业务规则）
        String applicationId;
        try {
            applicationId = cancelOrderApplicationService.createApplication(existingOrder.getcId(), cancelReason);
            log.info("退单申请记录创建成功，申请ID：{}", applicationId);
        } catch (ServiceException e) {
            log.error("创建退单申请失败：{}", e.getMessage());
            throw e;
        }

        // 4. 更新订单状态为"退单待审核"
        existingOrder.setnStatus(OrderStatusEnum.CANCEL_APPLICATION_PENDING.getCode());
        this.updateById(existingOrder);

        log.info("退单申请处理完成，订单ID：{}，申请ID：{}", existingOrder.getcId(), applicationId);
        return Boolean.TRUE;
    }

    /**
     * 取消退单申请
     * 用户主动取消退单申请，恢复订单状态
     *
     * 业务流程：
     * 1. 参数校验（订单ID不能为空）
     * 2. 查询订单和退单申请记录
     * 3. 校验订单状态必须为"退单待审核"(6)
     * 4. 校验退单审核状态必须为"待审核"(0)
     * 5. 调用退单申请服务取消申请
     * 6. 恢复订单状态到申请前的原始状态
     *
     * @param order 订单对象，需要包含cId（订单ID）
     * @return Boolean 操作结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean cancelApplyCancle(TOrder order) {
        log.info("开始取消退单申请，订单ID：{}", order.getcId());

        // 1. 参数校验
        if (StringUtils.isBlank(order.getcId())) {
            throw new ServiceException("订单ID不能为空");
        }

        // 2. 查询订单信息
        TOrder existingOrder = this.getById(order.getcId());
        if (existingOrder == null) {
            throw new ServiceException("订单不存在");
        }

        // 3. 校验订单状态 - 只有"退单待审核"状态的订单才能取消申请
        Integer currentStatus = existingOrder.getnStatus();
        if (!OrderStatusEnum.CANCEL_APPLICATION_PENDING.getCode().equals(currentStatus)) {
            throw new ServiceException("当前订单状态不允许取消退单申请");
        }

        // 4. 查询退单申请记录
        com.ylx.massage.domain.CancelOrderApplication application = cancelOrderApplicationService.getInfoByOrderId(order.getcId());
        if (application == null) {
            throw new ServiceException("退单申请记录不存在");
        }

        // 5. 校验退单申请状态 - 只有"待审核"的申请才能取消
        if (application.getAuditStatus() != 0) {
            throw new ServiceException("当前退单申请状态不允许取消");
        }

        // 6. 获取订单原始状态（在申请退单时保存的状态）
        Integer originalStatus = application.getOrderStatus();
        if (originalStatus == null) {
            throw new ServiceException("无法获取订单原始状态，取消申请失败");
        }

        // 7. 调用退单申请服务取消申请
        try {
            cancelOrderApplicationService.cancelApplication(order.getcId());
            log.info("退单申请记录取消成功，订单ID：{}", order.getcId());
        } catch (ServiceException e) {
            log.error("取消退单申请失败：{}", e.getMessage());
            throw e;
        }

        // 8. 恢复订单状态到原始状态
        existingOrder.setnStatus(originalStatus);
        boolean updated = this.updateById(existingOrder);
        if (!updated) {
            throw new ServiceException("恢复订单状态失败");
        }
        log.info("取消退单申请处理完成，订单ID：{}，恢复到状态：{}", order.getcId(), originalStatus);
        return Boolean.TRUE;
    }

    @Override
    public int countCompletedOrders(String openId, Date queryTime) {
        // 统计用户在指定时间之前完成的订单数量
        // 完成状态包括：4-待评价（已完成）和5-已完成（已评价）
        LambdaQueryWrapper<TOrder> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(TOrder::getcOpenId, openId)
                .in(TOrder::getnStatus, OrderStatusEnum.WAIT_EVALUATE.getCode(), OrderStatusEnum.COMPLETE.getCode())
                .lt(TOrder::getEndTime, queryTime);
        
        return Math.toIntExact(this.count(queryWrapper));
    }

}
