Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
19bff02
CLAP-104 CI/CD : dev 환경 cd 스크립트 checkout 버전수정
hyoseong-Choi Jan 23, 2025
a3fe141
Merge branch 'develop' of https://github.com/TaskFlow-CLAP/TaskFlow-S…
hyoseong-Choi Jan 23, 2025
87ee143
Merge branch 'develop' of https://github.com/TaskFlow-CLAP/TaskFlow-S…
hyoseong-Choi Jan 23, 2025
0a5bdf1
Merge branch 'develop' of https://github.com/TaskFlow-CLAP/TaskFlow-S…
hyoseong-Choi Jan 23, 2025
a685cf1
CLAP-61 Docs: 토큰 발급 로직 개선 및 책임 분리
joowojr Jan 23, 2025
d5e19b1
CLAP-61 Docs: 토큰 재발급 API 구현
joowojr Jan 23, 2025
c97d37c
CLAP-110 Feature : 담당자별 작업 처리량 조회 API 구현
hyoseong-Choi Jan 23, 2025
73bd762
CLAP-111 Refactor : 통계 조회 API 리팩토링 및 기능 수정
hyoseong-Choi Jan 23, 2025
d1992c8
CLAP-61 Fix: 리프레시 토큰 도메인 모델로 매핑되지 않는 오류 수정
joowojr Jan 23, 2025
2ae60f7
CLAP-61 Feat: 토큰 재발급 API 구현
joowojr Jan 23, 2025
639384a
Merge pull request #82 from TaskFlow-CLAP/CLAP-62
joowojr Jan 23, 2025
60ebc4a
내 작업한 내용에 대한 설명
nano-mm Jan 23, 2025
a88a6bf
내 작업한 내용에 대한 설명
nano-mm Jan 23, 2025
1bf4ffb
Resolve merge conflict in application.yml
nano-mm Jan 23, 2025
283e5a5
담당자 조회 API 구현
nano-mm Jan 24, 2025
0ec9b91
Merge branch 'develop' of https://github.com/TaskFlow-CLAP/TaskFlow-S…
hyoseong-Choi Jan 24, 2025
2a9eed4
CLAP-111 Refactor : 통계 조회 API 주소 통합, 리팩토링, 예외처리
hyoseong-Choi Jan 24, 2025
ea99e9f
CLAP-111 Test : 테스트코드 및 설정 수정
hyoseong-Choi Jan 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package clap.server.adapter.inbound.web.auth;

import clap.server.adapter.inbound.web.dto.auth.ReissueTokenResponse;
import clap.server.application.port.inbound.auth.ReissueTokenUsecase;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;

