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 new file mode 100644 index 00000000..bcf5bec7 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/admin/addCategoryController.java @@ -0,0 +1,36 @@ +package clap.server.adapter.inbound.web.admin; + +import clap.server.adapter.inbound.security.SecurityUserDetails; +import clap.server.adapter.inbound.web.dto.admin.AddMainCategoryRequest; +import clap.server.application.port.inbound.management.AddMainCategoryUsecase; +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.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@Tag(name = "카테고리 추가") +@WebAdapter +@RequiredArgsConstructor +public class addCategoryController { + private final AddMainCategoryUsecase addMainCategoryUsecase; + + @Operation(summary = "1차 카테고리 추가") + @PostMapping("/api/maincategory") + @Secured("ROLE_ADMIN") + public void addMainCategory(@AuthenticationPrincipal SecurityUserDetails userInfo, @Validated @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()); +// } + +} \ No newline at end of file diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/admin/AddMainCategoryRequest.java b/src/main/java/clap/server/adapter/inbound/web/dto/admin/AddMainCategoryRequest.java new file mode 100644 index 00000000..32e05296 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/admin/AddMainCategoryRequest.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 AddMainCategoryRequest( + @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/inbound/web/statistics/FindStatisticsController.java b/src/main/java/clap/server/adapter/inbound/web/statistics/FindStatisticsController.java index 91ac152f..742acb6e 100644 --- a/src/main/java/clap/server/adapter/inbound/web/statistics/FindStatisticsController.java +++ b/src/main/java/clap/server/adapter/inbound/web/statistics/FindStatisticsController.java @@ -6,6 +6,8 @@ import clap.server.application.port.inbound.statistics.*; import clap.server.common.annotation.architecture.WebAdapter; import clap.server.exception.StatisticsException; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; @@ -16,6 +18,7 @@ import java.util.List; import static clap.server.exception.code.StatisticsErrorCode.STATISTICS_BAD_REQUEST; +import static io.swagger.v3.oas.annotations.enums.ParameterIn.QUERY; @Tag(name = "작업 관련 통계") @WebAdapter @@ -28,6 +31,9 @@ public class FindStatisticsController { private final FindSubCategoryTaskRequestUsecase findSubCategoryTaskRequestUsecase; private final FindManagerTaskProcessUsecase findManagerTaskProcessUsecase; + @Operation(summary = "기본 통계 API") + @Parameter(name = "periodType", description = "day, week, month", required = true, in = QUERY) + @Parameter(name = "statisticsType", description = "request-by-period, process-by-period, request-by-category, process-by-manager", required = true, in = QUERY) @GetMapping public ResponseEntity> aggregateTaskStatistics(@RequestParam PeriodType periodType, @RequestParam StatisticsType statisticsType) { switch (statisticsType) { @@ -62,10 +68,13 @@ public ResponseEntity> aggregateTaskStatistics(@Request throw new StatisticsException(STATISTICS_BAD_REQUEST); } + @Operation(summary = "1차 카테고리 하위 2차 카테고리별 통계 API") + @Parameter(name = "periodType", description = "day, week, month", required = true, in = QUERY) + @Parameter(name = "mainCategory", description = "1차 카테고리 이름", required = true, in = QUERY) @GetMapping("/subcategory") - public ResponseEntity> aggregateSubCategoryTaskRequest(@RequestParam String period, @RequestParam String mainCategory) { + public ResponseEntity> aggregateSubCategoryTaskRequest(@RequestParam PeriodType periodType, @RequestParam String mainCategory) { return ResponseEntity.ok(findSubCategoryTaskRequestUsecase - .aggregateSubCategoryTaskRequest(period, mainCategory) + .aggregateSubCategoryTaskRequest(periodType.getType(), mainCategory) .entrySet() .stream() .map(result -> new StatisticsResponse(result.getKey(), result.getValue())) 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 776406b9..2caa61d4 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/CategoryPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/CategoryPersistenceAdapter.java @@ -3,6 +3,7 @@ import clap.server.adapter.outbound.persistense.entity.task.CategoryEntity; import clap.server.adapter.outbound.persistense.mapper.CategoryPersistenceMapper; import clap.server.adapter.outbound.persistense.repository.task.CategoryRepository; +import clap.server.application.port.outbound.task.CommandCategoryPort; import clap.server.application.port.outbound.task.LoadCategoryPort; import clap.server.common.annotation.architecture.PersistenceAdapter; import clap.server.domain.model.task.Category; @@ -12,7 +13,7 @@ @PersistenceAdapter @RequiredArgsConstructor -public class CategoryPersistenceAdapter implements LoadCategoryPort { +public class CategoryPersistenceAdapter implements LoadCategoryPort, CommandCategoryPort { private final CategoryRepository categoryRepository; private final CategoryPersistenceMapper categoryPersistenceMapper; @@ -22,4 +23,9 @@ public Optional findById(Long id) { Optional categoryEntity = categoryRepository.findById(id); return categoryEntity.map(categoryPersistenceMapper::toDomain); } -} + + @Override + public void save(Category category) { + categoryRepository.save(categoryPersistenceMapper.toEntity(category)); + } +} \ No newline at end of file diff --git a/src/main/java/clap/server/application/port/inbound/management/AddMainCategoryUsecase.java b/src/main/java/clap/server/application/port/inbound/management/AddMainCategoryUsecase.java new file mode 100644 index 00000000..9f701dfb --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/management/AddMainCategoryUsecase.java @@ -0,0 +1,5 @@ +package clap.server.application.port.inbound.management; + +public interface AddMainCategoryUsecase { + void addMainCategory(Long adminId, String code, String name); +} \ No newline at end of file diff --git a/src/main/java/clap/server/application/port/outbound/task/CommandCategoryPort.java b/src/main/java/clap/server/application/port/outbound/task/CommandCategoryPort.java new file mode 100644 index 00000000..04a11b09 --- /dev/null +++ b/src/main/java/clap/server/application/port/outbound/task/CommandCategoryPort.java @@ -0,0 +1,7 @@ +package clap.server.application.port.outbound.task; + +import clap.server.domain.model.task.Category; + +public interface CommandCategoryPort { + void save(Category category); +} \ No newline at end of file diff --git a/src/main/java/clap/server/application/service/admin/AddMainCategoryService.java b/src/main/java/clap/server/application/service/admin/AddMainCategoryService.java new file mode 100644 index 00000000..82310e66 --- /dev/null +++ b/src/main/java/clap/server/application/service/admin/AddMainCategoryService.java @@ -0,0 +1,30 @@ +package clap.server.application.service.admin; + +import clap.server.application.port.inbound.management.AddMainCategoryUsecase; +import clap.server.application.port.outbound.member.LoadMemberPort; +import clap.server.application.port.outbound.task.CommandCategoryPort; +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; + +@ApplicationService +@RequiredArgsConstructor +public class AddMainCategoryService implements AddMainCategoryUsecase { + private final CommandCategoryPort commandCategoryPort; + private final LoadMemberPort loadMemberPort; + + @Override + public void addMainCategory(Long adminId, String code, String name) { + Optional activeMember = loadMemberPort.findActiveMemberById(adminId); + Category mainCategory = Category.createMainCategory( + activeMember.orElseThrow(() -> new ApplicationException(ACTIVE_MEMBER_NOT_FOUND)), + code, name); + commandCategoryPort.save(mainCategory); + } +} \ 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 c74d5c17..36a87207 100644 --- a/src/main/java/clap/server/domain/model/task/Category.java +++ b/src/main/java/clap/server/domain/model/task/Category.java @@ -22,5 +22,13 @@ public class Category extends BaseTime { private String descriptionExample; private LocalDateTime createdAt; 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; + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ef3f186c..26f09508 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -7,9 +7,8 @@ spring: - swagger.yml - redis.yml - auth.yml - - optional:classpath:env.properties application: - name: taskflow2 + name: taskflow elasticsearch: uris: ${ELASTIC_URI:127.0.0.1:9200} @@ -24,12 +23,12 @@ server: max: 600 min-spare: 100 -logging: - level: - root: INFO - taskflow.clap.server: ERROR - org: - springframework: DEBUG +#logging: +# level: +# root: INFO +# taskflow.clap.server: ERROR +# org: +# springframework: DEBUG --- spring.config.activate.on-profile: local @@ -37,8 +36,8 @@ logging: level: root: INFO taskflow.clap.server: ERROR - org: - springframework: DEBUG +# org: +# springframework: DEBUG --- spring.config.activate.on-profile: dev diff --git a/src/main/resources/mysql.yml b/src/main/resources/mysql.yml index a8215604..3e095af1 100644 --- a/src/main/resources/mysql.yml +++ b/src/main/resources/mysql.yml @@ -10,7 +10,7 @@ spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://${DATABASE_HOST:localhost}:${DATABASE_PORT:3306}/${DATABASE_NAME:taskflowdev}?characterEncoding=UTF-8&serverTimezone=Asia/Seoul&autoReconnect=true + url: jdbc:mysql://${DATABASE_HOST:localhost}:${DATABASE_PORT:3306}/${DATABASE_NAME:taskflow}?characterEncoding=UTF-8&serverTimezone=Asia/Seoul&autoReconnect=true username: ${DATABASE_USERNAME} password: ${DATABASE_PASSWORD} data-source-properties: