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.exception.BusinessException; import com.tourguide.modules.order.domain.OrderStatus; import com.tourguide.modules.order.domain.RefundStatus; import com.tourguide.modules.order.dto.GuideEarningItemResponse; import com.tourguide.modules.order.dto.GuideEarningsResponse; import com.tourguide.modules.order.entity.TourOrder; import com.tourguide.modules.order.mapper.TourOrderMapper; import com.tourguide.modules.user.service.GuideUserService; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; /** 查询导游已完成订单的业绩快照。 */ @Service public class GuideEarningsService { private final TourOrderMapper orderMapper; private final GuideUserService guideUserService; public GuideEarningsService(TourOrderMapper orderMapper, GuideUserService guideUserService) { this.orderMapper = orderMapper; this.guideUserService = guideUserService; } public GuideEarningsResponse get(Long guideId, String serviceType, String scenicName, LocalDate date, long pageNum, long pageSize) { guideUserService.findEnabledById(guideId); validateServiceType(serviceType); long safePage = Math.max(pageNum, 1); long safeSize = Math.min(Math.max(pageSize, 1), 100); LocalDate today = LocalDate.now(); BigDecimal todayEarnings = safeAmount(orderMapper.selectGuideEarningSum( guideId, today.atStartOfDay(), today.plusDays(1).atStartOfDay())); LocalDate monthStart = today.withDayOfMonth(1); BigDecimal monthEarnings = safeAmount(orderMapper.selectGuideEarningSum( guideId, monthStart.atStartOfDay(), monthStart.plusMonths(1).atStartOfDay())); LambdaQueryWrapper query = new LambdaQueryWrapper() .eq(TourOrder::getGuideId, guideId) .and(wrapper -> wrapper.eq(TourOrder::getStatus, OrderStatus.COMPLETED.getCode()) .or(inner -> inner.eq(TourOrder::getStatus, OrderStatus.CANCELLED.getCode()) .eq(TourOrder::getRefundStatus, RefundStatus.PARTIAL.getCode()))) .isNotNull(TourOrder::getGuideEarningAmount) .and(wrapper -> wrapper.isNull(TourOrder::getRefundStatus) .or().in(TourOrder::getRefundStatus, Arrays.asList(RefundStatus.NONE.getCode(), RefundStatus.PARTIAL.getCode(), RefundStatus.REFUND_FAILED.getCode()))) .apply(TourOrderMapper.GUIDE_REMAINING_AMOUNT_SQL + " > 0") .eq(StringUtils.hasText(scenicName), TourOrder::getScenicName, StringUtils.hasText(scenicName) ? scenicName.trim() : null) .ge(date != null, TourOrder::getCompletedAt, date == null ? null : date.atStartOfDay()) .lt(date != null, TourOrder::getCompletedAt, date == null ? null : date.plusDays(1).atStartOfDay()) .orderByDesc(TourOrder::getCompletedAt) .orderByDesc(TourOrder::getId); if (StringUtils.hasText(serviceType)) { query.in(TourOrder::getOrderType, resolveOrderTypes(serviceType)); } Page result = orderMapper.selectPage(new Page(safePage, safeSize), query); List records = result.getRecords().stream() .map(GuideEarningItemResponse::new) .collect(Collectors.toList()); return new GuideEarningsResponse(todayEarnings, monthEarnings, orderMapper.selectGuideEarningScenics(guideId), records, result.getTotal(), result.getCurrent(), result.getSize()); } private void validateServiceType(String serviceType) { if (StringUtils.hasText(serviceType) && !"QUICK".equals(serviceType) && !"PREMIUM".equals(serviceType) && !"GROUP_QUICK".equals(serviceType) && !"GROUP_PREMIUM".equals(serviceType) && !"PRIVATE_QUICK".equals(serviceType) && !"PRIVATE_PREMIUM".equals(serviceType)) { throw new BusinessException(400, "订单类型不支持"); } } private List resolveOrderTypes(String serviceType) { if ("QUICK".equals(serviceType)) { return Arrays.asList("GROUP_QUICK", "PRIVATE_QUICK"); } if ("PREMIUM".equals(serviceType)) { return Arrays.asList("GROUP_PREMIUM", "PRIVATE_PREMIUM"); } return Collections.singletonList(serviceType); } private BigDecimal safeAmount(BigDecimal amount) { return amount == null ? BigDecimal.ZERO.setScale(2) : amount; } }