fix:异常信息返回中文化

This commit is contained in:
waner 2026-05-14 09:56:57 +08:00
parent e9240a38d6
commit 3040ed443b
34 changed files with 208 additions and 155 deletions

View File

@ -62,7 +62,7 @@ public class GlobalExceptionHandler {
@ExceptionHandler(MultipartException.class)
public ResponseEntity<ApiResponse<Void>> handleMultipartException(MultipartException ex, HttpServletRequest request) {
return response(HttpStatus.BAD_REQUEST, ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), "failed to parse multipart request")
return response(HttpStatus.BAD_REQUEST, ApiResponse.fail(ErrorCode.VALIDATE_FAILED.getCode(), "解析 multipart 上传请求失败")
.withPath(request.getRequestURI()));
}
@ -81,7 +81,7 @@ public class GlobalExceptionHandler {
}
log.warn("Replay protection 执行失败", ex);
return response(HttpStatus.SERVICE_UNAVAILABLE,
ApiResponse.fail(ErrorCode.SERVICE_UNAVAILABLE.getCode(), "replay protection unavailable")
ApiResponse.fail(ErrorCode.SERVICE_UNAVAILABLE.getCode(), "防重放校验服务不可用")
.withPath(request.getRequestURI()));
}
@ -91,7 +91,7 @@ public class GlobalExceptionHandler {
return ResponseEntity.noContent().build();
}
return response(HttpStatus.NOT_FOUND,
ApiResponse.fail(ErrorCode.NOT_FOUND.getCode(), "resource not found")
ApiResponse.fail(ErrorCode.NOT_FOUND.getCode(), "资源不存在")
.withPath(request.getRequestURI()));
}

View File

