diff --git a/src/main/java/com/process/clash/adapter/web/compete/rival/rival/dto/GetMyRivalActingDto.java b/src/main/java/com/process/clash/adapter/web/compete/rival/rival/dto/GetMyRivalActingDto.java index 3302e6caf..2e7f8b00f 100644 --- a/src/main/java/com/process/clash/adapter/web/compete/rival/rival/dto/GetMyRivalActingDto.java +++ b/src/main/java/com/process/clash/adapter/web/compete/rival/rival/dto/GetMyRivalActingDto.java @@ -1,6 +1,7 @@ package com.process.clash.adapter.web.compete.rival.rival.dto; import com.process.clash.application.compete.rival.rival.data.GetMyRivalActingData; +import com.process.clash.application.profile.data.EquippedItemsData; import com.process.clash.application.realtime.data.UserActivityStatus; import java.util.List; @@ -24,7 +25,8 @@ public static Response from(GetMyRivalActingData.Result result) { data.usingApp(), data.isStudying(), data.status(), - data.tier() + data.tier(), + data.equippedItems() )) .toList() ); @@ -41,6 +43,7 @@ private record MyRival( String usingApp, Boolean isStudying, UserActivityStatus status, - String tier + String tier, + EquippedItemsData equippedItems ) {} } diff --git a/src/main/java/com/process/clash/adapter/web/ranking/docs/controller/RankingControllerDocument.java b/src/main/java/com/process/clash/adapter/web/ranking/docs/controller/RankingControllerDocument.java index e675b00a9..7d700d609 100644 --- a/src/main/java/com/process/clash/adapter/web/ranking/docs/controller/RankingControllerDocument.java +++ b/src/main/java/com/process/clash/adapter/web/ranking/docs/controller/RankingControllerDocument.java @@ -41,9 +41,9 @@ public interface RankingControllerDocument { "point": 1200, "tier": "AURA", "equippedItems": { - "insigma": { + "insignia": { "id": 1, - "name": "기본 인시그니아", + "name": "기본 휘장", "image": "https://cdn.example.com/items/insignia.png" }, "nameplate": null, @@ -58,7 +58,7 @@ public interface RankingControllerDocument { "point": 980, "tier": "GOLD", "equippedItems": { - "insigma": null, + "insignia": null, "nameplate": null, "banner": null } diff --git a/src/main/java/com/process/clash/adapter/web/ranking/docs/response/GetRankingResponseDocument.java b/src/main/java/com/process/clash/adapter/web/ranking/docs/response/GetRankingResponseDocument.java index 0b03f9075..442b78ba3 100644 --- a/src/main/java/com/process/clash/adapter/web/ranking/docs/response/GetRankingResponseDocument.java +++ b/src/main/java/com/process/clash/adapter/web/ranking/docs/response/GetRankingResponseDocument.java @@ -57,8 +57,8 @@ public static class UserRankingDoc { @Schema(name = "EquippedItems", description = "장착 아이템 정보") public static class EquippedItemsDoc { - @Schema(description = "인시그니아") - private EquippedItemDoc insigma; + @Schema(description = "휘장") + private EquippedItemDoc insignia; @Schema(description = "네임플레이트") private EquippedItemDoc nameplate; @@ -73,7 +73,7 @@ public static class EquippedItemDoc { @Schema(description = "아이템 ID", example = "1") private Long id; - @Schema(description = "아이템 이름", example = "기본 인시그니아") + @Schema(description = "아이템 이름", example = "기본 휘장") private String name; @Schema(description = "아이템 이미지 URL", example = "https://cdn.example.com/items/insignia.png") diff --git a/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyItemDocument.java b/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyItemDocument.java index 84c8f514d..154df535b 100644 --- a/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyItemDocument.java +++ b/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyItemDocument.java @@ -8,7 +8,7 @@ public class GetMyItemDocument { @Schema(description = "아이템 ID", example = "1") public Long id; - @Schema(description = "아이템 제목", example = "기본 인시그니아") + @Schema(description = "아이템 제목", example = "기본 휘장") public String title; @Schema(description = "아이템 이미지 URL", example = "https://cdn.example.com/items/insignia.png") diff --git a/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyItemsResponseDocument.java b/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyItemsResponseDocument.java index d4585e4b3..6e0224760 100644 --- a/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyItemsResponseDocument.java +++ b/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyItemsResponseDocument.java @@ -13,7 +13,7 @@ "items": [ { "id": 1, - "title": "기본 인시그니아", + "title": "기본 휘장", "image": "https://cdn.example.com/items/insignia.png", "description": "기본 아이템", "category": "INSIGNIA", diff --git a/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyProfileDataDocument.java b/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyProfileDataDocument.java index 85f2576b6..2f1ce19ac 100644 --- a/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyProfileDataDocument.java +++ b/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyProfileDataDocument.java @@ -58,8 +58,8 @@ public class GetMyProfileDataDocument { @Schema(description = "장착 아이템") public static class EquippedItemsDocument { - @Schema(description = "인시그니아") - public EquippedItemDocument insigma; + @Schema(description = "휘장") + public EquippedItemDocument insignia; @Schema(description = "네임플레이트") public EquippedItemDocument nameplate; @@ -73,7 +73,7 @@ public static class EquippedItemDocument { @Schema(description = "아이템 ID", example = "1") public Long id; - @Schema(description = "아이템 이름", example = "기본 인시그니아") + @Schema(description = "아이템 이름", example = "기본 휘장") public String name; @Schema(description = "아이템 이미지 URL", example = "https://cdn.example.com/items/insignia.png") diff --git a/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyProfileResponseDocument.java b/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyProfileResponseDocument.java index 5e5925f9e..9f2f98298 100644 --- a/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyProfileResponseDocument.java +++ b/src/main/java/com/process/clash/adapter/web/user/docs/response/GetMyProfileResponseDocument.java @@ -26,9 +26,9 @@ "githubLinked": true, "activityStatus": "ONLINE", "equippedItems": { - "insigma": { + "insignia": { "id": 1, - "name": "기본 인시그니아", + "name": "기본 휘장", "image": "https://cdn.example.com/items/insignia.png" }, "nameplate": null, diff --git a/src/main/java/com/process/clash/adapter/web/user/dto/EquippedItemsDto.java b/src/main/java/com/process/clash/adapter/web/user/dto/EquippedItemsDto.java index e408ab0dc..dfcdddb32 100644 --- a/src/main/java/com/process/clash/adapter/web/user/dto/EquippedItemsDto.java +++ b/src/main/java/com/process/clash/adapter/web/user/dto/EquippedItemsDto.java @@ -5,7 +5,7 @@ public class EquippedItemsDto { public record Response( - Item insigma, + Item insignia, Item nameplate, Item banner ) { @@ -14,7 +14,7 @@ public static Response from(EquippedItemsData data) { return new Response(null, null, null); } return new Response( - Item.from(data.insigma()), + Item.from(data.insignia()), Item.from(data.nameplate()), Item.from(data.banner()) ); diff --git a/src/main/java/com/process/clash/application/compete/rival/rival/data/GetMyRivalActingData.java b/src/main/java/com/process/clash/application/compete/rival/rival/data/GetMyRivalActingData.java index c1a41dc71..3235c3f70 100644 --- a/src/main/java/com/process/clash/application/compete/rival/rival/data/GetMyRivalActingData.java +++ b/src/main/java/com/process/clash/application/compete/rival/rival/data/GetMyRivalActingData.java @@ -1,6 +1,7 @@ package com.process.clash.application.compete.rival.rival.data; import com.process.clash.application.common.actor.Actor; +import com.process.clash.application.profile.data.EquippedItemsData; import com.process.clash.application.realtime.data.UserActivityStatus; import com.process.clash.domain.user.user.entity.User; @@ -38,7 +39,8 @@ public record MyRival( String usingApp, Boolean isStudying, UserActivityStatus status, - String tier + String tier, + EquippedItemsData equippedItems ) { public static MyRival of( @@ -47,7 +49,8 @@ public static MyRival of( Long activeTime, String usingApp, Boolean isStudying, - UserActivityStatus status + UserActivityStatus status, + EquippedItemsData equippedItems ) { return new MyRival( rivalId, @@ -59,7 +62,8 @@ public static MyRival of( usingApp, isStudying, status, - user.tier() + user.tier(), + equippedItems ); } } diff --git a/src/main/java/com/process/clash/application/compete/rival/rival/service/GetMyRivalActingService.java b/src/main/java/com/process/clash/application/compete/rival/rival/service/GetMyRivalActingService.java index cedae4802..4a935c896 100644 --- a/src/main/java/com/process/clash/application/compete/rival/rival/service/GetMyRivalActingService.java +++ b/src/main/java/com/process/clash/application/compete/rival/rival/service/GetMyRivalActingService.java @@ -3,6 +3,8 @@ import com.process.clash.application.compete.rival.rival.data.GetMyRivalActingData; import com.process.clash.application.compete.rival.rival.port.in.GetMyRivalActingUseCase; import com.process.clash.application.compete.rival.rival.port.out.RivalRepositoryPort; +import com.process.clash.application.profile.data.EquippedItemsData; +import com.process.clash.application.profile.service.EquippedItemsAssembler; import com.process.clash.application.realtime.data.UserActivityStatus; import com.process.clash.application.realtime.port.out.UserPresencePort; import com.process.clash.application.record.v2.port.out.RecordSessionV2RepositoryPort; @@ -37,6 +39,7 @@ public class GetMyRivalActingService implements GetMyRivalActingUseCase { private final UserPresencePort userPresencePort; private final RecordProperties recordProperties; private final ZoneId recordZoneId; + private final EquippedItemsAssembler equippedItemsAssembler; @Override public GetMyRivalActingData.Result execute(GetMyRivalActingData.Command command) { @@ -86,6 +89,11 @@ public GetMyRivalActingData.Result execute(GetMyRivalActingData.Command command) Map activityStatusByUserId = userPresencePort.getStatuses(opponentIds); + List rivalIds = opponentIds.stream() + .filter(id -> !id.equals(command.actor().id())) + .toList(); + Map equippedItemsMap = equippedItemsAssembler.loadByUserIds(rivalIds); + Long myId = command.actor().id(); List myRivals = rivals.stream() @@ -112,13 +120,16 @@ public GetMyRivalActingData.Result execute(GetMyRivalActingData.Command command) String usingApp = resolveUsingApp(activeSession); boolean isStudying = activeSession != null; + EquippedItemsData equippedItems = equippedItemsMap.getOrDefault(opponentId, EquippedItemsData.empty()); + return GetMyRivalActingData.MyRival.of( rival.id(), opponent, activeTime, usingApp, isStudying, - activityStatus + activityStatus, + equippedItems ); }) .toList(); diff --git a/src/main/java/com/process/clash/application/profile/data/EquippedItemsData.java b/src/main/java/com/process/clash/application/profile/data/EquippedItemsData.java index 22cb8c41f..ac3b958d2 100644 --- a/src/main/java/com/process/clash/application/profile/data/EquippedItemsData.java +++ b/src/main/java/com/process/clash/application/profile/data/EquippedItemsData.java @@ -1,7 +1,7 @@ package com.process.clash.application.profile.data; public record EquippedItemsData( - EquippedItemData insigma, + EquippedItemData insignia, EquippedItemData nameplate, EquippedItemData banner ) { diff --git a/src/main/java/com/process/clash/application/profile/service/EquippedItemsAssembler.java b/src/main/java/com/process/clash/application/profile/service/EquippedItemsAssembler.java index 0679b924a..e7ee33825 100644 --- a/src/main/java/com/process/clash/application/profile/service/EquippedItemsAssembler.java +++ b/src/main/java/com/process/clash/application/profile/service/EquippedItemsAssembler.java @@ -67,7 +67,7 @@ private EquippedItemsData toEquippedItems( List userEquippedItems, Map productById ) { - EquippedItemsData.EquippedItemData insigma = null; + EquippedItemsData.EquippedItemData insignia = null; EquippedItemsData.EquippedItemData nameplate = null; EquippedItemsData.EquippedItemData banner = null; @@ -87,7 +87,7 @@ private EquippedItemsData toEquippedItems( ); if (userEquippedItem.category() == ProductCategory.INSIGNIA) { - insigma = summary; + insignia = summary; } else if (userEquippedItem.category() == ProductCategory.NAMEPLATE) { nameplate = summary; } else if (userEquippedItem.category() == ProductCategory.BANNER) { @@ -95,6 +95,6 @@ private EquippedItemsData toEquippedItems( } } - return new EquippedItemsData(insigma, nameplate, banner); + return new EquippedItemsData(insignia, nameplate, banner); } } diff --git a/src/test/java/com/process/clash/application/compete/rival/rival/service/GetMyRivalActingServiceTest.java b/src/test/java/com/process/clash/application/compete/rival/rival/service/GetMyRivalActingServiceTest.java index 516b0fb93..ea50f7247 100644 --- a/src/test/java/com/process/clash/application/compete/rival/rival/service/GetMyRivalActingServiceTest.java +++ b/src/test/java/com/process/clash/application/compete/rival/rival/service/GetMyRivalActingServiceTest.java @@ -3,6 +3,8 @@ import com.process.clash.application.common.actor.Actor; import com.process.clash.application.compete.rival.rival.data.GetMyRivalActingData; import com.process.clash.application.compete.rival.rival.port.out.RivalRepositoryPort; +import com.process.clash.application.profile.data.EquippedItemsData; +import com.process.clash.application.profile.service.EquippedItemsAssembler; import com.process.clash.application.realtime.data.UserActivityStatus; import com.process.clash.application.realtime.port.out.UserPresencePort; import com.process.clash.application.record.v2.port.out.RecordSessionV2RepositoryPort; @@ -51,6 +53,8 @@ class GetMyRivalActingServiceTest { private UserPresencePort userPresencePort; private GetMyRivalActingService getMyRivalActingService; + @Mock + private EquippedItemsAssembler equippedItemsAssembler; @BeforeEach void setUp() { @@ -59,8 +63,11 @@ void setUp() { recordSessionRepositoryPort, userRepositoryPort, userPresencePort, + new RecordProperties("UTC", 0), - ZoneId.of("UTC") + ZoneId.of("UTC"), + + equippedItemsAssembler ); } @@ -116,6 +123,7 @@ void execute_returnsRivalsWithActivityStatus() { )); when(userPresencePort.getStatuses(anyList())) .thenReturn(Map.of(2L, UserActivityStatus.ONLINE, 3L, UserActivityStatus.AWAY, 1L, UserActivityStatus.ONLINE)); + when(equippedItemsAssembler.loadByUserIds(anyList())).thenReturn(Map.of()); GetMyRivalActingData.Result result = getMyRivalActingService.execute(command); @@ -147,6 +155,7 @@ void execute_returnsTierAsExpTier_whenRankTierIsNone() { .thenReturn(Map.of()); when(recordSessionRepositoryPort.findAllActiveSessionsByUserIds(anyList())).thenReturn(List.of()); when(userPresencePort.getStatuses(anyList())).thenReturn(Map.of(2L, UserActivityStatus.OFFLINE)); + when(equippedItemsAssembler.loadByUserIds(anyList())).thenReturn(Map.of()); GetMyRivalActingData.Result result = getMyRivalActingService.execute( new GetMyRivalActingData.Command(actor)); @@ -170,6 +179,7 @@ void execute_returnsTierAsRankTier_whenRankTierIsNotNone() { .thenReturn(Map.of()); when(recordSessionRepositoryPort.findAllActiveSessionsByUserIds(anyList())).thenReturn(List.of()); when(userPresencePort.getStatuses(anyList())).thenReturn(Map.of(2L, UserActivityStatus.OFFLINE)); + when(equippedItemsAssembler.loadByUserIds(anyList())).thenReturn(Map.of()); GetMyRivalActingData.Result result = getMyRivalActingService.execute( new GetMyRivalActingData.Command(actor)); @@ -189,6 +199,41 @@ void execute_returnsEmptyList_whenNoRivals() { assertThat(result.myRivals()).isEmpty(); } + @Test + @DisplayName("라이벌의 equippedItems가 응답에 올바르게 포함된다") + void execute_returnsEquippedItems() { + Actor actor = new Actor(1L); + Rival rival = new Rival(100L, Instant.now().minusSeconds(86_400), Instant.now(), + RivalLinkingStatus.ACCEPTED, 1L, 2L); + + User rivalUser = createUser(2L, "rivalA", "Rival A"); + + EquippedItemsData equippedItemsData = new EquippedItemsData( + new EquippedItemsData.EquippedItemData(1L, "배지이름", "badge.png"), + new EquippedItemsData.EquippedItemData(2L, "닉네임판", "nameplate.png"), + null + ); + + when(rivalRepositoryPort.findAllByUserId(actor.id())).thenReturn(List.of(rival)); + when(userRepositoryPort.findAllByIds(anyList())) + .thenReturn(List.of(rivalUser, createUser(1L, "me", "Me"))); + when(recordSessionRepositoryPort.getTotalStudyTimeInSecondsByUserIds(anyList(), any(), any())) + .thenReturn(Map.of()); + when(recordSessionRepositoryPort.findAllActiveSessionsByUserIds(anyList())).thenReturn(List.of()); + when(userPresencePort.getStatuses(anyList())).thenReturn(Map.of(2L, UserActivityStatus.OFFLINE)); + when(equippedItemsAssembler.loadByUserIds(anyList())) + .thenReturn(Map.of(2L, equippedItemsData)); + + GetMyRivalActingData.Result result = getMyRivalActingService.execute( + new GetMyRivalActingData.Command(actor)); + + EquippedItemsData actual = result.myRivals().get(0).equippedItems(); + assertThat(actual.insignia().id()).isEqualTo(1L); + assertThat(actual.insignia().name()).isEqualTo("배지이름"); + assertThat(actual.nameplate().id()).isEqualTo(2L); + assertThat(actual.banner()).isNull(); + } + private User createUser(Long id, String username, String name) { return createUser(id, username, name, RankTier.NONE, ExpTier.UNRANKED); }