Skip to content

Commit d58937d

Browse files
authored
[FIX] #701: 솝탬프 캐시 저장 시 기수 정보를 고려하도록 변경 (#702)
2 parents a5d245a + 930f56d commit d58937d

File tree

3 files changed

+71
-32
lines changed

3 files changed

+71
-32
lines changed

src/main/java/org/sopt/app/application/soptamp/SoptampUserService.java

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ public class SoptampUserService {
3636
@Value("${makers.app.soptamp.appjam-mode:false}")
3737
private boolean appjamMode;
3838

39+
@Value("${sopt.current.generation}")
40+
private Long currentGeneration;
41+
3942
/* ==================== 조회/프로필 ==================== */
4043

4144
@Transactional(readOnly = true)
@@ -50,7 +53,7 @@ public SoptampUserInfo editProfileMessage(Long userId, String profileMessage) {
5053
SoptampUser soptampUser = soptampUserRepository.findByUserId(userId)
5154
.orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND));
5255
soptampUser.updateProfileMessage(profileMessage);
53-
eventPublisher.raise(SoptampUserProfileCacheSyncEvent.of(userId));
56+
this.raiseProfileCacheSyncEvent(soptampUser);
5457
return SoptampUserInfo.of(soptampUser);
5558
}
5659

@@ -95,9 +98,8 @@ private void upsertSoptampUserNormal(PlatformUserInfoResponse profile, Long user
9598

9699
private void updateSoptampUserNormal(SoptampUser registeredUser, PlatformUserInfoResponse profile,
97100
PlatformUserInfoResponse.SoptActivities latest) {
98-
Long userId = registeredUser.getUserId();
99101
String part = latest.part() == null ? "미상" : latest.part();
100-
String newNickname = generatePartBasedUniqueNickname(profile.name(), part, userId);
102+
String newNickname = generatePartBasedUniqueNickname(profile.name(), part, registeredUser.getUserId());
101103

102104
registeredUser.initTotalPoints();
103105
registeredUser.updateChangedGenerationInfo(
@@ -106,7 +108,7 @@ private void updateSoptampUserNormal(SoptampUser registeredUser, PlatformUserInf
106108
newNickname
107109
);
108110

109-
eventPublisher.raise(SoptampUserAllCacheSyncEvent.of(userId));
111+
this.raiseAllCacheSyncEvent(registeredUser);
110112
}
111113

112114
private void createSoptampUserNormal(PlatformUserInfoResponse profile, Long userId,
@@ -117,7 +119,7 @@ private void createSoptampUserNormal(PlatformUserInfoResponse profile, Long user
117119
SoptampUser newSoptampUser = createNewSoptampUser(userId, uniqueNickname, (long) profile.lastGeneration(),
118120
findSoptPartByPartName(part));
119121
soptampUserRepository.save(newSoptampUser);
120-
eventPublisher.raise(SoptampUserAllCacheSyncEvent.of(userId));
122+
this.raiseAllCacheSyncEvent(newSoptampUser);
121123
}
122124

123125
private boolean isGenerationChanged(SoptampUser registeredUser, Long profileGeneration) {
@@ -158,7 +160,7 @@ private void upsertSoptampUserForAppjam(PlatformUserInfoResponse profile,
158160

159161
// 앱잼 변환 시점에 한 번 포인트 초기화
160162
registeredUser.initTotalPoints();
161-
eventPublisher.raise(SoptampEvent.SoptampUserAllCacheSyncEvent.of(userId));
163+
this.raiseAllCacheSyncEvent(registeredUser);
162164
}
163165

164166
private void createSoptampUserAppjam(PlatformUserInfoResponse profile,
@@ -183,7 +185,7 @@ private void createSoptampUserAppjam(PlatformUserInfoResponse profile,
183185
newSoptampUser.initTotalPoints(); // 새 시즌이니 0점부터
184186

185187
soptampUserRepository.save(newSoptampUser);
186-
eventPublisher.raise(SoptampUserAllCacheSyncEvent.of(userId));
188+
this.raiseAllCacheSyncEvent(newSoptampUser);
187189
}
188190

189191
private boolean needsAppjamNicknameMigration(SoptampUser user) {
@@ -266,9 +268,7 @@ public void addPointByLevel(Long userId, Integer level) {
266268
.orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND));
267269
soptampUser.addPointsByLevel(level);
268270

269-
if (!appjamMode) {
270-
eventPublisher.raise(SoptampUserScoreCacheSyncEvent.of(userId));
271-
}
271+
this.raiseScoreCacheSyncEvent(soptampUser);
272272
}
273273

274274
@Transactional
@@ -277,9 +277,7 @@ public void subtractPointByLevel(Long userId, Integer level) {
277277
.orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND));
278278
soptampUser.subtractPointsByLevel(level);
279279

280-
if (!appjamMode) {
281-
eventPublisher.raise(SoptampUserScoreCacheSyncEvent.of(userId));
282-
}
280+
this.raiseScoreCacheSyncEvent(soptampUser);
283281
}
284282

285283
@Transactional
@@ -288,9 +286,7 @@ public void initPoint(Long userId) {
288286
.orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND));
289287
soptampUser.initTotalPoints();
290288
soptampUserRepository.save(soptampUser);
291-
if (!appjamMode) {
292-
eventPublisher.raise(SoptampUserScoreCacheSyncEvent.of(userId));
293-
}
289+
this.raiseScoreCacheSyncEvent(soptampUser);
294290
}
295291