@ -14,13 +14,13 @@ public class PcieErrorMapper {
lowByteErrorMap.put(0x1F, "UKey错误");
lowByteErrorMap.put(0x20, "生成密钥错误");
lowByteErrorMap.put(0x21, "状态错误");
lowByteErrorMap.put(0x22, "retry exceeded");
lowByteErrorMap.put(0x23, "device busy");
lowByteErrorMap.put(0x24, "error status");
lowByteErrorMap.put(0x25, "init status");
lowByteErrorMap.put(0x26, "already logined");
lowByteErrorMap.put(0x27, "timeout");
lowByteErrorMap.put(0x1D, "invalid argument");
lowByteErrorMap.put(0x22, "重试次数超限");
lowByteErrorMap.put(0x23, "设备忙");
lowByteErrorMap.put(0x24, "状态错误");
lowByteErrorMap.put(0x25, "初始化状态错误");
lowByteErrorMap.put(0x26, "已登录");
lowByteErrorMap.put(0x27, "操作超时");
lowByteErrorMap.put(0x1D, "参数无效");
}
public String toMessage(int retCode) {

View File

@ -2115,7 +2115,7 @@ public class JnaPcieCryptoService implements PcieCryptoService {
private static List<RecoverUserKeyRequest> requireUserKeyRequests(List<RecoverUserKeyRequest> requests) {
if (requests == null || requests.isEmpty()) {
throw new IllegalArgumentException("userKeyRequests must not be empty");
throw new IllegalArgumentException("userKeyRequests不能为空");
}
for (RecoverUserKeyRequest request : requests) {
RecoverUserKeyRequest req = requireRequest("userKeyRequest", request);
@ -2447,21 +2447,21 @@ public class JnaPcieCryptoService implements PcieCryptoService {
private static int requirePositive(String name, int value) {
if (value <= 0) {
throw new IllegalArgumentException(name + " must be > 0");
throw new IllegalArgumentException(name + "必须大于0");
}
return value;
}
private static int requireNonNegative(String name, int value) {
if (value < 0) {
throw new IllegalArgumentException(name + " must be >= 0");
throw new IllegalArgumentException(name + "必须大于等于0");
}
return value;
}
private static int requireByteRange(String name, int value) {
if (value < 0 || value > 255) {
throw new IllegalArgumentException(name + " must be in [0,255]");
throw new IllegalArgumentException(name + "必须在[0,255]范围内");
}
return value;
}
@ -2469,7 +2469,7 @@ public class JnaPcieCryptoService implements PcieCryptoService {
private int requireValidAlgId(String name, int algId) {
int value = requirePositive(name, algId);
if (strictAlgIdValidation && !Gm0018AlgorithmIds.isSupported(value)) {
throw new IllegalArgumentException(name + " is not a supported GM/T 0018 algorithm id: 0x" + Integer.toHexString(value));
throw new IllegalArgumentException(name + "不是支持的GM/T 0018算法标识0x" + Integer.toHexString(value));
}
return value;
}
@ -2482,14 +2482,14 @@ public class JnaPcieCryptoService implements PcieCryptoService {
private static byte[] requireNonEmptyBytes(String name, byte[] value) {
if (value == null || value.length == 0) {
throw new IllegalArgumentException(name + " must not be empty");
throw new IllegalArgumentException(name + "不能为空");
}
return value;
}
private static byte[] requireBytes(String name, byte[] value) {
if (value == null) {
throw new IllegalArgumentException(name + " must not be null");
throw new IllegalArgumentException(name + "不能为null");
}
return value;
}
@ -2557,10 +2557,10 @@ public class JnaPcieCryptoService implements PcieCryptoService {
int size = structure.size();
if (allowPartial) {
if (source.length > size) {
throw new IllegalArgumentException(name + " length must be <= " + size + ", actual: " + source.length);
throw new IllegalArgumentException(name + "长度必须小于等于" + size + ",实际为:" + source.length);
}
} else if (source.length != size) {
throw new IllegalArgumentException(name + " length must be exactly " + size + ", actual: " + source.length);
throw new IllegalArgumentException(name + "长度必须等于" + size + ",实际为:" + source.length);
}
Pointer pointer = structure.getPointer();
pointer.clear(size);
@ -2571,7 +2571,7 @@ public class JnaPcieCryptoService implements PcieCryptoService {
private static <T> T requireRequest(String name, T request) {
if (request == null) {
throw new IllegalArgumentException(name + " must not be null");
throw new IllegalArgumentException(name + "不能为null");
}
return request;
}

View File

@ -915,7 +915,7 @@ public class MockPcieCryptoService implements PcieCryptoService {
recoverIkComponent(2, req.getAuthIkComponent());
recoverIkComponent(1, req.getDeviceIkComponent());
if (req.getUserKeyRequests() == null || req.getUserKeyRequests().isEmpty()) {
throw new IllegalArgumentException("userKeyRequests must not be empty");
throw new IllegalArgumentException("userKeyRequests不能为空");
}
req.getUserKeyRequests().forEach(this::recoverUserKey);
if (!checkLmk()) {
@ -1099,49 +1099,49 @@ public class MockPcieCryptoService implements PcieCryptoService {
private static int requirePositive(String name, int value) {
if (value <= 0) {
throw new IllegalArgumentException(name + " must be > 0");
throw new IllegalArgumentException(name + "必须大于0");
}
return value;
}
private static int requireNonNegative(String name, int value) {
if (value < 0) {
throw new IllegalArgumentException(name + " must be >= 0");
throw new IllegalArgumentException(name + "必须大于等于0");
}
return value;
}
private static int requireByteRange(String name, int value) {
if (value < 0 || value > 255) {
throw new IllegalArgumentException(name + " must be in [0,255]");
throw new IllegalArgumentException(name + "必须在[0,255]范围内");
}
return value;
}
private static byte[] requireNonEmptyBytes(String name, byte[] value) {
if (value == null || value.length == 0) {
throw new IllegalArgumentException(name + " must not be empty");
throw new IllegalArgumentException(name + "不能为空");
}
return value;
}
private static byte[] requireBytes(String name, byte[] value) {
if (value == null) {
throw new IllegalArgumentException(name + " must not be null");
throw new IllegalArgumentException(name + "不能为null");
}
return value;
}
private static String requireNonBlank(String name, String value) {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException(name + " must not be blank");
throw new IllegalArgumentException(name + "不能为空");
}
return value;
}
private static <T> T requireRequest(String name, T request) {
if (request == null) {
throw new IllegalArgumentException(name + " must not be null");
throw new IllegalArgumentException(name + "不能为null");
}
return request;
}

View File

@ -164,12 +164,12 @@ public class AuthAdminServiceImpl implements AuthAdminService {
loadRole(targetRoleCode);
RoleCode targetRole = resolveRoleCode(targetRoleCode);
if (uid == null || uid < 1 || uid > targetRole.getRequiredUkeyCount()) {
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "uid exceeds role ukey requirement");
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "uid超出角色UKey席位要求");
}
AuthFullAccountEntity account = authFullAccountRepository.findByRoleCodeAndUid(targetRoleCode, uid)
.orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "目标角色完整账号不存在"));
if (!passwordHasher.matches(oldPassword, account.getPasswordSalt(), account.getPasswordHash())) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "old password is incorrect");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "旧密码错误");
}
applyAdminPasswordChange(account, newPassword);
authFullAccountRepository.update(account);
@ -476,7 +476,7 @@ public class AuthAdminServiceImpl implements AuthAdminService {
private void validateRoleUid(RoleCode targetRole, Integer uid) {
if (uid == null || uid < 1 || uid > targetRole.getRequiredUkeyCount()) {
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "uid exceeds role ukey requirement");
throw new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "uid超出角色UKey席位要求");
}
}
@ -484,7 +484,7 @@ public class AuthAdminServiceImpl implements AuthAdminService {
return List.of(RoleCode.values()).stream()
.filter(item -> item.getCode().equals(roleCode))
.findFirst()
.orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "invalid roleCode"));
.orElseThrow(() -> new BizException(ErrorCode.VALIDATE_FAILED.getCode(), "roleCode无效"));
}
private LocalDateTime now() {

View File

@ -154,7 +154,7 @@ public class AuthServiceImpl implements AuthService {
RoleUkeyBindingEntity binding = bindingsByUid.getOrDefault(proof.getUid(), List.of()).stream()
.filter(item -> item.getUkeyPubkey().equals(proof.getPubKey()))
.findFirst()
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey auth info does not match bound role"));
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "UKey认证信息与绑定角色不匹配"));
compatUkeyVerifier.verifyIssuedBinding(buildIssuePayload(request.getRoleCode(), proof, authKeyPair), proof.getIssueSignature());
compatUkeyVerifier.verifyLoginSignature(proof.getPubKey(), proof.getLoginPayload(), proof.getLoginSignature());
matchedSerials.add(binding.getUkeySerial());
@ -208,7 +208,7 @@ public class AuthServiceImpl implements AuthService {
PasswordComplexityValidator.validate(newPassword);
AuthSessionEntity session = requireActiveSession(sessionToken);
if (uid == null || !containsAuthenticatedUid(session, uid)) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "account is not authenticated in current session");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "当前会话未完成账号认证");
}
AuthFullAccountEntity fullAccount = authFullAccountRepository.findByRoleCodeAndUid(session.getRoleCode(), uid)
.orElseThrow(() -> new BizException(ErrorCode.UNAUTHORIZED.getCode(), "账号不存在"));
@ -237,7 +237,7 @@ public class AuthServiceImpl implements AuthService {
private void validateRoleStatus(RoleAccountEntity roleAccount) {
if (!RoleAccountStatus.ACTIVE.name().equals(roleAccount.getStatus())) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "role is not enabled");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "角色未启用");
}
}
@ -249,7 +249,7 @@ public class AuthServiceImpl implements AuthService {
if (accounts == null || accounts.size() != expectedCount) {
throw new BizException(
ErrorCode.UNAUTHORIZED.getCode(),
roleAccount.getRoleCode() + " requires exactly " + expectedCount + " account passwords"
roleAccount.getRoleCode() + "必须提交" + expectedCount + "个账号密码"
);
}
@ -258,7 +258,7 @@ public class AuthServiceImpl implements AuthService {
for (PasswordLoginAccountRequest account : accounts) {
Integer uid = account.getUid();
if (uid == null || !uniqueUids.add(uid)) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "duplicate account uid submitted");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "提交了重复的账号uid");
}
AuthFullAccountEntity roleAccountSeat = authFullAccountRepository
.findByRoleCodeAndUid(roleAccount.getRoleCode(), uid)
@ -274,11 +274,11 @@ public class AuthServiceImpl implements AuthService {
private void validateFullAccountStatus(AuthFullAccountEntity fullAccount) {
if (RoleAccountStatus.UNENABLED.name().equals(fullAccount.getStatus())) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "full account is not enabled");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "完整账号未启用");
}
if (RoleAccountStatus.LOCKED.name().equals(fullAccount.getStatus())
&& (fullAccount.getLockedUntil() == null || fullAccount.getLockedUntil().isAfter(now()))) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "full account is locked");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "完整账号已锁定");
}
}
@ -290,10 +290,10 @@ public class AuthServiceImpl implements AuthService {
fullAccount.setStatus(RoleAccountStatus.LOCKED.name());
fullAccount.setLockedUntil(now().plusMinutes(IDLE_TIMEOUT_MINUTES));
authFullAccountRepository.update(fullAccount);
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "full account is locked");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "完整账号已锁定");
}
authFullAccountRepository.update(fullAccount);
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "password is incorrect");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "密码错误");
}
private void resetValidatedAccounts(List<AuthFullAccountEntity> validatedAccounts) {
@ -320,7 +320,7 @@ public class AuthServiceImpl implements AuthService {
if (fullAccounts == null || fullAccounts.size() != expectedCount) {
throw new BizException(
ErrorCode.UNAUTHORIZED.getCode(),
roleAccount.getRoleCode() + " requires exactly " + expectedCount + " full-account passwords"
roleAccount.getRoleCode() + "必须提交" + expectedCount + "个完整账号密码"
);
}
@ -329,7 +329,7 @@ public class AuthServiceImpl implements AuthService {
for (FullLoginAccountRequest account : fullAccounts) {
Integer uid = account.getUid();
if (uid == null || !uniqueUids.add(uid)) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "duplicate full-account uid submitted");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "提交了重复的完整账号uid");
}
AuthFullAccountEntity fullAccount = authFullAccountRepository
.findByRoleCodeAndUid(roleAccount.getRoleCode(), uid)
@ -347,17 +347,17 @@ public class AuthServiceImpl implements AuthService {
List<AuthFullAccountEntity> fixedAccounts = authFullAccountRepository.findByRoleCode(roleCode);
int requiredCount = authPolicyService.requiredUkeyCount(RoleCode.valueOf(roleCode));
if (fixedAccounts.size() < requiredCount) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "role fixed account mapping is incomplete");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "角色固定账号映射不完整");
}
List<FullLoginAccountRequest> requests = new ArrayList<>();
for (UkeyLoginProof proof : proofs.stream().sorted(java.util.Comparator.comparing(UkeyLoginProof::getUid)).toList()) {
Integer uid = proof.getUid();
if (uid == null || uid < 1 || uid > fixedAccounts.size()) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "uid does not match fixed role account");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "uid与固定角色账号不匹配");
}
if (proof.getPassword() == null || proof.getPassword().isBlank()) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "account password不能为空");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "账号密码不能为空");
}
FullLoginAccountRequest accountRequest = new FullLoginAccountRequest();
accountRequest.setUid(uid);
@ -370,7 +370,7 @@ public class AuthServiceImpl implements AuthService {
private void ensureMasterKeyReady() {
MasterKeyStatus.StatusDetail status = lmkService.getMasterKeyStatus();
if (status == null || status.getCode() == MasterKeyStatus.ABNORMAL.getCode()) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "master key is not ready");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "主密钥未就绪");
}
}
@ -386,7 +386,7 @@ public class AuthServiceImpl implements AuthService {
Set<String> uniqueSerials = new LinkedHashSet<>(ukeySerials);
int requiredUkeyCount = authPolicyService.requiredUkeyCount(RoleCode.valueOf(roleAccount.getRoleCode()));
if (uniqueSerials.size() != requiredUkeyCount) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "UKey数量不满足角色要求");
}
List<RoleUkeyBindingEntity> bindings = roleUkeyBindingRepository.findActiveByRoleCode(roleAccount.getRoleCode());
@ -394,7 +394,7 @@ public class AuthServiceImpl implements AuthService {
.map(RoleUkeyBindingEntity::getUkeySerial)
.collect(Collectors.toSet());
if (!boundSerials.containsAll(uniqueSerials)) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey verification 执行失败");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "UKey校验执行失败");
}
long matchedUidCount = bindings.stream()
.filter(binding -> uniqueSerials.contains(binding.getUkeySerial()))
@ -402,7 +402,7 @@ public class AuthServiceImpl implements AuthService {
.distinct()
.count();
if (matchedUidCount != requiredUkeyCount) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "UKey数量不满足角色要求");
}
return authPolicyService.resolveAuthLevel(AuthMethod.UKEY);
}
@ -413,20 +413,20 @@ public class AuthServiceImpl implements AuthService {
List<UkeyLoginProof> proofs
) {
if (proofs == null || proofs.size() != authPolicyService.requiredUkeyCount(roleCode)) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "UKey数量不满足角色要求");
}
Set<Integer> requestUids = proofs.stream()
.map(UkeyLoginProof::getUid)
.collect(Collectors.toSet());
int requiredCount = authPolicyService.requiredUkeyCount(roleCode);
if (requestUids.size() != requiredCount) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "UKey数量不满足角色要求");
}
Set<Integer> activeUids = activeBindings.stream()
.map(RoleUkeyBindingEntity::getUid)
.collect(Collectors.toSet());
if (!activeUids.containsAll(requestUids)) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "ukey count does not satisfy role requirement");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "UKey数量不满足角色要求");
}
}
@ -437,7 +437,7 @@ public class AuthServiceImpl implements AuthService {
try {
return objectMapper.writeValueAsString(UKeySignEntity.getInstance(dto, authKeyPair, RoleCode.valueOf(roleCode).getCode()));
} catch (JsonProcessingException ex) {
throw new IllegalStateException("serialize ukey issue payload 执行失败", ex);
throw new IllegalStateException("序列化UKey签发载荷失败", ex);
}
}
@ -502,7 +502,7 @@ public class AuthServiceImpl implements AuthService {
try {
return objectMapper.writeValueAsString(authenticatedPrincipals);
} catch (JsonProcessingException ex) {
throw new IllegalStateException("serialize authenticated principals 执行失败", ex);
throw new IllegalStateException("序列化已认证主体失败", ex);
}
}
@ -536,7 +536,7 @@ public class AuthServiceImpl implements AuthService {
new TypeReference<List<AuthSessionPrincipal>>() { }
);
} catch (JsonProcessingException ex) {
throw new IllegalStateException("deserialize authenticated principals 执行失败", ex);
throw new IllegalStateException("反序列化已认证主体失败", ex);
}
}

