diff --git a/src/main/java/com/yau/digitalrmb/issuance/application/service/CentralBankIssuanceQueryService.java b/src/main/java/com/yau/digitalrmb/issuance/application/service/CentralBankIssuanceQueryService.java index 46113a3..909e42a 100644 --- a/src/main/java/com/yau/digitalrmb/issuance/application/service/CentralBankIssuanceQueryService.java +++ b/src/main/java/com/yau/digitalrmb/issuance/application/service/CentralBankIssuanceQueryService.java @@ -26,9 +26,9 @@ public class CentralBankIssuanceQueryService { private IssuanceRequest requireRequest(UUID id) { 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(() -> - new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, "issuance request was not found: " + id)); + new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, "发行申请不存在")); } } diff --git a/src/main/java/com/yau/digitalrmb/issuance/application/service/CommercialBankIssuanceApplicationService.java b/src/main/java/com/yau/digitalrmb/issuance/application/service/CommercialBankIssuanceApplicationService.java index 0ec567c..9586edc 100644 --- a/src/main/java/com/yau/digitalrmb/issuance/application/service/CommercialBankIssuanceApplicationService.java +++ b/src/main/java/com/yau/digitalrmb/issuance/application/service/CommercialBankIssuanceApplicationService.java @@ -186,17 +186,17 @@ public class CommercialBankIssuanceApplicationService { @Transactional(readOnly = true) public CommercialBankInventoryView getInventory() { 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(), inventory.getWarningThreshold(), inventory.getSuggestedSupplementAmount()); } private IssuanceRequest requireRequest(UUID id) { 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(() -> - new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, "issuance request was not found: " + id)); + new BusinessException(ErrorCode.RESOURCE_NOT_FOUND, "发行申请不存在")); } private String payloadFor(IssuanceRequest request) { @@ -215,7 +215,7 @@ public class CommercialBankIssuanceApplicationService { try { return objectMapper.writeValueAsString(payload); } 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) { if (auditActor == null) { - throw new IllegalArgumentException("audit actor must not be null"); + throw new IllegalArgumentException("当前操作人不能为空"); } return auditActor; } diff --git a/src/main/java/com/yau/digitalrmb/issuance/application/service/DenominationPlanService.java b/src/main/java/com/yau/digitalrmb/issuance/application/service/DenominationPlanService.java index 5584be3..44f796f 100644 --- a/src/main/java/com/yau/digitalrmb/issuance/application/service/DenominationPlanService.java +++ b/src/main/java/com/yau/digitalrmb/issuance/application/service/DenominationPlanService.java @@ -55,37 +55,37 @@ public class DenominationPlanService { private Map quantitiesByDenomination(List items) { if (items == null || items.size() != STANDARD_DENOMINATIONS.size()) { - throw validationError("items must contain every standard denomination"); + throw validationError("面额明细必须包含全部标准面额"); } Map quantities = new HashMap(); for (DenominationItemRequest item : items) { 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()); if (!STANDARD_DENOMINATIONS.contains(denomination) || item.getQuantity() < 0 || quantities.put(denomination, item.getQuantity()) != null) { - throw validationError("denomination plan contains an invalid item"); + throw validationError("面额结构包含无效明细"); } } if (quantities.size() != STANDARD_DENOMINATIONS.size()) { - throw validationError("denomination plan must not omit a standard denomination"); + throw validationError("面额结构不得缺少标准面额"); } return quantities; } private BigDecimal normalizeAmount(BigDecimal totalAmount) { if (totalAmount == null) { - throw validationError("totalAmount must not be null"); + throw validationError("申请总金额不能为空"); } try { BigDecimal amount = totalAmount.setScale(2, RoundingMode.UNNECESSARY); if (amount.signum() <= 0) { - throw validationError("totalAmount must be positive"); + throw validationError("申请总金额必须大于零"); } return amount; } catch (ArithmeticException exception) { - throw validationError("totalAmount must have at most two decimal places"); + throw validationError("申请总金额最多保留两位小数"); } } @@ -93,7 +93,7 @@ public class DenominationPlanService { try { return denomination.setScale(2, RoundingMode.UNNECESSARY); } catch (ArithmeticException exception) { - throw validationError("denomination must have at most two decimal places"); + throw validationError("面额最多保留两位小数"); } } diff --git a/src/main/java/com/yau/digitalrmb/issuance/domain/model/DenominationItem.java b/src/main/java/com/yau/digitalrmb/issuance/domain/model/DenominationItem.java index 52a922d..8f99ecb 100644 --- a/src/main/java/com/yau/digitalrmb/issuance/domain/model/DenominationItem.java +++ b/src/main/java/com/yau/digitalrmb/issuance/domain/model/DenominationItem.java @@ -14,13 +14,13 @@ public final class DenominationItem { } 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); if (normalizedDenomination.signum() <= 0) { - throw new IllegalArgumentException("denomination must be positive"); + throw new IllegalArgumentException("面额必须大于零"); } if (quantity <= 0) { - throw new IllegalArgumentException("quantity must be positive"); + throw new IllegalArgumentException("数量必须大于零"); } this.denomination = normalizedDenomination; this.quantity = quantity; diff --git a/src/main/java/com/yau/digitalrmb/issuance/domain/model/IssuanceRequest.java b/src/main/java/com/yau/digitalrmb/issuance/domain/model/IssuanceRequest.java index 4f72f5a..96ff17e 100644 --- a/src/main/java/com/yau/digitalrmb/issuance/domain/model/IssuanceRequest.java +++ b/src/main/java/com/yau/digitalrmb/issuance/domain/model/IssuanceRequest.java @@ -31,10 +31,10 @@ public class IssuanceRequest { private IssuanceRequest(IssuanceApplicationId id, String requestNo, String bankCode, String organizationId, BigDecimal totalAmount, String currency, List denominations) { - this.id = Objects.requireNonNull(id, "id must not be null"); - this.requestNo = Objects.requireNonNull(requestNo, "requestNo must not be null"); - this.bankCode = Objects.requireNonNull(bankCode, "bankCode must not be null"); - this.organizationId = Objects.requireNonNull(organizationId, "organizationId must not be null"); + this.id = Objects.requireNonNull(id, "申请标识不能为空"); + this.requestNo = Objects.requireNonNull(requestNo, "申请编号不能为空"); + this.bankCode = Objects.requireNonNull(bankCode, "申请机构不能为空"); + this.organizationId = Objects.requireNonNull(organizationId, "机构标识不能为空"); applyDraftValues(totalAmount, currency, denominations); this.status = IssuanceRequestStatus.DRAFT; this.centralBankReceiveStatus = CentralBankReceiveStatus.NOT_RECEIVED; @@ -185,34 +185,34 @@ public class IssuanceRequest { List copiedDenominations = copyDenominations(denominations); BigDecimal denominationTotal = calculateDenominationTotal(copiedDenominations); 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.currency = validatedCurrency; this.denominations = Collections.unmodifiableList(copiedDenominations); } private BigDecimal normalizeAmount(BigDecimal totalAmount) { - BigDecimal amount = Objects.requireNonNull(totalAmount, "totalAmount must not be null") + BigDecimal amount = Objects.requireNonNull(totalAmount, "申请总金额不能为空") .setScale(2, RoundingMode.UNNECESSARY); if (amount.signum() <= 0) { - throw new IllegalArgumentException("totalAmount must be positive"); + throw new IllegalArgumentException("申请总金额必须大于零"); } return amount; } private List copyDenominations(List denominations) { List copiedDenominations = new ArrayList( - Objects.requireNonNull(denominations, "denominations must not be null")); + Objects.requireNonNull(denominations, "面额明细不能为空")); if (copiedDenominations.isEmpty()) { - throw new IllegalArgumentException("denominations must not be empty"); + throw new IllegalArgumentException("面额明细不能为空"); } Set faceValues = new HashSet(); for (DenominationItem denomination : copiedDenominations) { - Objects.requireNonNull(denomination, "denomination item must not be null"); + Objects.requireNonNull(denomination, "面额明细不能为空"); if (!faceValues.add(denomination.getDenomination().stripTrailingZeros())) { - throw new IllegalArgumentException("denomination values must be unique"); + throw new IllegalArgumentException("面额不能重复"); } } return copiedDenominations; @@ -229,7 +229,7 @@ public class IssuanceRequest { private void requireStatus(IssuanceRequestStatus expectedStatus) { if (status != expectedStatus) { - throw new IllegalStateException("expected status " + expectedStatus + " but was " + status); + throw new IllegalStateException("当前申请状态不允许此操作"); } } } diff --git a/src/main/java/com/yau/digitalrmb/issuance/interfaces/rest/CommercialBankIssuanceController.java b/src/main/java/com/yau/digitalrmb/issuance/interfaces/rest/CommercialBankIssuanceController.java index c23c9f0..56e39c0 100644 --- a/src/main/java/com/yau/digitalrmb/issuance/interfaces/rest/CommercialBankIssuanceController.java +++ b/src/main/java/com/yau/digitalrmb/issuance/interfaces/rest/CommercialBankIssuanceController.java @@ -71,8 +71,8 @@ public class CommercialBankIssuanceController { Object claim=jwt.getClaim("userId"); if(claim==null) throw missingAuditClaim("userId"); 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 ApiResponse ok(T data){return ApiResponse.success(data,MDC.get(TraceIdFilter.MDC_KEY));} } diff --git a/src/main/java/com/yau/digitalrmb/security/interfaces/AuthController.java b/src/main/java/com/yau/digitalrmb/security/interfaces/AuthController.java index 183cc62..7115ae5 100644 --- a/src/main/java/com/yau/digitalrmb/security/interfaces/AuthController.java +++ b/src/main/java/com/yau/digitalrmb/security/interfaces/AuthController.java @@ -66,7 +66,7 @@ public class AuthController { try { return Long.parseLong(jwt.getSubject()); } catch (NumberFormatException exception) { - throw new BusinessException(ErrorCode.UNAUTHORIZED, "User identity is invalid"); + throw new BusinessException(ErrorCode.UNAUTHORIZED, "用户身份信息无效"); } } } diff --git a/src/test/java/com/yau/digitalrmb/issuance/interfaces/rest/IssuanceControllerTest.java b/src/test/java/com/yau/digitalrmb/issuance/interfaces/rest/IssuanceControllerTest.java index 9823ca4..89d9ec1 100644 --- a/src/test/java/com/yau/digitalrmb/issuance/interfaces/rest/IssuanceControllerTest.java +++ b/src/test/java/com/yau/digitalrmb/issuance/interfaces/rest/IssuanceControllerTest.java @@ -101,6 +101,17 @@ class IssuanceControllerTest { .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 void rejectsAnIssuanceOperationWhenAuditClaimsAreMissing() throws Exception { String requestBody = "{\"bankCode\":\"BKCHCNBJ00001\",\"organizationId\":\"ORG_3A4B5C6D7E8F\",\"totalAmount\":50000.00,\"currency\":\"DC\",\"denominations\":[{\"denomination\":100,\"quantity\":500}]}";