Skip to content

Commit 3bc8fb3

Browse files
hyoseong-Choinano-mmparkjaehak
authored
CLAP-328 통계 엘라스틱서치에 저장, 시간대 설정 오류, 응답 양식 수정
* CLAP-110 Feature : 담당자별 작업 처리량 조회 API 구현 <footer> - 관련: #73 * CLAP-111 Refactor : 통계 조회 API 리팩토링 및 기능 수정 <footer> - 관련: #74 * 내 작업한 내용에 대한 설명 * 담당자 조회 API 구현 * CLAP-111 Refactor : 통계 조회 API 주소 통합, 리팩토링, 예외처리 <footer> - 관련: #74 * Bug : 프로퍼티파일 수정 * CLAP-104 CI/CD : CI에서 s3.yml 파일 생성하도록 수정 * CLAP-146 Feature : 카테고리 목록 조회 API 구현 <footer> - 관련: #118 * CLAP-147 Feature : 카테고리 수정 API 구현 <footer> - 관련: #119 * CLAP-147 Feature : 카테고리 추가, 수정 API 리뷰반영 수정 <footer> - 관련: #119 * CLAP-148 Feature : 카테고리 삭제 API 구현 <footer> - 관련: #120 * CLAP-148 Feature : 카테고리 삭제 API 수정 <footer> - 관련: #120 * CLAP-148 Docs : 카테고리 API 스웨거 수정 <footer> - 관련: #120 * CLAP-107 Bug : CI test yml파일 key 중복 수정 <footer> - 관련: #60 * CLAP-148 Feature : 카테고리 CUD 리뷰 반영 수정, 조회 반환 양식 수정 <footer> - 관련: #120 * CLAP-214 Cleanup : 통계, 카테고리 미흡한부분 리팩토링 <footer> - 관련: #216 * CLAP-214 Fix : 통계조회 API 파라미터 바인딩 오류 수정 <footer> - 관련: #216 * CLAP-214 Fix : addConverter 수정 <footer> - 관련: #216 * CLAP-214 Hotfix : 추가한 파일들 제거 <footer> - 관련: #216 * CLAP-214 Hotfix : FindStatisticsController 스위치문 수정 <footer> - 관련: #216 * CLAP-214 Hotfix : AddCategoryService 지연로딩 오류 수정 <footer> - 관련: #216 * CLAP-214 Hotfix : DeleteCategoryService 카테고리 삭제 로직 수정 <footer> - 관련: #216 * CLAP-249 Refactor : 통계 조회 서비스 리팩토링 <footer> - 관련: #265 * CLAP-250 Test : 통계 조회 테스트코드 작성 <footer> - 관련: #266 * CLAP-214 Hotifx : 카테고리 수정 로직 수정 <footer> - 관련: #216 * CLAP-214 Hotifx : 카테고리 수정 로직 수정 <footer> - 관련: #216 * CLAP-256 fix: 스웨거 명세 추가 * CLAP-214 Hotifx : 카테고리 저장 트랜잭션 추가 <footer> - 관련: #216 * CLAP-214 Hotifx : 카테고리 저장 시간 수정 <footer> - 관련: #216 * CLAP-285 Hotifx : 모든 카테고리 조회 <footer> - 관련: #331 * CLAP-214 Hotifx : 카테고리 조회 Secured 설정 <footer> - 관련: #216 * CLAP-256 fix: convert 동작 mapper 추가 * CLAP-256 fix: 충돌해결 * CLAP-256 fix: 불필요한 테스트 파일 제거 * CLAP-256 fix: mapper statuslabel 수정 * CLAP-256 Fix : 팀 작업 현황 필터링 조회 수정 <footer> - 관련: #281 * CLAP-299 Feature : 사용자 작업 취소 <footer> - 관련: #352 * CLAP-299 Fix: 사용자 작업 취소 api 주소 수정 <footer> - 관련: #352 * CLAP-256 전체 작업수 계산 로직 개선 * fix: env 파일 제거 * fix: 어노테이션 수정 * fix: @RestController 제거 * fix: @ApplicationService로 수정 * CLAP-256 Fix : TeamStatusService 누락된 import 수정 <footer> - 관련: #281 * CLAP-256 Fix : TeamStatusService Secured 추가 <footer> - 관련: #281 * fix: 팀 작업 현황 필터링 조회 수정 및 정렬 기능 개선 * Delete src/main/resources/env.properties * Delete src/test/resources/application.yml * 충돌 해결: develop 브랜치와 병합 * Update application.yml * Revert application.yml files to match develop branch * CLAP-256 fix: Update application.yml * CLAP-256 fix: Update application.yml * CLAP-256 Fix : 팀 현황 필터조회 기여도순 정렬 수정 <footer> - 관련: #281 * CLAP-309 Fix : 엘라스틱서치에 저장할 작업 정보 수정 <footer> - 관련: #378 * CLAP-309 Fix : 엘라스틱서치에 저장 누락되는것 수정 <footer> - 관련: #378 * CLAP-test * CLAP-318 Fix : 2차카테고리 입력 양식(설명 예시) 추가 <footer> - 관련: #392 * CLAP-318 Test : 2차카테고리 입력 양식(설명 예시) 추가한 테스트코드 수정 <footer> - 관련: #392 * CLAP-318 Feature : 2차카테고리 단일조회 API <footer> - 관련: #392 * CLAP-318 Feature : 2차카테고리 경로 수정 원복 <footer> - 관련: #392 * CLAP-327 Fix: 팀 현황 필터링 조회 정렬 위치 수정 <footer> - 관련: #409 * CLAP-328 Fix : 통계 엘라스틱서치에 저장, 시간대 설정 오류, 응답 양식 수정 <footer> - 관련: #411 --------- Co-authored-by: nano-mm <nano123@gachon.ac.kr> Co-authored-by: parkjaehak <atom8426@gmail.com>
1 parent f2d68dc commit 3bc8fb3