View File

@ -56,7 +56,7 @@ public class InMemoryUkeyLoginRandomService implements UkeyLoginRandomService {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "登录随机数不存在或已过期");
}
if (randoms == null || randoms.size() != issuedRandoms.randoms().size() || !issuedRandoms.randoms().containsAll(randoms)) {
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "login random verification 执行失败");
throw new BizException(ErrorCode.UNAUTHORIZED.getCode(), "登录随机数校验失败");
}
}

View File

@ -533,7 +533,7 @@ public class ResourcePackageServiceImpl implements ResourcePackageService {
}
String resourceCode = trim(item.getResourceCode()).isEmpty() ? "UNKNOWN" : trim(item.getResourceCode());
String resolvedPath = trim(item.getResolvedPath());
String message = "required backup resource is unavailable: code=" + resourceCode + ", reason=" + reason;
String message = "必需备份资源不可用:code=" + resourceCode + ", reason=" + reason;
if (!resolvedPath.isEmpty()) {
message += ", path=" + resolvedPath;
}

View File

@ -63,7 +63,7 @@ public class ResourceBackupServiceImpl implements ResourceBackupService {
resourceTaskAdmissionGuard.assertCanStartBackup();
// 资源备份必须基于最近一次成功初始化快照收集上下文避免前端重新传一套可能失真的参数
InitTaskEntity initTask = initTaskRepository.findLatestByTaskTypeAndStatus("INIT", "SUCCESS")
.orElseThrow(() -> new BizException(ErrorCode.BIZ_ERROR.getCode(), "成功ful init task is missing"));
.orElseThrow(() -> new BizException(ErrorCode.BIZ_ERROR.getCode(), "缺少成功的初始化任务"));
// taskId 用于后台任务表和工作目录backupId 写入备份包 manifest
// 恢复创建时用户需要确认同一个 backupId避免把预检包和恢复包搞混
@ -184,7 +184,7 @@ public class ResourceBackupServiceImpl implements ResourceBackupService {
result.setContentLength(Files.size(packagePath));
return result;
} catch (Exception ex) {
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "failed to read resource backup package");
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "读取资源备份包失败");
}
}

View File

