package com.starry.admin.modules.order.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.starry.admin.common.exception.CustomException;
import com.starry.admin.modules.clerk.module.entity.PlayClerkLevelInfoEntity;
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
import com.starry.admin.modules.clerk.service.IPlayClerkLevelInfoService;
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
import com.starry.admin.modules.custom.module.entity.PlayCustomLevelInfoEntity;
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
import com.starry.admin.modules.order.module.constant.OrderConstant;
import com.starry.admin.modules.order.module.constant.OrderConstant.BalanceOperationType;
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderActor;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundFlag;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundRecordType;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundState;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderStatus;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderTriggerSource;
import com.starry.admin.modules.order.module.constant.OrderConstant.ReviewRequirement;
import com.starry.admin.modules.order.module.dto.*;
import com.starry.admin.modules.order.module.entity.PlayOrderComplaintInfoEntity;
import com.starry.admin.modules.order.module.entity.PlayOrderEvaluateInfoEntity;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.order.module.entity.PlayOrderRefundInfoEntity;
import com.starry.admin.modules.order.module.vo.*;
import com.starry.admin.modules.order.service.IOrderLifecycleService;
import com.starry.admin.modules.order.service.IPlayOrderComplaintInfoService;
import com.starry.admin.modules.order.service.IPlayOrderEvaluateInfoService;
import com.starry.admin.modules.order.service.IPlayOrderInfoService;
import com.starry.admin.modules.order.service.IPlayOrderRefundInfoService;
import com.starry.admin.modules.order.service.support.ClerkRevenueCalculator;
import com.starry.admin.modules.personnel.module.entity.PlayPersonnelGroupInfoEntity;
import com.starry.admin.modules.personnel.service.IPlayPersonnelGroupInfoService;
import com.starry.admin.modules.shop.module.constant.CouponUseState;
import com.starry.admin.modules.shop.service.IPlayCouponDetailsService;
import com.starry.admin.modules.weichat.entity.order.*;
import com.starry.admin.modules.weichat.service.NotificationSender;
import com.starry.admin.utils.DateRangeUtils;
import com.starry.admin.utils.SecurityUtils;
import com.starry.common.utils.ConvertUtil;
import com.starry.common.utils.IdUtils;
import com.starry.common.utils.StringUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 订单Service业务层处理
 *
 * @author admin
 * @since 2024-03-20
 */
