Skip to content

Commit 62c7f86

Browse files
committed
CLAP-81 Add: 작업 기록 로그에 대한 필터링
1 parent 7a773d3 commit 62c7f86

File tree

11 files changed

+156
-190
lines changed

11 files changed

+156
-190
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package clap.server.adapter.inbound.web.dto.log;
2+
3+
import clap.server.adapter.outbound.persistense.entity.log.constant.LogStatus;
4+
import io.swagger.v3.oas.annotations.media.Schema;
5+
import jakarta.validation.constraints.NotNull;
6+
7+
import java.util.List;
8+
9+
public record MemberLogRequest(
10+
@Schema(description = "검색 기간 (단위: 시간)", example = "1, 24, 168, 730, 2190 (1시간, 24시간, 1주일, 1개월, 3개월)")
11+
Integer term,
12+
@NotNull
13+
List<LogStatus> logStatus,
14+
@NotNull
15+
String nickName,
16+
@NotNull
17+
String ipAddress
18+
) {
19+
}
Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
package clap.server.adapter.inbound.web.log;
22

3+
import clap.server.adapter.inbound.security.SecurityUserDetails;
34
import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse;
5+
import clap.server.adapter.inbound.web.dto.log.MemberLogRequest;
46
import clap.server.adapter.inbound.web.dto.log.MemberLogResponse;
7+
import clap.server.adapter.inbound.web.dto.task.FilterTaskListRequest;
58
import clap.server.application.port.inbound.log.FindApiLogsUsecase;
69
import clap.server.common.annotation.architecture.WebAdapter;
710
import lombok.RequiredArgsConstructor;
11+
import org.springframework.data.domain.Page;
12+
import org.springframework.data.domain.PageRequest;
13+
import org.springframework.data.domain.Pageable;
814
import org.springframework.security.access.annotation.Secured;
9-
import org.springframework.web.bind.annotation.GetMapping;
10-
import org.springframework.web.bind.annotation.RequestMapping;
11-
import org.springframework.web.bind.annotation.RestController;
15+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
16+
import org.springframework.web.bind.annotation.*;
1217

1318
import java.util.List;
1419