@ -62,7 +62,7 @@ public class ResourceRestoreServiceImpl implements ResourceRestoreService {
ResourceRestorePrecheckResponse precheck = context.getResponse();
validatePrecheck(precheck);
if (!trim(request.getConfirmBackupId()).equals(trim(precheck.getBackupId()))) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "confirmed backup id does not match precheck result");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "确认的备份ID与预检结果不匹配");
}
FileRecordEntity packageFile = fileRecordRepository.findByFileId(context.getFileId())
.orElseThrow(() -> new BizException(ErrorCode.BIZ_ERROR.getCode(), "资源备份包文件不存在:" + trim(context.getFileId())));
@ -223,7 +223,7 @@ public class ResourceRestoreServiceImpl implements ResourceRestoreService {
}
// 兼容性和过期时间在创建恢复时再次校验防止用户拿旧的或不匹配的预检结果继续恢复
if (!Boolean.TRUE.equals(precheck.getCompatible())) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "resource restore precheck is not compatible");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "资源恢复预检结果不兼容");
}
String expiresAt = trim(precheck.getExpiresAt());
if (!expiresAt.isEmpty()) {

View File

@ -42,7 +42,7 @@ public class TimeConfigServiceImpl implements TimeConfigService {
}
if (!ZoneId.getAvailableZoneIds().contains(timezone)) {
throw new IllegalArgumentException( "Invalid timezone");
throw new IllegalArgumentException("时区不存在");
}
String datetime = request.getDatetime();
@ -131,7 +131,7 @@ public class TimeConfigServiceImpl implements TimeConfigService {
String status = executeCommand("chronyc", "tracking");
if (!status.contains("Reference ID")) {
throw new RuntimeException("NTP sync status check 执行失败");
throw new RuntimeException("NTP同步状态检查失败");
}
} catch (Exception e) {
@ -141,10 +141,10 @@ public class TimeConfigServiceImpl implements TimeConfigService {
executeCommand("systemctl", "restart", "chronyd");
}
} catch (Exception rollbackEx) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "Failed to apply new Chrony configuration. CRITICAL: Rollback also 执行失败!");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "应用新的Chrony配置失败且回滚也失败");
}
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "Failed to apply new Chrony configuration. Rolled back to original state.");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "应用新的Chrony配置失败已回滚到原始状态");
}
} else {
throw new IllegalArgumentException("未知模式:" + mode);

View File

@ -78,7 +78,7 @@ public final class CryptoPayloadCodec {
private static String requireText(String fieldName, String value) {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException(fieldName + " must not be blank");
throw new IllegalArgumentException(fieldName + "不能为空");
}
return value;
}
@ -88,10 +88,10 @@ public final class CryptoPayloadCodec {
try {
decoded = HexFormat.of().parseHex(hex);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(fieldName + " must be valid HEX", e);
throw new IllegalArgumentException(fieldName + "必须是有效HEX", e);
}
if (decoded.length != expectedBytes) {
throw new IllegalArgumentException(fieldName + " length must be " + expectedBytes + " bytes");
throw new IllegalArgumentException(fieldName + "长度必须为" + expectedBytes + "字节");
}
System.arraycopy(decoded, 0, target, offset, decoded.length);
}

View File

@ -51,7 +51,7 @@ public class FileServiceImpl implements FileService {
Files.createDirectories(target.getParent());
file.transferTo(target);
} catch (IOException ex) {
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "failed to save upload file");
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "保存上传文件失败");
}
// 直接落库存储绝对路径避免后续再依赖可能变化的配置重新推导文件位置
@ -84,7 +84,7 @@ public class FileServiceImpl implements FileService {
// 文件大小以磁盘上的真实文件为准保证查询结果反映当前实际落盘状态
response.setSize(Files.exists(path) ? Files.size(path) : 0L);
} catch (IOException ex) {
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "failed to read file detail");
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "读取文件详情失败");
}
return response;
}

View File