@Slf4j
@Service
public class PlayOrderInfoServiceImpl extends ServiceImpl<PlayOrderInfoMapper, PlayOrderInfoEntity>
        implements
        IPlayOrderInfoService {
    @Resource
    private PlayOrderInfoMapper orderInfoMapper;
    @Resource
    private IPlayClerkUserInfoService playClerkUserInfoService;
    @Resource
    private IPlayCustomUserInfoService userInfoService;
    @Resource
    private IPlayOrderRefundInfoService playOrderRefundInfoService;
    @Resource
    private IPlayOrderEvaluateInfoService playOrderEvaluateInfoService;
    @Resource
    private IPlayOrderComplaintInfoService playOrderComplaintInfoService;
    @Resource
    private IPlayPersonnelGroupInfoService playClerkGroupInfoService;

    @Resource
    private IPlayCouponDetailsService playCouponDetailsService;

    @Resource
    private NotificationSender notificationSender;

    @Resource
    private IPlayCustomUserInfoService customUserInfoService;

    @Resource
    private IPlayClerkLevelInfoService playClerkLevelInfoService;

    @Resource
    private IOrderLifecycleService orderLifecycleService;

    @Resource
    private ClerkRevenueCalculator clerkRevenueCalculator;

    @Override
    public List<PlayOrderInfoEntity> getTotalOrderInfo(String tenantId) {
        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaWrapper = new MPJLambdaWrapper<>();
        lambdaWrapper.eq(PlayOrderInfoEntity::getTenantId, tenantId);
        return this.baseMapper.selectList(lambdaWrapper);
    }

    @Override
    public ClerkEstimatedRevenueVo getClerkEstimatedRevenue(String clerkId, List<String> croupIds, String placeType,
                                                            String firstOrder, BigDecimal orderAmount) {
        return clerkRevenueCalculator.calculateEstimatedRevenue(clerkId, croupIds, placeType, firstOrder, orderAmount);
    }

    /**
     * 根据店员等级和订单金额，获取店员预计收入
     *
     * @param clerkId     店员ID
     * @param placeType   下单类型（-1:其他类型;0:指定单;1:随机单;2:打赏单）
     * @param firstOrder  是否是首单【0：不是，1：是】
     * @param orderAmount 订单金额
     * @return math.BigDecimal 店员预计收入
     * @author admin
     * @since 2024/6/3 11:12
     **/
    @Override
    public BigDecimal getEstimatedRevenue(String clerkId, String placeType, String firstOrder, BigDecimal orderAmount) {
        PlayClerkLevelInfoEntity entity = playClerkUserInfoService.queryLevelCommission(clerkId);
        boolean isFirst = OrderConstant.YesNoFlag.YES.getCode().equals(firstOrder);
        BigDecimal safeOrderAmount = orderAmount == null ? BigDecimal.ZERO : orderAmount;
        try {
            OrderConstant.PlaceType place = OrderConstant.PlaceType.fromCode(placeType);
            switch (place) {
                case SPECIFIED:
                    return calculateRevenue(safeOrderAmount,
                            isFirst ? entity.getFirstRegularRatio() : entity.getNotFirstRegularRatio());
                case RANDOM:
                    return calculateRevenue(safeOrderAmount,
                            isFirst ? entity.getFirstRandomRadio() : entity.getNotFirstRandomRadio());
                case REWARD:
                    return calculateRevenue(safeOrderAmount,
                            isFirst ? entity.getFirstRewardRatio() : entity.getNotFirstRewardRatio());
                case OTHER:
                default:
                    log.error("下单类型异常，placeType={}", placeType);
                    return safeOrderAmount;
            }
        } catch (IllegalArgumentException ex) {
            log.error("下单类型错误，placeType={}", placeType, ex);
            return safeOrderAmount;
        }
    }

    @Override
    public Integer getEstimatedRevenueRatio(String clerkId, String placeType, String firstOrder) {
        PlayClerkLevelInfoEntity entity = playClerkUserInfoService.queryLevelCommission(clerkId);
        boolean isFirst = OrderConstant.YesNoFlag.YES.getCode().equals(firstOrder);
        try {
            OrderConstant.PlaceType place = OrderConstant.PlaceType.fromCode(placeType);
            switch (place) {
                case SPECIFIED:
                    return isFirst ? entity.getFirstRegularRatio() : entity.getNotFirstRegularRatio();
                case RANDOM:
                    return isFirst ? entity.getFirstRandomRadio() : entity.getNotFirstRandomRadio();
                case REWARD:
                    return isFirst ? entity.getFirstRewardRatio() : entity.getNotFirstRewardRatio();
                case OTHER:
                default:
                    log.error("下单类型异常，placeType={}", placeType);
                    return 100;
            }
        } catch (IllegalArgumentException ex) {
            log.error("下单类型错误，placeType={}", placeType, ex);
            return 100;
        }
    }

    private BigDecimal calculateRevenue(BigDecimal amount, Integer ratio) {
        return amount.multiply(new BigDecimal(ratio).divide(new BigDecimal(100), 4, RoundingMode.HALF_UP))
                .setScale(2, RoundingMode.HALF_UP);
    }

    /**
     * 新增充值订单
     *
     * @param orderId     订单编号
     * @param orderMoney  订单金额
     * @param finalAmount 订单最终金额（支付金额）
     * @param purchaserBy 下单人ID
     */

    @Override
    public void createRechargeOrder(String orderId, BigDecimal orderMoney, BigDecimal finalAmount, String purchaserBy) {
        PlayOrderInfoEntity entity = new PlayOrderInfoEntity(IdUtils.getUuid(), OrderStatus.COMPLETED.getCode(), "0", "-1");
        entity.setOrderNo(orderId);
        entity.setId(IdUtils.getUuid());
        entity.setOrderMoney(orderMoney);
        entity.setFinalAmount(finalAmount);
        entity.setPurchaserBy(purchaserBy);
        entity.setPayMethod("1");
        entity.setPayState("0");
        this.baseMapper.insert(entity);
    }

    /**
     * 订单编号由2部分组成 第一部分是是时间，长度8位 第二部分是随机数，长度4位
     *
     * @return 订单编号
     */
    @Override
    public String getOrderNo() {
        String one = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String two = String.valueOf(new Random().nextInt(9000));
        return one + two;
    }

    @Override
    public List<PlayOrderInfoEntity> queryBySettlementOrder(String clerkId, String orderSettlementState) {
        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, clerkId);
        if (StrUtil.isNotBlank(orderSettlementState)) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderSettlementState, orderSettlementState);
        }

        return this.baseMapper.selectList(lambdaQueryWrapper);
    }

    @Override
    public List<PlayOrderInfoEntity> listByEndTime(String clerkId, LocalDateTime endTime) {
        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, clerkId);
        lambdaQueryWrapper.lt(PlayOrderInfoEntity::getOrderEndTime, endTime);
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderSettlementState, "0");
        return this.baseMapper.selectList(lambdaQueryWrapper);
    }

    @Override
    public List<PlayOrderInfoEntity> listByTime(String clerkId, String startTime, String endTime,
                                                List<String> orderStatus) {
        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
        if (StrUtil.isNotEmpty(clerkId)) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, clerkId);
        }
        if (orderStatus != null && !orderStatus.isEmpty()) {
            lambdaQueryWrapper.in(PlayOrderInfoEntity::getOrderStatus, orderStatus);
        }
        lambdaQueryWrapper.between(PlayOrderInfoEntity::getPurchaserTime, startTime, endTime);
        return this.baseMapper.selectList(lambdaQueryWrapper);
    }

    @Override
    public IPage<PlayOrderRewardReturnVo> selectRewardOrderInfoByPage(PlayOrderRewardQueryVo vo) {
        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
        // 查询主表全部字段
        lambdaQueryWrapper.selectAll(PlayOrderInfoEntity.class);
        // 查询顾客表
        lambdaQueryWrapper.selectAs(PlayCustomUserInfoEntity::getId, "customId")
                .selectAs(PlayCustomUserInfoEntity::getAvatar, "customAvatar")
                .selectAs(PlayCustomUserInfoEntity::getNickname, "customNickname");
        lambdaQueryWrapper.leftJoin(PlayCustomUserInfoEntity.class, PlayCustomUserInfoEntity::getId,
                PlayOrderInfoEntity::getPurchaserBy).leftJoin(PlayClerkUserInfoEntity.class,
                PlayClerkUserInfoEntity::getId, PlayOrderInfoEntity::getAcceptBy);
        // 查询陪聊表
        lambdaQueryWrapper.selectAs(PlayClerkUserInfoEntity::getId, "clerkId")
                .selectAs(PlayClerkUserInfoEntity::getAvatar, "clerkAvatar")
                .selectAs(PlayClerkUserInfoEntity::getNickname, "clerkNickname");

        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPlaceType, "2");
        if (StrUtil.isNotBlank(vo.getRewardType())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getRewardType, vo.getRewardType());
        }
        if (StrUtil.isNotBlank(vo.getAcceptBy())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, vo.getAcceptBy());
        }
        if (StrUtil.isNotBlank(vo.getPurchaserBy())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPurchaserBy, vo.getPurchaserBy());
        }
        if (vo.getPurchaserTime() != null && vo.getPurchaserTime().size() == 2) {
            lambdaQueryWrapper.between(PlayOrderInfoEntity::getPurchaserTime, vo.getPurchaserTime().get(0),
                    vo.getPurchaserTime().get(1));
        }
        lambdaQueryWrapper.orderByDesc(PlayOrderInfoEntity::getPurchaserTime);
        // 加入组员的筛选
        List<String> clerkIdList = playClerkGroupInfoService.getValidClerkIdList(SecurityUtils.getLoginUser(), null);
        lambdaQueryWrapper.in(PlayOrderInfoEntity::getAcceptBy, clerkIdList);

        return this.baseMapper.selectJoinPage(new Page<>(vo.getPageNum(), vo.getPageSize()),
                PlayOrderRewardReturnVo.class, lambdaQueryWrapper);

    }

    @Override
    public IPage<PlayOrderInfoEntity> selectRandomOrderByPage(PlayOrderInfoRandomQueryVo vo, String clerkId) {
        PlayClerkUserInfoEntity entity = playClerkUserInfoService.getById(clerkId);
        playClerkUserInfoService.ensureClerkIsActive(entity);
        LambdaQueryWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPlaceType, "1");
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.PENDING.getCode());
//        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getLevelId, entity.getLevelId());
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getSex, entity.getSex());
//        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getExcludeHistory, "0")
//                .or(wrapper1 -> wrapper1.ne(PlayOrderInfoEntity::getAcceptBy, clerkId)
//                        .and(wrapper2 -> wrapper2.eq(PlayOrderInfoEntity::getExcludeHistory, 1)));
        return this.baseMapper.selectPage(new Page<>(vo.getPageNum(), vo.getPageSize()), lambdaQueryWrapper);
    }

    @Override
    public IPage<PlayRewardInfoReturnVo> selectRewardByPage(PlayRewardOrderQueryVo vo) {
        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPlaceType, "2");
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getRefundType, OrderRefundFlag.NOT_REFUNDED.getCode());
        lambdaQueryWrapper.selectAll(PlayOrderInfoEntity.class);
        lambdaQueryWrapper.ne(PlayOrderInfoEntity::getOrderType, OrderConstant.OrderType.BLIND_BOX_PURCHASE.getCode());
        lambdaQueryWrapper.orderByDesc(PlayOrderInfoEntity::getPurchaserTime);
        // 查询陪聊表
        lambdaQueryWrapper.selectAs(PlayClerkUserInfoEntity::getId, "clerkId")
                .selectAs(PlayClerkUserInfoEntity::getAvatar, "clerkAvatar")
                .selectAs(PlayClerkUserInfoEntity::getNickname, "clerkNickname");
        // 子表
        lambdaQueryWrapper.leftJoin(PlayCustomUserInfoEntity.class, PlayCustomUserInfoEntity::getId,
                PlayOrderInfoEntity::getPurchaserBy).leftJoin(PlayClerkUserInfoEntity.class,
                PlayClerkUserInfoEntity::getId, PlayOrderInfoEntity::getAcceptBy);
        lambdaQueryWrapper.orderByDesc(PlayOrderInfoEntity::getPurchaserTime);
        return this.baseMapper.selectJoinPage(new Page<>(vo.getPageNum(), vo.getPageSize()),
                PlayRewardInfoReturnVo.class, lambdaQueryWrapper);
    }

    @Override
    public Boolean checkFirstOrderFlag(String customId, String clerkId) {
        LambdaQueryWrapper<PlayOrderInfoEntity> wrapper = Wrappers.lambdaQuery(PlayOrderInfoEntity.class)
                .eq(PlayOrderInfoEntity::getPurchaserBy, customId)
                .eq(PlayOrderInfoEntity::getAcceptBy, clerkId)
                .eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.COMPLETED.getCode());
        return this.baseMapper.selectCount(wrapper) == 0;
    }

    /**
     * 查询订单
     *
     * @param id 订单主键
     * @return 订单
     */
    @Override
    public PlayOrderInfoEntity selectOrderInfoById(String id) {
        PlayOrderInfoEntity entity = this.baseMapper.selectById(id);
        if (entity == null) {
            throw new CustomException("订单不存在");
        }
        return entity;
    }

    @Override
    public PlayOrderDetailsReturnVo selectById(String orderId) {
        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
        // 查询主表全部字段
        lambdaQueryWrapper.selectAll(PlayOrderInfoEntity.class);
        // 查询顾客表
        lambdaQueryWrapper.selectAs(PlayCustomUserInfoEntity::getId, "customId")
                .selectAs(PlayCustomUserInfoEntity::getAvatar, "customAvatar")
                .selectAs(PlayCustomUserInfoEntity::getNickname, "customNickname");
        // 查询陪聊表
        lambdaQueryWrapper.selectAs(PlayClerkUserInfoEntity::getId, "clerkId")
                .selectAs(PlayClerkUserInfoEntity::getAvatar, "clerkAvatar")
                .selectAs(PlayClerkUserInfoEntity::getNickname, "clerkNickname");
        // 子表
        lambdaQueryWrapper.leftJoin(PlayCustomUserInfoEntity.class, PlayCustomUserInfoEntity::getId,
                PlayOrderInfoEntity::getPurchaserBy).leftJoin(PlayClerkUserInfoEntity.class,
                PlayClerkUserInfoEntity::getId, PlayOrderInfoEntity::getAcceptBy);
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getId, orderId);
        lambdaQueryWrapper.orderByDesc(PlayOrderInfoEntity::getPurchaserTime);
        PlayOrderDetailsReturnVo vo = this.baseMapper.selectJoinOne(PlayOrderDetailsReturnVo.class, lambdaQueryWrapper);

        // Privacy protection: Hide customer info for pending random orders
        if (vo != null && OrderConstant.PlaceType.RANDOM.getCode().equals(vo.getPlaceType()) && OrderStatus.PENDING.getCode().equals(vo.getOrderStatus())) {
            vo.setCustomNickname("匿名用户");
            vo.setCustomAvatar("");
            vo.setCustomId("");
        }

        return vo;
    }

    /**
     * 分页查询订单信息
     *
     * @param vo 订单分页查询对象
     * @return 订单分页查询结果
     */
    @Override
    public IPage<PlayOrderInfoReturnVo> selectOrderInfoPage(PlayOrderInfoQueryVo vo) {

        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = getCommonOrderQueryVo(
                ConvertUtil.entityToVo(vo, PlayOrderInfoEntity.class), vo.getKeyword());
        lambdaQueryWrapper.in(PlayOrderInfoEntity::getPlaceType, "0", "1", "2");
        if (StringUtils.isNotBlank(vo.getGroupId())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getGroupId, vo.getGroupId());
        }
        if (StringUtils.isNotBlank(vo.getPurchaserBy())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPurchaserBy, vo.getPurchaserBy());
        }
        if (StringUtils.isNotBlank(vo.getAcceptBy())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, vo.getAcceptBy());
        }
        if (StringUtils.isNotBlank(vo.getOrderStatus())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderStatus, vo.getOrderStatus());
        }
        if (StringUtils.isNotBlank(vo.getPlaceType())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPlaceType, vo.getPlaceType());
        }
        if (StringUtils.isNotBlank(vo.getOrderType())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderType, vo.getOrderType());
        }
        if (StringUtils.isNotBlank(vo.getSex())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getSex, vo.getSex());
        }
        if (StringUtils.isNotBlank(vo.getPayMethod())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPayMethod, vo.getPayMethod());
        }
        if (StringUtils.isNotBlank(vo.getUseCoupon())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getUseCoupon, vo.getUseCoupon());
        }
        if (StringUtils.isNotBlank(vo.getBackendEntry())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getBackendEntry, vo.getBackendEntry());
        }
        if (StringUtils.isNotBlank(vo.getFirstOrder())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getFirstOrder, vo.getFirstOrder());
        }
        applyRangeFilter(lambdaQueryWrapper, vo.getPurchaserTime(), PlayOrderInfoEntity::getPurchaserTime);
        applyRangeFilter(lambdaQueryWrapper, vo.getAcceptTime(), PlayOrderInfoEntity::getAcceptTime);
        applyRangeFilter(lambdaQueryWrapper, vo.getStartOrderTime(), PlayOrderInfoEntity::getOrderStartTime);
        applyRangeFilter(lambdaQueryWrapper, vo.getEndOrderTime(), PlayOrderInfoEntity::getOrderEndTime);
        // 加入组员的筛选，要么acceptBy为空，要么就在in里面
        List<String> clerkIdList = playClerkGroupInfoService.getValidClerkIdList(SecurityUtils.getLoginUser(),
                vo.getClerkNickName());
        lambdaQueryWrapper.and(
                i -> i.isNull(PlayOrderInfoEntity::getAcceptBy).or().in(PlayOrderInfoEntity::getAcceptBy, clerkIdList));
        return this.baseMapper.selectJoinPage(new Page<>(vo.getPageNum(), vo.getPageSize()),
                PlayOrderInfoReturnVo.class, lambdaQueryWrapper);
    }

    @Override
    public PlayClerkOrderDetailsReturnVo clerkSelectOrderDetails(String clerkId, String orderId) {
        PlayOrderInfoEntity entity = new PlayOrderInfoEntity();
        entity.setId(orderId);
        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = getCommonOrderQueryVo(entity, null);
        // 拼接用户等级
        lambdaQueryWrapper.selectAs(PlayCustomLevelInfoEntity::getId, "customLevelId")
                .selectAs(PlayCustomLevelInfoEntity::getName, "customLevelName");
        lambdaQueryWrapper.leftJoin(PlayCustomLevelInfoEntity.class, PlayCustomLevelInfoEntity::getId,
                PlayCustomUserInfoEntity::getLevelId);
        PlayClerkOrderDetailsReturnVo returnVo = this.baseMapper.selectJoinOne(PlayClerkOrderDetailsReturnVo.class,
                lambdaQueryWrapper);
        if (returnVo == null) {
            throw new CustomException("订单不存在或已失效");
        }
        // 如果订单状态为退款，查询订单退款原因
        if (OrderStatus.CANCELLED.getCode().equals(returnVo.getOrderStatus())) {
            PlayOrderRefundInfoEntity orderRefundInfoEntity = playOrderRefundInfoService
                    .selectPlayOrderRefundInfoByOrderId(returnVo.getId());
            if (orderRefundInfoEntity != null) {
                returnVo.setRefundByType(orderRefundInfoEntity.getRefundByType());
                returnVo.setRefundById(orderRefundInfoEntity.getRefundById());
                returnVo.setRefundReason(orderRefundInfoEntity.getRefundReason());
            } else {
                log.warn("Refund info missing for cancelled order, orderId={}", returnVo.getId());
            }
        }

        // Privacy protection: Hide customer info for pending random orders
        if (OrderConstant.PlaceType.RANDOM.getCode().equals(returnVo.getPlaceType())
                && OrderStatus.PENDING.getCode().equals(returnVo.getOrderStatus())) {
            returnVo.setWeiChatCode("");
            returnVo.setCustomNickname("匿名用户");
            returnVo.setCustomAvatar("");
            returnVo.setCustomId("");
        }

        if (returnVo.getEstimatedRevenue() == null) {
            returnVo.setEstimatedRevenue(BigDecimal.ZERO);
        }
        return returnVo;
    }

    @Override
    public List<PlayOrderInfoEntity> clerkSelectOrderInfoList(String clerkId, String startTime, String endTime) {
        LambdaQueryWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, clerkId);
        String normalizedStart = DateRangeUtils.normalizeStartOptional(startTime);
        String normalizedEnd = DateRangeUtils.normalizeEndOptional(endTime);
        if (StrUtil.isNotBlank(normalizedStart) && StrUtil.isNotBlank(normalizedEnd)) {
            lambdaQueryWrapper.between(PlayOrderInfoEntity::getPurchaserTime, normalizedStart, normalizedEnd);
        } else {
            if (StrUtil.isNotBlank(normalizedStart)) {
                lambdaQueryWrapper.ge(PlayOrderInfoEntity::getPurchaserTime, normalizedStart);
            }
            if (StrUtil.isNotBlank(normalizedEnd)) {
                lambdaQueryWrapper.le(PlayOrderInfoEntity::getPurchaserTime, normalizedEnd);
            }
        }
        return this.baseMapper.selectList(lambdaQueryWrapper);
    }

    @Override
    public IPage<PlayClerkOrderListReturnVo> clerkSelectOrderInfoByPage(PlayClerkOrderInfoQueryVo vo) {
        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = getCommonOrderQueryVo(
                ConvertUtil.entityToVo(vo, PlayOrderInfoEntity.class), null);
        // 拼接用户等级
        lambdaQueryWrapper.selectAs(PlayCustomLevelInfoEntity::getId, "customLevelId")
                .selectAs(PlayCustomLevelInfoEntity::getName, "customLevelName");
        lambdaQueryWrapper.leftJoin(PlayCustomLevelInfoEntity.class, PlayCustomLevelInfoEntity::getId,
                PlayCustomUserInfoEntity::getLevelId);
        IPage<PlayClerkOrderListReturnVo> page = this.baseMapper.selectJoinPage(
                new Page<>(vo.getPageNum(), vo.getPageSize()),
                PlayClerkOrderListReturnVo.class,
                lambdaQueryWrapper);

        for (PlayClerkOrderListReturnVo record : page.getRecords()) {
            if (OrderConstant.PlaceType.RANDOM.getCode().equals(record.getPlaceType())
                    && OrderStatus.PENDING.getCode().equals(record.getOrderStatus())) {
                record.setCustomNickname("匿名用户");
                record.setCustomAvatar("");
                record.setCustomId("");
            }
        }

        return page;
    }

    @Override
    public PlayCustomOrderDetailsReturnVo customSelectOrderDetails(String customId, String orderId) {
        PlayOrderInfoEntity entity = new PlayOrderInfoEntity();
        entity.setId(orderId);
        entity.setPurchaserBy(customId);
        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = getCommonOrderQueryVo(entity, null);
        PlayCustomOrderDetailsReturnVo returnVo = this.baseMapper.selectJoinOne(PlayCustomOrderDetailsReturnVo.class,
                lambdaQueryWrapper);
        // 如果订单状态为退款，查询订单退款原因
        if (returnVo.getOrderStatus().equals(OrderStatus.CANCELLED.getCode())) {
            PlayOrderRefundInfoEntity orderRefundInfoEntity = playOrderRefundInfoService
                    .selectPlayOrderRefundInfoByOrderId(returnVo.getId());
            returnVo.setRefundByType(orderRefundInfoEntity.getRefundByType());
            returnVo.setRefundById(orderRefundInfoEntity.getRefundById());
            returnVo.setRefundReason(orderRefundInfoEntity.getRefundReason());
        }
        // 查询订单是否评价
        PlayOrderEvaluateInfoEntity orderEvaluateInfoEntity = playOrderEvaluateInfoService
                .queryCustomToOrderEvaluateInfo(customId, orderId);
        returnVo.setEvaluate(orderEvaluateInfoEntity != null ? "1" : "0");
        PlayOrderComplaintInfoEntity orderComplaintInfoEntity = playOrderComplaintInfoService
                .queryCustomToOrderOrderComplaintInfo(customId, orderId);
        // 查询订单是否投诉
        returnVo.setComplaint(orderComplaintInfoEntity != null ? "1" : "0");

        return returnVo;
    }

    @Override
    public IPage<PlayCustomOrderListReturnVo> customSelectOrderInfoByPage(PlayCustomOrderInfoQueryVo vo) {
        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = getCommonOrderQueryVo(
                ConvertUtil.entityToVo(vo, PlayOrderInfoEntity.class), null);
        if (StringUtils.isBlank(vo.getOrderType())) {
            lambdaQueryWrapper.notIn(PlayOrderInfoEntity::getOrderType,
                    OrderConstant.OrderType.RECHARGE.getCode(),
                    OrderConstant.OrderType.WITHDRAWAL.getCode());
        }
        IPage<PlayCustomOrderListReturnVo> page = this.baseMapper.selectJoinPage(
                new Page<>(vo.getPageNum(), vo.getPageSize()), PlayCustomOrderListReturnVo.class, lambdaQueryWrapper);
        // 获取当前顾客所有订单评价信息，将订单评价信息转化为 map<订单ID，订单ID>的结构
        PlayOrderEvaluateInfoEntity orderEvaluateInfoEntity = new PlayOrderEvaluateInfoEntity();
        orderEvaluateInfoEntity.setCustomId(vo.getPurchaserBy());
        Map<String, String> evaluateInfos = playOrderEvaluateInfoService.selectByList(orderEvaluateInfoEntity).stream()
                .collect(Collectors.toMap(PlayOrderEvaluateInfoEntity::getOrderId,
                        PlayOrderEvaluateInfoEntity::getOrderId));
        for (PlayCustomOrderListReturnVo record : page.getRecords()) {
            record.setEvaluate(evaluateInfos.containsKey(record.getId()) ? "1" : "0");
        }
        // 获取当前顾客所有订单投诉信息，将订单评价信息转化为 map<订单ID，订单ID>的结构
        PlayOrderComplaintInfoEntity orderComplaintInfoEntity = new PlayOrderComplaintInfoEntity();
        orderComplaintInfoEntity.setCustomId(vo.getPurchaserBy());
        Map<String, String> complaintInfos = playOrderComplaintInfoService.selectByList(orderComplaintInfoEntity)
                .stream().collect(Collectors.toMap(PlayOrderComplaintInfoEntity::getOrderId,
                        PlayOrderComplaintInfoEntity::getOrderId));
        for (PlayCustomOrderListReturnVo record : page.getRecords()) {
            record.setComplaint(complaintInfos.containsKey(record.getId()) ? "1" : "0");
        }
        return page;
    }

    @Override
    public List<PlayOrderInfoEntity> customSelectOrderInfoByList(String customId) {
        LambdaQueryWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPurchaserBy, customId);
        return this.baseMapper.selectList(lambdaQueryWrapper);
    }

    /**
     * 修改订单状态为接单 只有管理员或者店员本人才能操作
     *
     * @param operatorByType 操作人类型（0:顾客;1:店员;2:管理员）
     * @param operatorBy     操作人ID
     * @param acceptBy       接单人ID
     * @param orderId        订单Id
    **/
    @Override
    public void updateStateTo1(String operatorByType, String operatorBy, String acceptBy, String orderId) {
        boolean isClerkOperator = OrderConstant.OperatorType.CLERK.getCode().equals(operatorByType);
        boolean isAdminOperator = OrderConstant.OperatorType.ADMIN.getCode().equals(operatorByType);
        if (!isClerkOperator && !isAdminOperator) {
            throw new CustomException("禁止操作");
        }
        PlayOrderInfoEntity orderInfo = this.selectOrderInfoById(orderId);
        String previousStatus = orderInfo.getOrderStatus();
        if (OrderStatus.COMPLETED.getCode().equals(previousStatus)
                || OrderStatus.CANCELLED.getCode().equals(previousStatus)) {
            log.warn("Order already finished, cannot accept. orderId={}, orderNo={}, operatorByType={}, acceptBy={}",
                    orderId, orderInfo.getOrderNo(), operatorByType, acceptBy);
            throw new CustomException("订单已结束，无法接单");
        }
        PlayClerkUserInfoEntity clerkUserInfoEntity = playClerkUserInfoService.selectById(acceptBy);
        playClerkUserInfoService.ensureClerkIsActive(clerkUserInfoEntity);
        if (isClerkOperator && StringUtils.isNotBlank(orderInfo.getAcceptBy())
                && !orderInfo.getAcceptBy().equals(acceptBy)) {
            log.warn("Order already accepted by another clerk. orderId={}, orderNo={}, currentAcceptBy={}, requestAcceptBy={}",
                    orderId, orderInfo.getOrderNo(), orderInfo.getAcceptBy(), acceptBy);
            throw new CustomException("订单已被接单");
        }
        if (isClerkOperator && !OrderStatus.PENDING.getCode().equals(previousStatus)) {
            log.warn("Order status is not pending when clerk tries to accept. orderId={}, orderNo={}, status={}, acceptBy={}",
                    orderId, orderInfo.getOrderNo(), previousStatus, acceptBy);
            throw new CustomException("订单已被接单");
        }
        // 店员接单时，判断店员是否符合资格
        if (isClerkOperator && OrderConstant.PlaceType.RANDOM.getCode().equals(orderInfo.getPlaceType())) {
            validateClerkQualificationForRandomOrder(orderInfo, clerkUserInfoEntity, acceptBy);
        }
        String firstOrderFlag = resolveFirstOrderFlag(orderInfo.getPurchaserBy(), acceptBy);

        PlayOrderInfoEntity entity = new PlayOrderInfoEntity(orderId, OrderStatus.ACCEPTED.getCode());
        LocalDateTime acceptTime = LocalDateTime.now();
        entity.setAcceptBy(acceptBy);
        entity.setAcceptTime(acceptTime);
        entity.setFirstOrder(firstOrderFlag);
        ClerkEstimatedRevenueVo estimatedRevenueVo = this.getClerkEstimatedRevenue(
                acceptBy,
                orderInfo.getCouponIds(),
                orderInfo.getPlaceType(),
                firstOrderFlag,
                orderInfo.getOrderMoney());
        BigDecimal revenueAmount = estimatedRevenueVo.getRevenueAmount();
        entity.setEstimatedRevenue(revenueAmount);
        entity.setEstimatedRevenueRatio(estimatedRevenueVo.getRevenueRatio());

        LambdaUpdateWrapper<PlayOrderInfoEntity> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(PlayOrderInfoEntity::getId, orderId);
        if (isClerkOperator) {
            updateWrapper.eq(PlayOrderInfoEntity::getOrderStatus, OrderStatus.PENDING.getCode());
            if (StringUtils.isNotBlank(orderInfo.getAcceptBy())) {
                updateWrapper.eq(PlayOrderInfoEntity::getAcceptBy, orderInfo.getAcceptBy());
            } else {
                updateWrapper.isNull(PlayOrderInfoEntity::getAcceptBy);
            }
        }
        int updated = this.baseMapper.update(entity, updateWrapper);
        if (updated == 0) {
            if (isClerkOperator) {
                log.warn("Clerk failed to accept order due to concurrent update. orderId={}, orderNo={}, acceptBy={}",
                        orderId, orderInfo.getOrderNo(), acceptBy);
                throw new CustomException("订单已被接单");
            }
            log.warn("Failed to update order status to accepted for admin reassignment, orderId={}, orderNo={}",
                    orderId, orderInfo.getOrderNo());
            return;
        }

        // Set acceptBy on orderInfo for notification
        orderInfo.setAcceptBy(acceptBy);
        orderInfo.setAcceptTime(acceptTime);
        orderInfo.setOrderStatus(OrderStatus.ACCEPTED.getCode());
        orderInfo.setEstimatedRevenue(revenueAmount);
        orderInfo.setEstimatedRevenueRatio(estimatedRevenueVo.getRevenueRatio());
        orderInfo.setFirstOrder(firstOrderFlag);
        log.info("Order accepted successfully. orderId={}, orderNo={}, acceptBy={}, operatorByType={}",
                orderId, orderInfo.getOrderNo(), acceptBy, operatorByType);
        notificationSender.sendOrderMessageAsync(orderInfo);
    }

    private void validateClerkQualificationForRandomOrder(PlayOrderInfoEntity orderInfo,
                                                          PlayClerkUserInfoEntity clerkUserInfoEntity,
                                                          String acceptBy) {
        if (orderInfo.getLevelId().equals(clerkUserInfoEntity.getLevelId())) {
            PlayClerkLevelInfoEntity levelInfo = playClerkLevelInfoService
                    .selectPlayClerkLevelInfoById(orderInfo.getLevelId());
//                throw new CustomException("等级为" + levelInfo.getName() + "的店员才可接单");
        }
        if (!orderInfo.getSex().equals(clerkUserInfoEntity.getSex())) {
            OrderConstant.Gender requiredGender = OrderConstant.Gender.fromCode(orderInfo.getSex());
            throw new CustomException("性别为" + requiredGender.getDescription() + "的店员才可接单");
        }
        LambdaQueryWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, acceptBy);
        if (OrderConstant.EXCLUDE_HISTORY_YES.equals(orderInfo.getExcludeHistory())
                && this.baseMapper.selectOne(lambdaQueryWrapper) != null) {
//                throw new CustomException("只有未接单的店员才可接单");
        }
    }

    private String resolveFirstOrderFlag(String purchaserId, String clerkId) {
        if (StrUtil.isBlank(purchaserId) || StrUtil.isBlank(clerkId)) {
            return OrderConstant.YesNoFlag.NO.getCode();
        }
        Long completedCount = orderInfoMapper.selectCount(Wrappers.lambdaQuery(PlayOrderInfoEntity.class)
                .eq(PlayOrderInfoEntity::getPurchaserBy, purchaserId)
                .eq(PlayOrderInfoEntity::getAcceptBy, clerkId));

        return (completedCount == null || completedCount == 0)
                ? OrderConstant.YesNoFlag.YES.getCode()
                : OrderConstant.YesNoFlag.NO.getCode();
    }

    /**
     * 获取通用的订单查询对象 订单作为主表 连接顾客用户表、店员用户表、商品表
     *
     * @return MPJLambdaWrapper<PlayOrderInfoEntity>
     */
    public MPJLambdaWrapper<PlayOrderInfoEntity> getCommonOrderQueryVo(PlayOrderInfoEntity entity, String keyword) {
        MPJLambdaWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new MPJLambdaWrapper<>();
        // 查询主表全部字段
        lambdaQueryWrapper.selectAll(PlayOrderInfoEntity.class);
        // 查询顾客表
        lambdaQueryWrapper.selectAs(PlayCustomUserInfoEntity::getId, "customId")
                .selectAs(PlayCustomUserInfoEntity::getAvatar, "customAvatar")
                .selectAs(PlayCustomUserInfoEntity::getNickname, "customNickname");
        // 查询陪聊表
        lambdaQueryWrapper.selectAs(PlayClerkUserInfoEntity::getId, "clerkId")
                .selectAs(PlayClerkUserInfoEntity::getAvatar, "clerkAvatar")
                .selectAs(PlayClerkUserInfoEntity::getNickname, "clerkNickname");
        // 子表-店员服务项目表
        lambdaQueryWrapper.leftJoin(PlayCustomUserInfoEntity.class, PlayCustomUserInfoEntity::getId,
                PlayOrderInfoEntity::getPurchaserBy);
        // 子表-店员表
        lambdaQueryWrapper.leftJoin(PlayClerkUserInfoEntity.class, PlayClerkUserInfoEntity::getId,
                PlayOrderInfoEntity::getAcceptBy);
        if (StringUtils.isNotBlank(entity.getId())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getId, entity.getId());
        }
        if (StringUtils.isNotBlank(entity.getPurchaserBy())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPurchaserBy, entity.getPurchaserBy());
        }
        if (StringUtils.isNotBlank(entity.getAcceptBy())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getAcceptBy, entity.getAcceptBy());
        }
        if (StringUtils.isNotBlank(entity.getOrderStatus())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderStatus, entity.getOrderStatus());
        }
        if (StringUtils.isNotBlank(entity.getPlaceType())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getPlaceType, entity.getPlaceType());
        }
        if (StringUtils.isNotBlank(entity.getOrderType())) {
            lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderType, entity.getOrderType());
        }
        lambdaQueryWrapper.like(StringUtils.isNotEmpty(entity.getOrderNo()), PlayOrderInfoEntity::getOrderNo, entity.getOrderNo());
        if (StringUtils.isNotBlank(keyword)) {
            lambdaQueryWrapper.and(w -> w.like(PlayOrderInfoEntity::getOrderNo, keyword)
                    .or()
                    .like(PlayClerkUserInfoEntity::getNickname, keyword));
        }
        lambdaQueryWrapper.orderByDesc(PlayOrderInfoEntity::getCreatedTime);
        return lambdaQueryWrapper;

    }

    private void applyRangeFilter(MPJLambdaWrapper<PlayOrderInfoEntity> wrapper, List<String> range,
                                  SFunction<PlayOrderInfoEntity, ?> column) {
        if (CollectionUtil.isEmpty(range)) {
            return;
        }
        String start = normalizeRangeValue(range, 0, false);
        String end = normalizeRangeValue(range, 1, true);
        if (StrUtil.isNotBlank(start) && StrUtil.isNotBlank(end)) {
            wrapper.between(column, start, end);
        } else if (StrUtil.isNotBlank(start)) {
            wrapper.ge(column, start);
        } else if (StrUtil.isNotBlank(end)) {
            wrapper.le(column, end);
        }
    }

    private String normalizeRangeValue(List<String> range, int index, boolean isEnd) {
        if (range.size() <= index) {
            return null;
        }
        String raw = range.get(index);
        if (StrUtil.isBlank(raw)) {
            return null;
        }
        return isEnd ? DateRangeUtils.normalizeEndOptional(raw) : DateRangeUtils.normalizeStartOptional(raw);
    }

    public void updateStateTo23(String operatorByType, String operatorBy, String orderState, String orderId) {
        OperatorType operatorType = resolveOperatorTypeOrThrow(operatorByType);
        boolean isCustomer = operatorType == OperatorType.CUSTOMER;
        boolean isClerk = operatorType == OperatorType.CLERK;

        if (OrderStatus.IN_PROGRESS.getCode().equals(orderState) && isCustomer) {
            throw new CustomException("禁止操作");
        }
        if (OrderStatus.COMPLETED.getCode().equals(orderState) && isClerk) {
            throw new CustomException("禁止操作");
        }
        PlayOrderInfoEntity entity = this.selectOrderInfoById(orderId);
        if (isCustomer && !entity.getPurchaserBy().equals(operatorBy)) {
            throw new CustomException("只能操作本人订单");
        }
        if (isClerk && !entity.getAcceptBy().equals(operatorBy)) {
            throw new CustomException("只能操作本人订单");
        }

        if (OrderStatus.IN_PROGRESS.getCode().equals(orderState)) {
            if (!OrderStatus.ACCEPTED.getCode().equals(entity.getOrderStatus())) {
                log.error("订单状态异常，不能开始接单,orderId={},orderStace={}", orderId, orderState);
                throw new CustomException("订单状态异常，不能开始订单");
            }
            PlayOrderInfoEntity entity2 = new PlayOrderInfoEntity(orderId, OrderStatus.IN_PROGRESS.getCode());
            entity2.setOrderStartTime(LocalDateTime.now());
            this.baseMapper.updateById(entity2);
        } else if (OrderStatus.COMPLETED.getCode().equals(orderState)) {
            if (!OrderStatus.IN_PROGRESS.getCode().equals(entity.getOrderStatus())) {
                log.error("订单状态异常，不能完成订单,orderId={},orderStace={}", orderId, orderState);
                throw new CustomException("订单状态异常，不能开始订单");
            }
            if (operatorType == OperatorType.GROUP_LEADER) {
                ensureLeaderScope(operatorBy, entity);
            }
            OrderActor actor = resolveCompletionActor(operatorType.getCode());
            completeOrderInternal(
                    entity,
                    operatorType,
                    operatorBy,
                    "manual",
                    resolveCompletionSource(actor));
        } else {
            log.error("修改订单状态异常，orderId={},orderStace={}", orderId, orderState);
        }
    }

    public void completeOrderByManagement(String operatorByType, String operatorBy, String orderId, String remark) {
        OperatorType operatorType = resolveOperatorTypeOrThrow(operatorByType);
        if (operatorType != OperatorType.ADMIN && operatorType != OperatorType.GROUP_LEADER) {
            throw new CustomException("当前角色无权完成订单");
        }
        PlayOrderInfoEntity entity = this.selectOrderInfoById(orderId);
        if (operatorType == OperatorType.GROUP_LEADER) {
            ensureLeaderScope(operatorBy, entity);
        }
        if (OrderStatus.ACCEPTED.getCode().equals(entity.getOrderStatus())) {
            PlayOrderInfoEntity startUpdate = new PlayOrderInfoEntity(orderId, OrderStatus.IN_PROGRESS.getCode());
            startUpdate.setOrderStartTime(LocalDateTime.now());
            this.baseMapper.updateById(startUpdate);
            entity = this.selectOrderInfoById(orderId);
        }
        if (!OrderStatus.IN_PROGRESS.getCode().equals(entity.getOrderStatus())) {
            throw new CustomException("订单状态异常，无法完成");
        }
        completeOrderInternal(
                entity,
                operatorType,
                operatorBy,
                remark,
                OrderTriggerSource.WX_CLERK_MGMT);
    }

    /**
     * 修改订单状态为取消订单 管理员、店员、顾客均可操作
     *
     * @param operatorByType 操作人类型（0:顾客;1:店员;2:管理员）
     * @param operatorBy     操作人ID
     * @param orderId        订单Id
     **/

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStateTo4(String operatorByType, String operatorBy, String orderId, String refundReason,
                               List<String> images) {
        PlayOrderInfoEntity orderInfo = this.selectOrderInfoById(orderId);
        if ("0".equals(operatorByType) && !operatorBy.equals(orderInfo.getPurchaserBy())) {
            throw new CustomException("只能操作本人订单");
        }
        if ("1".equals(operatorByType) && !operatorBy.equals(orderInfo.getAcceptBy())) {
            throw new CustomException("只能操作本人订单");
        }
        // 取消订单（必须订单未接单或者为开始状态）
        if (!orderInfo.getOrderStatus().equals(OrderStatus.PENDING.getCode())
                && !orderInfo.getOrderStatus().equals(OrderStatus.ACCEPTED.getCode())) {
            throw new CustomException("订单状态异常，无法取消");
        }
        // 修改订单状态
        this.baseMapper.updateById(new PlayOrderInfoEntity(orderId, OrderStatus.CANCELLED.getCode()));
        // 用户增加余额
        PlayCustomUserInfoEntity customUserInfo = customUserInfoService.getById(orderInfo.getPurchaserBy());
        customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(),
                customUserInfo.getAccountBalance().add(orderInfo.getOrderMoney()), BalanceOperationType.REFUND.getCode(), "订单退款",
                orderInfo.getOrderMoney(), BigDecimal.ZERO, orderId);
        // 取消订单后，记录退款信息
        playOrderRefundInfoService.add(orderInfo.getId(), orderInfo.getPurchaserBy(), orderInfo.getAcceptBy(),
                orderInfo.getPayMethod(), OrderRefundRecordType.FULL.getCode(), orderInfo.getFinalAmount(), refundReason, operatorByType, operatorBy,
                OrderRefundState.PROCESSING.getCode(), ReviewRequirement.NOT_REQUIRED.getCode());
        restoreCouponsForOrder(orderInfo);
        notificationSender.sendOrderCancelMessageAsync(orderInfo, refundReason);
    }

    /**
     * 已接单/服务中的订单强制取消，仅允许店员本人或管理员操作
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void forceCancelOngoingOrder(String operatorByType, String operatorBy, String orderId, BigDecimal refundAmount,
                                        String refundReason, List<String> images) {
        if (!"2".equals(operatorByType)) {
            throw new CustomException("禁止操作");
        }
        PlayOrderInfoEntity orderInfo = this.selectOrderInfoById(orderId);
        if (!OrderStatus.ACCEPTED.getCode().equals(orderInfo.getOrderStatus())
                && !OrderStatus.IN_PROGRESS.getCode().equals(orderInfo.getOrderStatus())) {
            throw new CustomException("订单状态异常，无法取消");
        }
        BigDecimal actualRefundAmount = refundAmount != null ? refundAmount : orderInfo.getFinalAmount();
        if (actualRefundAmount.compareTo(BigDecimal.ZERO) < 0) {
            throw new CustomException("退款金额不能小于0");
        }
        if (actualRefundAmount.compareTo(orderInfo.getFinalAmount()) > 0) {
            throw new CustomException("退款金额不能大于支付金额");
        }

        LocalDateTime now = LocalDateTime.now();
        PlayOrderInfoEntity updateEntity = new PlayOrderInfoEntity(orderId, OrderStatus.CANCELLED.getCode());
        updateEntity.setRefundAmount(actualRefundAmount);
        updateEntity.setRefundReason(refundReason);
        updateEntity.setRefundType(OrderRefundFlag.REFUNDED.getCode());
        updateEntity.setOrderCancelTime(now);
        if (OrderStatus.IN_PROGRESS.getCode().equals(orderInfo.getOrderStatus())) {
            updateEntity.setOrderEndTime(now);
        }
        this.baseMapper.updateById(updateEntity);

        PlayCustomUserInfoEntity customUserInfo = customUserInfoService.getById(orderInfo.getPurchaserBy());
        if (customUserInfo == null) {
            throw new CustomException("顾客信息不存在");
        }

        customUserInfoService.updateAccountBalanceById(customUserInfo.getId(), customUserInfo.getAccountBalance(),
                customUserInfo.getAccountBalance().add(actualRefundAmount), BalanceOperationType.REFUND.getCode(), "订单取消退款",
                actualRefundAmount, BigDecimal.ZERO, orderId);

        OrderRefundRecordType forceCancelRefundType = actualRefundAmount.compareTo(orderInfo.getFinalAmount()) == 0
                ? OrderRefundRecordType.FULL
                : OrderRefundRecordType.PARTIAL;
        playOrderRefundInfoService.add(orderId, orderInfo.getPurchaserBy(), orderInfo.getAcceptBy(),
                orderInfo.getPayMethod(), forceCancelRefundType.getCode(), actualRefundAmount, refundReason, operatorByType, operatorBy,
                OrderRefundState.PROCESSING.getCode(), ReviewRequirement.NOT_REQUIRED.getCode());

        restoreCouponsForOrder(orderInfo);

        PlayOrderInfoEntity latest = this.selectOrderInfoById(orderId);
        notificationSender.sendOrderCancelMessageAsync(latest, refundReason);
    }

    @Override
    public void revokeCompletedOrder(OrderRevocationContext context) {
        if (context == null || StrUtil.isBlank(context.getOrderId())) {
            throw new CustomException("订单信息缺失");
        }
        orderLifecycleService.revokeCompletedOrder(context);
    }

    @Override
    public PlayOrderInfoEntity queryByOrderNo(String orderNo) {
        LambdaQueryWrapper<PlayOrderInfoEntity> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(PlayOrderInfoEntity::getOrderNo, orderNo);
        return this.baseMapper.selectOne(lambdaQueryWrapper);
    }

    /**
     * 修改订单
     *
     * @param orderInfo 订单
     * @return 结果
     */
    @Override
    public boolean update(PlayOrderInfoEntity orderInfo) {
        return updateById(orderInfo);
    }

    /**
     * 批量删除订单
     *
     * @param ids 需要删除的订单主键
     * @return 结果
     */
    @Override
    public int deleteOrderInfoByIds(String[] ids) {
        return orderInfoMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除订单信息
     *
     * @param id 订单主键
     * @return 结果
     */
    @Override
    public int deleteOrderInfoById(String id) {
        return orderInfoMapper.deleteById(id);
    }

    private void completeOrderInternal(
            PlayOrderInfoEntity order,
            OperatorType operatorType,
            String operatorBy,
            String remark,
            OrderTriggerSource triggerSource) {
        OrderActor actor = resolveCompletionActor(operatorType.getCode());
        orderLifecycleService.completeOrder(
                order.getId(),
                OrderCompletionContext.of(
                        actor,
                        actor == OrderActor.SYSTEM ? null : operatorBy,
                        triggerSource,
                        remark));
    }

    private void ensureLeaderScope(String operatorBy, PlayOrderInfoEntity order) {
        if (StrUtil.isBlank(operatorBy)) {
            throw new CustomException("操作人信息缺失");
        }
        PlayClerkUserInfoEntity leaderInfo = playClerkUserInfoService.getById(operatorBy);
        if (leaderInfo == null) {
            throw new CustomException("操作人信息不存在");
        }
        if (StrUtil.isBlank(leaderInfo.getSysUserId())) {
            throw new CustomException("账号未绑定系统用户，无法完成订单");
        }
        PlayPersonnelGroupInfoEntity groupInfo = playClerkGroupInfoService.selectByUserId(leaderInfo.getSysUserId());
        if (groupInfo == null) {
            throw new CustomException("仅限组长可操作");
        }
        String orderGroupId = resolveOrderGroupId(order);
        if (StrUtil.isBlank(orderGroupId) || !groupInfo.getId().equals(orderGroupId)) {
            throw new CustomException("您无权完成该订单");
        }
    }

    private String resolveOrderGroupId(PlayOrderInfoEntity order) {
        if (order == null) {
            return null;
        }
        if (StrUtil.isNotBlank(order.getGroupId())) {
            return order.getGroupId();
        }
        if (StrUtil.isNotBlank(order.getAcceptBy())) {
            PlayClerkUserInfoEntity acceptClerk = playClerkUserInfoService.getById(order.getAcceptBy());
            if (acceptClerk != null) {
                return acceptClerk.getGroupId();
            }
        }
        return null;
    }

    private OperatorType resolveOperatorTypeOrThrow(String operatorByType) {
        if (StrUtil.isBlank(operatorByType)) {
            throw new CustomException("操作人类型不能为空");
        }
        try {
            return OperatorType.fromCode(operatorByType);
        } catch (IllegalArgumentException ex) {
            log.warn("Unknown operator type {}", operatorByType, ex);
            throw new CustomException("操作人类型异常");
        }
    }

    private OrderActor resolveCompletionActor(String operatorType) {
        if (operatorType == null) {
            return OrderActor.SYSTEM;
        }
        try {
            OperatorType type = OperatorType.fromCode(operatorType);
            switch (type) {
                case CUSTOMER:
                    return OrderActor.CUSTOMER;
                case CLERK:
                    return OrderActor.CLERK;
                case GROUP_LEADER:
                    return OrderActor.GROUP_LEADER;
                case ADMIN:
                    return OrderActor.ADMIN;
                default:
                    return OrderActor.SYSTEM;
            }
        } catch (IllegalArgumentException ex) {
            log.warn("Unknown operator type {}, fallback to SYSTEM", operatorType, ex);
            return OrderActor.SYSTEM;
        }
    }

    private OrderTriggerSource resolveCompletionSource(OrderActor actor) {
        if (actor == null) {
            return OrderTriggerSource.UNKNOWN;
        }
        switch (actor) {
            case CUSTOMER:
                return OrderTriggerSource.WX_CUSTOMER;
            case CLERK:
                return OrderTriggerSource.WX_CLERK;
            case GROUP_LEADER:
                return OrderTriggerSource.WX_CLERK_MGMT;
            case ADMIN:
                return OrderTriggerSource.ADMIN_CONSOLE;
            default:
                return OrderTriggerSource.SYSTEM;
        }
    }

    private void restoreCouponsForOrder(PlayOrderInfoEntity orderInfo) {
        if (orderInfo == null || CollectionUtil.isEmpty(orderInfo.getCouponIds())) {
            return;
        }
        playCouponDetailsService.updateCouponUseStateByIds(orderInfo.getCouponIds(), CouponUseState.UNUSED.getCode());
    }
}
