fix: translate issuance API messages to Chinese

master
chenyuan 4 weeks ago
parent a0cf97a0f0
commit 2938bd4bda

@ -26,9 +26,9 @@ public class CentralBankIssuanceQueryService {
private IssuanceRequest requireRequest(UUID id) { private IssuanceRequest requireRequest(UUID id) {
if (id == null) { if (id == null) {
throw new BusinessException(ErrorCode.VALIDATION_ERROR, "request id must not be null"); throw new BusinessException(ErrorCode.VALIDATION_ERROR, "发行申请标识不能为空");
} }
return repository.findById(new IssuanceApplicationId(id)).orElseThrow(() -> return repository.findById(new IssuanceApplicationId(id)).orElseThrow(() ->
new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, "issuance request was not found: " + id)); new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, "发行申请不存在"));
} }
} }

@ -186,17 +186,17 @@ public class CommercialBankIssuanceApplicationService {
@Transactional(readOnly = true) @Transactional(readOnly = true)
public CommercialBankInventoryView getInventory() { public CommercialBankInventoryView getInventory() {
IssuanceBankInventory inventory = repository.findBuiltInInventory().orElseThrow(() -> IssuanceBankInventory inventory = repository.findBuiltInInventory().orElseThrow(() ->
new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, "built-in issuance inventory was not found")); new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, "发行库存配置不存在"));
return new CommercialBankInventoryView(inventory.getBankCode(), inventory.getCurrentBalance(), return new CommercialBankInventoryView(inventory.getBankCode(), inventory.getCurrentBalance(),
inventory.getWarningThreshold(), inventory.getSuggestedSupplementAmount()); inventory.getWarningThreshold(), inventory.getSuggestedSupplementAmount());
} }
private IssuanceRequest requireRequest(UUID id) { private IssuanceRequest requireRequest(UUID id) {
if (id == null) { if (id == null) {
throw new BusinessException(ErrorCode.VALIDATION_ERROR, "request id must not be null"); throw new BusinessException(ErrorCode.VALIDATION_ERROR, "发行申请标识不能为空");
} }
return repository.findById(new IssuanceApplicationId(id)).orElseThrow(() -> return repository.findById(new IssuanceApplicationId(id)).orElseThrow(() ->
new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, "issuance request was not found: " + id)); new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, "发行申请不存在"));
} }
private String payloadFor(IssuanceRequest request) { private String payloadFor(IssuanceRequest request) {
@ -215,7 +215,7 @@ public class CommercialBankIssuanceApplicationService {
try { try {
return objectMapper.writeValueAsString(payload); return objectMapper.writeValueAsString(payload);
} catch (JsonProcessingException exception) { } catch (JsonProcessingException exception) {
throw new IllegalStateException("unable to package issuance payload", exception); throw new IllegalStateException("无法封装发行请求报文", exception);
} }
} }
@ -236,7 +236,7 @@ public class CommercialBankIssuanceApplicationService {
private static IssuanceAuditActor auditActor(IssuanceAuditActor auditActor) { private static IssuanceAuditActor auditActor(IssuanceAuditActor auditActor) {
if (auditActor == null) { if (auditActor == null) {
throw new IllegalArgumentException("audit actor must not be null"); throw new IllegalArgumentException("当前操作人不能为空");
} }
return auditActor; return auditActor;
} }

@ -55,37 +55,37 @@ public class DenominationPlanService {
private Map<BigDecimal, Integer> quantitiesByDenomination(List<DenominationItemRequest> items) { private Map<BigDecimal, Integer> quantitiesByDenomination(List<DenominationItemRequest> items) {
if (items == null || items.size() != STANDARD_DENOMINATIONS.size()) { if (items == null || items.size() != STANDARD_DENOMINATIONS.size()) {
throw validationError("items must contain every standard denomination"); throw validationError("面额明细必须包含全部标准面额");
} }
Map<BigDecimal, Integer> quantities = new HashMap<BigDecimal, Integer>(); Map<BigDecimal, Integer> quantities = new HashMap<BigDecimal, Integer>();
for (DenominationItemRequest item : items) { for (DenominationItemRequest item : items) {
if (item == null || item.getDenomination() == null || item.getQuantity() == null) { if (item == null || item.getDenomination() == null || item.getQuantity() == null) {
throw validationError("denomination item must include denomination and quantity"); throw validationError("面额明细必须包含面额和数量");
} }
BigDecimal denomination = normalizeDenomination(item.getDenomination()); BigDecimal denomination = normalizeDenomination(item.getDenomination());
if (!STANDARD_DENOMINATIONS.contains(denomination) || item.getQuantity() < 0 if (!STANDARD_DENOMINATIONS.contains(denomination) || item.getQuantity() < 0
|| quantities.put(denomination, item.getQuantity()) != null) { || quantities.put(denomination, item.getQuantity()) != null) {
throw validationError("denomination plan contains an invalid item"); throw validationError("面额结构包含无效明细");
} }
} }
if (quantities.size() != STANDARD_DENOMINATIONS.size()) { if (quantities.size() != STANDARD_DENOMINATIONS.size()) {
throw validationError("denomination plan must not omit a standard denomination"); throw validationError("面额结构不得缺少标准面额");
} }
return quantities; return quantities;
} }
private BigDecimal normalizeAmount(BigDecimal totalAmount) { private BigDecimal normalizeAmount(BigDecimal totalAmount) {
if (totalAmount == null) { if (totalAmount == null) {
throw validationError("totalAmount must not be null"); throw validationError("申请总金额不能为空");
} }
try { try {
BigDecimal amount = totalAmount.setScale(2, RoundingMode.UNNECESSARY); BigDecimal amount = totalAmount.setScale(2, RoundingMode.UNNECESSARY);
if (amount.signum() <= 0) { if (amount.signum() <= 0) {
throw validationError("totalAmount must be positive"); throw validationError("申请总金额必须大于零");
} }
return amount; return amount;
} catch (ArithmeticException exception) { } catch (ArithmeticException exception) {
throw validationError("totalAmount must have at most two decimal places"); throw validationError("申请总金额最多保留两位小数");
} }
} }
@ -93,7 +93,7 @@ public class DenominationPlanService {
try { try {
return denomination.setScale(2, RoundingMode.UNNECESSARY); return denomination.setScale(2, RoundingMode.UNNECESSARY);
} catch (ArithmeticException exception) { } catch (ArithmeticException exception) {
throw validationError("denomination must have at most two decimal places"); throw validationError("面额最多保留两位小数");
} }
} }

@ -14,13 +14,13 @@ public final class DenominationItem {
} }
public DenominationItem(BigDecimal denomination, int quantity) { public DenominationItem(BigDecimal denomination, int quantity) {
BigDecimal normalizedDenomination = Objects.requireNonNull(denomination, "denomination must not be null") BigDecimal normalizedDenomination = Objects.requireNonNull(denomination, "面额不能为空")
.setScale(2, RoundingMode.UNNECESSARY); .setScale(2, RoundingMode.UNNECESSARY);
if (normalizedDenomination.signum() <= 0) { if (normalizedDenomination.signum() <= 0) {
throw new IllegalArgumentException("denomination must be positive"); throw new IllegalArgumentException("面额必须大于零");
} }
if (quantity <= 0) { if (quantity <= 0) {
throw new IllegalArgumentException("quantity must be positive"); throw new IllegalArgumentException("数量必须大于零");
} }
this.denomination = normalizedDenomination; this.denomination = normalizedDenomination;
this.quantity = quantity; this.quantity = quantity;

@ -31,10 +31,10 @@ public class IssuanceRequest {
private IssuanceRequest(IssuanceApplicationId id, String requestNo, String bankCode, String organizationId, private IssuanceRequest(IssuanceApplicationId id, String requestNo, String bankCode, String organizationId,
BigDecimal totalAmount, String currency, List<DenominationItem> denominations) { BigDecimal totalAmount, String currency, List<DenominationItem> denominations) {
this.id = Objects.requireNonNull(id, "id must not be null"); this.id = Objects.requireNonNull(id, "申请标识不能为空");
this.requestNo = Objects.requireNonNull(requestNo, "requestNo must not be null"); this.requestNo = Objects.requireNonNull(requestNo, "申请编号不能为空");
this.bankCode = Objects.requireNonNull(bankCode, "bankCode must not be null"); this.bankCode = Objects.requireNonNull(bankCode, "申请机构不能为空");
this.organizationId = Objects.requireNonNull(organizationId, "organizationId must not be null"); this.organizationId = Objects.requireNonNull(organizationId, "机构标识不能为空");
applyDraftValues(totalAmount, currency, denominations); applyDraftValues(totalAmount, currency, denominations);
this.status = IssuanceRequestStatus.DRAFT; this.status = IssuanceRequestStatus.DRAFT;
this.centralBankReceiveStatus = CentralBankReceiveStatus.NOT_RECEIVED; this.centralBankReceiveStatus = CentralBankReceiveStatus.NOT_RECEIVED;
@ -185,34 +185,34 @@ public class IssuanceRequest {
List<DenominationItem> copiedDenominations = copyDenominations(denominations); List<DenominationItem> copiedDenominations = copyDenominations(denominations);
BigDecimal denominationTotal = calculateDenominationTotal(copiedDenominations); BigDecimal denominationTotal = calculateDenominationTotal(copiedDenominations);
if (denominationTotal.compareTo(normalizedAmount) != 0) { if (denominationTotal.compareTo(normalizedAmount) != 0) {
throw new IllegalArgumentException("denomination total must equal totalAmount"); throw new IllegalArgumentException("各面额小计之和必须等于申请总金额");
} }
String validatedCurrency = Objects.requireNonNull(currency, "currency must not be null"); String validatedCurrency = Objects.requireNonNull(currency, "币种不能为空");
this.totalAmount = normalizedAmount; this.totalAmount = normalizedAmount;
this.currency = validatedCurrency; this.currency = validatedCurrency;
this.denominations = Collections.unmodifiableList(copiedDenominations); this.denominations = Collections.unmodifiableList(copiedDenominations);
} }
private BigDecimal normalizeAmount(BigDecimal totalAmount) { private BigDecimal normalizeAmount(BigDecimal totalAmount) {
BigDecimal amount = Objects.requireNonNull(totalAmount, "totalAmount must not be null") BigDecimal amount = Objects.requireNonNull(totalAmount, "申请总金额不能为空")
.setScale(2, RoundingMode.UNNECESSARY); .setScale(2, RoundingMode.UNNECESSARY);
if (amount.signum() <= 0) { if (amount.signum() <= 0) {
throw new IllegalArgumentException("totalAmount must be positive"); throw new IllegalArgumentException("申请总金额必须大于零");
} }
return amount; return amount;
} }
private List<DenominationItem> copyDenominations(List<DenominationItem> denominations) { private List<DenominationItem> copyDenominations(List<DenominationItem> denominations) {
List<DenominationItem> copiedDenominations = new ArrayList<DenominationItem>( List<DenominationItem> copiedDenominations = new ArrayList<DenominationItem>(
Objects.requireNonNull(denominations, "denominations must not be null")); Objects.requireNonNull(denominations, "面额明细不能为空"));
if (copiedDenominations.isEmpty()) { if (copiedDenominations.isEmpty()) {
throw new IllegalArgumentException("denominations must not be empty"); throw new IllegalArgumentException("面额明细不能为空");
} }
Set<BigDecimal> faceValues = new HashSet<BigDecimal>(); Set<BigDecimal> faceValues = new HashSet<BigDecimal>();
for (DenominationItem denomination : copiedDenominations) { for (DenominationItem denomination : copiedDenominations) {
Objects.requireNonNull(denomination, "denomination item must not be null"); Objects.requireNonNull(denomination, "面额明细不能为空");
if (!faceValues.add(denomination.getDenomination().stripTrailingZeros())) { if (!faceValues.add(denomination.getDenomination().stripTrailingZeros())) {
throw new IllegalArgumentException("denomination values must be unique"); throw new IllegalArgumentException("面额不能重复");
} }
} }
return copiedDenominations; return copiedDenominations;
@ -229,7 +229,7 @@ public class IssuanceRequest {
private void requireStatus(IssuanceRequestStatus expectedStatus) { private void requireStatus(IssuanceRequestStatus expectedStatus) {
if (status != expectedStatus) { if (status != expectedStatus) {
throw new IllegalStateException("expected status " + expectedStatus + " but was " + status); throw new IllegalStateException("当前申请状态不允许此操作");
} }
} }
} }

@ -71,8 +71,8 @@ public class CommercialBankIssuanceController {
Object claim=jwt.getClaim("userId"); Object claim=jwt.getClaim("userId");
if(claim==null) throw missingAuditClaim("userId"); if(claim==null) throw missingAuditClaim("userId");
try{return new IssuanceAuditActor(Long.parseLong(String.valueOf(claim)),username);} try{return new IssuanceAuditActor(Long.parseLong(String.valueOf(claim)),username);}
catch(NumberFormatException exception){throw new BusinessException(ErrorCode.UNAUTHORIZED,"current user id must be a number");} catch(NumberFormatException exception){throw new BusinessException(ErrorCode.UNAUTHORIZED,"当前用户 ID 必须为数字");}
} }
private BusinessException missingAuditClaim(String claim){return new BusinessException(ErrorCode.UNAUTHORIZED,"JWT missing required claim: "+claim);} private BusinessException missingAuditClaim(String claim){return new BusinessException(ErrorCode.UNAUTHORIZED,"登录凭据缺少必要信息:"+claim);}
private <T> ApiResponse<T> ok(T data){return ApiResponse.success(data,MDC.get(TraceIdFilter.MDC_KEY));} private <T> ApiResponse<T> ok(T data){return ApiResponse.success(data,MDC.get(TraceIdFilter.MDC_KEY));}
} }

@ -66,7 +66,7 @@ public class AuthController {
try { try {
return Long.parseLong(jwt.getSubject()); return Long.parseLong(jwt.getSubject());
} catch (NumberFormatException exception) { } catch (NumberFormatException exception) {
throw new BusinessException(ErrorCode.UNAUTHORIZED, "User identity is invalid"); throw new BusinessException(ErrorCode.UNAUTHORIZED, "用户身份信息无效");
} }
} }
} }