296292
@Transactional
@@ -300,7 +296,11 @@ public void initAllSoptampUserPoints() {
300296
soptampUserRepository.saveAll(soptampUserList);
301297
if (!appjamMode) {
302298
rankCacheService.deleteAll();
303-
rankCacheService.addAll(soptampUserList.stream().map(SoptampUserInfo::of).toList());
299+
List<SoptampUserInfo> currentGenerationUserInfos = soptampUserList.stream()
300+
.filter(u -> currentGeneration.equals(u.getGeneration()))
301+
.map(SoptampUserInfo::of)
302+
.toList();
303+
rankCacheService.addAll(currentGenerationUserInfos);
304304
}
305305
}
306306

@@ -310,9 +310,9 @@ public void initSoptampRankCache() {
310310
throw new BadRequestException(ErrorCode.INVALID_APPJAM_SEASON_REQUEST);
311311
}
312312

313-
List<SoptampUser> soptampUserList = soptampUserRepository.findAll();
313+
List<SoptampUser> currentGenerationUsers = soptampUserRepository.findAllByGeneration(currentGeneration);
314314
rankCacheService.deleteAll();
315-
rankCacheService.addAll(soptampUserList.stream().map(SoptampUserInfo::of).toList());
315+
rankCacheService.addAll(currentGenerationUsers.stream().map(SoptampUserInfo::of).toList());
316316
}
317317

318318
@Transactional
@@ -325,4 +325,25 @@ public void deleteAllSoptampUsers() {
325325
public void handleUserWithdrawEvent(final UserWithdrawEvent event) {
326326
soptampUserRepository.deleteByUserId(event.getUserId());
327327
}
328-
}
328+
329+
private void raiseScoreCacheSyncEvent(SoptampUser user) {
330+
if (appjamMode) return;
331+
if (currentGeneration.equals(user.getGeneration())) {
332+
eventPublisher.raise(SoptampUserScoreCacheSyncEvent.of(user.getUserId()));
333+
}
334+
}
335+
336+
private void raiseProfileCacheSyncEvent(SoptampUser user) {
337+
if (appjamMode) return;
338+
if (currentGeneration.equals(user.getGeneration())) {
339+
eventPublisher.raise(SoptampUserProfileCacheSyncEvent.of(user.getUserId()));
340+
}
341+
}
342+
343+
private void raiseAllCacheSyncEvent(SoptampUser user) {
344+
if (appjamMode) return;
345+
if (currentGeneration.equals(user.getGeneration())) {
346+
eventPublisher.raise(SoptampUserAllCacheSyncEvent.of(user.getUserId()));
347+
}
348+
}
349+
}

