package com.tourguide.modules.order.service;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.tourguide.common.api.PageResponse;
|
import com.tourguide.common.exception.BusinessException;
|
import com.tourguide.modules.order.domain.OrderStatus;
|
import com.tourguide.modules.order.dto.GuideOrderResponse;
|
import com.tourguide.modules.order.entity.TourOrder;
|
import com.tourguide.modules.order.mapper.TourOrderMapper;
|
import com.tourguide.modules.operation.entity.ScenicMeetingPoint;
|
import com.tourguide.modules.operation.entity.ScenicPricing;
|
import com.tourguide.modules.operation.mapper.ScenicMeetingPointMapper;
|
import com.tourguide.modules.operation.mapper.ScenicPricingMapper;
|
import com.tourguide.modules.user.entity.GuideReceivingSession;
|
import com.tourguide.modules.user.mapper.GuideRealtimeStatusMapper;
|
import com.tourguide.modules.user.mapper.GuideReceivingSessionMapper;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.util.StringUtils;
|
|
import java.time.LocalDateTime;
|
import java.util.Arrays;
|
import java.util.Collections;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.Set;
|
import java.util.stream.Collectors;
|
|
/** 导游端订单查询服务,所有查询均限定当前导游本人。 */
|
@Service
|
public class GuideTourOrderService {
|
private static final String TAB_ALL = "all";
|
private static final String TAB_ACCEPTED = "accepted";
|
private static final String TAB_SERVING = "serving";
|
private static final String TAB_COMPLETED = "completed";
|
|
private final TourOrderMapper orderMapper;
|
private final GuideReceivingSessionMapper sessionMapper;
|
private final GuideRealtimeStatusMapper realtimeStatusMapper;
|
private final ScenicPricingMapper pricingMapper;
|
private final ScenicMeetingPointMapper meetingPointMapper;
|
|
public GuideTourOrderService(TourOrderMapper orderMapper, GuideReceivingSessionMapper sessionMapper,
|
GuideRealtimeStatusMapper realtimeStatusMapper) {
|
this(orderMapper, sessionMapper, realtimeStatusMapper, null, null);
|
}
|
|
public GuideTourOrderService(TourOrderMapper orderMapper, GuideReceivingSessionMapper sessionMapper,
|
GuideRealtimeStatusMapper realtimeStatusMapper,
|
ScenicPricingMapper pricingMapper) {
|
this(orderMapper, sessionMapper, realtimeStatusMapper, pricingMapper, null);
|
}
|
|
@Autowired
|
public GuideTourOrderService(TourOrderMapper orderMapper, GuideReceivingSessionMapper sessionMapper,
|
GuideRealtimeStatusMapper realtimeStatusMapper,
|
ScenicPricingMapper pricingMapper,
|
ScenicMeetingPointMapper meetingPointMapper) {
|
this.orderMapper = orderMapper;
|
this.sessionMapper = sessionMapper;
|
this.realtimeStatusMapper = realtimeStatusMapper;
|
this.pricingMapper = pricingMapper;
|
this.meetingPointMapper = meetingPointMapper;
|
}
|
|
public PageResponse<GuideOrderResponse> page(Long guideId, String tab, long page, long size) {
|
LambdaQueryWrapper<TourOrder> query = new LambdaQueryWrapper<TourOrder>()
|
.eq(TourOrder::getGuideId, guideId);
|
applyTabFilter(query, normalizeTab(tab));
|
query.orderByDesc(TourOrder::getAcceptedAt)
|
.orderByDesc(TourOrder::getId);
|
Page<TourOrder> result = orderMapper.selectPage(new Page<TourOrder>(page, size), query);
|
Map<Long, Integer> durationByPackageId = durationByPackageId(result.getRecords());
|
Map<Long, ScenicMeetingPoint> meetingPointByOrderId = meetingPointByOrderId(result.getRecords());
|
return new PageResponse<GuideOrderResponse>(
|
result.getRecords().stream()
|
.map(order -> GuideOrderResponse.from(order,
|
durationByPackageId.get(order.getServicePackageId()),
|
meetingPointByOrderId.get(order.getId())))
|
.collect(Collectors.toList()),
|
result.getTotal(), result.getCurrent(), result.getSize());
|
}
|
|
public GuideOrderResponse detail(Long guideId, Long orderId) {
|
TourOrder order = findOwnedOrder(guideId, orderId);
|
if (order == null) {
|
throw new BusinessException(404, "订单不存在");
|
}
|
return toResponse(order);
|
}
|
|
@Transactional
|
public GuideOrderResponse start(Long guideId, Long orderId) {
|
TourOrder order = findOwnedOrder(guideId, orderId);
|
if (order == null) {
|
throw new BusinessException(404, "订单不存在");
|
}
|
if (OrderStatus.IN_SERVICE.matches(order.getStatus())) {
|
return toResponse(order);
|
}
|
if (!OrderStatus.PENDING_MEETING.matches(order.getStatus()) || order.getReceivingSessionId() == null) {
|
throw new BusinessException(400, "当前订单不能开始讲解");
|
}
|
|
GuideReceivingSession session = sessionMapper.selectForUpdate(order.getReceivingSessionId(), guideId);
|
if (session == null) {
|
throw new BusinessException(400, "接单会话不存在或已结束");
|
}
|
if ("IN_SERVICE".equals(session.getStatus())) {
|
order.setStatus(OrderStatus.IN_SERVICE.getCode());
|
return toResponse(order);
|
}
|
if (!"RECEIVING".equals(session.getStatus()) && !"ASSIGNED".equals(session.getStatus())) {
|
throw new BusinessException(400, "接单会话已结束,不能开始讲解");
|
}
|
|
LocalDateTime startedAt = LocalDateTime.now();
|
if (sessionMapper.startSession(session.getId(), guideId) != 1
|
|| orderMapper.startSessionOrders(session.getId(), startedAt) == 0) {
|
throw new BusinessException(409, "讲解状态已变化,请刷新后重试");
|
}
|
realtimeStatusMapper.disableReceiving(guideId);
|
order.setStatus(OrderStatus.IN_SERVICE.getCode());
|
order.setArrivedAt(startedAt);
|
order.setServiceStartedAt(startedAt);
|
return toResponse(order);
|
}
|
|
private GuideOrderResponse toResponse(TourOrder order) {
|
Integer durationMinutes = null;
|
if (pricingMapper != null && order.getServicePackageId() != null) {
|
ScenicPricing pricing = pricingMapper.selectById(order.getServicePackageId());
|
durationMinutes = pricing == null ? null : pricing.getDurationMinutes();
|
}
|
ScenicMeetingPoint meetingPoint = meetingPointByOrderId(Collections.singletonList(order)).get(order.getId());
|
return GuideOrderResponse.from(order, durationMinutes, meetingPoint);
|
}
|
|
private Map<Long, ScenicMeetingPoint> meetingPointByOrderId(List<TourOrder> orders) {
|
if (meetingPointMapper == null || orders == null || orders.isEmpty()) {
|
return Collections.emptyMap();
|
}
|
Set<Long> meetingPointIds = orders.stream()
|
.map(TourOrder::getMeetingPointId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toSet());
|
Set<Long> scenicSpotIds = orders.stream()
|
.map(TourOrder::getScenicSpotId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toSet());
|
if (meetingPointIds.isEmpty() && scenicSpotIds.isEmpty()) {
|
return Collections.emptyMap();
|
}
|
|
Map<Long, ScenicMeetingPoint> pointsById = new HashMap<Long, ScenicMeetingPoint>();
|
if (!meetingPointIds.isEmpty()) {
|
meetingPointMapper.selectBatchIds(meetingPointIds)
|
.forEach(point -> pointsById.put(point.getId(), point));
|
}
|
|
Map<String, ScenicMeetingPoint> pointsByScenicAndName = new HashMap<String, ScenicMeetingPoint>();
|
if (!scenicSpotIds.isEmpty()) {
|
meetingPointMapper.selectList(new LambdaQueryWrapper<ScenicMeetingPoint>()
|
.in(ScenicMeetingPoint::getScenicSpotId, scenicSpotIds))
|
.forEach(point -> pointsByScenicAndName.put(
|
meetingPointKey(point.getScenicSpotId(), point.getPointName()), point));
|
}
|
|
Map<Long, ScenicMeetingPoint> result = new HashMap<Long, ScenicMeetingPoint>();
|
for (TourOrder order : orders) {
|
ScenicMeetingPoint point = pointsById.get(order.getMeetingPointId());
|
if (point == null) {
|
point = pointsByScenicAndName.get(
|
meetingPointKey(order.getScenicSpotId(), order.getMeetingPointName()));
|
}
|
if (point != null && order.getId() != null) {
|
result.put(order.getId(), point);
|
}
|
}
|
return result;
|
}
|
|
private String meetingPointKey(Long scenicSpotId, String meetingPointName) {
|
return String.valueOf(scenicSpotId) + '\u0000' + String.valueOf(meetingPointName);
|
}
|
|
private Map<Long, Integer> durationByPackageId(List<TourOrder> orders) {
|
if (pricingMapper == null || orders == null || orders.isEmpty()) {
|
return Collections.emptyMap();
|
}
|
Set<Long> packageIds = orders.stream()
|
.map(TourOrder::getServicePackageId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toSet());
|
if (packageIds.isEmpty()) {
|
return Collections.emptyMap();
|
}
|
return pricingMapper.selectBatchIds(packageIds).stream()
|
.filter(pricing -> pricing.getDurationMinutes() != null)
|
.collect(Collectors.toMap(ScenicPricing::getId, ScenicPricing::getDurationMinutes));
|
}
|
|
private TourOrder findOwnedOrder(Long guideId, Long orderId) {
|
return orderMapper.selectOne(new LambdaQueryWrapper<TourOrder>()
|
.eq(TourOrder::getId, orderId)
|
.eq(TourOrder::getGuideId, guideId)
|
.last("LIMIT 1"));
|
}
|
|
private String normalizeTab(String tab) {
|
return StringUtils.hasText(tab) ? tab.trim() : TAB_ALL;
|
}
|
|
private void applyTabFilter(LambdaQueryWrapper<TourOrder> query, String tab) {
|
if (TAB_ALL.equals(tab)) {
|
query.ne(TourOrder::getStatus, OrderStatus.PENDING_PAYMENT.getCode());
|
return;
|
}
|
if (TAB_ACCEPTED.equals(tab)) {
|
query.in(TourOrder::getStatus,
|
OrderStatus.PENDING_ACCEPT.getCode(), OrderStatus.PENDING_MEETING.getCode());
|
return;
|
}
|
if (TAB_SERVING.equals(tab)) {
|
query.eq(TourOrder::getStatus, OrderStatus.IN_SERVICE.getCode());
|
return;
|
}
|
if (TAB_COMPLETED.equals(tab)) {
|
query.in(TourOrder::getStatus, Arrays.asList(
|
OrderStatus.COMPLETED.getCode(), OrderStatus.CANCELLED.getCode(), OrderStatus.REFUNDED.getCode()));
|
return;
|
}
|
throw new BusinessException(400, "订单筛选状态不合法");
|
}
|
}
|