package com.tourguide.modules.finance.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.tourguide.common.exception.BusinessException; import com.tourguide.common.huifu.biz.DouGongBasicDataBiz; import com.tourguide.modules.finance.dto.GuideElectronicLedgerRequest; import com.tourguide.modules.finance.dto.GuideElectronicLedgerResponse; import com.tourguide.modules.finance.dto.GuideElectronicLedgerStatusResponse; import com.tourguide.modules.finance.entity.GuideWithdrawalAccount; import com.tourguide.modules.finance.mapper.GuideWithdrawalAccountMapper; import com.tourguide.modules.user.entity.GuideUser; import com.tourguide.modules.user.service.GuideUserService; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.Map; import java.util.UUID; @Service public class GuideElectronicLedgerService { private static final int GUIDE_USER_TYPE = 2; private static final int HUIFU_TYPE = 1; private static final int ACTIVE_STATUS = 1; private static final int PENDING_AUDIT = 1; private static final String SUCCESS_CODE = "00000000"; private final GuideWithdrawalAccountMapper accountMapper; private final GuideUserService guideUserService; private final DouGongBasicDataBiz douGongBasicDataBiz; private final GuideWithdrawableBalanceService balanceService; public GuideElectronicLedgerService(GuideWithdrawalAccountMapper accountMapper, GuideUserService guideUserService, DouGongBasicDataBiz douGongBasicDataBiz, GuideWithdrawableBalanceService balanceService) { this.accountMapper = accountMapper; this.guideUserService = guideUserService; this.douGongBasicDataBiz = douGongBasicDataBiz; this.balanceService = balanceService; } public GuideElectronicLedgerResponse create(Long guideId, GuideElectronicLedgerRequest request) { GuideUser guide = guideUserService.findEnabledById(guideId); GuideWithdrawalAccount existing = findCurrent(guideId); if (existing != null && StringUtils.hasText(existing.getHuifuId())) { throw new BusinessException(409, "当前导游已完成斗拱进件,请使用修改接口"); } GuideWithdrawalAccount account = existing == null ? new GuideWithdrawalAccount() : existing; applyRequest(account, request); account.setUserId(toTableUserId(guideId)); account.setUserType(GUIDE_USER_TYPE); account.setType(HUIFU_TYPE); account.setStatus(ACTIVE_STATUS); account.setIsAudit(PENDING_AUDIT); account.setApplicationStatus("processing"); account.setFailureMsgsReason(null); account.setOutRequestNo(randomRequestNo()); account.setCreateTime(account.getCreateTime() == null ? LocalDateTime.now() : account.getCreateTime()); if (!StringUtils.hasText(account.getPhone())) account.setPhone(guide.getMobile()); save(account); try { Map basicResult = douGongBasicDataBiz.userBasicDataIndv(account); requireSuccess(basicResult, "斗拱个人基本信息进件失败"); account.setHuifuId(stringValue(basicResult.get("huifu_id"))); account.setApplicationStatus("basic_succeeded"); save(account); Map businessResult = douGongBasicDataBiz.userBusinessOpen(account); requireSuccess(businessResult, "斗拱业务进件失败"); account.setTokenNo(stringValue(businessResult.get("token_no"))); account.setApplicationStatus("submitted"); account.setFailureMsgsReason(null); save(account); return GuideElectronicLedgerResponse.from(account); } catch (BusinessException exception) { markFailed(account, exception.getMessage()); throw exception; } catch (Exception exception) { markFailed(account, exception.getMessage()); throw new BusinessException(502, "斗拱进件服务暂不可用,请稍后重试"); } } public GuideElectronicLedgerResponse update(Long guideId, GuideElectronicLedgerRequest request) { GuideWithdrawalAccount account = requireCurrent(guideId); if (!StringUtils.hasText(account.getHuifuId())) { throw new BusinessException(409, "斗拱基础进件尚未完成,请重新提交进件"); } applyRequest(account, request); account.setApplicationStatus("processing"); account.setFailureMsgsReason(null); save(account); try { requireSuccess(douGongBasicDataBiz.userBasicDataIndvModify(account), "斗拱个人信息修改失败"); Map businessResult = douGongBasicDataBiz.userBusinessOpenModify(account); requireSuccess(businessResult, "斗拱业务信息修改失败"); String tokenNo = stringValue(businessResult.get("token_no")); if (StringUtils.hasText(tokenNo)) account.setTokenNo(tokenNo); account.setApplicationStatus("submitted"); account.setFailureMsgsReason(null); save(account); return GuideElectronicLedgerResponse.from(account); } catch (BusinessException exception) { markFailed(account, exception.getMessage()); throw exception; } catch (Exception exception) { markFailed(account, exception.getMessage()); throw new BusinessException(502, "斗拱进件修改服务暂不可用,请稍后重试"); } } public GuideElectronicLedgerResponse detail(Long guideId) { return GuideElectronicLedgerResponse.from(requireCurrent(guideId)); } public GuideElectronicLedgerStatusResponse status(Long guideId) { guideUserService.findEnabledById(guideId); return new GuideElectronicLedgerStatusResponse(findCurrent(guideId)); } public BigDecimal balance(Long guideId) { GuideWithdrawalAccount account = requireCurrent(guideId); if (!StringUtils.hasText(account.getHuifuId())) { throw new BusinessException(409, "请先完成斗拱进件"); } return balanceService.getAvailableAmount(guideId); } private GuideWithdrawalAccount findCurrent(Long guideId) { return accountMapper.selectOne(new LambdaQueryWrapper() .eq(GuideWithdrawalAccount::getUserId, toTableUserId(guideId)) .eq(GuideWithdrawalAccount::getUserType, GUIDE_USER_TYPE) .eq(GuideWithdrawalAccount::getType, HUIFU_TYPE) .eq(GuideWithdrawalAccount::getStatus, ACTIVE_STATUS) .orderByDesc(GuideWithdrawalAccount::getId) .last("LIMIT 1")); } private GuideWithdrawalAccount requireCurrent(Long guideId) { guideUserService.findEnabledById(guideId); GuideWithdrawalAccount account = findCurrent(guideId); if (account == null) throw new BusinessException(404, "当前导游尚未提交斗拱进件"); return account; } private void applyRequest(GuideWithdrawalAccount account, GuideElectronicLedgerRequest request) { account.setCertificateName(trim(request.getCertificateName())); account.setCertificateType(request.getCertificateType() == null ? 0 : request.getCertificateType()); account.setCertificateCode(trim(request.getCertificateCode())); account.setCertificateExpireTime(trim(request.getCertificateExpireTime())); account.setLicenceIssuingAuthority(trim(request.getLicenceIssuingAuthority())); account.setBankName(normalizeBankName(request.getBankName())); account.setBankCardCode(trim(request.getBankCardCode())); account.setBankCode(trim(request.getBankCode())); account.setBankBranchCode(trim(request.getBankBranchCode())); account.setOpenAccountName(trim(request.getOpenAccountName())); account.setTransactionAuthorizationCode(trim(request.getTransactionAuthorizationCode())); account.setPhone(trim(request.getPhone())); account.setIdCardFrontPicture(trim(request.getIdCardFrontPicture())); account.setIdCardReversePicture(trim(request.getIdCardReversePicture())); account.setBankCardProvinceCode(trim(request.getBankCardProvinceCode())); account.setBankCardCityCode(trim(request.getBankCardCityCode())); account.setImgUrl(trim(request.getImgUrl())); account.setImgUrlOne(trim(request.getImgUrlOne())); } private void requireSuccess(Map result, String fallbackMessage) { if (result != null && SUCCESS_CODE.equals(stringValue(result.get("resp_code")))) return; String message = result == null ? null : stringValue(result.get("resp_desc")); throw new BusinessException(502, StringUtils.hasText(message) ? message : fallbackMessage); } private void markFailed(GuideWithdrawalAccount account, String message) { account.setApplicationStatus("failed"); account.setFailureMsgsReason(StringUtils.hasText(message) ? message : "斗拱进件失败"); save(account); } private void save(GuideWithdrawalAccount account) { if (account.getId() == null) accountMapper.insert(account); else accountMapper.updateById(account); } private int toTableUserId(Long guideId) { try { return Math.toIntExact(guideId); } catch (ArithmeticException exception) { throw new BusinessException(400, "导游ID超出斗拱进件表支持范围"); } } private String randomRequestNo() { return UUID.randomUUID().toString().replace("-", ""); } private String normalizeBankName(String value) { String bankName = trim(value); return bankName == null ? null : bankName.replace("& #40;", "(").replace("& #41;", ")"); } private String trim(String value) { return StringUtils.hasText(value) ? value.trim() : null; } private String stringValue(Object value) { return value == null ? null : String.valueOf(value); } }