Skip to content

Commit ab4ead3

Browse files
authored
Refactor : auth and jpa flush (#74)
* chore(*): remove invalid swagger server * refactor: modulize security configuration * refactor: remove unneccesary utils * refactor: group test utils * fix: recognize `MEMBER_ROLE` properly * refactor: move filter modules under `filter` packages * fix: response error-code when attendance auth failed * feat: log when auth failed * chore: turn off `open-in-view` * fix: revert `MEMBER_ROLE` name and change JWT claim * fix: remove unneccesary flush and use attached entity * test: apply attend role * refactor: log request user email when auth failed * refactor: register cors config to bean * refactor: utilize JPA's sorting * style: reformat all
1 parent b405d5d commit ab4ead3

File tree

59 files changed

+451
-455
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+451
-455
lines changed

build.gradle

-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,6 @@ sonar {
116116

117117
openapi3 {
118118
servers = [
119-
{ url = 'https://gdsc-konkuk.dev' },
120119
{ url = 'https://api.gdsc-konkuk.dev' },
121120
{ url = 'https://stage.gdsc-konkuk.dev' },
122121
{ url = 'http://localhost:8080' },

sql/v1.sql

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
-- 1. Add new required columns to attendance table
22
ALTER TABLE gdsc.attendance
3-
ADD COLUMN title varchar(255) NULL,
3+
ADD COLUMN title varchar(255) NULL,
44
ADD COLUMN attendance_time datetime(6) NULL;
55

66
-- 2. Migrate data from event table to attendance table
77
UPDATE gdsc.attendance a
8-
JOIN gdsc.event e ON a.event_id = e.id
9-
SET a.title = e.title,
10-
a.attendance_time = e.start_at;
8+
JOIN gdsc.event e
9+
ON a.event_id = e.id
10+
SET a.title = e.title, a.attendance_time = e.start_at;
1111

1212
-- 3. Modify member table
1313
-- 3.1. Rename member_id to student_id
@@ -41,15 +41,17 @@ DROP TABLE gdsc.event;
4141
-- 5.3 Drop unused columns
4242
ALTER TABLE gdsc.attendance
4343
DROP COLUMN event_id;
44+
4445
ALTER TABLE gdsc.member
4546
DROP COLUMN password,
4647
MODIFY COLUMN member_role enum ('CORE', 'LEAD', 'MEMBER') NOT NULL;
48+
4749
ALTER TABLE gdsc.participant
4850
DROP COLUMN attendance;
4951

5052
-- 6. Make migrated columns non-nullable
5153
ALTER TABLE gdsc.attendance
52-
MODIFY COLUMN title varchar(255) NOT NULL,
54+
MODIFY COLUMN title varchar (255) NOT NULL,
5355
MODIFY COLUMN attendance_time datetime(6) NOT NULL;
5456

5557
-- 7. Migration complete

src/main/java/gdsc/konkuk/platformcore/application/attendance/AttendanceService.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ public Attendance registerAttendance(String title, String batch) {
5252
Attendance newAttendance = Attendance.builder().title(title)
5353
.attendanceTime(LocalDateTime.now()).build();
5454
newAttendance.generateQr();
55-
attendanceRepository.saveAndFlush(newAttendance);
55+
Attendance savedAttendance = attendanceRepository.save(newAttendance);
5656

5757
List<Member> members = memberRepository.findAllByBatch(batch);
58-
registerParticipants(newAttendance, members);
58+
registerParticipants(savedAttendance, members);
5959
return newAttendance;
6060
}
6161

src/main/java/gdsc/konkuk/platformcore/application/attendance/dtos/MemberAttendanceInfo.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ public class MemberAttendanceInfo {
2020

2121
public static MemberAttendanceInfo from(MemberAttendanceQueryDto attendanceInfo) {
2222
return MemberAttendanceInfo.builder()
23-
.memberId(attendanceInfo.getMemberId())
24-
.attendanceDate(attendanceInfo.getAttendanceDate())
25-
.participantId(attendanceInfo.getParticipantId())
26-
.attendanceId(attendanceInfo.getAttendanceId())
27-
.attendanceType(attendanceInfo.getAttendanceType())
28-
.build();
23+
.memberId(attendanceInfo.getMemberId())
24+
.attendanceDate(attendanceInfo.getAttendanceDate())
25+
.participantId(attendanceInfo.getParticipantId())
26+
.attendanceId(attendanceInfo.getAttendanceId())
27+
.attendanceType(attendanceInfo.getAttendanceType())
28+
.build();
2929
}
3030
}

src/main/java/gdsc/konkuk/platformcore/application/auth/CustomOAuthUserService.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2Authenticatio
2828
return processOAuth2User(oidcUser);
2929
} catch (Exception ex) {
3030
throw new OAuth2AuthenticationException(
31-
new OAuth2Error("processing_error", "Failed to process user info", null));
31+
new OAuth2Error("processing_error",
32+
"Failed to process user info - " + userRequest.getIdToken().getEmail(),
33+
null));
3234
}
3335
}
3436

src/main/java/gdsc/konkuk/platformcore/application/email/EmailService.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ public class EmailService {
2424
private final EmailTaskRepository emailTaskRepository;
2525

2626
public List<EmailTask> getAllTaskAsList() {
27-
return emailTaskRepository.findAll();
27+
return emailTaskRepository.findAllByOrderBySendAtDesc();
2828
}
29+
2930
public List<EmailTask> getTasksInIds(List<Long> emailIds) {
3031
return emailTaskRepository.findAllById(emailIds);
3132
}

src/main/java/gdsc/konkuk/platformcore/application/email/EmailTaskFacade.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
import org.springframework.stereotype.Service;
1111
import org.springframework.transaction.annotation.Transactional;
1212

13-
import static org.hibernate.internal.util.collections.ArrayHelper.forEach;
14-
1513
@Service
1614
@RequiredArgsConstructor
1715
@Transactional
@@ -46,12 +44,14 @@ public void cancelAll(List<Long> emailIds) {
4644
}
4745

4846
private void cancelIfTaskNotProcessed(EmailTask emailTask) {
49-
if(emailTask.isSent()) return;
47+
if (emailTask.isSent()) {
48+
return;
49+
}
5050
emailTaskScheduler.cancelTask(String.valueOf(emailTask.getId()));
5151
}
5252

5353
private void cancelUnProcessedTasks(List<EmailTask> taskList) {
54-
for(EmailTask task : taskList) {
54+
for (EmailTask task : taskList) {
5555
cancelIfTaskNotProcessed(task);
5656
}
5757
}

src/main/java/gdsc/konkuk/platformcore/application/email/EmailTaskScheduler.java

+14-16
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,20 @@ public void scheduleSyncTask(Object emailTask, long delay) {
4141
Long id = email.getId();
4242

4343
Runnable sendEmailTask =
44-
() -> {
45-
transactionTemplate.execute(status -> {
46-
try {
47-
EmailTask sendingTask = emailService.findById(id);
48-
emailClient.sendEmailToReceivers(sendingTask);
49-
emailService.markAsCompleted(id);
50-
} catch (Exception e) {
51-
log.error("[ERROR] : 이메일 전송과정에서 에러가 발생했습니다.", e);
52-
discordClient.sendErrorMessage(e);
53-
status.setRollbackOnly();
54-
} finally {
55-
taskInMemoryRepository.removeTask(String.valueOf(id));
56-
}
57-
return null;
58-
});
59-
};
44+
() -> transactionTemplate.execute(status -> {
45+
try {
46+
EmailTask sendingTask = emailService.findById(id);
47+
emailClient.sendEmailToReceivers(sendingTask);
48+
emailService.markAsCompleted(id);
49+
} catch (Exception e) {
50+
log.error("[ERROR] : 이메일 전송과정에서 에러가 발생했습니다.", e);
51+
discordClient.sendErrorMessage(e);
52+
status.setRollbackOnly();
53+
} finally {
54+
taskInMemoryRepository.removeTask(String.valueOf(id));
55+
}
56+
return null;
57+
});
6058
ScheduledFuture<?> future = executor.schedule(sendEmailTask, delay, SECONDS);
6159
taskInMemoryRepository.addTask(String.valueOf(email.getId()), future);
6260
}

src/main/java/gdsc/konkuk/platformcore/application/member/MemberAttendanceAggregator.java

+60-49
Original file line numberDiff line numberDiff line change
@@ -4,65 +4,76 @@
44
import gdsc.konkuk.platformcore.application.attendance.dtos.MemberAttendanceQueryDto;
55
import gdsc.konkuk.platformcore.application.member.dtos.MemberAttendanceAggregate;
66
import gdsc.konkuk.platformcore.application.member.dtos.MemberAttendanceInfos;
7-
87
import java.util.HashMap;
98
import java.util.List;
109
import java.util.Map;
1110

1211
public class MemberAttendanceAggregator {
13-
public static List<MemberAttendanceAggregate> process(final List<MemberAttendanceQueryDto> queryDtos) {
14-
Map<Long, MemberAttendanceAggregate> memberAttendanceAggregates = new HashMap<>();
15-
Map<Long, MemberAttendanceInfos> memberAttendanceInfos = new HashMap<>();
16-
aggregateMemberAttendances(memberAttendanceAggregates, memberAttendanceInfos, queryDtos);
17-
validateAggregation(memberAttendanceAggregates, memberAttendanceInfos);
18-
return mergeIntoList(memberAttendanceAggregates, memberAttendanceInfos);
19-
}
2012

21-
/***
22-
* 사용자별 출석 정보를 집계하는 함수로 사용자별 출석 통계 정보와 출석 상세 정보를 집계한다.
23-
* @param memberAttendanceAggregates 출석 통계 정보
24-
* @param memberAttendanceInfos 출석 상세 정보
25-
* @param queries 출석정보 쿼리 dto
26-
*/
27-
private static void aggregateMemberAttendances(
28-
final Map<Long, MemberAttendanceAggregate> memberAttendanceAggregates,
29-
final Map<Long, MemberAttendanceInfos> memberAttendanceInfos,
30-
final List<MemberAttendanceQueryDto> queries) {
31-
for (MemberAttendanceQueryDto queryDto : queries) {
32-
memberAttendanceAggregates.putIfAbsent(queryDto.getMemberId(), MemberAttendanceAggregate.from(queryDto));
33-
addToMemberAttendanceRecords(memberAttendanceInfos, queryDto);
13+
public static List<MemberAttendanceAggregate> process(
14+
final List<MemberAttendanceQueryDto> queryDtos) {
15+
Map<Long, MemberAttendanceAggregate> memberAttendanceAggregates = new HashMap<>();
16+
Map<Long, MemberAttendanceInfos> memberAttendanceInfos = new HashMap<>();
17+
aggregateMemberAttendances(memberAttendanceAggregates, memberAttendanceInfos, queryDtos);
18+
validateAggregation(memberAttendanceAggregates, memberAttendanceInfos);
19+
return mergeIntoList(memberAttendanceAggregates, memberAttendanceInfos);
20+
}
21+
22+
/**
23+
* 사용자별 출석 정보를 집계하는 함수로 사용자별 출석 통계 정보와 출석 상세 정보를 집계한다.
24+
*
25+
* @param memberAttendanceAggregates 출석 통계 정보
26+
* @param memberAttendanceInfos 출석 상세 정보
27+
* @param queries 출석정보 쿼리 dto
28+
*/
29+
private static void aggregateMemberAttendances(
30+
final Map<Long, MemberAttendanceAggregate> memberAttendanceAggregates,
31+
final Map<Long, MemberAttendanceInfos> memberAttendanceInfos,
32+
final List<MemberAttendanceQueryDto> queries) {
33+
for (MemberAttendanceQueryDto queryDto : queries) {
34+
memberAttendanceAggregates.putIfAbsent(queryDto.getMemberId(),
35+
MemberAttendanceAggregate.from(queryDto));
36+
addToMemberAttendanceRecords(memberAttendanceInfos, queryDto);
37+
}
3438
}
35-
}
3639

37-
// query 데이터를 읽고 사용자별 기록에 추가하는 함수
38-
private static void addToMemberAttendanceRecords(final Map<Long, MemberAttendanceInfos> attendanceRecordsMap,
39-
final MemberAttendanceQueryDto queryDto) {
40-
MemberAttendanceInfos records = attendanceRecordsMap
41-
.computeIfAbsent(queryDto.getMemberId(), key -> new MemberAttendanceInfos());
42-
records.addAttendanceInfo(MemberAttendanceInfo.from(queryDto));
43-
}
40+
/**
41+
* query 데이터를 읽고 사용자별 기록에 추가하는 함수
42+
*/
43+
private static void addToMemberAttendanceRecords(
44+
final Map<Long, MemberAttendanceInfos> attendanceRecordsMap,
45+
final MemberAttendanceQueryDto queryDto) {
46+
MemberAttendanceInfos records = attendanceRecordsMap
47+
.computeIfAbsent(queryDto.getMemberId(), key -> new MemberAttendanceInfos());
48+
records.addAttendanceInfo(MemberAttendanceInfo.from(queryDto));
49+
}
4450

45-
private static void validateAggregation(final Map<Long, MemberAttendanceAggregate> memberAttendanceSummary,
46-
final Map<Long, MemberAttendanceInfos> memberAttendanceDetails) {
47-
if(memberAttendanceSummary.size() != memberAttendanceDetails.size()) {
48-
throw new IllegalStateException("Member attendance summary and details are not matched");
51+
private static void validateAggregation(
52+
final Map<Long, MemberAttendanceAggregate> memberAttendanceSummary,
53+
final Map<Long, MemberAttendanceInfos> memberAttendanceDetails) {
54+
if (memberAttendanceSummary.size() != memberAttendanceDetails.size()) {
55+
throw new IllegalStateException(
56+
"Member attendance summary and details are not matched");
57+
}
4958
}
50-
}
5159

52-
/***
53-
* 사용자별 출석 통계 정보에 출석 상세 정보들을 추가하고 리스트로 반환하는 함수
54-
* @param memberAttendanceAggregates 출석 통계 정보
55-
* @param memberAttendanceInfos 출석 상세 정보
56-
* @return 사용자별 출석 통계 정보 리스트
57-
*/
58-
private static List<MemberAttendanceAggregate> mergeIntoList(final Map<Long, MemberAttendanceAggregate> memberAttendanceAggregates,
59-
final Map<Long, MemberAttendanceInfos> memberAttendanceInfos) {
60-
for (Map.Entry<Long, MemberAttendanceInfos> memberAttendance : memberAttendanceInfos.entrySet()) {
61-
MemberAttendanceAggregate aggregate = memberAttendanceAggregates.get(memberAttendance.getKey());
62-
aggregate.setAttendanceInfoList(memberAttendance.getValue().getAttendanceInfoList());
63-
aggregate.setTotalAttendances(memberAttendance.getValue().getTotalAttendances());
64-
aggregate.setActualAttendances(memberAttendance.getValue().getActualAttendances());
60+
/**
61+
* 사용자별 출석 통계 정보에 출석 상세 정보들을 추가하고 리스트로 반환하는 함수
62+
*
63+
* @param memberAttendanceAggregates 출석 통계 정보
64+
* @param memberAttendanceInfos 출석 상세 정보
65+
* @return 사용자별 출석 통계 정보 리스트
66+
*/
67+
private static List<MemberAttendanceAggregate> mergeIntoList(
68+
final Map<Long, MemberAttendanceAggregate> memberAttendanceAggregates,
69+
final Map<Long, MemberAttendanceInfos> memberAttendanceInfos) {
70+
for (Map.Entry<Long, MemberAttendanceInfos> memberAttendance : memberAttendanceInfos.entrySet()) {
71+
MemberAttendanceAggregate aggregate = memberAttendanceAggregates.get(
72+
memberAttendance.getKey());
73+
aggregate.setAttendanceInfoList(memberAttendance.getValue().getAttendanceInfoList());
74+
aggregate.setTotalAttendances(memberAttendance.getValue().getTotalAttendances());
75+
aggregate.setActualAttendances(memberAttendance.getValue().getActualAttendances());
76+
}
77+
return memberAttendanceAggregates.values().stream().toList();
6578
}
66-
return memberAttendanceAggregates.values().stream().toList();
67-
}
6879
}
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,40 @@
11
package gdsc.konkuk.platformcore.application.member;
22

3+
import static java.util.function.Function.identity;
4+
import static java.util.stream.Collectors.toMap;
5+
36
import gdsc.konkuk.platformcore.application.member.exceptions.MemberErrorCode;
47
import gdsc.konkuk.platformcore.application.member.exceptions.UserNotFoundException;
58
import gdsc.konkuk.platformcore.domain.member.entity.Member;
69
import gdsc.konkuk.platformcore.domain.member.repository.MemberRepository;
7-
import lombok.RequiredArgsConstructor;
8-
import org.springframework.stereotype.Component;
9-
1010
import java.util.List;
1111
import java.util.Map;
1212
import java.util.Optional;
13-
14-
import static java.util.function.Function.identity;
15-
import static java.util.stream.Collectors.toMap;
13+
import lombok.RequiredArgsConstructor;
14+
import org.springframework.stereotype.Component;
1615

1716
@Component
1817
@RequiredArgsConstructor
1918
public class MemberFinder {
2019

21-
private final MemberRepository memberRepository;
20+
private final MemberRepository memberRepository;
2221

23-
public Member fetchMemberById(Long memberId) {
24-
return memberRepository
25-
.findById(memberId)
26-
.orElseThrow(() -> UserNotFoundException.of(MemberErrorCode.USER_NOT_FOUND));
27-
}
22+
public Member fetchMemberById(Long memberId) {
23+
return memberRepository
24+
.findById(memberId)
25+
.orElseThrow(() -> UserNotFoundException.of(MemberErrorCode.USER_NOT_FOUND));
26+
}
2827

29-
public Map<Long, Member> fetchMembersByIdsAndBatch(List<Long> memberIds, String batch) {
30-
List<Member> members = memberRepository.findAllByIdsAndBatch(memberIds, batch);
31-
if (members.size() != memberIds.size()) {
32-
throw UserNotFoundException.of(MemberErrorCode.USER_NOT_FOUND);
28+
public Map<Long, Member> fetchMembersByIdsAndBatch(List<Long> memberIds, String batch) {
29+
List<Member> members = memberRepository.findAllByIdsAndBatch(memberIds, batch);
30+
if (members.size() != memberIds.size()) {
31+
throw UserNotFoundException.of(MemberErrorCode.USER_NOT_FOUND);
32+
}
33+
return members.stream().collect(toMap(Member::getId, identity()));
3334
}
34-
return members.stream().collect(toMap(Member::getId, identity()));
35-
}
3635

37-
public boolean checkMemberExistWithStudentId(String studentId) {
38-
Optional<Member> member = memberRepository.findByStudentId(studentId);
39-
return member.isPresent();
40-
}
36+
public boolean checkMemberExistWithStudentId(String studentId) {
37+
Optional<Member> member = memberRepository.findByStudentId(studentId);
38+
return member.isPresent();
39+
}
4140
}

0 commit comments

Comments
 (0)