File tree

12 files changed

+69
-58
lines changed

12 files changed

+69
-58
lines changed

src/main/java/clap/server/adapter/outbound/infrastructure/elastic/PeriodConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
@Getter
1111
@RequiredArgsConstructor
1212
public enum PeriodConfig {
13-
DAY(1, CalendarInterval.Hour, 11, 16),
13+
DAY(1, CalendarInterval.Hour, 11, 13),
1414
WEEK(7, CalendarInterval.Day, 0, 10),
1515
MONTH(-1, CalendarInterval.Day, 0, 10);
1616

src/main/java/clap/server/adapter/outbound/infrastructure/elastic/TaskDocumentAdapter.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
public class TaskDocumentAdapter implements TaskDocumentPort {
2424
private final TaskElasticRepository taskElasticRepository;
2525
private final ElasticsearchOperations elasticsearchOperations;
26+
private static final String TIME_ZONE = "Asia/Seoul";
2627

2728
@Override
2829
public void saveStatistics(List<Task> statistics) {
@@ -75,9 +76,12 @@ private NativeQuery buildPeriodTaskRequestQuery(PeriodConfig config) {
7576
.range(r -> r
7677
.date(d -> d
7778
.field("created_at")
78-
.gte(String.valueOf(LocalDate.now().minusDays(config.getDaysToSubtract()))))))
79+
.timeZone(TIME_ZONE)
80+
.gte(String.valueOf(LocalDate.now().minusDays(config.getDaysToSubtract())))
81+
.lt(String.valueOf(LocalDate.now())))))
7982
.withAggregation("period_task", AggregationBuilders.dateHistogram()
8083
.field("created_at")
84+
.timeZone(TIME_ZONE)
8185
.calendarInterval(config.getCalendarInterval())
8286
.build()._toAggregation())
8387
.withMaxResults(0)
@@ -90,12 +94,16 @@ private NativeQuery buildPeriodTaskProcessQuery(PeriodConfig config) {
9094
.range(r -> r
9195
.date(d -> d
9296
.field("created_at")
93-
.gte(String.valueOf(LocalDate.now().minusDays(config.getDaysToSubtract())))))).build();
97+
.timeZone(TIME_ZONE)
98+
.gte(String.valueOf(LocalDate.now().minusDays(config.getDaysToSubtract())))
99+
.lt(String.valueOf(LocalDate.now())))))
100+
.build();
94101
NativeQuery statusQuery = NativeQuery.builder()
95102
.withQuery(q -> q
96103
.term(v -> v
97104
.field("status")
98-
.value("completed"))).build();
105+
.value("completed")))
106+
.build();
99107

