| | |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | @Service |
| | |
| | | @Scheduled(fixedDelayString = "${tourguide.guide.auto-disable-scan-delay-millis:60000}") |
| | | @Transactional |
| | | public void disableEligibleGuides() { |
| | | synchronizeGuideStatuses(); |
| | | } |
| | | |
| | | @Transactional |
| | | public void synchronizeGuideStatuses() { |
| | | GuideAutoDisableConfig config = configMapper.selectById(1); |
| | | if (!valid(config)) return; |
| | | restoreGuidesNoLongerEligible(config); |
| | | |
| | | 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) |
| | | .eq(GuideUser::getAutoDisableWhitelisted, 0) |
| | | .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)); |
| | | for (GuideUser guide : candidates) { |
| | | disableIfEligible(guide, config); |
| | | } |
| | | return candidates == null ? Collections.emptyList() : candidates; |
| | | } |
| | | |
| | | void disableIfEligible(GuideUser guide, GuideAutoDisableConfig config) { |
| | |
| | | guide.getId(), guide.getAcceptedOrderCount(), guide.getRating()); |
| | | } |
| | | |
| | | private void restoreGuidesNoLongerEligible(GuideAutoDisableConfig config) { |
| | | int restored = guideUserMapper.restoreAutoDisabledGuidesNoLongerEligible( |
| | | config.getOrderThreshold(), config.getRatingThreshold()); |
| | | if (restored > 0) { |
| | | log.info("Restored automatically disabled guides after rule reconciliation, count={}", restored); |
| | | } |
| | | } |
| | | |
| | | private boolean hasActiveServiceOrder(Long guideId) { |
| | | return orderMapper.selectCount(new LambdaQueryWrapper<TourOrder>() |
| | | .eq(TourOrder::getGuideId, guideId) |
| | |
| | | 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 |