@Tag(name = "토큰 재발급")
@WebAdapter
@RequiredArgsConstructor
@RequestMapping("/api/auths")
public class ReissueTokenController {
private final ReissueTokenUsecase reissueTokenUsecase;

@Operation(summary = "토큰 재발급 API")
@PostMapping("/reissuance")
public ResponseEntity<ReissueTokenResponse> login(@RequestHeader String refreshToken) {
return ResponseEntity.ok(reissueTokenUsecase.reissueToken(refreshToken));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package clap.server.adapter.inbound.web.dto.admin;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.List;

@Getter
@AllArgsConstructor
public class FindManagersResponse {

private Long memberId;
private String nickname;
private String imageUrl;
private int remainingTasks;

public static List<FindManagersResponse> emptyListResponse() {
return List.of();
}

}


Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package clap.server.adapter.inbound.web.dto.auth;

public record ReissueTokenResponse(
String accessToken,
String refreshToken
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package clap.server.adapter.inbound.web.dto.statistics;

import clap.server.exception.StatisticsException;
import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import static clap.server.exception.code.StatisticsErrorCode.STATISTICS_BAD_REQUEST;

@Getter
@RequiredArgsConstructor
public enum PeriodType {
DAY("day"),
WEEK("week"),
MONTH("month");

private final String type;

@JsonCreator
public static PeriodType from(String input) {
for (PeriodType periodType : PeriodType.values()) {
if (periodType.getType().equals(input)) {
return periodType;
}
}
throw new StatisticsException(STATISTICS_BAD_REQUEST);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package clap.server.adapter.inbound.web.dto.statistics;

public record StatisticsResponse(String key, long count) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package clap.server.adapter.inbound.web.dto.statistics;

import com.fasterxml.jackson.annotation.JsonCreator;
import lombok.Getter;
import lombok.RequiredArgsConstructor;


@Getter
@RequiredArgsConstructor
public enum StatisticsType {
REQUEST_BY_PERIOD("request-by-period"),
PROCESS_BY_PERIOD("process-by-period"),
REQUEST_BY_CATEGORY("request-by-category"),
PROCESS_BY_MANAGER("process-by-manager");

private final String type;

@JsonCreator
public static StatisticsType from(String input) {
for (StatisticsType statisticsType : StatisticsType.values()) {
if (statisticsType.getType().equals(input)) {
return statisticsType;
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package clap.server.adapter.inbound.web.member;

import clap.server.application.port.inbound.domain.FindManagersUsecase;
import clap.server.adapter.inbound.web.dto.admin.FindManagersResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/manager")
@RequiredArgsConstructor
public class ManagerController {

private final FindManagersUsecase findManagersUsecase;

@GetMapping
public List<FindManagersResponse> findManagers() {

List<FindManagersResponse> managers = findManagersUsecase.execute();

if (managers.isEmpty()) {
return FindManagersResponse.emptyListResponse();
}

return managers;
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package clap.server.adapter.inbound.web.statistics;

import clap.server.adapter.inbound.web.dto.statistics.PeriodType;
import clap.server.adapter.inbound.web.dto.statistics.StatisticsResponse;
import clap.server.adapter.inbound.web.dto.statistics.StatisticsType;
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.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 org.springframework.web.bind.annotation.RequestParam;

import java.util.Map;
import java.util.List;

import static clap.server.exception.code.StatisticsErrorCode.STATISTICS_BAD_REQUEST;

@Tag(name = "작업 관련 통계")
@WebAdapter
Expand All @@ -20,30 +26,49 @@ public class FindStatisticsController {
private final FindPeriodTaskProcessUsecase findPeriodTaskProcessUsecase;
private final FindCategoryTaskRequestUsecase findCategoryTaskRequestUsecase;
private final FindSubCategoryTaskRequestUsecase findSubCategoryTaskRequestUsecase;
private final ManagerTaskProcessUsecase managerTaskProcessUsecase;

@GetMapping(value = "/task-requests-by-period")
public ResponseEntity<Map<String, Long>> aggregatePeriodTaskRequest(@RequestParam String period) {
return ResponseEntity.ok(findPeriodTaskRequestUsecase.aggregatePeriodTaskRequest(period));
}

@GetMapping("/task-processed-by-period")
public ResponseEntity<Map<String, Long>> aggregatePeriodTaskProcess(@RequestParam String period) {
return ResponseEntity.ok(findPeriodTaskProcessUsecase.aggregatePeriodTaskProcess(period));
}

@GetMapping("/task-requests-by-category")
public ResponseEntity<Map<String, Long>> aggregateCategoryTaskRequest(@RequestParam String period) {
return ResponseEntity.ok(findCategoryTaskRequestUsecase.aggregateCategoryTaskRequest(period));
}
private final FindManagerTaskProcessUsecase findManagerTaskProcessUsecase;

@GetMapping("/task-requests-by-subcategory")
public ResponseEntity<Map<String, Long>> aggregateSubCategoryTaskRequest(@RequestParam String period, @RequestParam String mainCategory) {
return ResponseEntity.ok(findSubCategoryTaskRequestUsecase.aggregateSubCategoryTaskRequest(period, mainCategory));
@GetMapping
public ResponseEntity<List<StatisticsResponse>> aggregateTaskStatistics(@RequestParam PeriodType periodType, @RequestParam StatisticsType statisticsType) {
switch (statisticsType) {
case REQUEST_BY_PERIOD ->
ResponseEntity.ok(findPeriodTaskRequestUsecase
.aggregatePeriodTaskRequest(periodType.getType())
.entrySet()
.stream()
.map(result -> new StatisticsResponse(result.getKey(), result.getValue()))
.toList());
case PROCESS_BY_PERIOD ->
ResponseEntity.ok(findPeriodTaskProcessUsecase
.aggregatePeriodTaskProcess(periodType.getType())
.entrySet()
.stream()
.map(result -> new StatisticsResponse(result.getKey(), result.getValue()))
.toList());
case REQUEST_BY_CATEGORY ->
ResponseEntity.ok(findCategoryTaskRequestUsecase.aggregateCategoryTaskRequest(periodType.getType())
.entrySet()
.stream()
.map(result -> new StatisticsResponse(result.getKey(), result.getValue()))
.toList());
case PROCESS_BY_MANAGER ->
ResponseEntity.ok(findManagerTaskProcessUsecase
.aggregateManagerTaskProcess(periodType.getType())
.entrySet()
.stream()
.map(result -> new StatisticsResponse(result.getKey(), result.getValue()))
.toList());
}
throw new StatisticsException(STATISTICS_BAD_REQUEST);
}

@GetMapping("/tasks-processed-by-manager")
public ResponseEntity<Map<String, Long>> aggregateSubCategoryTaskRequest(@RequestParam String period) {
return ResponseEntity.ok(managerTaskProcessUsecase.aggregateManagerTaskProcess(period));
@GetMapping("/subcategory")
public ResponseEntity<List<StatisticsResponse>> aggregateSubCategoryTaskRequest(@RequestParam String period, @RequestParam String mainCategory) {
return ResponseEntity.ok(findSubCategoryTaskRequestUsecase
.aggregateSubCategoryTaskRequest(period, mainCategory)
.entrySet()
.stream()
.map(result -> new StatisticsResponse(result.getKey(), result.getValue()))
.toList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,27 @@
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

@Getter
@RequiredArgsConstructor
public enum PeriodConfig {
DAY(1, CalendarInterval.Hour, 11, 19),
WEEK(14, CalendarInterval.Day, 0, 10);
DAY(1, CalendarInterval.Hour, 11, 16),
WEEK(7, CalendarInterval.Day, 0, 10),
MONTH(-1, CalendarInterval.Day, 0, 10);

private final long daysToSubtract;
private final CalendarInterval calendarInterval;
private final int substringStart;
private final int substringEnd;

}
// MONTH에 대해 동적으로 daysToSubtract를 계산하는 메서드
public long getDaysToSubtract() {
if (this == MONTH) {
return ChronoUnit.DAYS.between(LocalDate.now().minusMonths(1), LocalDate.now());
}
return daysToSubtract;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,23 +50,23 @@ public Map<String, Long> findCategoryTaskRequestByPeriod(String period) {
PeriodConfig periodConfig = PeriodConfig.valueOf(period.toUpperCase());

NativeQuery query = buildCategoryTaskRequestQuery(periodConfig);
return getCategoryTaskResults(executeQuery(query));
return getNonPeriodTaskResults(executeQuery(query), "category_task");
}

@Override
public Map<String, Long> findSubCategoryTaskRequestByPeriod(String period, String mainCategory) {
PeriodConfig periodConfig = PeriodConfig.valueOf(period.toUpperCase());

NativeQuery query = buildSubCategoryTaskRequestQuery(periodConfig, mainCategory);
return getCategoryTaskResults(executeQuery(query));
return getNonPeriodTaskResults(executeQuery(query), "category_task");
}

@Override
public Map<String, Long> findManagerTaskProcessByPeriod(String period) {
PeriodConfig periodConfig = PeriodConfig.valueOf(period.toUpperCase());

NativeQuery query = buildManagerTaskProcessQuery(periodConfig);
return getManagerTaskResults(executeQuery(query));
return getNonPeriodTaskResults(executeQuery(query), "manager_task");
}

private NativeQuery buildPeriodTaskRequestQuery(PeriodConfig config) {
Expand Down Expand Up @@ -188,25 +188,9 @@ private Map<String, Long> getPeriodTaskResults(ElasticsearchAggregations aggrega
);
}

private Map<String, Long> getCategoryTaskResults(ElasticsearchAggregations aggregations) {
private Map<String, Long> getNonPeriodTaskResults(ElasticsearchAggregations aggregations, String name) {
return new TreeMap<>(
aggregations.get("category_task")
.aggregation()
.getAggregate()
.sterms()
.buckets()
.array()
.stream()
.collect(Collectors.toMap(
bucket -> bucket.key().stringValue(),
MultiBucketBase::docCount
))
);
}

private Map<String, Long> getManagerTaskResults(ElasticsearchAggregations aggregations) {
return new TreeMap<>(
aggregations.get("manager_task")
aggregations.get(name)
.aggregation()
.getAggregate()
.sterms()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package clap.server.adapter.outbound.infrastructure.redis.refresh;

import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
Expand All @@ -8,6 +9,7 @@

@Getter
@RedisHash("refreshToken")
@Builder
@ToString(of = {"memberId", "token", "ttl"})
@EqualsAndHashCode(of = {"memberId", "token"})
public class RefreshTokenEntity {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package clap.server.adapter.outbound.infrastructure.redis.refresh;


import clap.server.adapter.outbound.persistense.mapper.MemberPersistenceMapper;
import clap.server.domain.model.auth.RefreshToken;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;

@Mapper(componentModel = "spring", uses = {MemberPersistenceMapper.class})
@Mapper(componentModel = "spring")
public interface RefreshTokenMapper {
@InheritInverseConfiguration
RefreshToken toDomain(final RefreshTokenEntity entity);
Expand Down
Loading
Loading