@@ -18,17 +23,22 @@
1823
@RequiredArgsConstructor
1924
public class LogController {
2025

21-
private final FindApiLogsUsecase getApiLogsUseCase;
26+
private final FindApiLogsUsecase findApiLogsUsecase;
2227

2328
@Secured({"ROLE_ADMIN"})
2429
@GetMapping("/login")
2530
public List<AnonymousLogResponse> getLoginAttempts() {
26-
return getApiLogsUseCase.getAnonymousLogs();
31+
return findApiLogsUsecase.getAnonymousLogs();
2732
}
2833

2934
@Secured({"ROLE_ADMIN"})
3035
@GetMapping("/general")
31-
public List<MemberLogResponse> getApiCalls() {
32-
return getApiLogsUseCase.getMemberLogs();
36+
public Page<MemberLogResponse> getApiCalls(
37+
@RequestParam(defaultValue = "0") int page,
38+
@RequestParam(defaultValue = "20") int pageSize,
39+
@ModelAttribute MemberLogRequest memberLogRequest,
40+
@AuthenticationPrincipal SecurityUserDetails userInfo) {
41+
Pageable pageable = PageRequest.of(page, pageSize);
42+
return findApiLogsUsecase.filterMemberLogs(memberLogRequest, pageable);
3343
}
3444
}

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

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

3-
import clap.server.adapter.outbound.persistense.entity.log.AnonymousLogEntity;
4-
import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity;
3+
import clap.server.adapter.inbound.web.dto.log.MemberLogRequest;
54

5+
import clap.server.adapter.inbound.web.dto.log.MemberLogResponse;
6+
import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity;
67
import clap.server.adapter.outbound.persistense.mapper.ApiLogPersistenceMapper;
78
import clap.server.adapter.outbound.persistense.mapper.MemberPersistenceMapper;
89
import clap.server.adapter.outbound.persistense.repository.log.AnonymousLogRepository;
910
import clap.server.adapter.outbound.persistense.repository.log.ApiLogRepository;
1011
import clap.server.adapter.outbound.persistense.repository.log.MemberLogRepository;
12+
import clap.server.application.mapper.response.LogMapper;
1113
import clap.server.application.port.outbound.log.CommandLogPort;
1214
import clap.server.application.port.outbound.log.LoadLogPort;
1315
import clap.server.common.annotation.architecture.PersistenceAdapter;
@@ -16,6 +18,8 @@
1618
import clap.server.domain.model.log.MemberLog;
1719

1820
import lombok.RequiredArgsConstructor;
21+
import org.springframework.data.domain.Page;
22+
import org.springframework.data.domain.Pageable;
1923

2024
import java.util.List;
2125

@@ -55,9 +59,9 @@ public List<AnonymousLog> findAnonymousLogs() {
5559
}
5660

5761
@Override
58-
public List<MemberLog> findMemberLogs() {
59-
return memberLogRepository.findAll().stream()
60-
.map(apiLogPersistenceMapper::mapMemberLogEntityToDomain)
61-
.toList();
62+
public Page<MemberLogResponse> filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable) {
63+
Page<MemberLog> memberLogs = memberLogRepository.filterMemberLogs(memberLogRequest, pageable)
64+
.map(apiLogPersistenceMapper::mapMemberLogEntityToDomain);
65+
return memberLogs.map(LogMapper::toMemberLogResponse);
6266
}
6367
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package clap.server.adapter.outbound.persistense.repository.log;
2+
3+
import clap.server.adapter.inbound.web.dto.log.MemberLogRequest;
4+
import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity;
5+
import org.springframework.data.domain.Page;
6+
import org.springframework.data.domain.Pageable;
7+
8+
public interface MemberLogCustomRepository {
9+
Page<MemberLogEntity> filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable);
10+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package clap.server.adapter.outbound.persistense.repository.log;
2+
3+
4+
import clap.server.adapter.inbound.web.dto.log.MemberLogRequest;
5+
import clap.server.adapter.outbound.persistense.entity.log.MemberLogEntity;
6+
import com.querydsl.core.BooleanBuilder;
7+
import com.querydsl.jpa.impl.JPAQueryFactory;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.data.domain.Page;
10+
import org.springframework.data.domain.PageImpl;
11+
import org.springframework.data.domain.Pageable;
12+
import org.springframework.stereotype.Repository;
13+
14+
import java.time.LocalDateTime;
15+
import java.util.List;
16+
17+
import static clap.server.adapter.outbound.persistense.entity.log.QMemberLogEntity.memberLogEntity;
18+
import static clap.server.adapter.outbound.persistense.entity.member.QMemberEntity.memberEntity;
19+
20+
21+
@Repository
22+
@RequiredArgsConstructor
23+
public class MemberLogCustomRepositoryImpl implements MemberLogCustomRepository{
24+
25+
private final JPAQueryFactory queryFactory;
26+
27+
@Override
28+
public Page<MemberLogEntity> filterMemberLogs(MemberLogRequest request, Pageable pageable) {
29+
BooleanBuilder builder = new BooleanBuilder();
30+
31+
if (request.term() != null) {
32+
LocalDateTime fromDate = LocalDateTime.now().minusHours(request.term());
33+
builder.and(memberLogEntity.createdAt.after(fromDate));
34+
}
35+
if (request.logStatus().isEmpty()) {
36+
builder.and(memberLogEntity.logStatus.in(request.logStatus()));
37+
}
38+
if (!request.nickName().isEmpty()) {
39+
builder.and(memberEntity.nickname.contains(request.nickName()));
40+
}
41+
if (!request.ipAddress().isEmpty()) {
42+
builder.and(memberLogEntity.serverIp.eq(request.ipAddress()));
43+
}
44+
45+
List<MemberLogEntity> result = queryFactory
46+
.selectFrom(memberLogEntity)
47+
.where(builder)
48+
.leftJoin(memberLogEntity.member, memberEntity)
49+
.orderBy(memberLogEntity.createdAt.desc())
50+
.offset(pageable.getOffset())
51+
.limit(pageable.getPageSize())
52+
.fetch();
53+
long total = queryFactory
54+
.selectFrom(memberLogEntity)
55+
.where(builder)
56+
.fetch().size();
57+
return new PageImpl<>(result, pageable, total);
58+
}
59+
}

src/main/java/clap/server/adapter/outbound/persistense/repository/log/MemberLogRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@
77
import java.util.List;
88

99
@Repository
10-
public interface MemberLogRepository extends JpaRepository<MemberLogEntity, Long> {
10+
public interface MemberLogRepository extends JpaRepository<MemberLogEntity, Long>, MemberLogCustomRepository {
1111
}

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

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,16 @@ public class TaskCustomRepositoryImpl implements TaskCustomRepository {
2525

2626
private final JPAQueryFactory queryFactory;
2727

28+
2829
@Override
2930
public Page<TaskEntity> findTasksRequestedByUser(Long requesterId, Pageable pageable, FilterTaskListRequest filterTaskListRequest) {
30-
BooleanBuilder whereClause = createFilter(filterTaskListRequest);
31+
BooleanBuilder builder = createFilter(filterTaskListRequest);
3132
if (!filterTaskListRequest.nickName().isEmpty()) {
32-
whereClause.and(taskEntity.processor.nickname.eq(filterTaskListRequest.nickName()));
33+
builder.and(taskEntity.processor.nickname.eq(filterTaskListRequest.nickName()));
3334
}
34-
whereClause.and(taskEntity.requester.memberId.eq(requesterId));
35+
builder.and(taskEntity.requester.memberId.eq(requesterId));
3536

36-
return getTasksPage(pageable, whereClause, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection());
37+
return getTasksPage(pageable, builder, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection());
3738
}
3839

3940
@Override
@@ -49,60 +50,60 @@ public Page<TaskEntity> findTasksAssignedByManager(Long processorId, Pageable pa
4950

5051
@Override
5152
public Page<TaskEntity> findPendingApprovalTasks(Pageable pageable, FilterTaskListRequest filterTaskListRequest) {
52-
BooleanBuilder whereClause = createFilter(filterTaskListRequest);
53+
BooleanBuilder builder = createFilter(filterTaskListRequest);
5354
if (!filterTaskListRequest.nickName().isEmpty()) {
54-
whereClause.and(taskEntity.requester.nickname.eq(filterTaskListRequest.nickName()));
55+
builder.and(taskEntity.requester.nickname.eq(filterTaskListRequest.nickName()));
5556
}
56-
whereClause.and(taskEntity.taskStatus.eq(TaskStatus.REQUESTED));
57-
return getTasksPage(pageable, whereClause, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection());
57+
builder.and(taskEntity.taskStatus.eq(TaskStatus.REQUESTED));
58+
return getTasksPage(pageable, builder, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection());
5859
}
5960

6061
@Override
6162
public Page<TaskEntity> findAllTasks(Pageable pageable, FilterTaskListRequest filterTaskListRequest) {
62-
BooleanBuilder whereClause = createFilter(filterTaskListRequest);
63+
BooleanBuilder builder = createFilter(filterTaskListRequest);
6364
if (!filterTaskListRequest.nickName().isEmpty()) {
64-
whereClause.and(
65+
builder.and(
6566
taskEntity.requester.nickname.eq(filterTaskListRequest.nickName())
6667
.or(taskEntity.processor.nickname.eq(filterTaskListRequest.nickName()))
6768
);
6869
}
69-
return getTasksPage(pageable, whereClause, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection());
70+
return getTasksPage(pageable, builder, filterTaskListRequest.orderRequest().sortBy(), filterTaskListRequest.orderRequest().sortDirection());
7071
}
7172

7273
private BooleanBuilder createFilter(FilterTaskListRequest request) {
73-
BooleanBuilder whereClause = new BooleanBuilder();
74+
BooleanBuilder builder = new BooleanBuilder();
7475
if (request.term() != null) {
7576
LocalDateTime fromDate = LocalDateTime.now().minusHours(request.term());
76-
whereClause.and(taskEntity.createdAt.after(fromDate));
77+
builder.and(taskEntity.createdAt.after(fromDate));
7778
}
7879
if (!request.categoryIds().isEmpty()) {
79-
whereClause.and(taskEntity.category.categoryId.in(request.categoryIds()));
80+
builder.and(taskEntity.category.categoryId.in(request.categoryIds()));
8081
}
8182
if (!request.mainCategoryIds().isEmpty()) {
82-
whereClause.and(taskEntity.category.mainCategory.categoryId.in(request.mainCategoryIds()));
83+
builder.and(taskEntity.category.mainCategory.categoryId.in(request.mainCategoryIds()));
8384
}
8485
if (!request.title().isEmpty()) {
85-
whereClause.and(taskEntity.title.containsIgnoreCase(request.title()));
86+
builder.and(taskEntity.title.containsIgnoreCase(request.title()));
8687
}
8788
if (!request.taskStatus().isEmpty()) {
88-
whereClause.and(taskEntity.taskStatus.in(request.taskStatus()));
89+
builder.and(taskEntity.taskStatus.in(request.taskStatus()));
8990
}
90-
return whereClause;
91+
return builder;
9192
}
9293

93-
private Page<TaskEntity> getTasksPage(Pageable pageable, BooleanBuilder whereClause, String sortBy, String sortDirection) {
94+
private Page<TaskEntity> getTasksPage(Pageable pageable, BooleanBuilder builder, String sortBy, String sortDirection) {
9495
OrderSpecifier<?> orderSpecifier = getOrderSpecifier(sortBy, sortDirection);
9596

9697
List<TaskEntity> result = queryFactory
9798
.selectFrom(taskEntity)
98-
.where(whereClause)
99+
.where(builder)
99100
.orderBy(orderSpecifier)
100101
.offset(pageable.getOffset())
101102
.limit(pageable.getPageSize())
102103
.fetch();
103104
long total = queryFactory
104105
.selectFrom(taskEntity)
105-
.where(whereClause)
106+
.where(builder)
106107
.fetch().size();
107108
return new PageImpl<>(result, pageable, total);
108109
}

src/main/java/clap/server/application/port/inbound/log/FindApiLogsUsecase.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22

33

44
import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse;
5+
import clap.server.adapter.inbound.web.dto.log.MemberLogRequest;
56
import clap.server.adapter.inbound.web.dto.log.MemberLogResponse;
67
import clap.server.domain.model.log.ApiLog;
8+
import org.springframework.data.domain.Page;
9+
import org.springframework.data.domain.Pageable;
710

811
import java.util.List;
912

1013
public interface FindApiLogsUsecase {
1114
List<AnonymousLogResponse> getAnonymousLogs();
12-
List<MemberLogResponse> getMemberLogs();
1315
List<ApiLog> getApiLogs();
16+
Page<MemberLogResponse> filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable);
1417
}
Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package clap.server.application.port.outbound.log;
22

3+
import clap.server.adapter.inbound.web.dto.log.MemberLogRequest;
4+
import clap.server.adapter.inbound.web.dto.log.MemberLogResponse;
35
import clap.server.domain.model.log.AnonymousLog;
46
import clap.server.domain.model.log.ApiLog;
57
import clap.server.domain.model.log.MemberLog;
8+
import org.springframework.data.domain.Page;
9+
import org.springframework.data.domain.Pageable;
610

711
import java.util.List;
812

913
public interface LoadLogPort {
1014
List<ApiLog> findAllLogs();
1115
List<AnonymousLog> findAnonymousLogs();
12-
List<MemberLog> findMemberLogs();
16+
17+
Page<MemberLogResponse> filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable);
1318
}

src/main/java/clap/server/application/service/log/FindApiLogsService.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
package clap.server.application.service.log;
22

33
import clap.server.adapter.inbound.web.dto.log.AnonymousLogResponse;
4+
import clap.server.adapter.inbound.web.dto.log.MemberLogRequest;
45
import clap.server.adapter.inbound.web.dto.log.MemberLogResponse;
56
import clap.server.adapter.outbound.persistense.ApiLogPersistenceAdapter;
67
import clap.server.application.mapper.response.LogMapper;
78
import clap.server.application.port.inbound.domain.LoginDomainService;
89
import clap.server.application.port.inbound.log.FindApiLogsUsecase;
10+
import clap.server.application.port.outbound.log.LoadLogPort;
911
import clap.server.common.annotation.architecture.ApplicationService;
1012
import clap.server.domain.model.log.ApiLog;
13+
import clap.server.domain.model.log.MemberLog;
1114
import lombok.RequiredArgsConstructor;
15+
import org.springframework.data.domain.Page;
16+
import org.springframework.data.domain.Pageable;
1217
import org.springframework.transaction.annotation.Transactional;
1318

1419
import java.util.List;
@@ -20,6 +25,7 @@ public class FindApiLogsService implements FindApiLogsUsecase {
2025

2126
private final ApiLogPersistenceAdapter apiLogPersistenceAdapter;
2227
private final LoginDomainService loginDomainService;
28+
private final LoadLogPort loadLogPort;
2329

2430
@Override
2531
public List<AnonymousLogResponse> getAnonymousLogs() {
@@ -31,12 +37,9 @@ public List<AnonymousLogResponse> getAnonymousLogs() {
3137
.toList();
3238
}
3339

34-
//TODO: Paging으로 수정
3540
@Override
36-
public List<MemberLogResponse> getMemberLogs() {
37-
return apiLogPersistenceAdapter.findMemberLogs().stream()
38-
.map(LogMapper::toMemberLogResponse)
39-
.toList();
41+
public Page<MemberLogResponse> filterMemberLogs(MemberLogRequest memberLogRequest, Pageable pageable) {
42+
return loadLogPort.filterMemberLogs(memberLogRequest, pageable);
4043
}
4144

4245
//테스트용

0 commit comments

Comments
 (0)