| | |
| | | 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.order.service.GuideSessionTransitionService; |
| | | import com.tourguide.modules.user.dto.GuideReceivingOptionResponse; |
| | | import com.tourguide.modules.user.dto.GuideReceivingStartRequest; |
| | | import com.tourguide.modules.user.entity.GuideRealtimeStatus; |
| | |
| | | private final GuideRealtimeStatusMapper realtimeStatusMapper; |
| | | private final PlatformSettingsService platformSettingsService; |
| | | private final TourOrderMapper orderMapper; |
| | | private final GuideSessionTransitionService sessionTransitionService; |
| | | |
| | | public GuideReceivingService(GuideUserService guideUserService, GuideScenicConfigMapper guideScenicMapper, |
| | | GuideTeamScenicConfigMapper teamScenicMapper, OperationScenicSpotMapper scenicMapper, |
| | | ScenicMeetingPointMapper meetingPointMapper, ScenicPricingMapper pricingMapper, |
| | | GuideReceivingSessionMapper sessionMapper, GuideRealtimeStatusMapper realtimeStatusMapper, |
| | | PlatformSettingsService platformSettingsService, TourOrderMapper orderMapper) { |
| | | PlatformSettingsService platformSettingsService, TourOrderMapper orderMapper, |
| | | GuideSessionTransitionService sessionTransitionService) { |
| | | this.guideUserService = guideUserService; |
| | | this.guideScenicMapper = guideScenicMapper; |
| | | this.teamScenicMapper = teamScenicMapper; |
| | |
| | | this.realtimeStatusMapper = realtimeStatusMapper; |
| | | this.platformSettingsService = platformSettingsService; |
| | | this.orderMapper = orderMapper; |
| | | this.sessionTransitionService = sessionTransitionService; |
| | | } |
| | | |
| | | public GuideReceivingOptionResponse options(Long guideId) { |
| | |
| | | new LambdaQueryWrapper<ScenicPricing>().eq(ScenicPricing::getScenicSpotId, scenic.getId()) |
| | | .orderByAsc(ScenicPricing::getId)).stream() |
| | | .map(item -> new GuideReceivingOptionResponse.ServiceOption(item.getId(), item.getServiceType(), |
| | | item.getDurationMinutes(), item.getMinParticipants(), item.getMaxParticipants())) |
| | | item.getDurationMinutes(), item.getMinParticipants(), item.getMaxParticipants(), |
| | | item.getOriginalPrice(), item.getDiscountPrice())) |
| | | .collect(Collectors.toList()); |
| | | rows.add(new GuideReceivingOptionResponse.ScenicOption(scenic.getId(), scenic.getScenicName(), points, services)); |
| | | rows.add(new GuideReceivingOptionResponse.ScenicOption(scenic.getId(), scenic.getScenicName(), |
| | | positive(scenic.getFirstOrderLeadMinutes(), 15), points, services)); |
| | | } |
| | | return new GuideReceivingOptionResponse(premiumEnabled, value(settings.getPreciseDispatchOrderCount()), |
| | | settings.getPreciseDispatchRating(), rows); |
| | |
| | | |
| | | public GuideReceivingSession current(Long guideId) { |
| | | guideUserService.findEnabledById(guideId); |
| | | return sessionMapper.selectOne(new LambdaQueryWrapper<GuideReceivingSession>() |
| | | GuideReceivingSession session = sessionMapper.selectOne(new LambdaQueryWrapper<GuideReceivingSession>() |
| | | .eq(GuideReceivingSession::getGuideId, guideId) |
| | | .in(GuideReceivingSession::getStatus, "RECEIVING", "ASSIGNED", "IN_SERVICE") |
| | | .orderByDesc(GuideReceivingSession::getId).last("LIMIT 1")); |
| | | if (session == null) { |
| | | return null; |
| | | } |
| | | if (hasActiveServiceOrder(session)) { |
| | | return session; |
| | | } |
| | | |
| | | GuideRealtimeStatus realtimeStatus = realtimeStatusMapper.selectById(guideId); |
| | | if (realtimeStatus == null |
| | | || !Integer.valueOf(1).equals(realtimeStatus.getOnlineStatus()) |
| | | || !Integer.valueOf(1).equals(realtimeStatus.getAvailableStatus())) { |
| | | return null; |
| | | } |
| | | return session; |
| | | } |
| | | |
| | | @Transactional |
| | |
| | | guideUserService.findEnabledById(guideId); |
| | | stopActiveSessions(guideId); |
| | | realtimeStatusMapper.update(null, new LambdaUpdateWrapper<GuideRealtimeStatus>() |
| | | .eq(GuideRealtimeStatus::getGuideId, guideId).set(GuideRealtimeStatus::getAvailableStatus, 0)); |
| | | .eq(GuideRealtimeStatus::getGuideId, guideId) |
| | | .set(GuideRealtimeStatus::getOnlineStatus, 0) |
| | | .set(GuideRealtimeStatus::getAvailableStatus, 0)); |
| | | } |
| | | |
| | | @Transactional |
| | |
| | | } |
| | | |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | int orderCount = orderMapper.completeSessionOrders(guideId, session.getId(), now); |
| | | if (orderCount == 0) { |
| | | if (!sessionTransitionService.completeSession(session, now)) { |
| | | throw new BusinessException(400, "no active service order"); |
| | | } |
| | | |
| | | sessionMapper.update(null, new LambdaUpdateWrapper<GuideReceivingSession>() |
| | | .eq(GuideReceivingSession::getId, session.getId()) |
| | | .eq(GuideReceivingSession::getGuideId, guideId) |
| | | .eq(GuideReceivingSession::getStatus, "IN_SERVICE") |
| | | .set(GuideReceivingSession::getStatus, "STOPPED")); |
| | | realtimeStatusMapper.update(null, new LambdaUpdateWrapper<GuideRealtimeStatus>() |
| | | .eq(GuideRealtimeStatus::getGuideId, guideId) |
| | | .set(GuideRealtimeStatus::getOnlineStatus, 0) |
| | | .set(GuideRealtimeStatus::getAvailableStatus, 0)); |
| | | } |
| | | |
| | | private Set<String> configuredScenicNames(GuideUser guide) { |
| | |
| | | .eq(ScenicPricing::getScenicSpotId, scenicId)).stream() |
| | | .map(ScenicPricing::getServiceType).collect(Collectors.toSet()); |
| | | for (String type : serviceTypes) { |
| | | if (!type.startsWith(mode + "_") || !configured.contains(type)) { |
| | | boolean modeMatches = "ALL".equals(mode) |
| | | ? (type.startsWith("GROUP_") || type.startsWith("PRIVATE_")) |
| | | : type.startsWith(mode + "_"); |
| | | if (!modeMatches || !configured.contains(type)) { |
| | | throw new BusinessException(400, "service type does not match receiving mode or scenic configuration"); |
| | | } |
| | | if (type.endsWith("_PREMIUM") && !premiumEnabled(guide, platformSettingsService.getSettings())) { |
| | |
| | | } |
| | | |
| | | private boolean premiumEnabled(GuideUser guide, PlatformSettingsResponse settings) { |
| | | if (guide.getAutoDisableWhitelisted() != null && guide.getAutoDisableWhitelisted() == 1) { |
| | | if (guide == null) { |
| | | return false; |
| | | } |
| | | if (Integer.valueOf(1).equals(guide.getPremiumEnabled()) |
| | | || Integer.valueOf(1).equals(guide.getAutoDisableWhitelisted())) { |
| | | return true; |
| | | } |
| | | if (settings == null) { |
| | | return false; |
| | | } |
| | | long orderCount = guide.getAcceptedOrderCount() == null ? 0 : guide.getAcceptedOrderCount(); |
| | | BigDecimal rating = guide.getRating() == null ? BigDecimal.ZERO : guide.getRating(); |
| | |
| | | .eq(GuideReceivingSession::getGuideId, guideId) |
| | | .in(GuideReceivingSession::getStatus, "RECEIVING", "ASSIGNED") |
| | | .set(GuideReceivingSession::getStatus, "STOPPED")); |
| | | |
| | | // 订单取消后可能遗留 IN_SERVICE 会话;无未完成订单时才安全关闭。 |
| | | long activeOrderCount = orderMapper.selectCount(new LambdaQueryWrapper<TourOrder>() |
| | | .eq(TourOrder::getGuideId, guideId) |
| | | .in(TourOrder::getStatus, |
| | | OrderStatus.PENDING_ACCEPT.getCode(), |
| | | OrderStatus.PENDING_MEETING.getCode(), |
| | | OrderStatus.IN_SERVICE.getCode())); |
| | | if (activeOrderCount == 0) { |
| | | sessionMapper.update(null, new LambdaUpdateWrapper<GuideReceivingSession>() |
| | | .eq(GuideReceivingSession::getGuideId, guideId) |
| | | .eq(GuideReceivingSession::getStatus, "IN_SERVICE") |
| | | .set(GuideReceivingSession::getStatus, "STOPPED")); |
| | | } |
| | | } |
| | | |
| | | private boolean hasActiveServiceOrder(GuideReceivingSession session) { |
| | | return orderMapper.selectCount(new LambdaQueryWrapper<TourOrder>() |
| | | .eq(TourOrder::getGuideId, session.getGuideId()) |
| | | .eq(TourOrder::getReceivingSessionId, session.getId()) |
| | | .in(TourOrder::getStatus, |
| | | OrderStatus.PENDING_MEETING.getCode(), |
| | | OrderStatus.IN_SERVICE.getCode())) > 0; |
| | | } |
| | | |
| | | private void saveRealtimeStatus(Long guideId, GuideReceivingStartRequest request, Long scenicId) { |
| | |
| | | } |
| | | |
| | | private String normalizeMode(String mode) { |
| | | if (!"GROUP".equals(mode) && !"PRIVATE".equals(mode)) throw new BusinessException(400, "invalid receiving mode"); |
| | | if (!"ALL".equals(mode) && !"GROUP".equals(mode) && !"PRIVATE".equals(mode)) { |
| | | throw new BusinessException(400, "invalid receiving mode"); |
| | | } |
| | | return mode; |
| | | } |
| | | |
| | | private long value(Integer number) { return number == null ? 0L : number.longValue(); } |
| | | |
| | | private int positive(Integer number, int fallback) { |
| | | return number == null || number < 1 ? fallback : number; |
| | | } |
| | | } |