@ -387,7 +387,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
String raw = firstNonBlank(configuredPath, defaultPath);
Path path = Paths.get(raw).normalize().toAbsolutePath();
if (!Files.exists(path) || !Files.isRegularFile(path)) {
throw new IllegalArgumentException(fieldName + " not found: " + path);
throw new IllegalArgumentException(fieldName + "不存在:" + path);
}
return path;
}
@ -1427,7 +1427,7 @@ public class ConfigurableInitStepExecutor implements InitStepExecutor {
private void verifyCommon(Map<String, String> configMap, PlanContext context, List<String> failures) {
requireNotBlank(configMap, "MY_CIPSID_OR_BIC", failures);
if (!normalize(unquote(configMap.get("MY_CIPSID_OR_BIC"))).equals(normalize(context.orgCode()))) {
failures.add("MY_CIPSID_OR_BIC mismatch");
failures.add("MY_CIPSID_OR_BIC不匹配");
}
}

View File

@ -258,7 +258,7 @@ public class InitServiceImpl implements InitService {
public CurrentInitConfigResponse loadCurrentInitConfig() {
Optional<InitTaskEntity> latestInit = initTaskRepository.findLatestByTaskTypeAndStatus(TASK_TYPE_INIT, "SUCCESS");
if (latestInit.isEmpty()) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "device is not initialized");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "设备未初始化");
}
return resolveCurrentInitConfigFromTask(latestInit.get());
}
@ -306,17 +306,17 @@ public class InitServiceImpl implements InitService {
InitTaskStepEntity step = requireStep(taskId, stepNo);
if (isBlank(step.getLogPath())) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "log file not generated for task step");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "任务步骤日志文件尚未生成");
}
Path logPath = Paths.get(step.getLogPath()).normalize().toAbsolutePath();
Path logDir = Paths.get(initExecutorProperties.getLogDir()).normalize().toAbsolutePath();
// 只允许读取配置日志目录下的文件防止路径穿越读取任意系统文件
if (!logPath.startsWith(logDir)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "log path is out of allowed directory");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "日志路径超出允许目录");
}
if (!Files.exists(logPath) || !Files.isRegularFile(logPath)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "log file does not exist");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "日志文件不存在");
}
try {
@ -329,7 +329,7 @@ public class InitServiceImpl implements InitService {
response.setContent(content);
return response;
} catch (IOException ex) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to read log file");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "读取日志文件失败");
}
}
@ -352,7 +352,7 @@ public class InitServiceImpl implements InitService {
InitTaskEntity task = requireTask(taskId);
List<InitTaskStepEntity> steps = initTaskStepRepository.findByTaskIdOrderByStepNo(taskId);
if (steps.isEmpty()) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "init task has no steps: " + taskId);
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "初始化任务没有步骤:" + taskId);
}
if (steps.stream().allMatch(step -> "SUCCESS".equals(step.getStatus()))) {
task.setStatus("SUCCESS");
@ -478,7 +478,7 @@ public class InitServiceImpl implements InitService {
try {
return objectMapper.writeValueAsString(root);
} catch (JsonProcessingException ex) {
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "failed to serialize init plan");
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "序列化初始化计划失败");
}
}
@ -501,7 +501,7 @@ public class InitServiceImpl implements InitService {
try {
return objectMapper.writeValueAsString(root);
} catch (JsonProcessingException ex) {
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "failed to serialize reset plan");
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "序列化重置计划失败");
}
}
@ -587,7 +587,7 @@ public class InitServiceImpl implements InitService {
private List<String> resetSteps(String productType, String mqType) {
if ("DIRECT".equals(productType)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "reset is not supported for current product type: " + productType);
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "当前产品类型不支持重置:" + productType);
}
return List.of(
"STOP_APPS",
@ -627,7 +627,7 @@ public class InitServiceImpl implements InitService {
private String resolveSupportedResetProductType() {
String productType = resolvePresetProductType();
if ("DIRECT".equals(productType)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "reset is not supported for current product type: " + productType);
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "当前产品类型不支持重置:" + productType);
}
return productType;
}
@ -672,11 +672,11 @@ public class InitServiceImpl implements InitService {
String productType = resolveSupportedResetProductType();
Optional<InitTaskEntity> latestInit = initTaskRepository.findLatestByTaskTypeAndStatus(TASK_TYPE_INIT, "SUCCESS");
if (latestInit.isEmpty()) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "device is not initialized, reset is forbidden");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "设备未初始化,禁止重置");
}
Optional<InitTaskEntity> latestReset = initTaskRepository.findLatestByTaskTypeAndStatus(TASK_TYPE_RESET, "SUCCESS");
if (latestReset.isPresent() && compareTaskRecency(latestReset.get(), latestInit.get()) >= 0) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "device is not initialized, reset is forbidden");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "设备未初始化,禁止重置");
}
ResolvedResetContext context = resolveResetContextFromInitTask(latestInit.get());
@ -692,10 +692,10 @@ public class InitServiceImpl implements InitService {
private ResolvedResetContext resolveResetContextFromInitTask(InitTaskEntity task) {
if (task == null) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "成功ful init task is missing");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "缺少成功的初始化任务");
}
if (isBlank(task.getInitPlanJson())) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "成功ful init task snapshot is missing");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "成功初始化任务快照缺失");
}
try {
JsonNode root = objectMapper.readTree(task.getInitPlanJson());
@ -707,20 +707,20 @@ public class InitServiceImpl implements InitService {
String channelUsername = firstNonBlank(text(mq, "channelUsername"), text(request, "channelUsername"));
if (isBlank(productType) || isBlank(orgCode) || isBlank(mqType)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "成功ful init task snapshot is incomplete");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "成功初始化任务快照不完整");
}
return new ResolvedResetContext(productType, normalize(orgCode), normalizeUpper(mqType), normalize(channelUsername), task.getTaskId());
} catch (JsonProcessingException ex) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to parse 成功ful init task snapshot");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "解析成功初始化任务快照失败");
}
}
private CurrentInitConfigResponse resolveCurrentInitConfigFromTask(InitTaskEntity task) {
if (task == null) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "成功ful init task is missing");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "缺少成功的初始化任务");
}
if (isBlank(task.getInitPlanJson())) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "成功ful init task snapshot is missing");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "成功初始化任务快照缺失");
}
try {
JsonNode root = objectMapper.readTree(task.getInitPlanJson());
@ -747,7 +747,7 @@ public class InitServiceImpl implements InitService {
response.setCfmqFileNames(buildCfmqFileNames(mq, mqType));
return response;
} catch (JsonProcessingException ex) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to parse 成功ful init task snapshot");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "解析成功初始化任务快照失败");
}
}
@ -869,13 +869,13 @@ public class InitServiceImpl implements InitService {
private static void requireAllowed(String field, String value, Set<String> allowed) {
if (!allowed.contains(value)) {
throw new IllegalArgumentException(field + " must be one of " + allowed);
throw new IllegalArgumentException(field + "必须是以下值之一:" + allowed);
}
}
private static void requireNotBlank(String field, String value) {
if (isBlank(value)) {
throw new IllegalArgumentException(field + " is required");
throw new IllegalArgumentException(field + "不能为空");
}
}
@ -887,7 +887,7 @@ public class InitServiceImpl implements InitService {
private static void requireFileId(String field, String value) {
if (isBlank(value) || !FILE_ID_PATTERN.matcher(normalize(value)).matches()) {
throw new IllegalArgumentException(field + " must be a fileId");
throw new IllegalArgumentException(field + "必须是fileId");
}
}

View File

@ -398,12 +398,12 @@ public class LmkServiceImpl implements LmkService {
private byte[] decodeConfiguredPin(String fieldName, String base64Value) {
if (base64Value == null || base64Value.isBlank()) {
throw new IllegalStateException(fieldName + " is not configured");
throw new IllegalStateException(fieldName + "未配置");
}
try {
return Base64.getDecoder().decode(base64Value);
} catch (IllegalArgumentException e) {
throw new IllegalStateException(fieldName + " is not valid Base64", e);
throw new IllegalStateException(fieldName + "不是有效Base64");
}
}
@ -417,7 +417,7 @@ public class LmkServiceImpl implements LmkService {
private Map<Integer, String> parseNativeComponentMap(byte[] componentBytes, String name) {
if (componentBytes.length % NATIVE_COMPONENT_COUNT != 0) {
throw new IllegalStateException(name + " native component bytes length is invalid: " + componentBytes.length);
throw new IllegalStateException(name + "原生分量字节长度无效:" + componentBytes.length);
}
// 原生分量总长度必须能被 3 整除每一段等长依次映射为 1/2/3 号分量
int componentLength = componentBytes.length / NATIVE_COMPONENT_COUNT;
@ -440,18 +440,18 @@ public class LmkServiceImpl implements LmkService {
private String requiredComponent(Map<Integer, String> componentMap, int componentIndex, String name) {
String component = componentMap.get(componentIndex);
if (component == null || component.isBlank()) {
throw new IllegalArgumentException(name + " component missing: " + componentIndex);
throw new IllegalArgumentException(name + "分量缺失:" + componentIndex);
}
return component;
}
private String requiredPacketComponent(Map<Integer, String> componentMap, int componentIndex, String name, int packetIndex) {
if (componentMap == null) {
throw new IllegalArgumentException(name + " component missing in packet " + packetIndex);
throw new IllegalArgumentException(name + "在分包" + packetIndex + "中缺少分量");
}
String component = componentMap.get(componentIndex);
if (component == null || component.isBlank()) {
throw new IllegalArgumentException(name + " component " + componentIndex + " missing in packet " + packetIndex);
throw new IllegalArgumentException(name + "分量" + componentIndex + "在分包" + packetIndex + "中缺失");
}
return component;
}
@ -462,7 +462,7 @@ public class LmkServiceImpl implements LmkService {
String packet1Overlap = requiredPacketComponent(packet1Components, NATIVE_COMPONENT_TWO, name, PACKET_INDEX_ONE);
String packet2Overlap = requiredPacketComponent(packet2Components, NATIVE_COMPONENT_TWO, name, PACKET_INDEX_TWO);
if (!packet1Overlap.equals(packet2Overlap)) {
throw new IllegalArgumentException(name + " component 2 is inconsistent");
throw new IllegalArgumentException(name + "分量2不一致");
}
return packet1Overlap;
}

View File

@ -85,7 +85,7 @@ public class ReplayProtectionServiceImpl implements ReplayProtectionService {
var reloaded = replayNonceRepository.findByScopeAndPrincipalIdAndNonce(scope, principalId, nonce);
if (reloaded.isEmpty()) {
throw new ReplayProtectionException("replay nonce claim 执行失败 without existing record");
throw new ReplayProtectionException("防重放nonce占用失败且未找到已存在记录");
}
return buildReplayResult(request, reloaded.get());
}
@ -170,7 +170,7 @@ public class ReplayProtectionServiceImpl implements ReplayProtectionService {
}
private String buildMismatchDetail(ReplayCheckRequest request) {
return "request fingerprint mismatch: scope=" + request.getScope().name()
return "请求指纹不匹配:scope=" + request.getScope().name()
+ ", method=" + normalize(request.getRequestMethod())
+ ", path=" + normalize(request.getRequestPath())
+ ", bodyHash=" + normalize(request.getBodyHash());

View File

@ -199,7 +199,7 @@ public class UpgradeTaskRunner {
Path resolved = stagedDir.resolve(trim(scriptPath)).normalize().toAbsolutePath();
// 防止脚本路径通过 ../ 跳出升级包解压目录
if (!resolved.startsWith(stagedDir) || !Files.exists(resolved)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade " + phase + " script not found");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级" + phase + "脚本不存在");
}
Process process = new ProcessBuilder("bash", resolved.toString(), task.getTaskId())
.directory(stagedDir.toFile())
@ -218,7 +218,7 @@ public class UpgradeTaskRunner {
: "upgrade 成功, scheduling detached TMS restart");
triggerDetachedTmsRestart(logPath, rollback);
} catch (IOException ex) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to schedule detached tms restart");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "调度TMS后台重启失败");
}
}

View File

@ -68,11 +68,11 @@ public class UpgradePackageService {
String targetVersion = normalize(manifest.getVersion());
if (!currentVersion.isEmpty() && versionComparator.compare(targetVersion, currentVersion) < 0) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "target version is lower than current version");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "目标版本低于当前版本");
}
String minCompatibleVersion = normalize(manifest.getMinCompatibleVersion());
if (!currentVersion.isEmpty() && !minCompatibleVersion.isEmpty() && versionComparator.compare(currentVersion, minCompatibleVersion) < 0) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "current version does not satisfy minimum compatible version");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "当前版本不满足最低兼容版本要求");
}
UpgradePreviewResponse response = new UpgradePreviewResponse();
@ -145,7 +145,7 @@ public class UpgradePackageService {
String currentProductType = normalizeUpper(cisdPresetProperties.getProductType());
String packageType = normalizeUpper(packageProductType);
if (!currentProductType.isEmpty() && !packageType.equals(currentProductType)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade package product type does not match current device");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级包产品类型与当前设备不匹配");
}
}
@ -163,7 +163,7 @@ public class UpgradePackageService {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级payload.zip不存在");
}
if (!expected.equals(sm3Hex(payloadZip))) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade package payload sm3 mismatch");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级包payload SM3摘要不匹配");
}
}