src/test/java/org/sopt/app/application/SoptampUserServiceTest.java

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.mockito.junit.jupiter.MockitoExtension;
3333
import org.sopt.app.application.platform.dto.PlatformUserInfoResponse;
3434
import org.sopt.app.application.platform.dto.PlatformUserInfoResponse.SoptActivities;
35+
import org.sopt.app.application.rank.RankCacheService;
3536
import org.sopt.app.application.soptamp.SoptampEvent.SoptampUserAllCacheSyncEvent;
3637
import org.sopt.app.application.soptamp.SoptampEvent.SoptampUserProfileCacheSyncEvent;
3738
import org.sopt.app.application.soptamp.SoptampEvent.SoptampUserScoreCacheSyncEvent;
@@ -58,12 +59,21 @@ class SoptampUserServiceTest {
5859
@Mock
5960
AppjamUserRepository appjamUserRepository;
6061

62+
@Mock
63+
RankCacheService rankCacheService;
64+
6165
@Mock
6266
EventPublisher eventPublisher;
6367

6468
@InjectMocks
6569
SoptampUserService soptampUserService;
6670

71+
@BeforeEach
72+
void setUp() {
73+
ReflectionTestUtils.setField(soptampUserService, "appjamMode", false);
74+
ReflectionTestUtils.setField(soptampUserService, "currentGeneration", 37L);
75+
}
76+
6777
private PlatformUserInfoResponse buildProfile(String name, int lastGeneration, String part) {
6878
return buildProfile(name, lastGeneration, part, true); // SOPT 정규 활동 기본값
6979
}
@@ -87,18 +97,13 @@ private PlatformUserInfoResponse buildProfile(String name, int lastGeneration, S
8797
);
8898
}
8999

90-
@BeforeEach
91-
void setUp() {
92-
// 기본은 NORMAL 모드로 두고, 테스트에서 필요할 때 변경
93-
ReflectionTestUtils.setField(soptampUserService, "appjamMode", false);
94-
}
95-
96100
/* ==================== NORMAL 모드 테스트 ==================== */
97101

