package com.tourguide.modules.finance.service;
|
|
import com.tourguide.common.exception.BusinessException;
|
import com.tourguide.modules.finance.dto.WithdrawalResponse;
|
import com.tourguide.modules.finance.entity.GuideWithdrawal;
|
import com.tourguide.modules.finance.mapper.GuideWithdrawalMapper;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.util.StringUtils;
|
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.concurrent.ThreadLocalRandom;
|
|
/** 管理提现资金流程的本地事务状态,不在数据库事务中调用外部资金接口。 */
|
@Service
|
public class GuideWithdrawalStateService {
|
static final String PENDING = "PENDING";
|
static final String TRANSFERRING = "TRANSFERRING";
|
static final String TRANSFER_FAILED = "TRANSFER_FAILED";
|
static final String TRANSFERRED = "TRANSFERRED";
|
static final String PROCESSING = "PROCESSING";
|
static final String APPROVED = "APPROVED";
|
static final String REJECTED = "REJECTED";
|
|
private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd");
|
private static final DateTimeFormatter SEQ_TIME_FORMAT = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
|
|
private final GuideWithdrawalMapper withdrawalMapper;
|
private final GuideWithdrawableBalanceService balanceService;
|
|
public GuideWithdrawalStateService(GuideWithdrawalMapper withdrawalMapper,
|
GuideWithdrawableBalanceService balanceService) {
|
this.withdrawalMapper = withdrawalMapper;
|
this.balanceService = balanceService;
|
}
|
|
@Transactional
|
public WithdrawalResponse reject(Long id, String remark, String processor) {
|
GuideWithdrawal withdrawal = requireForUpdate(id);
|
if (!canProcess(withdrawal)) throw new BusinessException(409, "提现申请已处理");
|
if (!StringUtils.hasText(remark)) throw new BusinessException(400, "驳回提现时请填写处理备注");
|
if (withdrawal.getTeamId() == null) {
|
balanceService.credit(withdrawal.getGuideId(), withdrawal.getAmount());
|
} else {
|
balanceService.creditTeam(withdrawal.getTeamId(), withdrawal.getAmount());
|
}
|
withdrawal.setStatus(REJECTED);
|
withdrawal.setAuditRemark(remark.trim());
|
withdrawal.setProcessor(processor);
|
withdrawal.setProcessedAt(LocalDateTime.now());
|
update(withdrawal);
|
return new WithdrawalResponse(withdrawal);
|
}
|
|
@Transactional
|
public GuideWithdrawal prepareTransfer(Long id, String remark, String processor) {
|
GuideWithdrawal withdrawal = requireForUpdate(id);
|
if (!canProcess(withdrawal)) throw new BusinessException(409, "提现申请已处理");
|
LocalDateTime now = LocalDateTime.now();
|
withdrawal.setStatus(TRANSFERRING);
|
withdrawal.setTransferReqSeqId(sequence("TF"));
|
withdrawal.setTransferReqDate(now.format(DATE_FORMAT));
|
withdrawal.setTransferHfSeqId(null);
|
withdrawal.setPayoutMessage("等待主账户划账");
|
withdrawal.setAuditRemark(StringUtils.hasText(remark) ? remark.trim() : null);
|
withdrawal.setProcessor(processor);
|
withdrawal.setProcessedAt(now);
|
update(withdrawal);
|
return withdrawal;
|
}
|
|
@Transactional
|
public GuideWithdrawal markTransferred(Long id, String hfSeqId) {
|
GuideWithdrawal withdrawal = requireForUpdate(id);
|
if (!TRANSFERRING.equals(withdrawal.getStatus())) return withdrawal;
|
withdrawal.setStatus(TRANSFERRED);
|
withdrawal.setTransferHfSeqId(hfSeqId);
|
withdrawal.setTransferredAt(LocalDateTime.now());
|
withdrawal.setPayoutMessage("主账户划账成功,等待发起取现");
|
update(withdrawal);
|
return withdrawal;
|
}
|
|
@Transactional
|
public GuideWithdrawal markTransferPending(Long id, String hfSeqId, String message) {
|
GuideWithdrawal withdrawal = requireForUpdate(id);
|
if (!TRANSFERRING.equals(withdrawal.getStatus())) return withdrawal;
|
if (StringUtils.hasText(hfSeqId)) withdrawal.setTransferHfSeqId(hfSeqId);
|
withdrawal.setPayoutMessage(limit(message));
|
update(withdrawal);
|
return withdrawal;
|
}
|
|
@Transactional
|
public GuideWithdrawal markTransferFailed(Long id, String message) {
|
GuideWithdrawal withdrawal = requireForUpdate(id);
|
if (!TRANSFERRING.equals(withdrawal.getStatus())) return withdrawal;
|
withdrawal.setStatus(TRANSFER_FAILED);
|
withdrawal.setPayoutMessage(limit(message));
|
update(withdrawal);
|
return withdrawal;
|
}
|
|
@Transactional
|
public GuideWithdrawal prepareCash(Long id) {
|
GuideWithdrawal withdrawal = requireForUpdate(id);
|
if (!TRANSFERRED.equals(withdrawal.getStatus())) return null;
|
LocalDateTime now = LocalDateTime.now();
|
withdrawal.setStatus(PROCESSING);
|
withdrawal.setCashReqSeqId(sequence("CW"));
|
withdrawal.setCashReqDate(now.format(DATE_FORMAT));
|
withdrawal.setCashHfSeqId(null);
|
withdrawal.setPayoutMessage("已发起导游账户取现");
|
update(withdrawal);
|
return withdrawal;
|
}
|
|
@Transactional
|
public GuideWithdrawal resumeCashQuery(Long id) {
|
GuideWithdrawal withdrawal = requireForUpdate(id);
|
if (!TRANSFERRED.equals(withdrawal.getStatus())
|
|| !StringUtils.hasText(withdrawal.getCashReqSeqId())
|
|| !StringUtils.hasText(withdrawal.getCashReqDate())) {
|
return null;
|
}
|
withdrawal.setStatus(PROCESSING);
|
withdrawal.setPayoutMessage("检测到原取现流水,等待查询最终状态");
|
update(withdrawal);
|
return withdrawal;
|
}
|
|
@Transactional
|
public GuideWithdrawal markCashPending(Long id, String hfSeqId, String message) {
|
GuideWithdrawal withdrawal = requireForUpdate(id);
|
if (!PROCESSING.equals(withdrawal.getStatus())) return withdrawal;
|
if (StringUtils.hasText(hfSeqId)) withdrawal.setCashHfSeqId(hfSeqId);
|
withdrawal.setPayoutMessage(limit(message));
|
update(withdrawal);
|
return withdrawal;
|
}
|
|
@Transactional
|
public GuideWithdrawal markCashRetry(Long id, String message) {
|
GuideWithdrawal withdrawal = requireForUpdate(id);
|
if (!PROCESSING.equals(withdrawal.getStatus())) return withdrawal;
|
LocalDateTime now = LocalDateTime.now();
|
String retryMessage = limit(message);
|
if (withdrawalMapper.resetCashForRetry(id, retryMessage, now, now) != 1) {
|
throw new BusinessException(500, "提现状态更新失败");
|
}
|
withdrawal.setStatus(TRANSFERRED);
|
withdrawal.setCashReqSeqId(null);
|
withdrawal.setCashReqDate(null);
|
withdrawal.setCashHfSeqId(null);
|
withdrawal.setPayoutMessage(retryMessage);
|
withdrawal.setProcessedAt(now);
|
withdrawal.setUpdatedAt(now);
|
return withdrawal;
|
}
|
|
@Transactional
|
public GuideWithdrawal markApproved(Long id, String hfSeqId) {
|
GuideWithdrawal withdrawal = requireForUpdate(id);
|
if (!PROCESSING.equals(withdrawal.getStatus())) return withdrawal;
|
withdrawal.setStatus(APPROVED);
|
if (StringUtils.hasText(hfSeqId)) withdrawal.setCashHfSeqId(hfSeqId);
|
withdrawal.setPayoutMessage("取现到账成功");
|
withdrawal.setProcessedAt(LocalDateTime.now());
|
update(withdrawal);
|
return withdrawal;
|
}
|
|
public GuideWithdrawal find(Long id) {
|
GuideWithdrawal withdrawal = withdrawalMapper.selectById(id);
|
if (withdrawal == null) throw new BusinessException(404, "提现申请不存在");
|
return withdrawal;
|
}
|
|
private GuideWithdrawal requireForUpdate(Long id) {
|
GuideWithdrawal withdrawal = withdrawalMapper.selectForUpdate(id);
|
if (withdrawal == null) throw new BusinessException(404, "提现申请不存在");
|
return withdrawal;
|
}
|
|
private boolean canProcess(GuideWithdrawal withdrawal) {
|
return PENDING.equals(withdrawal.getStatus()) || TRANSFER_FAILED.equals(withdrawal.getStatus());
|
}
|
|
private void update(GuideWithdrawal withdrawal) {
|
withdrawal.setUpdatedAt(LocalDateTime.now());
|
if (withdrawalMapper.updateById(withdrawal) != 1) {
|
throw new BusinessException(500, "提现状态更新失败");
|
}
|
}
|
|
private String sequence(String prefix) {
|
return prefix + LocalDateTime.now().format(SEQ_TIME_FORMAT)
|
+ String.format("%04d", ThreadLocalRandom.current().nextInt(10000));
|
}
|
|
private String limit(String message) {
|
if (!StringUtils.hasText(message)) return "斗拱资金处理结果待确认";
|
String value = message.trim();
|
return value.length() <= 255 ? value : value.substring(0, 255);
|
}
|
}
|