100108
return NativeQuery.builder()
101109
.withQuery(q -> q
@@ -104,6 +112,7 @@ private NativeQuery buildPeriodTaskProcessQuery(PeriodConfig config) {
104112
)
105113
.withAggregation("period_task", AggregationBuilders.dateHistogram()
106114
.field("created_at")
115+
.timeZone(TIME_ZONE)
107116
.calendarInterval(config.getCalendarInterval())
108117
.build()._toAggregation())
109118
.withMaxResults(0)
@@ -116,7 +125,9 @@ private NativeQuery buildCategoryTaskRequestQuery(PeriodConfig config) {
116125
.range(r -> r
117126
.date(d -> d
118127
.field("created_at")
119-
.gte(String.valueOf(LocalDate.now().minusDays(config.getDaysToSubtract()))))))
128+
.timeZone(TIME_ZONE)
129+
.gte(String.valueOf(LocalDate.now().minusDays(config.getDaysToSubtract())))
130+
.lt(String.valueOf(LocalDate.now())))))
120131
.withAggregation("category_task", AggregationBuilders.terms()
121132
.field("main_category")
122133
.build()._toAggregation())
@@ -130,7 +141,10 @@ private NativeQuery buildSubCategoryTaskRequestQuery(PeriodConfig config, String
130141
.range(r -> r
131142
.date(d -> d
132143
.field("created_at")
133-
.gte(String.valueOf(LocalDate.now().minusDays(config.getDaysToSubtract())))))).build();
144+
.timeZone(TIME_ZONE)
145+
.gte(String.valueOf(LocalDate.now().minusDays(config.getDaysToSubtract())))
146+
.lt(String.valueOf(LocalDate.now())))))
147+
.build();
134148
NativeQuery categoryQuery = NativeQuery.builder()
135149
.withQuery(q -> q
136150
.term(v -> v
@@ -155,7 +169,9 @@ private NativeQuery buildManagerTaskProcessQuery(PeriodConfig config) {
155169
.range(r -> r
156170
.date(d -> d
157171
.field("created_at")
158-
.gte(String.valueOf(LocalDate.now().minusDays(config.getDaysToSubtract()))))))
172+
.timeZone(TIME_ZONE)
173+
.gte(String.valueOf(LocalDate.now().minusDays(config.getDaysToSubtract())))
174+
.lt(String.valueOf(LocalDate.now())))))
159175
.withAggregation("manager_task", AggregationBuilders.terms()
160176
.field("processor")
161177
.build()._toAggregation())

src/main/java/clap/server/adapter/outbound/infrastructure/elastic/document/TaskDocument.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public TaskDocument(Task taskEntity) {
3434
this.mainCategory = taskEntity.getCategory().getMainCategory().getName();
3535
this.subCategory = taskEntity.getCategory().getName();
3636
this.status = taskEntity.getTaskStatus().name().toLowerCase();
37-
this.processor = taskEntity.getProcessor().getMemberInfo().getNickname();
37+
this.processor = taskEntity.getProcessor() != null ? taskEntity.getProcessor().getMemberInfo().getNickname() : "";
3838
this.createdAt = taskEntity.getCreatedAt();
3939
}
4040
}

src/main/java/clap/server/adapter/outbound/persistense/TaskPersistenceAdapter.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package clap.server.adapter.outbound.persistense;
22

