From 9d2fc024ea6817b494b77ceda159ad3bd911b6c4 Mon Sep 17 00:00:00 2001 From: siujamo Date: Tue, 24 Mar 2026 15:49:48 +0800 Subject: [PATCH] feat: add department name uniqueness check and corresponding error message --- .../helix/exception/BizException.java | 27 -------- .../helix/manager/DepartmentManager.java | 6 ++ .../repository/DepartmentRepository.java | 2 + .../helix/service/DepartmentService.java | 7 ++ .../onixbyte/helix/shared/MessageName.java | 2 + src/main/resources/i18n/messages.properties | 68 +++++++++++++------ .../resources/i18n/messages_en_GB.properties | 1 + .../resources/i18n/messages_zh_CN.properties | 1 + 8 files changed, 68 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/onixbyte/helix/exception/BizException.java b/src/main/java/com/onixbyte/helix/exception/BizException.java index 8e99c74..d864077 100644 --- a/src/main/java/com/onixbyte/helix/exception/BizException.java +++ b/src/main/java/com/onixbyte/helix/exception/BizException.java @@ -44,40 +44,13 @@ public class BizException extends RuntimeException { private final String messageCode; private final Object[] messageArgs; - /** - * Constructs a new business exception with the specified HTTP status and message. - * - * @param message the detailed error message explaining the business logic violation - */ - public BizException(String message) { - super(message); - this.status = HttpStatus.INTERNAL_SERVER_ERROR; - this.messageCode = null; - this.messageArgs = new Object[0]; - } - - /** - * Constructs a new business exception with the specified HTTP status and message. - * - * @param status the HTTP status code to associate with this exception - * @param message the detailed error message explaining the business logic violation - */ - public BizException(HttpStatus status, String message) { - super(message); - this.status = status; - this.messageCode = null; - this.messageArgs = new Object[0]; - } - public BizException(String messageCode, Object... messageArgs) { - super(messageCode); this.status = HttpStatus.INTERNAL_SERVER_ERROR; this.messageCode = messageCode; this.messageArgs = messageArgs == null ? new Object[0] : messageArgs; } public BizException(HttpStatus status, String messageCode, Object... messageArgs) { - super(messageCode); this.status = status; this.messageCode = messageCode; this.messageArgs = messageArgs == null ? new Object[0] : messageArgs; diff --git a/src/main/java/com/onixbyte/helix/manager/DepartmentManager.java b/src/main/java/com/onixbyte/helix/manager/DepartmentManager.java index c6f7b3a..1f07468 100644 --- a/src/main/java/com/onixbyte/helix/manager/DepartmentManager.java +++ b/src/main/java/com/onixbyte/helix/manager/DepartmentManager.java @@ -3,6 +3,8 @@ package com.onixbyte.helix.manager; import com.onixbyte.helix.domain.entity.Department; import com.onixbyte.helix.exception.BizException; import com.onixbyte.helix.repository.DepartmentRepository; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -68,4 +70,8 @@ public class DepartmentManager { departmentToEdit.setUpdatedAt(updatedAt); return departmentToEdit; } + + public boolean existsByName(String name) { + return departmentRepository.existsByName(name); + } } diff --git a/src/main/java/com/onixbyte/helix/repository/DepartmentRepository.java b/src/main/java/com/onixbyte/helix/repository/DepartmentRepository.java index 79a1849..49ad5c2 100644 --- a/src/main/java/com/onixbyte/helix/repository/DepartmentRepository.java +++ b/src/main/java/com/onixbyte/helix/repository/DepartmentRepository.java @@ -15,4 +15,6 @@ public interface DepartmentRepository extends JpaRepository { or d.parentId = :parentId """) Integer findMaxSort(Long parentId); + + boolean existsByName(String name); } diff --git a/src/main/java/com/onixbyte/helix/service/DepartmentService.java b/src/main/java/com/onixbyte/helix/service/DepartmentService.java index c13793b..5400e5d 100644 --- a/src/main/java/com/onixbyte/helix/service/DepartmentService.java +++ b/src/main/java/com/onixbyte/helix/service/DepartmentService.java @@ -4,10 +4,13 @@ import com.onixbyte.helix.domain.common.TreeNode; import com.onixbyte.helix.domain.entity.Department; import com.onixbyte.helix.domain.web.request.DepartmentRequest; import com.onixbyte.helix.enumeration.Status; +import com.onixbyte.helix.exception.BizException; import com.onixbyte.helix.manager.DepartmentManager; +import com.onixbyte.helix.shared.MessageName; import com.onixbyte.helix.utils.TreeUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -38,6 +41,10 @@ public class DepartmentService { public Department addDepartment(DepartmentRequest request) { var createdAt = LocalDateTime.now(); + if (departmentManager.existsByName(request.name())) { + throw new BizException(HttpStatus.CONFLICT, MessageName.REQUEST_CREATE_DEPARTMENT_NAME_DUPLICATED); + } + var parentId = request.parentId(); var sort = Optional.ofNullable(request.sort()) .orElseGet(() -> departmentManager.getNextSort(parentId)); diff --git a/src/main/java/com/onixbyte/helix/shared/MessageName.java b/src/main/java/com/onixbyte/helix/shared/MessageName.java index c872f02..7c33f66 100644 --- a/src/main/java/com/onixbyte/helix/shared/MessageName.java +++ b/src/main/java/com/onixbyte/helix/shared/MessageName.java @@ -79,4 +79,6 @@ public class MessageName { public static final String REQUEST_QUERY_ROLE_STATUS_INVALID = "request.query-role.status.invalid"; public static final String REQUEST_QUERY_USER_STATUS_INVALID = "request.query-user.status.invalid"; + + public static final String REQUEST_CREATE_DEPARTMENT_NAME_DUPLICATED = "request.create-department.name-duplicated"; } diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties index a204e38..f4a9f5b 100644 --- a/src/main/resources/i18n/messages.properties +++ b/src/main/resources/i18n/messages.properties @@ -19,28 +19,58 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. # -tree.multiple-roots= -user.not-found=User with ID [{0}] not found. -security.context-user-not-found=Cannot retrieve user information from security context. -role.not-exists=Role does not exist in database. -captcha.generate-failed= -auth.provider.password-not-configured=Username or password is incorrect. -auth.provider.bad-credentials=Username or password is incorrect. -auth.provider.failed=Authentication failed, please try again later. -auth.login.failed=Cannot perform login due to server error. -auth.login.captcha-incorrect=Captcha is incorrect. -auth.login.captcha-not-found=Captcha not found. -authority.not-found=Authority with ID [{0}] not found. -authority.code-used=Authority code [{0}] is already in use. -asset.upload-failed=Failed to upload file: {0} -asset.delete-forbidden=You are not able to delete an asset that is not uploaded by you. -asset.invalid-prefix=Prefix must not be empty and must not start with '/' or '..'. + asset.not-empty = File cannot be empty. +asset.invalid-prefix = Prefix must not be empty and must not start with '/' or '..'. +asset.delete-forbidden = You are not able to delete an asset that is not uploaded by you. +asset.upload-failed = Failed to upload file: {0} authority.deleted = Authority [{0}] deleted. +authority.code-used = Authority code [{0}] is already in use. +authority.not-found = Authority with ID [{0}] not found. + +auth.login.captcha-not-found = Captcha not found. +auth.login.captcha-incorrect = Captcha is incorrect. +auth.login.failed = Cannot perform login due to server error. + +auth.provider.failed = Authentication failed, please try again later. +auth.provider.bad-credentials = Username or password is incorrect. +auth.provider.password-not-configured=Username or password is incorrect. + +captcha.generate-failed = Unable to generate captcha image. +role.not-exists=Name of the authority cannot be null. +role.not-found = Role with ID {0} not found. +role.deleted = Role [{0}] deleted. user.password-reset-success = Password has been reset. user.deleted = User [{0}] deleted. -role.not-found=Role with ID {0} not found. -role.deleted=Role [{0}] deleted. -request.role.name.not-empty=Name of the role cannot be empty. \ No newline at end of file +user.not-found = User with ID [{0}] not found. + +security.context-user-not-found = Cannot retrieve user information from security context. +tree.multiple-roots = Multiple root items found in given values. + +request.add-user.username.not-empty = Username cannot be empty. +request.add-user.password.not-empty = Password cannot be empty. +request.add-user.full-name.not-empty = Full name cannot be empty. + +request.authority.code.not-editable = Code cannot be edited. +request.authority.code.not-null = Code cannot be null. +request.authority.name.not-null = Name of the authority cannot be null. + +request.department.name.not-null = Name of the department should not be null. + +request.edit-role.id.not-null = Role ID cannot be null. +request.edit-role.status.invalid = Status can only be ACTIVE or INACTIVE. + +request.edit-user.id.not-null = User ID cannot be null. +request.edit-user.id.positive = User ID must be positive. + +request.reset-password.password.not-empty = Password cannot be empty. + +request.role.name.not-empty = Name of the role cannot be empty. +request.role.code.not-empty = Code of the role cannot be empty. +request.role.sort.not-null = Sort number cannot be null. + +request.query-role.status.invalid = Status can only be ACTIVE or INACTIVE. +request.query-user.status.invalid = Status can only be ACTIVE, INACTIVE, or LOCKED. +request.create-department.name-duplicated=Name of the department already exists. diff --git a/src/main/resources/i18n/messages_en_GB.properties b/src/main/resources/i18n/messages_en_GB.properties index b5a19db..ef1634a 100644 --- a/src/main/resources/i18n/messages_en_GB.properties +++ b/src/main/resources/i18n/messages_en_GB.properties @@ -74,3 +74,4 @@ request.role.sort.not-null = Sort number cannot be null. request.query-role.status.invalid = Status can only be ACTIVE or INACTIVE. request.query-user.status.invalid = Status can only be ACTIVE, INACTIVE, or LOCKED. +request.create-department.name-duplicated=Name of the department already exists. diff --git a/src/main/resources/i18n/messages_zh_CN.properties b/src/main/resources/i18n/messages_zh_CN.properties index 9c61320..7bb0fdb 100644 --- a/src/main/resources/i18n/messages_zh_CN.properties +++ b/src/main/resources/i18n/messages_zh_CN.properties @@ -74,3 +74,4 @@ request.role.sort.not-null = 排序编号不能为空。 request.query-role.status.invalid = 状态只能是 ACTIVE 或 INACTIVE。 request.query-user.status.invalid = 状态只能是 ACTIVE、INACTIVE 或 LOCKED。 +request.create-department.name-duplicated=部门名称已存在。