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.Collections;
|
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;
|
Long lastGuideId = null;
|
do {
|
List<GuideUser> candidates = selectCandidates(config, lastGuideId);
|
if (candidates.isEmpty()) return;
|
for (GuideUser guide : candidates) {
|
disableIfEligible(guide, config);
|
}
|
lastGuideId = candidates.get(candidates.size() - 1).getId();
|
if (candidates.size() < BATCH_SIZE) return;
|
} while (true);
|
}
|
|
/** 导游移出白名单后立即复核,避免等待下一次定时扫描。 */
|
@Transactional
|
public void disableGuideIfEligible(Long guideId) {
|
if (guideId == null) return;
|
GuideAutoDisableConfig config = configMapper.selectById(1);
|
if (!valid(config)) return;
|
GuideUser guide = guideUserMapper.selectById(guideId);
|
if (!matchesRule(guide, config)) return;
|
disableIfEligible(guide, config);
|
}
|
|
private List<GuideUser> selectCandidates(GuideAutoDisableConfig config, Long lastGuideId) {
|
List<GuideUser> candidates = guideUserMapper.selectList(
|
new LambdaQueryWrapper<GuideUser>()
|
.eq(GuideUser::getStatus, GuideUserService.STATUS_ENABLED)
|
.and(wrapper -> wrapper.isNull(GuideUser::getAutoDisableWhitelisted)
|
.or().eq(GuideUser::getAutoDisableWhitelisted, 0))
|
.ge(GuideUser::getAcceptedOrderCount, config.getOrderThreshold())
|
.gt(GuideUser::getRating, BigDecimal.ZERO)
|
.le(GuideUser::getRating, config.getRatingThreshold())
|
.gt(lastGuideId != null, GuideUser::getId, lastGuideId)
|
.orderByAsc(GuideUser::getId)
|
.last("LIMIT " + BATCH_SIZE));
|
return candidates == null ? Collections.emptyList() : candidates;
|
}
|
|
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<TourOrder>()
|
.eq(TourOrder::getGuideId, guideId)
|
.in(TourOrder::getStatus, Arrays.asList(
|
OrderStatus.PENDING_MEETING.getCode(), OrderStatus.IN_SERVICE.getCode()))) > 0;
|
}
|
|
private boolean matchesRule(GuideUser guide, GuideAutoDisableConfig config) {
|
return guide != null
|
&& Integer.valueOf(GuideUserService.STATUS_ENABLED).equals(guide.getStatus())
|
&& !Integer.valueOf(1).equals(guide.getAutoDisableWhitelisted())
|
&& guide.getAcceptedOrderCount() != null
|
&& guide.getAcceptedOrderCount() >= config.getOrderThreshold()
|
&& guide.getRating() != null
|
&& guide.getRating().compareTo(BigDecimal.ZERO) > 0
|
&& guide.getRating().compareTo(config.getRatingThreshold()) <= 0;
|
}
|
|
private boolean valid(GuideAutoDisableConfig config) {
|
return config != null
|
&& config.getOrderThreshold() != null && config.getOrderThreshold() > 0
|
&& config.getRatingThreshold() != null;
|
}
|
}
|