This commit is contained in:
2026-08-14 09:14:52 +08:00
parent 274a3f34c5
commit db80701534
8 changed files with 186 additions and 75 deletions
@@ -4,14 +4,12 @@ import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.baiyue.finance.client.FinanceFileClient;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
@@ -26,14 +24,12 @@ import java.util.Map;
public class FinanceService {
private final JdbcTemplate jdbc;
private final ObjectMapper objectMapper;
@Autowired(required = false)
private FinanceFileClient fileClient;
@org.springframework.beans.factory.annotation.Value("${baiyue.security.internal-service-token:}")
private String fileToken;
private final FinanceVoucherValidator voucherValidator;
public FinanceService(JdbcTemplate jdbc, ObjectMapper objectMapper) {
public FinanceService(JdbcTemplate jdbc, ObjectMapper objectMapper, FinanceVoucherValidator voucherValidator) {
this.jdbc = jdbc;
this.objectMapper = objectMapper;
this.voucherValidator = voucherValidator;
}
public Map<String, Object> customerTerms(long tenantId) {
@@ -203,20 +199,8 @@ public class FinanceService {
}
private void validateVoucherFiles(long tenantId, List<String> fileIds) {
if (fileIds == null || fileIds.isEmpty()) throw bad("财务凭证不能为空");
if (fileClient == null) throw new BizException(ResultCode.SERVICE_UNAVAILABLE, "File服务校验能力不可用");
for (String raw : fileIds) {
long id; try { id = Long.parseLong(raw); } catch (Exception ex) { throw bad("凭证文件ID无效"); }
var response = fileClient.metadata(fileToken, tenantId, tenantId, id);
var metadata = response == null ? null : response.data();
if (metadata == null || metadata.fileId() == null || !tenantIdEquals(metadata.tenantId(), tenantId)
|| !"FINANCE_VOUCHER".equals(metadata.businessType()) || metadata.contentType() == null
|| !metadata.contentType().toLowerCase(java.util.Locale.ROOT).startsWith("image/")) {
throw bad("凭证文件不存在、归属不匹配或不是有效图片");
}
}
voucherValidator.validate(tenantId, fileIds, "财务凭证");
}
private static boolean tenantIdEquals(Long actual, long expected) { return actual != null && actual == expected; }
@Transactional
public Map<String, Object> createCost(long tenantId, long operatorId, CostRequest request, String idempotencyKey) {
@@ -1,6 +1,5 @@
package com.baiyue.finance.service;
import com.baiyue.finance.client.FinanceFileClient;
import com.baiyue.finance.dto.FinanceSettlementDtos.CreatePayableSettlementRequest;
import com.baiyue.finance.dto.FinanceSettlementDtos.CreateReceivableSettlementRequest;
import com.baiyue.finance.dto.FinanceSettlementDtos.CreateSettlementPaymentRequest;
@@ -14,7 +13,6 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
@@ -47,15 +45,12 @@ public class FinanceSettlementService {
private static final Set<String> PAYMENT_METHODS = Set.of("BANK_TRANSFER", "CASH", "OTHER");
private final JdbcTemplate jdbc;
private final ObjectMapper objectMapper;
private final FinanceFileClient fileClient;
private final FinanceVoucherValidator voucherValidator;
@Value("${baiyue.security.internal-service-token:}")
private String fileToken;
public FinanceSettlementService(JdbcTemplate jdbc, ObjectMapper objectMapper, FinanceFileClient fileClient) {
public FinanceSettlementService(JdbcTemplate jdbc, ObjectMapper objectMapper, FinanceVoucherValidator voucherValidator) {
this.jdbc = jdbc;
this.objectMapper = objectMapper;
this.fileClient = fileClient;
this.voucherValidator = voucherValidator;
}
@Transactional
@@ -355,26 +350,7 @@ public class FinanceSettlementService {
}
private void validateVoucherFiles(long customerId, List<String> fileIds) {
if (fileClient == null || !StringUtils.hasText(fileToken)) throw error("FIN_503", "File服务校验能力不可用");
for (String raw : fileIds) {
long fileId;
try { fileId = Long.parseLong(raw); } catch (RuntimeException invalid) { throw error("FIN_400", "付款凭证文件ID无效"); }
FinanceFileClient.Metadata metadata;
try {
var response = fileClient.metadata(fileToken, customerId, customerId, fileId);
metadata = response == null ? null : response.data();
} catch (RuntimeException unavailable) {
throw error("FIN_503", "File服务校验不可用");
}
if (metadata == null || metadata.fileId() == null || metadata.fileId() != fileId
|| metadata.tenantId() == null || metadata.tenantId() != customerId
|| !"FINANCE_VOUCHER".equals(metadata.businessType())
|| !String.valueOf(customerId).equals(metadata.businessId())
|| !StringUtils.hasText(metadata.contentType())
|| !metadata.contentType().toLowerCase(Locale.ROOT).startsWith("image/")) {
throw error("FIN_400", "付款凭证不存在、客户归属不匹配或文件类型无效");
}
}
voucherValidator.validate(customerId, fileIds, "付款凭证");
}
private Long reserve(long customerId, long operatorId, String operation, String key,
@@ -0,0 +1,56 @@
package com.baiyue.finance.service;
import com.baiyue.common.core.api.ResultCode;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.finance.client.FinanceFileClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Locale;
import java.util.HashSet;
@Component
public class FinanceVoucherValidator {
private final FinanceFileClient fileClient;
private final String fileToken;
@Autowired
public FinanceVoucherValidator(FinanceFileClient fileClient,
@Value("${baiyue.security.internal-service-token:}") String fileToken) {
this.fileClient = fileClient;
this.fileToken = fileToken;
}
public void validate(long customerId, List<String> fileIds, String message) {
if (fileIds == null || fileIds.isEmpty()) throw bad(message + "不能为空");
if (fileClient == null || !StringUtils.hasText(fileToken)) throw unavailable("File服务校验能力不可用");
if (new HashSet<>(fileIds).size() != fileIds.size()) throw bad(message + "文件ID不能重复");
for (String raw : fileIds) {
final long fileId;
try { fileId = Long.parseLong(raw); } catch (RuntimeException invalid) { throw bad(message + "文件ID无效"); }
FinanceFileClient.Metadata metadata;
try {
var response = fileClient.metadata(fileToken, customerId, customerId, fileId);
metadata = response == null ? null : response.data();
} catch (RuntimeException unavailable) {
throw unavailable("File服务校验不可用");
}
if (metadata == null || metadata.fileId() == null || metadata.fileId() != fileId
|| metadata.tenantId() == null || metadata.tenantId() != customerId
|| !String.valueOf(customerId).equals(metadata.businessId())
|| !"FINANCE_VOUCHER".equals(metadata.businessType())
|| !StringUtils.hasText(metadata.contentType())
|| !metadata.contentType().toLowerCase(Locale.ROOT).startsWith("image/")) {
throw bad(message + "不存在、归属不匹配或不是有效图片");
}
}
}
private static BizException bad(String message) { return new BizException(ResultCode.BAD_REQUEST, message); }
private static BizException unavailable(String message) {
return new BizException(ResultCode.SERVICE_UNAVAILABLE, message);
}
}