View File

@ -109,7 +109,7 @@ public class UpgradeService {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), blankAs(preview.getBlockReason(), "升级预检不允许继续"));
}
if (!trim(request.getTaskType()).equals(trim(preview.getTaskType()))) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "selected taskType does not match package taskType");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "选择的taskType与升级包taskType不匹配");
}
FileRecordEntity fileRecord = fileRecordRepository.findByFileId(request.getFileId().trim())
.orElseThrow(() -> new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级包文件不存在:" + request.getFileId().trim()));
@ -148,7 +148,7 @@ public class UpgradeService {
// 第一版按一体机单机串行升级处理避免同时升级 TMS收发器或固件造成状态不可控
UpgradeTaskEntity runningTask = upgradeTaskRepository.findRunningTask().orElse(null);
if (runningTask != null && !runningTask.getTaskId().equals(taskId)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "another upgrade task is running");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "已有升级任务正在执行");
}
if (activeExecutions.containsKey(taskId)) {
return toResponse(loadTask(taskId));
@ -179,7 +179,7 @@ public class UpgradeService {
}
UpgradeTaskEntity runningTask = upgradeTaskRepository.findRunningTask().orElse(null);
if (runningTask != null && !runningTask.getTaskId().equals(taskId)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "another upgrade task is running");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "已有升级任务正在执行");
}
if (activeExecutions.containsKey(taskId)) {
return toResponse(loadTask(taskId));
@ -235,16 +235,16 @@ public class UpgradeService {
UpgradeTaskEntity task = loadTask(taskId);
String logPath = trim(task.getDetailLogPath());
if (logPath.isEmpty()) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade log not generated");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级日志尚未生成");
}
Path path = Path.of(logPath).normalize().toAbsolutePath();
Path allowedRoot = Path.of(trim(upgradeProperties.getLogDir())).normalize().toAbsolutePath();
// 日志路径必须落在升级日志根目录下防止通过篡改任务记录读取任意文件
if (!path.startsWith(allowedRoot)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade log path is out of allowed directory");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级日志路径超出允许目录");
}
if (!Files.exists(path)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade log file does not exist");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级日志文件不存在");
}
try {
UpgradeLogResponse response = new UpgradeLogResponse();
@ -253,7 +253,7 @@ public class UpgradeService {
response.setContent(Files.readString(path));
return response;
} catch (IOException ex) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to read upgrade log");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "读取升级日志失败");
}
}
@ -304,7 +304,7 @@ public class UpgradeService {
}
return path;
} catch (IOException ex) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to prepare upgrade log file");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "准备升级日志文件失败");
}
}

View File

@ -46,7 +46,7 @@ public class SoftUpgradePackageSignatureVerifier implements UpgradePackageSignat
// 第一版升级包仅对 manifest.json 做离线软验签避免在包内脚本执行前信任未授权包
PublicKey publicKey = loadPublicKey();
byte[] manifestBytes = readBytes(manifestPath, "failed to read manifest for signature verification");
byte[] manifestBytes = readBytes(manifestPath, "读取验签manifest失败");
byte[] signatureBytes = loadSignature(signaturePath);
try {
@ -59,14 +59,14 @@ public class SoftUpgradePackageSignatureVerifier implements UpgradePackageSignat
} catch (BizException ex) {
throw ex;
} catch (Exception ex) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade package signature verification 执行失败");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级包签名验签失败");
}
}
private PublicKey loadPublicKey() {
Path pemPath = Path.of(normalize(upgradeProperties.getSignaturePublicKeyPemPath())).normalize().toAbsolutePath();
if (normalize(upgradeProperties.getSignaturePublicKeyPemPath()).isEmpty()) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "upgrade signature public key pem path not configured");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级签名公钥PEM路径未配置");
}
if (!Files.exists(pemPath)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级签名公钥PEM文件不存在");
@ -84,14 +84,14 @@ public class SoftUpgradePackageSignatureVerifier implements UpgradePackageSignat
} catch (BizException ex) {
throw ex;
} catch (IOException ex) {
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "failed to read upgrade signature public key pem");
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "读取升级签名公钥PEM失败");
} catch (Exception ex) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级签名公钥PEM无效");
}
}
private byte[] loadSignature(Path signaturePath) {
byte[] raw = readBytes(signaturePath, "failed to read upgrade signature file");
byte[] raw = readBytes(signaturePath, "读取升级签名文件失败");
if (raw.length == 0) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级包签名文件为空");
}

View File

