package com.tourguide.modules.finance.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.finance.dto.FinanceLedgerPageResponse;
|
import com.tourguide.modules.finance.dto.FinanceLedgerResponse;
|
import com.tourguide.modules.finance.dto.WithdrawalProcessRequest;
|
import com.tourguide.modules.finance.dto.WithdrawalResponse;
|
import com.tourguide.modules.finance.entity.GuideWithdrawal;
|
import com.tourguide.modules.finance.mapper.GuideWithdrawalMapper;
|
import com.tourguide.modules.operation.dto.PlatformSettingsResponse;
|
import com.tourguide.modules.operation.service.PlatformSettingsService;
|
import com.tourguide.modules.order.entity.TourOrder;
|
import com.tourguide.modules.order.mapper.TourOrderMapper;
|
import com.tourguide.modules.user.entity.GuideTeam;
|
import com.tourguide.modules.user.entity.GuideUser;
|
import com.tourguide.modules.user.mapper.GuideTeamMapper;
|
import com.tourguide.modules.user.mapper.GuideUserMapper;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.util.StringUtils;
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.util.ArrayList;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
import java.util.stream.Collectors;
|
|
@Service
|
public class AdminFinanceService {
|
private final TourOrderMapper orderMapper;
|
private final GuideWithdrawalMapper withdrawalMapper;
|
private final GuideUserMapper guideUserMapper;
|
private final GuideTeamMapper guideTeamMapper;
|
private final PlatformSettingsService settingsService;
|
private final GuideWithdrawalService guideWithdrawalService;
|
|
public AdminFinanceService(TourOrderMapper orderMapper, GuideWithdrawalMapper withdrawalMapper,
|
GuideUserMapper guideUserMapper, GuideTeamMapper guideTeamMapper,
|
PlatformSettingsService settingsService,
|
GuideWithdrawalService guideWithdrawalService) {
|
this.orderMapper = orderMapper;
|
this.withdrawalMapper = withdrawalMapper;
|
this.guideUserMapper = guideUserMapper;
|
this.guideTeamMapper = guideTeamMapper;
|
this.settingsService = settingsService;
|
this.guideWithdrawalService = guideWithdrawalService;
|
}
|
|
public FinanceLedgerPageResponse ledger(long page, long size, String direction, String keyword,
|
String paymentMethod, LocalDate startDate, LocalDate endDate) {
|
boolean refund = "REFUND".equals(direction);
|
LambdaQueryWrapper<TourOrder> query = ledgerQuery(refund, keyword, paymentMethod, startDate, endDate);
|
Page<TourOrder> result = orderMapper.selectPage(new Page<TourOrder>(page, size), query);
|
PlatformSettingsResponse settings = settingsService.getSettings();
|
Map<Long, GuideUser> guides = loadGuides(result.getRecords());
|
Map<Long, String> teams = loadTeams(result.getRecords(), guides.values());
|
List<FinanceLedgerResponse> records = result.getRecords().stream()
|
.map(order -> toLedger(order, refund, settings, guides.get(order.getGuideId()), teams))
|
.collect(Collectors.toList());
|
BigDecimal totalAmount = orderMapper.selectList(
|
ledgerQuery(refund, keyword, paymentMethod, startDate, endDate)).stream()
|
.map(order -> refund ? safeAmount(order.getRefundAmount()) : paymentAmount(order))
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
if (refund) totalAmount = totalAmount.negate();
|
return new FinanceLedgerPageResponse(
|
new PageResponse<FinanceLedgerResponse>(records, result.getTotal(), result.getCurrent(), result.getSize()),
|
totalAmount.setScale(2, RoundingMode.HALF_UP));
|
}
|
|
public PageResponse<WithdrawalResponse> withdrawals(long page, long size, String keyword, String status,
|
LocalDate startDate, LocalDate endDate) {
|
LambdaQueryWrapper<GuideWithdrawal> query = new LambdaQueryWrapper<GuideWithdrawal>();
|
if (StringUtils.hasText(keyword)) {
|
String value = keyword.trim();
|
query.and(wrapper -> wrapper.like(GuideWithdrawal::getWithdrawalNo, value)
|
.or().like(GuideWithdrawal::getGuideName, value)
|
.or().like(GuideWithdrawal::getGuideMobile, value));
|
}
|
query.eq(StringUtils.hasText(status), GuideWithdrawal::getStatus, status)
|
.ge(startDate != null, GuideWithdrawal::getAppliedAt, startDate == null ? null : startDate.atStartOfDay())
|
.lt(endDate != null, GuideWithdrawal::getAppliedAt, endDate == null ? null : endDate.plusDays(1).atStartOfDay())
|
.orderByDesc(GuideWithdrawal::getId);
|
Page<GuideWithdrawal> result = withdrawalMapper.selectPage(new Page<GuideWithdrawal>(page, size), query);
|
return new PageResponse<WithdrawalResponse>(result.getRecords().stream()
|
.map(WithdrawalResponse::new).collect(Collectors.toList()),
|
result.getTotal(), result.getCurrent(), result.getSize());
|
}
|
|
@Transactional
|
public WithdrawalResponse processWithdrawal(Long id, WithdrawalProcessRequest request, String processor) {
|
return guideWithdrawalService.process(id, request.getStatus(), request.getAuditRemark(), processor);
|
}
|
|
private LambdaQueryWrapper<TourOrder> ledgerQuery(boolean refund, String keyword, String paymentMethod,
|
LocalDate startDate, LocalDate endDate) {
|
LambdaQueryWrapper<TourOrder> query = new LambdaQueryWrapper<TourOrder>();
|
if (StringUtils.hasText(keyword)) {
|
String value = keyword.trim();
|
query.and(wrapper -> wrapper.like(TourOrder::getOrderNo, value)
|
.or().like(TourOrder::getPaymentOrderNo, value)
|
.or().like(TourOrder::getRefundOrderNo, value));
|
}
|
if (refund) {
|
query.isNotNull(TourOrder::getRefundTime)
|
.ge(startDate != null, TourOrder::getRefundTime, startDate == null ? null : startDate.atStartOfDay())
|
.lt(endDate != null, TourOrder::getRefundTime, endDate == null ? null : endDate.plusDays(1).atStartOfDay())
|
.orderByDesc(TourOrder::getRefundTime);
|
} else {
|
query.isNotNull(TourOrder::getPaymentTime)
|
.ge(startDate != null, TourOrder::getPaymentTime, startDate == null ? null : startDate.atStartOfDay())
|
.lt(endDate != null, TourOrder::getPaymentTime, endDate == null ? null : endDate.plusDays(1).atStartOfDay())
|
.orderByDesc(TourOrder::getPaymentTime);
|
}
|
query.eq(StringUtils.hasText(paymentMethod), TourOrder::getPaymentMethod, paymentMethod);
|
return query;
|
}
|
|
private FinanceLedgerResponse toLedger(TourOrder order, boolean refund, PlatformSettingsResponse settings,
|
GuideUser guide, Map<Long, String> teams) {
|
BigDecimal amount = refund ? safeAmount(order.getRefundAmount()) : paymentAmount(order);
|
BigDecimal orderAmount = paymentAmount(order);
|
BigDecimal guideIncome;
|
BigDecimal platformIncome;
|
if (order.getGuideEarningAmount() != null) {
|
guideIncome = order.getGuideEarningAmount().setScale(2, RoundingMode.HALF_UP);
|
platformIncome = orderAmount.subtract(guideIncome).setScale(2, RoundingMode.HALF_UP);
|
} else {
|
BigDecimal commissionRate = settings.getCommissionRate().movePointLeft(2);
|
platformIncome = orderAmount.multiply(commissionRate).setScale(2, RoundingMode.HALF_UP);
|
guideIncome = orderAmount.subtract(platformIncome).setScale(2, RoundingMode.HALF_UP);
|
}
|
if (refund) {
|
platformIncome = platformIncome.negate();
|
guideIncome = guideIncome.negate();
|
amount = amount.negate();
|
}
|
LocalDateTime settledAt = order.getSettledAt() == null
|
? order.getSettlementDueAt() : order.getSettledAt();
|
Long teamId = order.getCommissionTeamId() != null
|
? order.getCommissionTeamId()
|
: guide == null ? null : guide.getTeamId();
|
String teamName = teamId == null ? "个人导游" : teams.get(teamId);
|
return new FinanceLedgerResponse(order.getId(),
|
refund ? order.getRefundOrderNo() : "IN" + order.getOrderNo(), order.getOrderNo(),
|
paymentAmount(order), platformIncome, guideIncome, guideName(order, guide), guideMobile(order, guide),
|
teamName, refund ? order.getRefundOrderNo() : order.getPaymentOrderNo(),
|
refund ? order.getRefundMethod() : order.getPaymentMethod(), amount,
|
refund ? order.getRefundTime() : order.getPaymentTime(), settledAt);
|
}
|
|
private String guideName(TourOrder order, GuideUser guide) {
|
return StringUtils.hasText(order.getGuideName())
|
? order.getGuideName() : guide == null ? null
|
: StringUtils.hasText(guide.getRealName()) ? guide.getRealName() : guide.getUsername();
|
}
|
|
private String guideMobile(TourOrder order, GuideUser guide) {
|
return StringUtils.hasText(order.getGuideMobile())
|
? order.getGuideMobile() : guide == null ? null : guide.getMobile();
|
}
|
|
private Map<Long, GuideUser> loadGuides(List<TourOrder> orders) {
|
Set<Long> guideIds = orders.stream().map(TourOrder::getGuideId)
|
.filter(id -> id != null).collect(Collectors.toSet());
|
if (guideIds.isEmpty()) return new HashMap<Long, GuideUser>();
|
return guideUserMapper.selectBatchIds(guideIds).stream()
|
.collect(Collectors.toMap(GuideUser::getId, guide -> guide));
|
}
|
|
private Map<Long, String> loadTeams(List<TourOrder> orders, java.util.Collection<GuideUser> guides) {
|
Set<Long> teamIds = guides.stream().map(GuideUser::getTeamId)
|
.filter(id -> id != null).collect(Collectors.toSet());
|
orders.stream().map(TourOrder::getCommissionTeamId)
|
.filter(id -> id != null).forEach(teamIds::add);
|
if (teamIds.isEmpty()) return new HashMap<Long, String>();
|
return guideTeamMapper.selectBatchIds(teamIds).stream()
|
.collect(Collectors.toMap(GuideTeam::getId, GuideTeam::getTeamName));
|
}
|
|
private BigDecimal paymentAmount(TourOrder order) {
|
return safeAmount(order.getPaymentAmount() == null ? order.getAmount() : order.getPaymentAmount());
|
}
|
|
private BigDecimal safeAmount(BigDecimal amount) {
|
return amount == null ? BigDecimal.ZERO : amount;
|
}
|
}
|