98102
@Test
99103
@DisplayName("NORMAL 모드 - 프로필이 null이면 아무 동작도 하지 않는다")
100104
void 일반모드_프로필널이면_동작없음() {
101105
// given
106+
ReflectionTestUtils.setField(soptampUserService, "appjamMode", false);
102107
final long userId = 1L;
103108

104109
// when
@@ -112,6 +117,7 @@ void setUp() {
112117
@DisplayName("NORMAL 모드 - 활동 내역이 없으면 아무 동작도 하지 않는다")
113118
void 일반모드_활동내역없으면_동작없음() {
114119
// given
120+
ReflectionTestUtils.setField(soptampUserService, "appjamMode", false);
115121
final long userId = 1L;
116122

117123
PlatformUserInfoResponse profile = new PlatformUserInfoResponse(
@@ -197,6 +203,8 @@ void setUp() {
197203
void 일반모드_기수변경되면_닉네임재생성과_포인트리셋() {
198204
// given
199205
ReflectionTestUtils.setField(soptampUserService, "appjamMode", false);
206+
// 기수 필터링을 위해 현재 기수를 38로 임시 설정
207+
ReflectionTestUtils.setField(soptampUserService, "currentGeneration", 38L);
200208

201209
final long userId = 1L;
202210
PlatformUserInfoResponse profile = buildProfile("김솝트", 38, "서버");
@@ -232,6 +240,7 @@ void setUp() {
232240
@DisplayName("NORMAL 모드 - isSopt=false인 Makers 활동만 있으면 SoptampUser를 생성하지 않는다")
233241
void 일반모드_Makers이면_솝탬프유저_생성안함() {
234242
// given
243+
ReflectionTestUtils.setField(soptampUserService, "appjamMode", false);
235244
final long userId = 1L;
236245
// "백엔드" 파트, isSopt=false → getLatestSoptActivity() = null
237246
PlatformUserInfoResponse profile = buildProfile("김솝트", 37, "백엔드", false);
@@ -247,6 +256,7 @@ void setUp() {
247256
@DisplayName("NORMAL 모드 - 메이커스(isSopt=false)이면서 동시에 SOPT 서버 파트(isSopt=true) 활동이 있으면 SOPT 활동 기준으로 생성된다")
248257
void 일반모드_메이커스이면서_솝트파트있으면_솝트파트기준으로_생성() {
249258
// given
259+
ReflectionTestUtils.setField(soptampUserService, "appjamMode", false);
250260
final long userId = 1L;
251261
PlatformUserInfoResponse.SoptActivities presidentActivity =
252262
new PlatformUserInfoResponse.SoptActivities(1, 37, "BE", null, false);
@@ -312,7 +322,8 @@ void setUp() {
312322
assertThat(saved.getTotalPoints()).isZero();
313323
assertThat(saved.getGeneration()).isEqualTo(37L);
314324

315-
verify(eventPublisher).raise(any(SoptampUserAllCacheSyncEvent.class));
325+
// 앱잼 모드에서는 이벤트를 발행하지 않음 (Helper 로직 기준)
326+
verify(eventPublisher, never()).raise(any());
316327
}
317328

318329
@Test
@@ -341,7 +352,7 @@ void setUp() {
341352
assertThat(saved.getNickname()).contains("김솝트");
342353
assertThat(saved.getTotalPoints()).isZero();
343354

344-
verify(eventPublisher).raise(any(SoptampUserAllCacheSyncEvent.class));
355+
verify(eventPublisher, never()).raise(any());
345356
}
346357

347358
@Test
@@ -370,7 +381,7 @@ void setUp() {
370381
assertThat(saved.getNickname()).contains("김솝트");
371382
assertThat(saved.getTotalPoints()).isZero();
372383

373-
verify(eventPublisher).raise(any(SoptampUserAllCacheSyncEvent.class));
384+
verify(eventPublisher, never()).raise(any());
374385
}
375386

376387
@Test
@@ -416,7 +427,7 @@ void setUp() {
416427
assertThat(existing.getTotalPoints()).isZero();
417428
assertThat(existing.getGeneration()).isEqualTo(37L);
418429

419-
verify(eventPublisher).raise(any(SoptampUserAllCacheSyncEvent.class));
430+
verify(eventPublisher, never()).raise(any());
420431
}
421432

422433
@Test
@@ -494,15 +505,14 @@ void setUp() {
494505
assertThat(existing.getNickname()).isEqualTo("비트김솝트A");
495506
assertThat(existing.getTotalPoints()).isZero();
496507

497-
verify(eventPublisher).raise(any(SoptampUserAllCacheSyncEvent.class));
508+
verify(eventPublisher, never()).raise(any());
498509
}
499510

500511
@Test
501512
@DisplayName("NORMAL 모드 - 다른 유저가 같은 파트 기반 닉네임을 쓰고 있으면 접미사 A를 붙인다")
502513
void 일반모드_닉네임충돌시_접미사A추가() {
503514
// given
504515
ReflectionTestUtils.setField(soptampUserService, "appjamMode", false);
505-
506516
final long userId = 1L;
507517
PlatformUserInfoResponse profile = buildProfile("김솝트", 37, "서버");
508518

src/test/java/org/sopt/app/application/soptamp/SoptampCacheSyncIntegrationTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import static org.mockito.Mockito.never;
55
import static org.mockito.Mockito.verify;
66

7+
import org.junit.jupiter.api.BeforeEach;
78
import org.junit.jupiter.api.DisplayName;
89
import org.junit.jupiter.api.Test;
910
import org.sopt.app.application.rank.RankCacheService;
@@ -13,6 +14,7 @@
1314
import org.springframework.beans.factory.annotation.Autowired;
1415
import org.springframework.boot.test.mock.mockito.MockBean;
1516
import org.springframework.context.annotation.Import;
17+
import org.springframework.test.util.ReflectionTestUtils;
1618

1719
@Import({SoptampUserService.class, SoptampEventListener.class, EventPublisher.class})
1820
class SoptampCacheSyncIntegrationTest extends IntegrationTestSupport {
@@ -23,6 +25,12 @@ class SoptampCacheSyncIntegrationTest extends IntegrationTestSupport {
2325
@MockBean
2426
private RankCacheService rankCacheService;
2527

28+
@BeforeEach
29+
void setUp() {
30+
ReflectionTestUtils.setField(soptampUserService, "currentGeneration", 37L);
31+
ReflectionTestUtils.setField(soptampUserService, "appjamMode", false);
32+
}
33+
2634
@Test
2735
@DisplayName("FAILURE_트랜잭션 롤백 시 AFTER_COMMIT 리스너가 실행되지 않음")
2836
void FAILURE_rollback_prevent_cache_sync() {

0 commit comments

Comments
 (0)