3-
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskListRequest;
43
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskBoardRequest;
4+
import clap.server.adapter.inbound.web.dto.task.request.FilterTaskListRequest;
55
import clap.server.adapter.inbound.web.dto.task.request.FilterTeamStatusRequest;
66
import clap.server.adapter.inbound.web.dto.task.response.TeamTaskResponse;
77
import clap.server.adapter.outbound.persistense.entity.task.TaskEntity;
@@ -12,19 +12,14 @@
1212
import clap.server.application.port.outbound.task.LoadTaskPort;
1313
import clap.server.common.annotation.architecture.PersistenceAdapter;
1414
import clap.server.domain.model.task.Task;
15-
import clap.server.exception.ApplicationException;
16-
import clap.server.exception.code.NotificationErrorCode;
17-
import com.fasterxml.jackson.core.JsonProcessingException;
18-
import com.fasterxml.jackson.databind.JsonNode;
19-
import com.fasterxml.jackson.databind.ObjectMapper;
2015
import lombok.RequiredArgsConstructor;
2116
import lombok.extern.slf4j.Slf4j;
2217
import org.springframework.data.domain.Page;
2318
import org.springframework.data.domain.Pageable;
24-
import org.springframework.data.domain.Slice;
25-
import org.springframework.data.domain.SliceImpl;
2619

20+
import java.time.LocalDate;
2721
import java.time.LocalDateTime;
22+
import java.time.LocalTime;
2823
import java.util.List;
2924
import java.util.Optional;
3025

