liujie
2026-09-09 39f6fca28e3f78abe48f08e474ec011abb990b36
backend/src/main/java/com/tourguide/modules/order/service/OrderLifecycleScheduler.java
@@ -1,11 +1,14 @@
package com.tourguide.modules.order.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.tourguide.config.properties.UniPushProperties;
import com.tourguide.modules.operation.entity.OperationScenicSpot;
import com.tourguide.modules.operation.mapper.OperationScenicSpotMapper;
import com.tourguide.modules.order.domain.OrderStatus;
import com.tourguide.modules.order.domain.RefundStatus;
import com.tourguide.modules.order.entity.TourOrder;
import com.tourguide.modules.order.mapper.TourOrderMapper;
import com.tourguide.modules.system.service.GuideNotificationService;
import com.tourguide.modules.user.entity.GuideReceivingSession;
import com.tourguide.modules.user.mapper.GuideRealtimeStatusMapper;
import com.tourguide.modules.user.mapper.GuideReceivingSessionMapper;
@@ -23,22 +26,29 @@
    private final GuideOrderMatchingService matchingService;
    private final GuideReceivingSessionMapper sessionMapper;
    private final GuideSessionTransitionService sessionTransitionService;
    private final GuideNotificationService notificationService;
    private final UniPushProperties uniPushProperties;
    public OrderLifecycleScheduler(TourOrderMapper orderMapper, OperationScenicSpotMapper scenicMapper,
                                   GuideOrderMatchingService matchingService,
                                   GuideReceivingSessionMapper sessionMapper,
                                   GuideSessionTransitionService sessionTransitionService) {
                                   GuideSessionTransitionService sessionTransitionService,
                                   GuideNotificationService notificationService,
                                   UniPushProperties uniPushProperties) {
        this.orderMapper = orderMapper;
        this.scenicMapper = scenicMapper;
        this.matchingService = matchingService;
        this.sessionMapper = sessionMapper;
        this.sessionTransitionService = sessionTransitionService;
        this.notificationService = notificationService;
        this.uniPushProperties = uniPushProperties;
    }
    @Scheduled(fixedDelayString = "${tourguide.order.lifecycle-scan-delay-millis:10000}")
    public void process() {
        cancelExpiredUnpaidOrders();
        retryPendingOrders();
        sendServiceReminders();
        startDueGroupSessions();
    }
@@ -59,7 +69,10 @@
    void retryPendingOrders() {
        List<TourOrder> orders = orderMapper.selectList(new LambdaQueryWrapper<TourOrder>()
                .eq(TourOrder::getStatus, OrderStatus.PENDING_ACCEPT.getCode())
                .isNotNull(TourOrder::getPaymentTime).orderByAsc(TourOrder::getPaymentTime).last("LIMIT " + BATCH_SIZE));
                .isNotNull(TourOrder::getPaymentTime)
                .and(wrapper -> wrapper.isNull(TourOrder::getRefundStatus)
                        .or().eq(TourOrder::getRefundStatus, RefundStatus.NONE.getCode()))
                .orderByAsc(TourOrder::getPaymentTime).last("LIMIT " + BATCH_SIZE));
        for (TourOrder order : orders) matchingService.match(order.getId());
    }
@@ -73,4 +86,32 @@
            sessionTransitionService.startIfDue(session);
        }
    }
    void sendServiceReminders() {
        int reminderMinutes = Math.max(1, uniPushProperties.getServiceReminderMinutes());
        LocalDateTime now = LocalDateTime.now();
        List<GuideReceivingSession> sessions = sessionMapper.selectList(
                new LambdaQueryWrapper<GuideReceivingSession>()
                        .eq(GuideReceivingSession::getStatus, "RECEIVING")
                        .isNull(GuideReceivingSession::getReminderPushedAt)
                        .isNotNull(GuideReceivingSession::getServiceStartAt)
                        .gt(GuideReceivingSession::getServiceStartAt, now)
                        .le(GuideReceivingSession::getServiceStartAt, now.plusMinutes(reminderMinutes))
                        .orderByAsc(GuideReceivingSession::getServiceStartAt)
                        .last("LIMIT " + BATCH_SIZE));
        for (GuideReceivingSession session : sessions) {
            TourOrder order = orderMapper.selectOne(new LambdaQueryWrapper<TourOrder>()
                    .eq(TourOrder::getReceivingSessionId, session.getId())
                    .eq(TourOrder::getGuideId, session.getGuideId())
                    .eq(TourOrder::getStatus, OrderStatus.PENDING_MEETING.getCode())
                    .orderByAsc(TourOrder::getId)
                    .last("LIMIT 1"));
            if (order == null) continue;
            LocalDateTime claimedAt = LocalDateTime.now();
            if (sessionMapper.claimServiceReminder(session.getId(), claimedAt) != 1) continue;
            if (!notificationService.pushServiceReminder(order)) {
                sessionMapper.releaseServiceReminder(session.getId(), claimedAt);
            }
        }
    }
}