diff --git a/backend/pom.xml b/backend/pom.xml
index 4f433a24..72dab6f0 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -154,6 +154,19 @@
hutool-all
${hutool.version}
+
+ org.springframework.retry
+ spring-retry
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+ com.google.guava
+ guava
+ 32.1.3-jre
+
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/BackendApplication.java b/backend/src/main/java/com/qqchen/deploy/backend/BackendApplication.java
index 8914c48f..b824e3d5 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/BackendApplication.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/BackendApplication.java
@@ -4,7 +4,9 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.retry.annotation.EnableRetry;
+@EnableRetry
@SpringBootApplication
@EnableFeignClients
public class BackendApplication {
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/controller/BaseController.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/controller/BaseController.java
index 38459e85..d4d25e34 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/framework/controller/BaseController.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/controller/BaseController.java
@@ -7,11 +7,13 @@ import com.qqchen.deploy.backend.framework.query.BaseQuery;
import com.qqchen.deploy.backend.framework.dto.BaseRequest;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.service.IBaseService;
+import jakarta.servlet.http.HttpServletResponse;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;
import java.io.Serializable;
import java.util.List;
+import java.util.concurrent.CompletableFuture;
/**
* 通用REST控制器
@@ -70,4 +72,17 @@ public abstract class BaseController, ID extends Serializab
List entities = service.findAll(query);
return Response.success(converter.toResponseList(entities));
}
+
+ @PostMapping("/batch")
+ public CompletableFuture> batchProcess(@RequestBody List entities) {
+ return CompletableFuture.runAsync(() -> {
+ service.batchProcess(entities);
+ }).thenApply(v -> Response.success());
+ }
+
+ @GetMapping("/export")
+ public void export(HttpServletResponse response, BaseQuery query) {
+ List data = service.findAll(query);
+ // 导出逻辑
+ }
}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/domain/Entity.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/domain/Entity.java
index 4b530107..44d45cde 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/framework/domain/Entity.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/domain/Entity.java
@@ -6,6 +6,8 @@ import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
+import jakarta.persistence.OptimisticLockException;
+import jakarta.persistence.PreUpdate;
import jakarta.persistence.Version;
import lombok.Getter;
import lombok.Setter;
@@ -45,9 +47,22 @@ public abstract class Entity implements Serializable {
private LocalDateTime updateTime;
@Version
- private Integer version;
+ @Column(name = "version", nullable = false)
+ private Integer version = 0;
@Column(nullable = false)
private Boolean deleted = false;
+ @PreUpdate
+ protected void onPreUpdate() {
+ if (this.version == null) {
+ this.version = 0;
+ }
+ }
+
+ public void checkVersion(Integer expectedVersion) {
+ if (expectedVersion != null && !expectedVersion.equals(this.version)) {
+ throw new OptimisticLockException("数据已被其他用户修改");
+ }
+ }
}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/enums/ResponseCode.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/enums/ResponseCode.java
index acd8bdae..2dbab190 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/framework/enums/ResponseCode.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/enums/ResponseCode.java
@@ -17,6 +17,12 @@ public enum ResponseCode {
TENANT_NOT_FOUND(1001, "tenant.not.found"),
DATA_NOT_FOUND(1002, "data.not.found"),
+ // 系统异常 (1开头)
+ OPTIMISTIC_LOCK_ERROR(1003, "system.optimistic.lock.error"), // 乐观锁异常
+ PESSIMISTIC_LOCK_ERROR(1004, "system.pessimistic.lock.error"), // 悲观锁异常
+ CONCURRENT_UPDATE_ERROR(1005, "system.concurrent.update.error"), // 并发更新异常
+ RETRY_EXCEEDED_ERROR(1006, "system.retry.exceeded.error"), // 重试次数超限异常
+
// 用户相关错误码(2开头)
USER_NOT_FOUND(2001, "user.not.found"),
USERNAME_EXISTS(2002, "user.username.exists"),
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/exception/BusinessException.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/exception/BusinessException.java
index 04b4cd86..f19430f0 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/framework/exception/BusinessException.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/exception/BusinessException.java
@@ -1,22 +1,21 @@
package com.qqchen.deploy.backend.framework.exception;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
-import com.qqchen.deploy.backend.framework.utils.MessageUtils;
import lombok.Getter;
@Getter
public class BusinessException extends RuntimeException {
private final ResponseCode errorCode;
+ private final Object[] args;
public BusinessException(ResponseCode errorCode) {
- super(MessageUtils.getMessage(errorCode.getMessageKey()));
- this.errorCode = errorCode;
+ this(errorCode, null);
}
-
- public BusinessException(ResponseCode errorCode, Throwable cause) {
- super(MessageUtils.getMessage(errorCode.getMessageKey()), cause);
+ public BusinessException(ResponseCode errorCode, Object[] args) {
+ super();
this.errorCode = errorCode;
+ this.args = args;
}
}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/exception/GlobalExceptionHandler.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/exception/GlobalExceptionHandler.java
index 2340df54..fbde0c38 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/framework/exception/GlobalExceptionHandler.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/exception/GlobalExceptionHandler.java
@@ -2,26 +2,59 @@ package com.qqchen.deploy.backend.framework.exception;
import com.qqchen.deploy.backend.framework.api.Response;
import com.qqchen.deploy.backend.framework.enums.ResponseCode;
+import jakarta.persistence.OptimisticLockException;
+import jakarta.persistence.PessimisticLockException;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.MessageSource;
+import org.springframework.context.NoSuchMessageException;
+import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
+import java.util.ConcurrentModificationException;
+
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
-// @ExceptionHandler(AuthenticationException.class)
-// public ApiResult handleAuthenticationException(AuthenticationException e) {
-// log.error("认证异常", e);
-// if (e instanceof BadCredentialsException) {
-// return ApiResult.error(401, "用户名或密码错误");
-// }
-// return ApiResult.error(401, "认证失败:" + e.getMessage());
-// }
+ @Autowired
+ private MessageSource messageSource;
+
+ private String getMessage(String key) {
+ try {
+ return messageSource.getMessage(key, null, LocaleContextHolder.getLocale());
+ } catch (NoSuchMessageException e) {
+ return key;
+ }
+ }
@ExceptionHandler(BusinessException.class)
public Response> handleApiException(BusinessException e) {
- return Response.error(e.getErrorCode());
+ String message = messageSource.getMessage(
+ e.getErrorCode().getMessageKey(),
+ e.getArgs(),
+ LocaleContextHolder.getLocale()
+ );
+ return Response.error(e.getErrorCode(), message);
+ }
+
+ @ExceptionHandler(OptimisticLockException.class)
+ public Response handleOptimisticLockException(OptimisticLockException ex) {
+ log.warn("Optimistic lock exception", ex);
+ return Response.error(ResponseCode.OPTIMISTIC_LOCK_ERROR, getMessage("system.optimistic.lock.error"));
+ }
+
+ @ExceptionHandler(PessimisticLockException.class)
+ public Response handlePessimisticLockException(PessimisticLockException ex) {
+ log.warn("Pessimistic lock exception", ex);
+ return Response.error(ResponseCode.PESSIMISTIC_LOCK_ERROR, getMessage("system.pessimistic.lock.error"));
+ }
+
+ @ExceptionHandler(ConcurrentModificationException.class)
+ public Response handleConcurrentModificationException(ConcurrentModificationException ex) {
+ log.warn("Concurrent modification exception", ex);
+ return Response.error(ResponseCode.CONCURRENT_UPDATE_ERROR, getMessage("system.concurrent.update.error"));
}
@ExceptionHandler(Exception.class)
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/repository/IBaseRepository.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/repository/IBaseRepository.java
index cb334a58..a38423f7 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/framework/repository/IBaseRepository.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/repository/IBaseRepository.java
@@ -1,6 +1,7 @@
package com.qqchen.deploy.backend.framework.repository;
import com.qqchen.deploy.backend.framework.domain.Entity;
+import jakarta.persistence.LockModeType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.NoRepositoryBean;
@@ -8,6 +9,8 @@ import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
+import org.springframework.data.repository.query.Param;
+import org.springframework.data.jpa.repository.Lock;
import java.io.Serializable;
import java.util.List;
@@ -121,4 +124,49 @@ public interface IBaseRepository, ID extends Serializable>
iterable.forEach(result::add);
return result;
}
+
+ // 批量插入优化
+ @Modifying
+ @Query(value = "INSERT INTO #{#entityName} (id, create_time, create_by) VALUES (:id, :createTime, :createBy)",
+ nativeQuery = true)
+ void batchInsert(
+ @Param("id") ID id,
+ @Param("createTime") LocalDateTime createTime,
+ @Param("createBy") String createBy
+ );
+
+ // 批量更新优化
+ @Modifying
+ @Query("UPDATE #{#entityName} e SET e.updateTime = :updateTime, e.updateBy = :updateBy WHERE e.id IN :ids")
+ void batchUpdate(
+ @Param("ids") Collection ids,
+ @Param("updateTime") LocalDateTime updateTime,
+ @Param("updateBy") String updateBy
+ );
+
+ // 批量逻辑删除优化
+ @Modifying
+ @Query("UPDATE #{#entityName} e SET e.deleted = true, e.updateTime = :updateTime, e.updateBy = :updateBy WHERE e.id IN :ids")
+ void batchLogicDelete(
+ @Param("ids") Collection ids,
+ @Param("updateTime") LocalDateTime updateTime,
+ @Param("updateBy") String updateBy
+ );
+
+ // 添加悲观锁查询
+ @Lock(LockModeType.PESSIMISTIC_WRITE)
+ @Query("SELECT e FROM #{#entityName} e WHERE e.id = :id AND e.deleted = false")
+ Optional findByIdWithLock(@Param("id") ID id);
+
+ // 批量更新时添加版本控制
+ @Modifying
+ @Query("UPDATE #{#entityName} e SET e.updateTime = :updateTime, " +
+ "e.updateBy = :updateBy, e.version = e.version + 1 " +
+ "WHERE e.id IN :ids AND e.version = :version")
+ int batchUpdateWithVersion(
+ @Param("ids") Collection ids,
+ @Param("updateTime") LocalDateTime updateTime,
+ @Param("updateBy") String updateBy,
+ @Param("version") Integer version
+ );
}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/service/IBaseService.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/service/IBaseService.java
index 2f409c1e..ec3eee8f 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/framework/service/IBaseService.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/service/IBaseService.java
@@ -24,4 +24,8 @@ public interface IBaseService, ID extends Serializable> {
List findAll(BaseQuery query);
Page page(BaseQuery query);
+
+ void batchProcess(List entities);
+
+ T updateWithRetry(T entity);
}
\ No newline at end of file
diff --git a/backend/src/main/java/com/qqchen/deploy/backend/framework/service/impl/BaseServiceImpl.java b/backend/src/main/java/com/qqchen/deploy/backend/framework/service/impl/BaseServiceImpl.java
index 92972191..4ae573af 100644
--- a/backend/src/main/java/com/qqchen/deploy/backend/framework/service/impl/BaseServiceImpl.java
+++ b/backend/src/main/java/com/qqchen/deploy/backend/framework/service/impl/BaseServiceImpl.java
@@ -5,7 +5,9 @@ import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
+import com.google.common.collect.Lists;
import com.qqchen.deploy.backend.framework.annotation.LogicDelete;
import com.qqchen.deploy.backend.framework.domain.Entity;
import com.qqchen.deploy.backend.framework.enums.QueryType;
@@ -16,28 +18,41 @@ import com.qqchen.deploy.backend.framework.repository.IBaseRepository;
import com.qqchen.deploy.backend.framework.annotation.QueryField;
import com.qqchen.deploy.backend.framework.service.IBaseService;
import com.qqchen.deploy.backend.framework.utils.EntityPathResolver;
+import com.qqchen.deploy.backend.framework.security.SecurityUtils;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.*;
+import com.qqchen.deploy.backend.framework.exception.EntityNotFoundException;
+import jakarta.persistence.OptimisticLockException;
+import lombok.extern.slf4j.Slf4j;
import org.springframework.core.GenericTypeResolver;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
+import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
+import org.springframework.retry.annotation.Backoff;
+import org.springframework.retry.annotation.Retryable;
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.PersistenceContext;
import java.io.Serializable;
import java.lang.reflect.Field;
-@Transactional
+@Transactional(readOnly = true)
+@Slf4j
public abstract class BaseServiceImpl, ID extends Number & Serializable> implements IBaseService {
protected final IBaseRepository repository;
protected final EntityPath entityPath;
+ @PersistenceContext
+ protected EntityManager entityManager;
+
protected BaseServiceImpl(IBaseRepository repository) {
this.repository = repository;
this.entityPath = getEntityPath();
@@ -234,7 +249,7 @@ public abstract class BaseServiceImpl, ID extends Number &
return new BigDecimal(strValue);
}
} catch (NumberFormatException e) {
- // 忽略转换错误
+ // 忽略转换误
}
return null;
}
@@ -260,8 +275,13 @@ public abstract class BaseServiceImpl, ID extends Number &
return repository.save(entity);
}
+ @Transactional
@Override
public T update(T entity) {
+ // 先查询最新版本
+ T currentEntity = findById(entity.getId());
+ // 版本检查
+ currentEntity.checkVersion(entity.getVersion());
return repository.save(entity);
}
@@ -273,7 +293,7 @@ public abstract class BaseServiceImpl, ID extends Number &
@Override
public T findById(ID id) {
return repository.findById(id)
- .orElseThrow(() -> new RuntimeException("Entity not found"));
+ .orElseThrow(() -> new EntityNotFoundException(id));
}
@Override
@@ -312,4 +332,72 @@ public abstract class BaseServiceImpl, ID extends Number &
iterable.forEach(result::add);
return result;
}
+
+ @Transactional(propagation = Propagation.REQUIRES_NEW)
+ @Override
+ public void batchProcess(List entities) {
+ LocalDateTime now = LocalDateTime.now();
+ String operator = SecurityUtils.getCurrentUsername();
+
+ Lists.partition(entities, 500).forEach(batch -> {
+ try {
+ // 加锁查询最新数据
+ List currentEntities = batch.stream()
+ .map(e -> repository.findById(e.getId())
+ .orElseThrow(() -> new EntityNotFoundException(getEntityClass().getSimpleName(), e.getId())))
+ .collect(Collectors.toList());
+
+ // 版本检查
+ for (int i = 0; i < batch.size(); i++) {
+ currentEntities.get(i).checkVersion(batch.get(i).getVersion());
+ }
+
+ // 批量更新
+ List ids = currentEntities.stream()
+ .map(Entity::getId)
+ .collect(Collectors.toList());
+
+ repository.batchUpdate(ids, now, operator);
+ repository.flush();
+ entityManager.clear();
+ } catch (OptimisticLockException e) {
+ // 记录失败的批次
+ log.error("Batch update failed for batch size: {}", batch.size(), e);
+ throw e;
+ }
+ });
+ }
+
+ @Transactional
+ @Retryable(
+ value = {OptimisticLockException.class},
+ maxAttempts = 3,
+ backoff = @Backoff(delay = 1000, multiplier = 2)
+ )
+ @Override
+ public T updateWithRetry(T entity) {
+ try {
+ return update(entity);
+ } catch (OptimisticLockException e) {
+ // 重试前先刷新实体
+ entityManager.refresh(entity);
+ throw e; // 抛出异常触发重试
+ }
+ }
+
+ // 添加悲观锁查询方法
+ @Transactional
+ public T findByIdWithLock(ID id) {
+ return repository.findByIdWithLock(id)
+ .orElseThrow(() -> new EntityNotFoundException(getEntityClass().getSimpleName(), id));
+ }
+
+ @SuppressWarnings("unchecked")
+ protected Class getEntityClass() {
+ Class>[] genericTypes = GenericTypeResolver.resolveTypeArguments(getClass(), BaseServiceImpl.class);
+ if (genericTypes != null && genericTypes.length > 0) {
+ return (Class) genericTypes[0];
+ }
+ throw new IllegalStateException("Could not resolve entity class");
+ }
}
\ No newline at end of file
diff --git a/backend/src/main/resources/messages.properties b/backend/src/main/resources/messages.properties
index dc8b4376..a61c74c1 100644
--- a/backend/src/main/resources/messages.properties
+++ b/backend/src/main/resources/messages.properties
@@ -1,18 +1,29 @@
-# \u4E2D\u6587
-response.success=\u64CD\u4F5C\u6210\u529F
-response.error=\u7CFB\u7EDF\u9519\u8BEF
-response.invalid.param=\u65E0\u6548\u7684\u53C2\u6570
-response.unauthorized=\u672A\u6388\u6743
-response.forbidden=\u7981\u6B62\u8BBF\u95EE
-response.not.found=\u8D44\u6E90\u672A\u627E\u5230
-response.conflict=\u8D44\u6E90\u51B2\u7A81
-response.unauthorized.full=\u8BBF\u95EE\u6B64\u8D44\u6E90\u9700\u8981\u5B8C\u5168\u8EAB\u4EFD\u9A8C\u8BC1
+# 通用响应
+response.success=操作成功
+response.error=系统错误
+response.invalid.param=无效的参数
+response.unauthorized=未授权
+response.forbidden=禁止访问
+response.not.found=资源未找到
+response.conflict=资源冲突
+response.unauthorized.full=访问此资源需要完全身份验证
+# 业务错误
+tenant.not.found=租户不存在
+data.not.found=找不到ID为{0}的{1}
-tenant.not.found=\u79DF\u6237\u4E0D\u5B58\u5728
+# 用户相关
+user.not.found=用户不存在
+user.username.exists=用户名已存在
+user.email.exists=邮箱已存在
-user.not.found=\u7528\u6237\u4E0D\u5B58\u5728
-user.username.exists=\u7528\u6237\u540D\u5DF2\u5B58\u5728
-user.email.exists=\u90AE\u7BB1\u5DF2\u5B58\u5728
+# 系统异常消息
+system.optimistic.lock.error=数据已被其他用户修改,请刷新后重试
+system.pessimistic.lock.error=数据正被其他用户操作,请稍后重试
+system.concurrent.update.error=并发更新冲突,请重试
+system.retry.exceeded.error=操作重试次数超限,请稍后再试
-data.not.found=数据不存在
\ No newline at end of file
+# Entity Not Found Messages
+entity.not.found.id=找不到ID为{0}的实体
+entity.not.found.message={0}
+entity.not.found.name.id=找不到ID为{1}的{0}
\ No newline at end of file
diff --git a/backend/src/main/resources/messages_en.properties b/backend/src/main/resources/messages_en.properties
index 414c139f..ff38898e 100644
--- a/backend/src/main/resources/messages_en.properties
+++ b/backend/src/main/resources/messages_en.properties
@@ -1,4 +1,4 @@
-# \u9ED8\u8BA4\u8BED\u8A00\uFF08\u82F1\u6587\uFF09
+# Common Response
response.success=Success
response.error=System Error
response.invalid.param=Invalid Parameter
@@ -8,11 +8,22 @@ response.not.found=Resource Not Found
response.conflict=Resource Conflict
response.unauthorized.full=Full authentication is required to access this resource
-
+# Business Error
tenant.not.found=Tenant not found
+data.not.found={0} with id {1} not found
+# User Related
user.not.found=User not found
user.username.exists=Username already exists
user.email.exists=Email already exists
-data.not.found=Data not found
\ No newline at end of file
+# System Exception Messages
+system.optimistic.lock.error=Data has been modified by another user, please refresh and try again
+system.pessimistic.lock.error=Data is being operated by another user, please try again later
+system.concurrent.update.error=Concurrent update conflict, please try again
+system.retry.exceeded.error=Operation retry limit exceeded, please try again later
+
+# Entity Not Found Messages
+entity.not.found.id=Entity with id {0} not found
+entity.not.found.message={0}
+entity.not.found.name.id={0} with id {1} not found
\ No newline at end of file
diff --git a/backend/src/main/resources/messages_zh_CN.properties b/backend/src/main/resources/messages_zh_CN.properties
index da4c906b..6069cbab 100644
--- a/backend/src/main/resources/messages_zh_CN.properties
+++ b/backend/src/main/resources/messages_zh_CN.properties
@@ -1,16 +1,29 @@
-# \u4E2D\u6587
-response.success=\u64CD\u4F5C\u6210\u529F
-response.error=\u7CFB\u7EDF\u9519\u8BEF
-response.invalid.param=\u65E0\u6548\u7684\u53C2\u6570
-response.unauthorized=\u672A\u6388\u6743
-response.forbidden=\u7981\u6B62\u8BBF\u95EE
-response.not.found=\u8D44\u6E90\u672A\u627E\u5230
-response.conflict=\u8D44\u6E90\u51B2\u7A81
-response.unauthorized.full=\u8BBF\u95EE\u6B64\u8D44\u6E90\u9700\u8981\u5B8C\u5168\u8EAB\u4EFD\u9A8C\u8BC1
+# 通用响应
+response.success=操作成功
+response.error=系统错误
+response.invalid.param=无效的参数
+response.unauthorized=未授权
+response.forbidden=禁止访问
+response.not.found=资源未找到
+response.conflict=资源冲突
+response.unauthorized.full=访问此资源需要完全身份验证
-tenant.not.found=\u79DF\u6237\u4E0D\u5B58\u5728
+# 业务错误
+tenant.not.found=租户不存在
+data.not.found=数据不存在
-user.not.found=\u7528\u6237\u4E0D\u5B58\u5728
-user.username.exists=\u7528\u6237\u540D\u5DF2\u5B58\u5728
-user.email.exists=\u90AE\u7BB1\u5DF2\u5B58\u5728
-data.not.found=数据不存在
\ No newline at end of file
+# 用户相关
+user.not.found=用户不存在
+user.username.exists=用户名已存在
+user.email.exists=邮箱已存在
+
+# 系统异常消息
+system.optimistic.lock.error=数据已被其他用户修改,请刷新后重试
+system.pessimistic.lock.error=数据正被其他用户操作,请稍后重试
+system.concurrent.update.error=并发更新冲突,请重试
+system.retry.exceeded.error=操作重试次数超限,请稍后再试
+
+# Entity Not Found Messages
+entity.not.found.id=找不到ID为{0}的实体
+entity.not.found.message={0}
+entity.not.found.name.id=找不到ID为{1}的{0}
\ No newline at end of file