@@ -34,7 +29,6 @@
3429
public class TaskPersistenceAdapter implements CommandTaskPort, LoadTaskPort {
3530
private final TaskRepository taskRepository;
3631
private final TaskPersistenceMapper taskPersistenceMapper;
37-
private final ObjectMapper objectMapper;
3832

3933
@Override
4034
public Task save(Task task) {
@@ -74,7 +68,8 @@ public Optional<Task> findByIdAndStatus(Long id, TaskStatus status) {
7468
}
7569

7670
@Override
77-
public List<Task> findYesterdayTaskByDate(LocalDateTime now) {
71+
public List<Task> findYesterdayTaskByDate() {
72+
LocalDateTime now = LocalDateTime.of(LocalDate.now(), LocalTime.of(0, 0, 0, 0));
7873
return taskRepository.findYesterdayTaskByUpdatedAtIsBetween(now.minusDays(1), now)
7974
.stream().map(taskPersistenceMapper::toDomain).toList();
8075
}

src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskCustomRepositoryImpl.java

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
1111
import com.querydsl.core.BooleanBuilder;
1212
import com.querydsl.core.types.OrderSpecifier;
13-
import com.querydsl.core.types.dsl.CaseBuilder;
1413
import com.querydsl.core.types.dsl.DateTimePath;
1514
import com.querydsl.jpa.impl.JPAQueryFactory;
1615
import lombok.RequiredArgsConstructor;
@@ -20,10 +19,12 @@
2019
import org.springframework.stereotype.Repository;
2120

2221
import java.time.LocalDateTime;
22+
import java.util.Comparator;
2323
import java.util.LinkedHashMap;
2424
import java.util.List;
2525
import java.util.stream.Collectors;
2626

27+
import static clap.server.adapter.inbound.web.dto.task.request.SortBy.CONTRIBUTE;
2728
import static clap.server.adapter.outbound.persistense.entity.task.QTaskEntity.taskEntity;
2829
import static com.querydsl.core.types.Order.ASC;
2930
import static com.querydsl.core.types.Order.DESC;
@@ -91,33 +92,18 @@ public List<TeamTaskResponse> findTeamStatus(Long memberId, FilterTeamStatusRequ
9192
SortBy sortBy = (filter != null && filter.sortBy() != null) ? filter.sortBy() : SortBy.DEFAULT;
9293

9394
assert filter != null;
94-
OrderSpecifier<?> orderBy;
95-
if (sortBy == SortBy.CONTRIBUTE) {
96-
// 기여도순 (진행 중 + 검토 중 작업 개수 합 기준 내림차순)
97-
orderBy = new CaseBuilder()
98-
.when(taskEntity.taskStatus.eq(TaskStatus.IN_PROGRESS)
99-
.or(taskEntity.taskStatus.eq(TaskStatus.IN_REVIEWING)))
100-
.then(1)
101-
.otherwise(0)
102-
.desc();
103-
} else {
104-
// 기본순 (닉네임 오름차순)
105-
orderBy = taskEntity.processor.nickname.asc();
106-
}
107-
10895
// 쿼리 실행
10996
List<TaskEntity> taskEntities = queryFactory
11097
.selectFrom(taskEntity)
11198
.where(builder)
112-
.orderBy(orderBy)
11399
.fetch();
114100

115101
// null 또는 빈 리스트 처리
116102
if (taskEntities == null || taskEntities.isEmpty()) {
117103
return List.of(); // 빈 리스트 반환
118104
}
119105

120-
return taskEntities.stream()
106+
List<TeamTaskResponse> members = taskEntities.stream()
121107
.collect(Collectors.groupingBy(t -> t.getProcessor().getMemberId(), LinkedHashMap::new, Collectors.toList()))
122108
.entrySet().stream()
123109
.map(entry -> {
@@ -157,6 +143,12 @@ public List<TeamTaskResponse> findTeamStatus(Long memberId, FilterTeamStatusRequ
157143
);
158144
}).collect(Collectors.toList());
159145

146+
// 기여도순 (진행 중 + 검토 중 작업 개수 합 기준 내림차순)
147+
if (sortBy.equals(CONTRIBUTE)) members.sort((a, b) -> b.totalTaskCount() - a.totalTaskCount());
148+
// 기본순 (닉네임 오름차순)
149+
else members.sort(Comparator.comparing(TeamTaskResponse::nickname));
150+
151+
return members;
160152
}
161153

162154

src/main/java/clap/server/application/port/outbound/task/LoadTaskPort.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
public interface LoadTaskPort {
1717
Optional<Task> findById(Long id);
1818

19-
List<Task> findYesterdayTaskByDate(LocalDateTime now);
19+
List<Task> findYesterdayTaskByDate();
2020

2121
Page<Task> findTasksRequestedByUser(Long requesterId, Pageable pageable, FilterTaskListRequest findTaskListRequest);
2222

src/main/java/clap/server/application/service/statistics/FindTaskProcessService.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,16 @@ public List<StatisticsResponse> aggregateManagerTaskProcess(String period) {
3131
@Override
3232
public List<StatisticsResponse> aggregatePeriodTaskProcess(String period) {
3333
if (period.equals("week") || period.equals("month")) {
34-
return FindTaskStatisticsMapper.toStatisticsResponse( taskStatisticsPolicy.filterAndFormatWeekdayStatistics(taskDocumentPort.findPeriodTaskProcessByPeriod(period)));
34+
return FindTaskStatisticsMapper.toStatisticsResponse(taskStatisticsPolicy.formatStatistics(taskDocumentPort.findPeriodTaskProcessByPeriod(period)));
3535
}
36-
return FindTaskStatisticsMapper.toStatisticsResponse(taskDocumentPort.findPeriodTaskProcessByPeriod(period));
36+
return FindTaskStatisticsMapper.toStatisticsResponse(taskStatisticsPolicy.formatDayStatistics(taskDocumentPort.findPeriodTaskProcessByPeriod(period)));
3737
}
3838

3939
@Override
4040
public List<StatisticsResponse> aggregatePeriodTaskRequest(String period) {
4141
if (period.equals("week") || period.equals("month")) {
42-
return FindTaskStatisticsMapper.toStatisticsResponse(taskStatisticsPolicy.filterAndFormatWeekdayStatistics(taskDocumentPort.findPeriodTaskRequestByPeriod(period)));
42+
return FindTaskStatisticsMapper.toStatisticsResponse(taskStatisticsPolicy.formatStatistics(taskDocumentPort.findPeriodTaskRequestByPeriod(period)));
4343
}
44-
return FindTaskStatisticsMapper.toStatisticsResponse(taskDocumentPort.findPeriodTaskRequestByPeriod(period));
44+
return FindTaskStatisticsMapper.toStatisticsResponse(taskStatisticsPolicy.formatDayStatistics(taskDocumentPort.findPeriodTaskRequestByPeriod(period)));
4545
}
4646
}

src/main/java/clap/server/application/service/statistics/StatisticsIndexingService.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
import org.springframework.scheduling.annotation.Scheduled;
88
import org.springframework.transaction.annotation.Transactional;
99

10-
import java.time.LocalDateTime;
11-
1210
@ApplicationService
1311
@RequiredArgsConstructor
1412
public class StatisticsIndexingService {
@@ -19,7 +17,7 @@ public class StatisticsIndexingService {
1917
@Transactional
2018
public void IndexStatistics() {
2119
taskDocumentPort.saveStatistics(
22-
loadTaskPort.findYesterdayTaskByDate(LocalDateTime.now().withNano(0))
20+
loadTaskPort.findYesterdayTaskByDate()
2321
);
2422
}
2523
}

src/main/java/clap/server/application/service/task/TeamStatusService.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111

1212
import java.util.List;
1313

14-
import static clap.server.adapter.inbound.web.dto.task.request.SortBy.CONTRIBUTE;
15-
1614
@ApplicationService
1715
public class TeamStatusService implements LoadTeamStatusUsecase, FilterTeamStatusUsecase {
1816

@@ -43,7 +41,6 @@ public TeamStatusResponse filterTeamStatus(FilterTeamStatusRequest filter) {
4341
// 전체 팀의 진행 중 & 검토 중 작업 수 계산
4442
int totalInProgressTaskCount = members.stream().mapToInt(TeamTaskResponse::inProgressTaskCount).sum();
4543
int totalInReviewingTaskCount = members.stream().mapToInt(TeamTaskResponse::inReviewingTaskCount).sum();
46-
if (filter.sortBy().equals(CONTRIBUTE)) members.sort((a, b) -> b.totalTaskCount() - a.totalTaskCount());
4744

4845
return new TeamStatusResponse(members, totalInProgressTaskCount, totalInReviewingTaskCount);
4946
}

src/main/java/clap/server/domain/policy/task/TaskStatisticsPolicy.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
import clap.server.common.annotation.architecture.Policy;
44

5-
import java.time.DayOfWeek;
65
import java.time.LocalDate;
76
import java.time.format.DateTimeFormatter;
7+
import java.util.LinkedHashMap;
88
import java.util.Map;
99
import java.util.Map.Entry;
1010
import java.util.TreeMap;
@@ -14,9 +14,8 @@
1414
public class TaskStatisticsPolicy {
1515
private static final String DISPLAY_FORMAT = "M월 d일";
1616

17-
public Map<String, Long> filterAndFormatWeekdayStatistics(Map<String, Long> statistics) {
17+
public Map<String, Long> formatStatistics(Map<String, Long> statistics) {
1818
return statistics.entrySet().stream()
19-
.filter(this::isWeekday)
2019
.collect(Collectors.toMap(
2120
entry -> formatDate(entry.getKey()),
2221
Entry::getValue,
@@ -25,13 +24,20 @@ public Map<String, Long> filterAndFormatWeekdayStatistics(Map<String, Long> stat
2524
));
2625
}
2726

28-
private boolean isWeekday(Entry<String, Long> entry) {
29-
LocalDate date = LocalDate.parse(entry.getKey());
30-
return !(date.getDayOfWeek() == DayOfWeek.SATURDAY || date.getDayOfWeek() == DayOfWeek.SUNDAY);
31-
}
32-
3327
private String formatDate(String dateString) {
3428
LocalDate date = LocalDate.parse(dateString);
3529
return date.format(DateTimeFormatter.ofPattern(DISPLAY_FORMAT));
3630
}
31+
32+
public Map<String, Long> formatDayStatistics(Map<String, Long> statistics) {
33+
for (int i = 0; i <= 23; i++) statistics.putIfAbsent(String.valueOf(i), 0L);
34+
35+
return statistics.entrySet().stream()
36+
.collect(Collectors.toMap(
37+
entry -> Integer.parseInt(entry.getKey()) + "시",
38+
Entry::getValue,
39+
(v1, v2) -> v1,
40+
LinkedHashMap::new
41+
));
42+
}
3743
}

0 commit comments

Comments
 (0)