diff --git a/src/main/java/clap/server/adapter/inbound/web/admin/addCategoryController.java b/src/main/java/clap/server/adapter/inbound/web/admin/AddCategoryController.java similarity index 59% rename from src/main/java/clap/server/adapter/inbound/web/admin/addCategoryController.java rename to src/main/java/clap/server/adapter/inbound/web/admin/AddCategoryController.java index bcf5bec7..36a8a6f4 100644 --- a/src/main/java/clap/server/adapter/inbound/web/admin/addCategoryController.java +++ b/src/main/java/clap/server/adapter/inbound/web/admin/AddCategoryController.java @@ -1,36 +1,39 @@ package clap.server.adapter.inbound.web.admin; import clap.server.adapter.inbound.security.SecurityUserDetails; +import clap.server.adapter.inbound.web.dto.AddSubCategoryRequest; import clap.server.adapter.inbound.web.dto.admin.AddMainCategoryRequest; import clap.server.application.port.inbound.management.AddMainCategoryUsecase; +import clap.server.application.port.inbound.management.AddSubCategoryUsecase; import clap.server.common.annotation.architecture.WebAdapter; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.security.access.annotation.Secured; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @Tag(name = "카테고리 추가") @WebAdapter @RequiredArgsConstructor -public class addCategoryController { +public class AddCategoryController { private final AddMainCategoryUsecase addMainCategoryUsecase; + private final AddSubCategoryUsecase addSubCategoryUsecase; @Operation(summary = "1차 카테고리 추가") @PostMapping("/api/maincategory") @Secured("ROLE_ADMIN") - public void addMainCategory(@AuthenticationPrincipal SecurityUserDetails userInfo, @Validated @RequestBody AddMainCategoryRequest addMainCategoryRequest) { + public void addMainCategory(@AuthenticationPrincipal SecurityUserDetails userInfo, @Valid @RequestBody AddMainCategoryRequest addMainCategoryRequest) { addMainCategoryUsecase.addMainCategory(userInfo.getUserId(), addMainCategoryRequest.code(), addMainCategoryRequest.name()); } -// @Operation(summary = "2차 카테고리 추가") -// @PostMapping("/api/subcategory") -// @Secured("ROLE_ADMIN") -// public void addSubCategory(@Validated @RequestBody AddCategoryRequest addCategoryRequest) { -// addMainCategoryUsecase.addSubCategory(addCategoryRequest.code(), addCategoryRequest.name()); -// } + @Operation(summary = "2차 카테고리 추가") + @PostMapping("/api/subcategory") + @Secured("ROLE_ADMIN") + public void addSubCategory(@AuthenticationPrincipal SecurityUserDetails userInfo, @Valid @RequestBody AddSubCategoryRequest addCategoryRequest) { + addSubCategoryUsecase.addSubCategory(userInfo.getUserId(), addCategoryRequest.mainCategoryId(), addCategoryRequest.code(), addCategoryRequest.name()); + } } \ No newline at end of file diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/AddSubCategoryRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/AddSubCategoryRequest.java new file mode 100644 index 00000000..e5dab089 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/AddSubCategoryRequest.java @@ -0,0 +1,15 @@ +package clap.server.adapter.inbound.web.dto; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import org.hibernate.validator.constraints.Length; + +public record AddSubCategoryRequest( + @Min(1) + Long mainCategoryId, + @NotBlank @Length(max = 20) + String name, + @NotBlank @Pattern(regexp = "^[A-Z]{1,2}$", message = "올바른 카테고리 코드 형식이 아닙니다.") + String code) { +} diff --git a/src/main/java/clap/server/application/port/inbound/management/AddSubCategoryUsecase.java b/src/main/java/clap/server/application/port/inbound/management/AddSubCategoryUsecase.java new file mode 100644 index 00000000..314d04ad --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/management/AddSubCategoryUsecase.java @@ -0,0 +1,6 @@ +package clap.server.application.port.inbound.management; + + +public interface AddSubCategoryUsecase { + void addSubCategory(Long adminId, Long mainCategoryId, String code, String name); +} diff --git a/src/main/java/clap/server/application/service/admin/AddSubCategoryService.java b/src/main/java/clap/server/application/service/admin/AddSubCategoryService.java new file mode 100644 index 00000000..0b6dfdd0 --- /dev/null +++ b/src/main/java/clap/server/application/service/admin/AddSubCategoryService.java @@ -0,0 +1,36 @@ +package clap.server.application.service.admin; + +import clap.server.application.port.inbound.management.AddSubCategoryUsecase; +import clap.server.application.port.outbound.member.LoadMemberPort; +import clap.server.application.port.outbound.task.CommandCategoryPort; +import clap.server.application.port.outbound.task.LoadCategoryPort; +import clap.server.common.annotation.architecture.ApplicationService; +import clap.server.domain.model.member.Member; +import clap.server.domain.model.task.Category; +import clap.server.exception.ApplicationException; +import lombok.RequiredArgsConstructor; + +import java.util.Optional; + +import static clap.server.exception.code.MemberErrorCode.ACTIVE_MEMBER_NOT_FOUND; +import static clap.server.exception.code.TaskErrorCode.CATEGORY_NOT_FOUND; + +@ApplicationService +@RequiredArgsConstructor +public class AddSubCategoryService implements AddSubCategoryUsecase { + private final CommandCategoryPort commandCategoryPort; + private final LoadMemberPort loadMemberPort; + private final LoadCategoryPort loadCategoryPort; + + @Override + public void addSubCategory(Long adminId, Long mainCategoryId, String code, String name) { + Optional activeMember = loadMemberPort.findActiveMemberById(adminId); + Optional mainCategory = loadCategoryPort.findById(mainCategoryId); + + Category subCategory = Category.createSubCategory( + activeMember.orElseThrow(() -> new ApplicationException(ACTIVE_MEMBER_NOT_FOUND)), + mainCategory.orElseThrow(() -> new ApplicationException(CATEGORY_NOT_FOUND)), + code, name); + commandCategoryPort.save(subCategory); + } +} diff --git a/src/main/java/clap/server/domain/model/task/Category.java b/src/main/java/clap/server/domain/model/task/Category.java index 36a87207..6cc479de 100644 --- a/src/main/java/clap/server/domain/model/task/Category.java +++ b/src/main/java/clap/server/domain/model/task/Category.java @@ -24,11 +24,20 @@ public class Category extends BaseTime { private LocalDateTime updatedAt; public static Category createMainCategory(Member admin, String code, String name) { - Category category = new Category(); - category.admin = admin; - category.code = code; - category.name = name; - return category; + return Category.builder() + .admin(admin) + .code(code) + .name(name) + .build(); + } + + public static Category createSubCategory(Member admin, Category mainCategory, String code, String name) { + return Category.builder() + .mainCategory(mainCategory) + .admin(admin) + .code(code) + .name(name) + .build(); } } diff --git a/src/test/java/clap/server/application/service/admin/AddCategoryServiceTest.java b/src/test/java/clap/server/application/service/admin/AddCategoryServiceTest.java new file mode 100644 index 00000000..b35c58fb --- /dev/null +++ b/src/test/java/clap/server/application/service/admin/AddCategoryServiceTest.java @@ -0,0 +1,82 @@ +package clap.server.application.service.admin; + +import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; +import clap.server.adapter.outbound.persistense.entity.member.constant.MemberRole; +import clap.server.adapter.outbound.persistense.entity.member.constant.MemberStatus; +import clap.server.adapter.outbound.persistense.entity.task.CategoryEntity; +import jakarta.persistence.EntityManager; +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.Rollback; +import org.springframework.test.context.DynamicPropertyRegistry; +import org.springframework.test.context.DynamicPropertySource; +import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +@Testcontainers +@Transactional +class AddCategoryServiceTest { + + @Container + public static ElasticsearchContainer ES_CONTAINER = new ElasticsearchContainer("docker.elastic.co/elasticsearch/elasticsearch:7.17.5") + .withReuse(true); + + @DynamicPropertySource + static void elasticProperties(DynamicPropertyRegistry registry) { + // Elasticsearch 설정 + registry.add("spring.elasticsearch.uris", ES_CONTAINER::getHttpHostAddress); + } + + @Autowired + private AddMainCategoryService addMainCategoryService; + @Autowired + private AddSubCategoryService addsubCategoryService; + @Autowired + private EntityManager entityManager; + + @Test + @Transactional + @Rollback(false) + void addMainCategory() { + // 관리자 추가 + MemberEntity admin = MemberEntity.builder() + .name("Admin") + .email("admin@example.com") + .nickname("admin.ad") + .isReviewer(false) + .role(MemberRole.ROLE_ADMIN) + .departmentRole("Admin") + .status(MemberStatus.ACTIVE) + .password("admin123") + .build(); + entityManager.persist(admin); + + admin = entityManager.find(MemberEntity.class, 1); + addMainCategoryService.addMainCategory(admin.getMemberId(), "VM", "가상머신"); + + CategoryEntity category = entityManager.find(CategoryEntity.class, 1); + assertThat(category.getCategoryId()).isEqualTo(1); + assertThat(category.getName()).isEqualTo("가상머신"); + assertThat(category.getCode()).isEqualTo("VM"); + assertThat(category.getAdmin().getMemberId()).isEqualTo(1); + } + + @Test + void addSubCategory() { + MemberEntity admin = entityManager.find(MemberEntity.class, 1); + addsubCategoryService.addSubCategory(admin.getMemberId(), 1L, "CR", "생성"); + + CategoryEntity category = entityManager.find(CategoryEntity.class, 2); + assertThat(category.getCategoryId()).isEqualTo(2); + assertThat(category.getMainCategory().getCategoryId()).isEqualTo(1); + assertThat(category.getName()).isEqualTo("생성"); + assertThat(category.getCode()).isEqualTo("CR"); + assertThat(category.getAdmin().getMemberId()).isEqualTo(1); + } +} \ No newline at end of file