@ -31,7 +31,7 @@ public class UpgradePackageStagingService {
unzip(packagePath, taskDir, "");
return taskDir;
} catch (IOException ex) {
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "failed to stage upgrade package");
throw new BizException(ErrorCode.INTERNAL_ERROR.getCode(), "暂存升级包失败");
}
}
@ -45,7 +45,7 @@ public class UpgradePackageStagingService {
deleteDirectoryIfExists(root.resolve("payload"));
unzip(payloadZip, root, "payload/");
} catch (IOException ex) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "failed to extract upgrade payload.zip");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "解压升级payload.zip失败");
}
}
@ -56,12 +56,12 @@ public class UpgradePackageStagingService {
while ((entry = zipInputStream.getNextEntry()) != null) {
String entryName = entry.getName();
if (!requiredEntryPrefix.isEmpty() && !entryName.startsWith(requiredEntryPrefix)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "invalid upgrade payload entry");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级payload条目无效");
}
Path target = targetDir.resolve(entry.getName()).normalize().toAbsolutePath();
// 防止恶意 zip 条目通过 ../ 写出 staging 目录
if (!target.startsWith(targetDir)) {
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "invalid upgrade package entry");
throw new BizException(ErrorCode.BIZ_ERROR.getCode(), "升级包条目无效");
}
if (entry.isDirectory()) {
Files.createDirectories(target);

View File

@ -58,7 +58,7 @@ public class InternalApiReplayInterceptor implements HandlerInterceptor {
String timestamp = trimHeader(request, TIMESTAMP_HEADER);
String nonce = trimHeader(request, NONCE_HEADER);
if (isBlank(timestamp) || isBlank(nonce)) {
writeJson(response, HttpServletResponse.SC_BAD_REQUEST, ErrorCode.BAD_REQUEST.getCode(), "missing replay protection headers");
writeJson(response, HttpServletResponse.SC_BAD_REQUEST, ErrorCode.BAD_REQUEST.getCode(), "防重放请求头缺失");
return false;
}
@ -66,7 +66,7 @@ public class InternalApiReplayInterceptor implements HandlerInterceptor {
try {
requestTimestamp = Long.parseLong(timestamp);
} catch (NumberFormatException ex) {
writeJson(response, HttpServletResponse.SC_BAD_REQUEST, ErrorCode.BAD_REQUEST.getCode(), "invalid replay timestamp");
writeJson(response, HttpServletResponse.SC_BAD_REQUEST, ErrorCode.BAD_REQUEST.getCode(), "防重放时间戳格式无效");
return false;
}
@ -74,7 +74,7 @@ public class InternalApiReplayInterceptor implements HandlerInterceptor {
try {
ReplayCheckResult result = replayProtectionService.check(replayRequest);
if (result.isReplayed()) {
writeJson(response, HttpServletResponse.SC_CONFLICT, ErrorCode.CONFLICT.getCode(), "replayed nonce");
writeJson(response, HttpServletResponse.SC_CONFLICT, ErrorCode.CONFLICT.getCode(), "nonce已被重放");
return false;
}
} catch (ReplayProtectionException ex) {
@ -153,6 +153,6 @@ public class InternalApiReplayInterceptor implements HandlerInterceptor {
return;
}
writeJson(response, HttpServletResponse.SC_SERVICE_UNAVAILABLE, ErrorCode.SERVICE_UNAVAILABLE.getCode(),
"replay protection unavailable");
"防重放校验服务不可用");
}
}

View File

@ -51,14 +51,14 @@ public class OpenApiSignAuthInterceptor implements HandlerInterceptor {
String signature = trimHeader(request, SIGNATURE_HEADER);
if (isBlank(appId) || isBlank(timestamp) || isBlank(nonce) || isBlank(signature)) {
writeUnauthorized(response, "missing openapi auth headers");
writeUnauthorized(response, "OpenAPI认证请求头缺失");
return false;
}
Map<String, String> clients = securityProperties.getOpenapi().getClients();
String appSecret = clients.get(appId);
if (isBlank(appSecret)) {
writeUnauthorized(response, "unknown app id");
writeUnauthorized(response, "未知的appId");
return false;
}
@ -66,7 +66,7 @@ public class OpenApiSignAuthInterceptor implements HandlerInterceptor {
try {
requestEpoch = Long.parseLong(timestamp);
} catch (NumberFormatException ex) {
writeUnauthorized(response, "invalid timestamp");
writeUnauthorized(response, "时间戳格式无效");
return false;
}
@ -93,7 +93,7 @@ public class OpenApiSignAuthInterceptor implements HandlerInterceptor {
// 先完成签名校验再占用 nonce避免伪造请求提前消耗合法 nonce
ReplayCheckResult result = replayProtectionService.check(buildReplayRequest(request, appId, nonce, requestEpoch));
if (result.isReplayed()) {
writeJson(response, HttpServletResponse.SC_CONFLICT, ErrorCode.CONFLICT.getCode(), "replayed nonce");
writeJson(response, HttpServletResponse.SC_CONFLICT, ErrorCode.CONFLICT.getCode(), "nonce已被重放");
return false;
}
} catch (ReplayProtectionException ex) {
@ -159,6 +159,6 @@ public class OpenApiSignAuthInterceptor implements HandlerInterceptor {
return;
}
writeJson(response, HttpServletResponse.SC_SERVICE_UNAVAILABLE,
ErrorCode.SERVICE_UNAVAILABLE.getCode(), "replay protection unavailable");
ErrorCode.SERVICE_UNAVAILABLE.getCode(), "防重放校验服务不可用");
}
}

View File

@ -27,6 +27,15 @@ class ChineseErrorMessageContractTest {
private static final Pattern VALIDATION_MESSAGE = Pattern.compile(
"@(?:NotNull|NotBlank|NotEmpty|Size|Pattern|Min|Max|Positive|AssertTrue)\\([^\\n]*message\\s*=\\s*\"([^\"]+)\""
);
private static final Pattern USER_FACING_STATEMENT = Pattern.compile(
"(?s)(?:new\\s+(?:BizException|IllegalArgumentException|IllegalStateException|RuntimeException|ReplayProtectionException)\\([^;]*;"
+ "|ApiResponse\\.fail\\([^;]*;"
+ "|write(?:Unauthorized|Forbidden|SessionInvalid|Json)\\([^;]*;"
+ "|InitStepExecutionResult\\.failure\\([^;]*;"
+ "|new\\s+PcieCryptoException\\([^;]*;"
+ "|new\\s+RequiredBackupResourceMissingException\\([^;]*;)"
);
private static final Pattern STRING_LITERAL_WITH_ENGLISH = Pattern.compile("\"([^\"]*[A-Za-z][^\"]*)\"");
@Test
void productionErrorMessagesShouldBeChinese() throws IOException {
@ -41,7 +50,9 @@ class ChineseErrorMessageContractTest {
private static void collectViolations(Path path, List<String> violations) {
try {
List<String> lines = Files.readAllLines(path);
String content = Files.readString(path);
collectStatements(content, path, violations);
List<String> lines = content.lines().toList();
for (int index = 0; index < lines.size(); index++) {
String line = lines.get(index);
if (line.trim().startsWith("//") || line.contains("log.")) {
@ -55,6 +66,48 @@ class ChineseErrorMessageContractTest {
}
}
private static void collectStatements(String content, Path path, List<String> violations) {
Matcher statementMatcher = USER_FACING_STATEMENT.matcher(content);
while (statementMatcher.find()) {
String statement = statementMatcher.group();
if (statement.trim().startsWith("//") || statement.contains("log.")) {
continue;
}
if (CHINESE.matcher(statement).find()) {
continue;
}
Matcher literalMatcher = STRING_LITERAL_WITH_ENGLISH.matcher(statement);
int literalIndex = 0;
while (literalMatcher.find()) {
String message = literalMatcher.group(1);
if (isNonMessagePcieApiName(statement, message, literalIndex)) {
literalIndex++;
continue;
}
if (!CHINESE.matcher(message).find()) {
violations.add(path + ":" + lineNumber(content, statementMatcher.start() + literalMatcher.start()) + " -> " + message);
}
literalIndex++;
}
}
}
private static boolean isNonMessagePcieApiName(String statement, String message, int literalIndex) {
return statement.contains("PcieCryptoException(")
&& literalIndex == 0
&& message.matches("[A-Za-z0-9_./-]+");
}
private static int lineNumber(String content, int offset) {
int line = 1;
for (int i = 0; i < offset; i++) {
if (content.charAt(i) == '\n') {
line++;
}
}
return line;
}
private static void collect(String line, Pattern pattern, Path path, int index, List<String> violations) {
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {

View File

@ -46,7 +46,7 @@ class GlobalExceptionHandlerTest {
Assertions.assertNotNull(response.getBody());
Assertions.assertFalse(response.getBody().isSuccess());
Assertions.assertEquals(ErrorCode.VALIDATE_FAILED.getCode(), response.getBody().getCode());
Assertions.assertEquals("failed to parse multipart request", response.getBody().getMsg());
Assertions.assertEquals("解析 multipart 上传请求失败", response.getBody().getMsg());
Assertions.assertEquals("/api/v1/files/upload", response.getBody().getPath());
}
@ -78,7 +78,7 @@ class GlobalExceptionHandlerTest {
Assertions.assertNotNull(response.getBody());
Assertions.assertFalse(response.getBody().isSuccess());
Assertions.assertEquals(ErrorCode.NOT_FOUND.getCode(), response.getBody().getCode());
Assertions.assertEquals("resource not found", response.getBody().getMsg());
Assertions.assertEquals("资源不存在", response.getBody().getMsg());
Assertions.assertEquals("/missing.js", response.getBody().getPath());
}
}

View File

@ -98,7 +98,7 @@ class AuthAdminServiceTest {
));
Assertions.assertEquals(ErrorCode.UNAUTHORIZED.getCode(), exception.getCode());
Assertions.assertEquals("old password is incorrect", exception.getMessage());
Assertions.assertEquals("旧密码错误", exception.getMessage());
}
@Test

View File

@ -140,7 +140,7 @@ class AuthServiceTest {
BizException exception = Assertions.assertThrows(BizException.class, () -> service.login(request));
Assertions.assertEquals("full account is locked", exception.getMessage());
Assertions.assertEquals("完整账号已锁定", exception.getMessage());
AuthFullAccountEntity locked = accounts.findByRoleCodeAndUid(RoleCode.SUPER_ADMIN.getCode(), 1).orElseThrow();
AuthFullAccountEntity untouched = accounts.findByRoleCodeAndUid(RoleCode.SUPER_ADMIN.getCode(), 2).orElseThrow();
Assertions.assertEquals(RoleAccountStatus.LOCKED.name(), locked.getStatus());

View File

@ -8,8 +8,8 @@ class RequiredBackupResourceMissingExceptionTest {
@Test
void shouldExposeMessage() {
RequiredBackupResourceMissingException exception =
new RequiredBackupResourceMissingException("required backup resource is unavailable");
new RequiredBackupResourceMissingException("必需备份资源不可用");
Assertions.assertEquals("required backup resource is unavailable", exception.getMessage());
Assertions.assertEquals("必需备份资源不可用", exception.getMessage());
}
}

View File

@ -98,7 +98,7 @@ class ResourceBackupServiceTest {
Mockito.same(initTask),
Mockito.eq(discoveredItems),
Mockito.any(CreateResourceBackupRequest.class)
)).thenThrow(new RequiredBackupResourceMissingException("required backup resource is unavailable: code=TMS_CONFIG"));
)).thenThrow(new RequiredBackupResourceMissingException("必需备份资源不可用:code=TMS_CONFIG"));
ResourceBackupService service = new ResourceBackupServiceImpl(
initTaskRepository,

View File

@ -101,7 +101,7 @@ class UpgradePackageServiceTest {
BizException exception = Assertions.assertThrows(BizException.class, () -> service.preview(fileRecord.getFileId()));
Assertions.assertEquals(ErrorCode.BIZ_ERROR.getCode(), exception.getCode());
Assertions.assertEquals("upgrade package product type does not match current device", exception.getMessage());
Assertions.assertEquals("升级包产品类型与当前设备不匹配", exception.getMessage());
}
@Test
@ -124,7 +124,7 @@ class UpgradePackageServiceTest {
);
BizException exception = Assertions.assertThrows(BizException.class, () -> service.preview(fileRecord.getFileId()));
Assertions.assertEquals("target version is lower than current version", exception.getMessage());
Assertions.assertEquals("目标版本低于当前版本", exception.getMessage());
}
@Test
@ -147,7 +147,7 @@ class UpgradePackageServiceTest {
);
BizException exception = Assertions.assertThrows(BizException.class, () -> service.preview(fileRecord.getFileId()));
Assertions.assertEquals("current version does not satisfy minimum compatible version", exception.getMessage());
Assertions.assertEquals("当前版本不满足最低兼容版本要求", exception.getMessage());
}
@Test
@ -210,7 +210,7 @@ class UpgradePackageServiceTest {
);
BizException exception = Assertions.assertThrows(BizException.class, () -> service.preview(fileRecord.getFileId()));
Assertions.assertEquals("upgrade package payload sm3 mismatch", exception.getMessage());
Assertions.assertEquals("升级包payload SM3摘要不匹配", exception.getMessage());
}
@Test

View File

@ -108,7 +108,7 @@ class UpgradeServiceTest {
BizException exception = Assertions.assertThrows(BizException.class, () -> service.createTask(request));
Assertions.assertEquals("selected taskType does not match package taskType", exception.getMessage());
Assertions.assertEquals("选择的taskType与升级包taskType不匹配", exception.getMessage());
}
@Test
@ -129,7 +129,7 @@ class UpgradeServiceTest {
BizException exception = Assertions.assertThrows(BizException.class, () -> service.executeTask("UPG-001"));
Assertions.assertEquals("another upgrade task is running", exception.getMessage());
Assertions.assertEquals("已有升级任务正在执行", exception.getMessage());
}
@Test

View File

@ -99,7 +99,7 @@ class InternalApiReplayInterceptorTest {
Assertions.assertFalse(allowed);
Assertions.assertEquals(400, response.getStatus());
Assertions.assertTrue(response.getContentAsString().contains("missing replay protection headers"));
Assertions.assertTrue(response.getContentAsString().contains("防重放请求头缺失"));
Mockito.verifyNoInteractions(replayProtectionService);
}
@ -127,7 +127,7 @@ class InternalApiReplayInterceptorTest {
Assertions.assertFalse(allowed);
Assertions.assertEquals(409, response.getStatus());
Assertions.assertTrue(response.getContentAsString().contains("replayed nonce"));
Assertions.assertTrue(response.getContentAsString().contains("nonce已被重放"));
Assertions.assertTrue(response.getContentAsString().contains("\"code\":" + ErrorCode.CONFLICT.getCode()));
}
@ -153,7 +153,7 @@ class InternalApiReplayInterceptorTest {
Assertions.assertFalse(allowed);
Assertions.assertEquals(503, response.getStatus());
Assertions.assertTrue(response.getContentAsString().contains("replay protection unavailable"));
Assertions.assertTrue(response.getContentAsString().contains("防重放校验服务不可用"));
Assertions.assertTrue(response.getContentAsString().contains("\"code\":" + ErrorCode.SERVICE_UNAVAILABLE.getCode()));
}

View File

@ -117,7 +117,7 @@ class OpenApiSignAuthInterceptorTest {
Assertions.assertFalse(allowed);
Assertions.assertEquals(409, response.getStatus());
Assertions.assertTrue(response.getContentAsString().contains("replayed nonce"));
Assertions.assertTrue(response.getContentAsString().contains("nonce已被重放"));
Assertions.assertTrue(response.getContentAsString().contains("\"code\":" + ErrorCode.CONFLICT.getCode()));
}
@ -147,7 +147,7 @@ class OpenApiSignAuthInterceptorTest {
Assertions.assertFalse(allowed);
Assertions.assertEquals(503, response.getStatus());
Assertions.assertTrue(response.getContentAsString().contains("replay protection unavailable"));
Assertions.assertTrue(response.getContentAsString().contains("防重放校验服务不可用"));
Assertions.assertTrue(response.getContentAsString().contains("\"code\":" + ErrorCode.SERVICE_UNAVAILABLE.getCode()));
}