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 index 1384546e..da832d07 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,8 +1,8 @@ package clap.server.adapter.inbound.web.admin; import clap.server.adapter.inbound.security.SecurityUserDetails; -import clap.server.adapter.inbound.web.dto.admin.AddSubCategoryRequest; import clap.server.adapter.inbound.web.dto.admin.AddMainCategoryRequest; +import clap.server.adapter.inbound.web.dto.admin.AddSubCategoryRequest; import clap.server.application.port.inbound.admin.AddCategoryUsecase; import clap.server.common.annotation.architecture.WebAdapter; import io.swagger.v3.oas.annotations.Operation; @@ -18,7 +18,7 @@ @Tag(name = "카테고리 추가") @WebAdapter @RequiredArgsConstructor -@RequestMapping("/api/managements") +@RequestMapping("/api/management") public class AddCategoryController { private final AddCategoryUsecase addCategoryUsecase; diff --git a/src/main/java/clap/server/adapter/inbound/web/admin/FindCategoryController.java b/src/main/java/clap/server/adapter/inbound/web/admin/FindCategoryController.java new file mode 100644 index 00000000..ea1d7725 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/admin/FindCategoryController.java @@ -0,0 +1,45 @@ +package clap.server.adapter.inbound.web.admin; + +import clap.server.adapter.inbound.web.dto.admin.FindAllCategoryResponse; +import clap.server.adapter.inbound.web.dto.admin.FindMainCategoryResponse; +import clap.server.adapter.inbound.web.dto.admin.FindSubCategoryResponse; +import clap.server.application.port.inbound.admin.FindAllCategoryUsecase; +import clap.server.application.port.inbound.admin.FindMainCategoryUsecase; +import clap.server.application.port.inbound.admin.FindSubCategoryUsecase; +import clap.server.common.annotation.architecture.WebAdapter; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.util.List; + +@Tag(name = "카테고리 조회") +@WebAdapter +@RequiredArgsConstructor +@RequestMapping("/api") +public class FindCategoryController { + private final FindAllCategoryUsecase findAllCategoryUsecase; + private final FindMainCategoryUsecase findmainCategoryUsecase; + private final FindSubCategoryUsecase findsubCategoryUsecase; + + @Operation(summary = "모든 카테고리 조회") + @GetMapping("/category") + public ResponseEntity findAllCategory() { + return ResponseEntity.ok(findAllCategoryUsecase.findAllCategory()); + } + + @Operation(summary = "1차 카테고리 조회") + @GetMapping("/main-category") + public ResponseEntity> findMainCategory() { + return ResponseEntity.ok(findmainCategoryUsecase.findMainCategory()); + } + + @Operation(summary = "2차 카테고리 조회") + @GetMapping("/sub-category") + public ResponseEntity> findSubCategory() { + return ResponseEntity.ok(findsubCategoryUsecase.findSubCategory()); + } +} diff --git a/src/main/java/clap/server/adapter/inbound/web/admin/UpdateCategoryController.java b/src/main/java/clap/server/adapter/inbound/web/admin/UpdateCategoryController.java new file mode 100644 index 00000000..0a690b6f --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/admin/UpdateCategoryController.java @@ -0,0 +1,30 @@ +package clap.server.adapter.inbound.web.admin; + +import clap.server.adapter.inbound.security.SecurityUserDetails; +import clap.server.adapter.inbound.web.dto.admin.UpdateCategoryRequest; +import clap.server.application.port.inbound.admin.UpdateCategoryUsecase; +import clap.server.common.annotation.architecture.WebAdapter; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +@Tag(name = "카테고리 수정") +@WebAdapter +@RequiredArgsConstructor +@RequestMapping("/api/management") +public class UpdateCategoryController { + private final UpdateCategoryUsecase updateCategoryUsecase; + + @Operation(summary = "카테고리 수정") + @PatchMapping("/categories/categoryId") + @Secured("ROLE_ADMIN") + public void updateCategory(@AuthenticationPrincipal SecurityUserDetails userInfo, Long categoryId, + @RequestBody UpdateCategoryRequest updateCategoryRequest) { + updateCategoryUsecase.updateCategory(userInfo.getUserId(), categoryId, updateCategoryRequest.name(), updateCategoryRequest.code()); + } +} \ No newline at end of file diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/admin/FindAllCategoryResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/admin/FindAllCategoryResponse.java new file mode 100644 index 00000000..74dc547a --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/admin/FindAllCategoryResponse.java @@ -0,0 +1,9 @@ +package clap.server.adapter.inbound.web.dto.admin; + +import java.util.List; + +public record FindAllCategoryResponse( + List mainCategory, + List subCategory +) { +} diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/admin/FindMainCategoryResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/admin/FindMainCategoryResponse.java new file mode 100644 index 00000000..3920fe31 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/admin/FindMainCategoryResponse.java @@ -0,0 +1,8 @@ +package clap.server.adapter.inbound.web.dto.admin; + +public record FindMainCategoryResponse( + Long id, + String name, + String code +) { +} diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/admin/FindSubCategoryResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/admin/FindSubCategoryResponse.java new file mode 100644 index 00000000..0372dd42 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/admin/FindSubCategoryResponse.java @@ -0,0 +1,9 @@ +package clap.server.adapter.inbound.web.dto.admin; + +public record FindSubCategoryResponse( + Long id, + Long mainCategoryId, + String name, + String code +) { +} diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/admin/UpdateCategoryRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/admin/UpdateCategoryRequest.java new file mode 100644 index 00000000..fb9afd10 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/admin/UpdateCategoryRequest.java @@ -0,0 +1,13 @@ +package clap.server.adapter.inbound.web.dto.admin; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import org.hibernate.validator.constraints.Length; + +public record UpdateCategoryRequest( + @NotBlank @Length(max = 20) + String name, + @NotBlank @Pattern(regexp = "^[A-Z]{1,2}$", message = "올바른 카테고리 코드 형식이 아닙니다.") + String code +) { +} \ No newline at end of file diff --git a/src/main/java/clap/server/adapter/outbound/persistense/CategoryPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/CategoryPersistenceAdapter.java index 2caa61d4..5fa85209 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/CategoryPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/CategoryPersistenceAdapter.java @@ -9,6 +9,7 @@ import clap.server.domain.model.task.Category; import lombok.RequiredArgsConstructor; +import java.util.List; import java.util.Optional; @PersistenceAdapter @@ -24,6 +25,30 @@ public Optional findById(Long id) { return categoryEntity.map(categoryPersistenceMapper::toDomain); } + @Override + public List findAll() { + return categoryRepository.findByIsDeletedFalse() + .stream() + .map(categoryPersistenceMapper::toDomain) + .toList(); + } + + @Override + public List findMainCategory() { + return categoryRepository.findByIsDeletedFalseAndMainCategoryIsNull() + .stream() + .map(categoryPersistenceMapper::toDomain) + .toList(); + } + + @Override + public List findSubCategory() { + return categoryRepository.findByIsDeletedFalseAndMainCategoryIsNotNull() + .stream() + .map(categoryPersistenceMapper::toDomain) + .toList(); + } + @Override public void save(Category category) { categoryRepository.save(categoryPersistenceMapper.toEntity(category)); diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/CategoryRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/CategoryRepository.java index dce2b14c..017e760e 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/CategoryRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/CategoryRepository.java @@ -3,6 +3,12 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface CategoryRepository extends JpaRepository { + + List findByIsDeletedFalse(); + List findByIsDeletedFalseAndMainCategoryIsNull(); + List findByIsDeletedFalseAndMainCategoryIsNotNull(); } \ No newline at end of file diff --git a/src/main/java/clap/server/application/mapper/response/CategoryResponseMapper.java b/src/main/java/clap/server/application/mapper/response/CategoryResponseMapper.java new file mode 100644 index 00000000..19b780fd --- /dev/null +++ b/src/main/java/clap/server/application/mapper/response/CategoryResponseMapper.java @@ -0,0 +1,25 @@ +package clap.server.application.mapper.response; + +import clap.server.adapter.inbound.web.dto.admin.FindAllCategoryResponse; +import clap.server.adapter.inbound.web.dto.admin.FindMainCategoryResponse; +import clap.server.adapter.inbound.web.dto.admin.FindSubCategoryResponse; +import clap.server.domain.model.task.Category; + +import java.util.List; + +public class CategoryResponseMapper { + + public static FindAllCategoryResponse toFindAllCategoryResponse( + List mainCategoryResponses, + List subCategoryResponses) { + return new FindAllCategoryResponse(mainCategoryResponses, subCategoryResponses); + } + + public static FindMainCategoryResponse toFindMainCategoryResponse(Category category) { + return new FindMainCategoryResponse(category.getCategoryId(), category.getName(), category.getCode()); + } + + public static FindSubCategoryResponse toFindSubCategoryResponse(Category category) { + return new FindSubCategoryResponse(category.getCategoryId(), category.getMainCategory().getCategoryId(), category.getName(), category.getCode()); + } +} diff --git a/src/main/java/clap/server/application/port/inbound/admin/FindAllCategoryUsecase.java b/src/main/java/clap/server/application/port/inbound/admin/FindAllCategoryUsecase.java new file mode 100644 index 00000000..e9c3c26d --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/admin/FindAllCategoryUsecase.java @@ -0,0 +1,7 @@ +package clap.server.application.port.inbound.admin; + +import clap.server.adapter.inbound.web.dto.admin.FindAllCategoryResponse; + +public interface FindAllCategoryUsecase { + FindAllCategoryResponse findAllCategory(); +} diff --git a/src/main/java/clap/server/application/port/inbound/admin/FindMainCategoryUsecase.java b/src/main/java/clap/server/application/port/inbound/admin/FindMainCategoryUsecase.java new file mode 100644 index 00000000..42c654d6 --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/admin/FindMainCategoryUsecase.java @@ -0,0 +1,9 @@ +package clap.server.application.port.inbound.admin; + +import clap.server.adapter.inbound.web.dto.admin.FindMainCategoryResponse; + +import java.util.List; + +public interface FindMainCategoryUsecase { + List findMainCategory(); +} diff --git a/src/main/java/clap/server/application/port/inbound/admin/FindSubCategoryUsecase.java b/src/main/java/clap/server/application/port/inbound/admin/FindSubCategoryUsecase.java new file mode 100644 index 00000000..4922669b --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/admin/FindSubCategoryUsecase.java @@ -0,0 +1,9 @@ +package clap.server.application.port.inbound.admin; + +import clap.server.adapter.inbound.web.dto.admin.FindSubCategoryResponse; + +import java.util.List; + +public interface FindSubCategoryUsecase { + List findSubCategory(); +} diff --git a/src/main/java/clap/server/application/port/inbound/admin/UpdateCategoryUsecase.java b/src/main/java/clap/server/application/port/inbound/admin/UpdateCategoryUsecase.java new file mode 100644 index 00000000..a4c7b5f6 --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/admin/UpdateCategoryUsecase.java @@ -0,0 +1,5 @@ +package clap.server.application.port.inbound.admin; + +public interface UpdateCategoryUsecase { + void updateCategory(Long adminId, Long categoryId, String name, String code); +} \ No newline at end of file diff --git a/src/main/java/clap/server/application/port/outbound/task/LoadCategoryPort.java b/src/main/java/clap/server/application/port/outbound/task/LoadCategoryPort.java index ca0707d7..49cc6eeb 100644 --- a/src/main/java/clap/server/application/port/outbound/task/LoadCategoryPort.java +++ b/src/main/java/clap/server/application/port/outbound/task/LoadCategoryPort.java @@ -2,8 +2,13 @@ import clap.server.domain.model.task.Category; +import java.util.List; import java.util.Optional; public interface LoadCategoryPort { Optional findById(Long id); + + List findAll(); + List findMainCategory(); + List findSubCategory(); } diff --git a/src/main/java/clap/server/application/service/admin/FindAllCategoryService.java b/src/main/java/clap/server/application/service/admin/FindAllCategoryService.java new file mode 100644 index 00000000..6fe93c55 --- /dev/null +++ b/src/main/java/clap/server/application/service/admin/FindAllCategoryService.java @@ -0,0 +1,35 @@ +package clap.server.application.service.admin; + +import clap.server.adapter.inbound.web.dto.admin.FindAllCategoryResponse; +import clap.server.adapter.inbound.web.dto.admin.FindMainCategoryResponse; +import clap.server.adapter.inbound.web.dto.admin.FindSubCategoryResponse; +import clap.server.application.mapper.response.CategoryResponseMapper; +import clap.server.application.port.inbound.admin.FindAllCategoryUsecase; +import clap.server.application.port.outbound.task.LoadCategoryPort; +import clap.server.common.annotation.architecture.ApplicationService; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@ApplicationService +@RequiredArgsConstructor +public class FindAllCategoryService implements FindAllCategoryUsecase { + private final LoadCategoryPort loadCategoryPort; + + @Override + @Transactional(readOnly = true) + public FindAllCategoryResponse findAllCategory() { + List mainCategoryResponses = new ArrayList<>(); + List subCategoryResponses = new ArrayList<>(); + loadCategoryPort.findAll().forEach(category -> { + if (category.getMainCategory() == null) { + mainCategoryResponses.add(CategoryResponseMapper.toFindMainCategoryResponse(category)); + } else { + subCategoryResponses.add(CategoryResponseMapper.toFindSubCategoryResponse(category)); + } + }); + return CategoryResponseMapper.toFindAllCategoryResponse(mainCategoryResponses, subCategoryResponses); + } +} \ No newline at end of file diff --git a/src/main/java/clap/server/application/service/admin/FindMainCategoryService.java b/src/main/java/clap/server/application/service/admin/FindMainCategoryService.java new file mode 100644 index 00000000..80947a51 --- /dev/null +++ b/src/main/java/clap/server/application/service/admin/FindMainCategoryService.java @@ -0,0 +1,26 @@ +package clap.server.application.service.admin; + +import clap.server.adapter.inbound.web.dto.admin.FindMainCategoryResponse; +import clap.server.application.mapper.response.CategoryResponseMapper; +import clap.server.application.port.inbound.admin.FindMainCategoryUsecase; +import clap.server.application.port.outbound.task.LoadCategoryPort; +import clap.server.common.annotation.architecture.ApplicationService; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@ApplicationService +@RequiredArgsConstructor +public class FindMainCategoryService implements FindMainCategoryUsecase { + private final LoadCategoryPort loadCategoryPort; + + @Override + @Transactional + public List findMainCategory() { + return loadCategoryPort.findMainCategory() + .stream() + .map(CategoryResponseMapper::toFindMainCategoryResponse) + .toList(); + } +} diff --git a/src/main/java/clap/server/application/service/admin/FindSubCategoryService.java b/src/main/java/clap/server/application/service/admin/FindSubCategoryService.java new file mode 100644 index 00000000..71ef4b21 --- /dev/null +++ b/src/main/java/clap/server/application/service/admin/FindSubCategoryService.java @@ -0,0 +1,26 @@ +package clap.server.application.service.admin; + +import clap.server.adapter.inbound.web.dto.admin.FindSubCategoryResponse; +import clap.server.application.mapper.response.CategoryResponseMapper; +import clap.server.application.port.inbound.admin.FindSubCategoryUsecase; +import clap.server.application.port.outbound.task.LoadCategoryPort; +import clap.server.common.annotation.architecture.ApplicationService; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@ApplicationService +@RequiredArgsConstructor +public class FindSubCategoryService implements FindSubCategoryUsecase { + private final LoadCategoryPort loadCategoryPort; + + @Override + @Transactional + public List findSubCategory() { + return loadCategoryPort.findSubCategory() + .stream() + .map(CategoryResponseMapper::toFindSubCategoryResponse) + .toList(); + } +} diff --git a/src/main/java/clap/server/application/service/admin/UpdateCategoryService.java b/src/main/java/clap/server/application/service/admin/UpdateCategoryService.java new file mode 100644 index 00000000..3c09e16f --- /dev/null +++ b/src/main/java/clap/server/application/service/admin/UpdateCategoryService.java @@ -0,0 +1,29 @@ +package clap.server.application.service.admin; + +import clap.server.application.port.inbound.admin.UpdateCategoryUsecase; +import clap.server.application.port.outbound.member.LoadMemberPort; +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.exception.ApplicationException; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +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 UpdateCategoryService implements UpdateCategoryUsecase { + private final LoadCategoryPort loadCategoryPort; + private final LoadMemberPort loadMemberPort; + + @Override + @Transactional + public void updateCategory(Long adminId, Long categoryId, String name, String code) { + Member admin = loadMemberPort.findActiveMemberById(adminId).orElseThrow(() -> new ApplicationException(ACTIVE_MEMBER_NOT_FOUND)); + loadCategoryPort.findById(categoryId) + .orElseThrow(() -> new ApplicationException(CATEGORY_NOT_FOUND)) + .updateCategory(admin, name, code); + } +} \ No newline at end of file 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 bd624b17..fd6018c5 100644 --- a/src/main/java/clap/server/domain/model/task/Category.java +++ b/src/main/java/clap/server/domain/model/task/Category.java @@ -40,8 +40,10 @@ public static Category createSubCategory(Member admin, Category mainCategory, St .build(); } - public void updateMainCategory(Category mainCategory){ - this.mainCategory = mainCategory; + public void updateCategory(Member admin, String code, String name){ + this.admin = admin; + this.code = code; + this.name = name; } }