@ -101,6 +101,17 @@ class IssuanceControllerTest {
.andExpect(jsonPath("$.data.denominations.length()").value(6)); .andExpect(jsonPath("$.data.denominations.length()").value(6));
} }
@Test
void returnsChineseMessageWhenDenominationTotalDoesNotMatchTheApplicationAmount() throws Exception {
String requestBody = "{\"bankCode\":\"BKCHCNBJ00001\",\"organizationId\":\"ORG_3A4B5C6D7E8F\",\"totalAmount\":50000.00,\"currency\":\"DC\",\"denominations\":[{\"denomination\":100,\"quantity\":1}]}";
mockMvc.perform(post("/api/v1/commercial-banks/issuance/requests")
.with(jwt().jwt(jwt -> jwt.subject("487").claim("userId", 487L).claim("preferred_username", "tzs001")))
.contentType(MediaType.APPLICATION_JSON).content(requestBody))
.andExpect(jsonPath("$.code").value("VALIDATION_ERROR"))
.andExpect(jsonPath("$.message").value("各面额小计之和必须等于申请总金额"));
}
@Test @Test
void rejectsAnIssuanceOperationWhenAuditClaimsAreMissing() throws Exception { void rejectsAnIssuanceOperationWhenAuditClaimsAreMissing() throws Exception {
String requestBody = "{\"bankCode\":\"BKCHCNBJ00001\",\"organizationId\":\"ORG_3A4B5C6D7E8F\",\"totalAmount\":50000.00,\"currency\":\"DC\",\"denominations\":[{\"denomination\":100,\"quantity\":500}]}"; String requestBody = "{\"bankCode\":\"BKCHCNBJ00001\",\"organizationId\":\"ORG_3A4B5C6D7E8F\",\"totalAmount\":50000.00,\"currency\":\"DC\",\"denominations\":[{\"denomination\":100,\"quantity\":500}]}";

Loading…
Cancel
Save