package com.tourguide.modules.user.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.tourguide.modules.order.domain.OrderStatus; import com.tourguide.modules.order.entity.TourOrder; import com.tourguide.modules.order.mapper.TourOrderMapper; import com.tourguide.modules.user.entity.GuideAutoDisableConfig; import com.tourguide.modules.user.entity.GuideUser; import com.tourguide.modules.user.mapper.GuideAutoDisableConfigMapper; import com.tourguide.modules.user.mapper.GuideRealtimeStatusMapper; import com.tourguide.modules.user.mapper.GuideReceivingSessionMapper; import com.tourguide.modules.user.mapper.GuideUserMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.Arrays; import java.util.List; @Service public class GuideAutoDisableService { private static final Logger log = LoggerFactory.getLogger(GuideAutoDisableService.class); private static final int BATCH_SIZE = 100; private final GuideAutoDisableConfigMapper configMapper; private final GuideUserMapper guideUserMapper; private final TourOrderMapper orderMapper; private final GuideRealtimeStatusMapper realtimeStatusMapper; private final GuideReceivingSessionMapper receivingSessionMapper; public GuideAutoDisableService(GuideAutoDisableConfigMapper configMapper, GuideUserMapper guideUserMapper, TourOrderMapper orderMapper, GuideRealtimeStatusMapper realtimeStatusMapper, GuideReceivingSessionMapper receivingSessionMapper) { this.configMapper = configMapper; this.guideUserMapper = guideUserMapper; this.orderMapper = orderMapper; this.realtimeStatusMapper = realtimeStatusMapper; this.receivingSessionMapper = receivingSessionMapper; } @Scheduled(fixedDelayString = "${tourguide.guide.auto-disable-scan-delay-millis:60000}") @Transactional public void disableEligibleGuides() { GuideAutoDisableConfig config = configMapper.selectById(1); if (!valid(config)) return; List candidates = guideUserMapper.selectList( new LambdaQueryWrapper() .eq(GuideUser::getStatus, GuideUserService.STATUS_ENABLED) .eq(GuideUser::getAutoDisableWhitelisted, 0) .ge(GuideUser::getAcceptedOrderCount, config.getOrderThreshold()) .gt(GuideUser::getRating, BigDecimal.ZERO) .le(GuideUser::getRating, config.getRatingThreshold()) .orderByAsc(GuideUser::getId) .last("LIMIT " + BATCH_SIZE)); for (GuideUser guide : candidates) { disableIfEligible(guide, config); } } void disableIfEligible(GuideUser guide, GuideAutoDisableConfig config) { if (guide == null || Integer.valueOf(1).equals(guide.getAutoDisableWhitelisted())) return; if (hasActiveServiceOrder(guide.getId())) return; int updated = guideUserMapper.autoDisableIfEligible(guide.getId(), config.getOrderThreshold(), config.getRatingThreshold()); if (updated != 1) return; realtimeStatusMapper.disableReceiving(guide.getId()); receivingSessionMapper.stopReceivingByGuide(guide.getId()); log.warn("Guide auto-disabled, guideId={}, acceptedOrderCount={}, rating={}", guide.getId(), guide.getAcceptedOrderCount(), guide.getRating()); } private boolean hasActiveServiceOrder(Long guideId) { return orderMapper.selectCount(new LambdaQueryWrapper() .eq(TourOrder::getGuideId, guideId) .in(TourOrder::getStatus, Arrays.asList( OrderStatus.PENDING_MEETING.getCode(), OrderStatus.IN_SERVICE.getCode()))) > 0; } private boolean valid(GuideAutoDisableConfig config) { return config != null && config.getOrderThreshold() != null && config.getOrderThreshold() > 0 && config.getRatingThreshold() != null; } }