From f8eec0a7717a8dd873183d6af795d535e7e63aa6 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Thu, 24 Jul 2025 23:29:52 +0900 Subject: [PATCH 01/23] =?UTF-8?q?[#26]=20feat:=20=ED=81=AC=EB=A3=A8=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20res?= =?UTF-8?q?ponse=20dto=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/dto/response/MemberCrewStatusResponse.java | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/main/java/run/backend/domain/member/dto/response/MemberCrewStatusResponse.java diff --git a/src/main/java/run/backend/domain/member/dto/response/MemberCrewStatusResponse.java b/src/main/java/run/backend/domain/member/dto/response/MemberCrewStatusResponse.java new file mode 100644 index 0000000..6070d51 --- /dev/null +++ b/src/main/java/run/backend/domain/member/dto/response/MemberCrewStatusResponse.java @@ -0,0 +1,10 @@ +package run.backend.domain.member.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record MemberCrewStatusResponse( + + @Schema(description = "유저의 크루 가입 상태 (NONE: 가입된 크루 없음, APPLIED: 가입 대기 중, APPROVED: 가입 완료") + String status +) { +} From bdc52a4090e77beb2955bdb12db3bfc5dcaca067 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Thu, 24 Jul 2025 23:30:28 +0900 Subject: [PATCH 02/23] =?UTF-8?q?[#26]=20delete:=20memberService=20interfa?= =?UTF-8?q?ce=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/MemberServiceImpl.java | 58 ------------------- .../member/service/MemberServiceTest.java | 2 +- 2 files changed, 1 insertion(+), 59 deletions(-) delete mode 100644 src/main/java/run/backend/domain/member/service/MemberServiceImpl.java diff --git a/src/main/java/run/backend/domain/member/service/MemberServiceImpl.java b/src/main/java/run/backend/domain/member/service/MemberServiceImpl.java deleted file mode 100644 index eb96c68..0000000 --- a/src/main/java/run/backend/domain/member/service/MemberServiceImpl.java +++ /dev/null @@ -1,58 +0,0 @@ -package run.backend.domain.member.service; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.multipart.MultipartFile; -import run.backend.domain.crew.entity.Crew; -import run.backend.domain.crew.enums.JoinStatus; -import run.backend.domain.file.service.FileService; -import run.backend.domain.member.dto.request.MemberInfoRequest; -import run.backend.domain.member.dto.response.MemberInfoResponse; -import run.backend.domain.member.entity.Member; -import run.backend.domain.member.repository.MemberRepository; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class MemberServiceImpl implements MemberService { - - private final MemberRepository memberRepository; - private final FileService fileService; - - @Override - public MemberInfoResponse getMemberInfo(Member member) { - - String crewName = memberRepository.findCrewByMemberIdAndStatus(member.getId(), JoinStatus.APPROVED) - .map(Crew::getName) - .orElse("N/A"); - return new MemberInfoResponse(member.getProfileImage(), member.getNickname(), crewName); - } - - @Override - @Transactional - public void updateMemberInfo(Member member, String imageStatus, MultipartFile image, MemberInfoRequest data) { - - switch (imageStatus) { - - case "updated" : - fileService.deleteImage(member.getProfileImage()); // 기존 이미지 지우기 - String newImageName = fileService.saveProfileImage(image); // 새로운 이미지 저장 - member.updateImage(newImageName); - break ; - case "removed" : - fileService.deleteImage(member.getProfileImage()); - member.updateImage("default-profile-image.png"); - break ; - } - - if (data.gender() != null) - member.updateGender(data.gender()); - if (data.age() != null) - member.updateAge(data.age()); - if (data.nickname() != null) - member.updateNickname(data.nickname()); - - memberRepository.save(member); - } -} diff --git a/src/test/java/run/backend/domain/member/service/MemberServiceTest.java b/src/test/java/run/backend/domain/member/service/MemberServiceTest.java index ce2bf76..180e580 100644 --- a/src/test/java/run/backend/domain/member/service/MemberServiceTest.java +++ b/src/test/java/run/backend/domain/member/service/MemberServiceTest.java @@ -34,7 +34,7 @@ public class MemberServiceTest { private FileService fileService; @InjectMocks - private MemberServiceImpl memberService; + private MemberService memberService; private Member testMember; private Crew testCrew; From bdb32aa1b086df1a0b661f81b48fed8fdc471be5 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Thu, 24 Jul 2025 23:30:57 +0900 Subject: [PATCH 03/23] =?UTF-8?q?[#26]=20feat:=20=EA=B0=80=EC=9E=85?= =?UTF-8?q?=EB=90=9C=20=ED=81=AC=EB=A3=A8=20=EC=9E=88=EB=8A=94=EC=A7=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=ED=95=98=EB=8A=94=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crew/repository/JoinCrewRepository.java | 2 + .../member/controller/MemberController.java | 13 +++- .../domain/member/service/MemberService.java | 69 ++++++++++++++++--- 3 files changed, 73 insertions(+), 11 deletions(-) diff --git a/src/main/java/run/backend/domain/crew/repository/JoinCrewRepository.java b/src/main/java/run/backend/domain/crew/repository/JoinCrewRepository.java index a971cc8..fd551ba 100644 --- a/src/main/java/run/backend/domain/crew/repository/JoinCrewRepository.java +++ b/src/main/java/run/backend/domain/crew/repository/JoinCrewRepository.java @@ -16,6 +16,8 @@ public interface JoinCrewRepository extends JpaRepository { boolean existsByMemberAndJoinStatus(Member member, JoinStatus joinStatus); + Optional findByMember(Member member); + @Query(""" SELECT jc.member FROM JoinCrew jc diff --git a/src/main/java/run/backend/domain/member/controller/MemberController.java b/src/main/java/run/backend/domain/member/controller/MemberController.java index c9b8422..81821d0 100644 --- a/src/main/java/run/backend/domain/member/controller/MemberController.java +++ b/src/main/java/run/backend/domain/member/controller/MemberController.java @@ -6,9 +6,10 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import run.backend.domain.member.dto.request.MemberInfoRequest; +import run.backend.domain.member.dto.response.MemberCrewStatusResponse; import run.backend.domain.member.dto.response.MemberInfoResponse; import run.backend.domain.member.entity.Member; -import run.backend.domain.member.service.MemberServiceImpl; +import run.backend.domain.member.service.MemberService; import run.backend.global.annotation.member.Login; import run.backend.global.common.response.CommonResponse; @@ -18,7 +19,7 @@ @Tag(name = "Members", description = "Member 관련 API") public class MemberController { - private final MemberServiceImpl memberService; + private final MemberService memberService; @Operation(summary = "유저 정보 조회", description = "마이페이지 상단 유저 정보를 조회하는 API 입니다.") @GetMapping @@ -39,4 +40,12 @@ public CommonResponse updateMemberInfo( memberService.updateMemberInfo(member, imageStatus, image, data); return new CommonResponse<>("유저 정보 수정 성공"); } + + @GetMapping("/me/crews/exists") + @Operation(summary = "가입된 크루가 있는지 확인", description = "유저가 가입된 크루가 있는지 확인하는 API 입니다.") + public CommonResponse getMembersCrewExists(@Login Member member) { + + MemberCrewStatusResponse response = memberService.getMembersCrewExists(member); + return new CommonResponse<>("유저 크루 가입 여부 조회 완료", response); + } } diff --git a/src/main/java/run/backend/domain/member/service/MemberService.java b/src/main/java/run/backend/domain/member/service/MemberService.java index 0de9444..5d43975 100644 --- a/src/main/java/run/backend/domain/member/service/MemberService.java +++ b/src/main/java/run/backend/domain/member/service/MemberService.java @@ -1,21 +1,72 @@ package run.backend.domain.member.service; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; +import run.backend.domain.crew.entity.Crew; +import run.backend.domain.crew.entity.JoinCrew; +import run.backend.domain.crew.enums.JoinStatus; +import run.backend.domain.crew.repository.JoinCrewRepository; +import run.backend.domain.file.service.FileService; import run.backend.domain.member.dto.request.MemberInfoRequest; +import run.backend.domain.member.dto.response.MemberCrewStatusResponse; import run.backend.domain.member.dto.response.MemberInfoResponse; import run.backend.domain.member.entity.Member; +import run.backend.domain.member.repository.MemberRepository; -public interface MemberService { +import java.util.Optional; - MemberInfoResponse getMemberInfo(Member member); +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class MemberService { - void updateMemberInfo(Member member, String imageStatus, MultipartFile image, MemberInfoRequest data); + private final FileService fileService; + private final MemberRepository memberRepository; + private final JoinCrewRepository joinCrewRepository; -// void updateMember(); + public MemberInfoResponse getMemberInfo(Member member) { -// void deleteMember(Member member); -// -// void leaveCrew(Member member, Long crewId); -// -// void joinCrew(String crewCode); + String crewName = memberRepository.findCrewByMemberIdAndStatus(member.getId(), JoinStatus.APPROVED) + .map(Crew::getName) + .orElse("N/A"); + return new MemberInfoResponse(member.getProfileImage(), member.getNickname(), crewName); + } + + @Transactional + public void updateMemberInfo(Member member, String imageStatus, MultipartFile image, MemberInfoRequest data) { + + switch (imageStatus) { + + case "updated" : + fileService.deleteImage(member.getProfileImage()); // 기존 이미지 지우기 + String newImageName = fileService.saveProfileImage(image); // 새로운 이미지 저장 + member.updateImage(newImageName); + break ; + case "removed" : + fileService.deleteImage(member.getProfileImage()); + member.updateImage("default-profile-image.png"); + break ; + } + + if (data.gender() != null) + member.updateGender(data.gender()); + if (data.age() != null) + member.updateAge(data.age()); + if (data.nickname() != null) + member.updateNickname(data.nickname()); + + memberRepository.save(member); + } + + public MemberCrewStatusResponse getMembersCrewExists(Member member) { + + Optional joinCrew = joinCrewRepository.findByMember(member); + + if (joinCrew.isEmpty()) { + return new MemberCrewStatusResponse("NONE"); + } + return new MemberCrewStatusResponse(joinCrew.get().getJoinStatus().toString()); + } } From 4935b338148c6a6120fde9ec9125cf489f73b1dd Mon Sep 17 00:00:00 2001 From: choiseoji Date: Thu, 24 Jul 2025 23:31:34 +0900 Subject: [PATCH 04/23] =?UTF-8?q?[#26]=20refact:=20operation=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/domain/member/controller/MemberController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/run/backend/domain/member/controller/MemberController.java b/src/main/java/run/backend/domain/member/controller/MemberController.java index 81821d0..a29a3f2 100644 --- a/src/main/java/run/backend/domain/member/controller/MemberController.java +++ b/src/main/java/run/backend/domain/member/controller/MemberController.java @@ -21,16 +21,16 @@ public class MemberController { private final MemberService memberService; - @Operation(summary = "유저 정보 조회", description = "마이페이지 상단 유저 정보를 조회하는 API 입니다.") @GetMapping + @Operation(summary = "유저 정보 조회", description = "마이페이지 상단 유저 정보를 조회하는 API 입니다.") public CommonResponse getMemberInfo(@Login Member member) { MemberInfoResponse response = memberService.getMemberInfo(member); return new CommonResponse<>("유저 정보 조회 성공", response); } - @Operation(summary = "유저 정보 수정", description = "마이페이지에서 유저 정보를 수정하는 API 입니다.") @PostMapping + @Operation(summary = "유저 정보 수정", description = "마이페이지에서 유저 정보를 수정하는 API 입니다.") public CommonResponse updateMemberInfo( @Login Member member, @RequestParam String imageStatus, From a8d606be47835ea823ac2241f5d9e3156e0b6196 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Fri, 25 Jul 2025 00:46:20 +0900 Subject: [PATCH 05/23] =?UTF-8?q?[#26]=20feat:=20=EA=B3=B5=ED=86=B5=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20dto=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/common/response/PageResponse.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 src/main/java/run/backend/global/common/response/PageResponse.java diff --git a/src/main/java/run/backend/global/common/response/PageResponse.java b/src/main/java/run/backend/global/common/response/PageResponse.java new file mode 100644 index 0000000..7db9403 --- /dev/null +++ b/src/main/java/run/backend/global/common/response/PageResponse.java @@ -0,0 +1,12 @@ +package run.backend.global.common.response; + +import java.util.List; + +public record PageResponse( + int page, + int size, + int totalPages, + long totalElements, + boolean isLast, + List content +) {} \ No newline at end of file From 3ceaab424ab48cb11b1383e37f5e130a0bbe9f4f Mon Sep 17 00:00:00 2001 From: choiseoji Date: Fri, 25 Jul 2025 00:46:34 +0900 Subject: [PATCH 06/23] =?UTF-8?q?[#26]=20feat:=20CrewRankingResponse=20dto?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/crew/dto/response/CrewRankingResponse.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/run/backend/domain/crew/dto/response/CrewRankingResponse.java diff --git a/src/main/java/run/backend/domain/crew/dto/response/CrewRankingResponse.java b/src/main/java/run/backend/domain/crew/dto/response/CrewRankingResponse.java new file mode 100644 index 0000000..a22800d --- /dev/null +++ b/src/main/java/run/backend/domain/crew/dto/response/CrewRankingResponse.java @@ -0,0 +1,9 @@ +package run.backend.domain.crew.dto.response; + +public record CrewRankingResponse( + Long crewId, + String name, + String image, + int monthlyDistanceKm +) { +} From 284906f8ea13c7c84a5bf6025b7fcb89a8786da2 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Fri, 25 Jul 2025 00:46:53 +0900 Subject: [PATCH 07/23] =?UTF-8?q?[#26]=20feat:=20=ED=81=AC=EB=A3=A8=20?= =?UTF-8?q?=EB=9E=AD=ED=82=B9=20=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crew/controller/CrewController.java | 15 ++++-- .../crew/repository/CrewRepository.java | 5 ++ .../crew/service/CrewRankingService.java | 46 +++++++++++++++++++ 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 src/main/java/run/backend/domain/crew/service/CrewRankingService.java diff --git a/src/main/java/run/backend/domain/crew/controller/CrewController.java b/src/main/java/run/backend/domain/crew/controller/CrewController.java index 0aa0d01..4138a38 100644 --- a/src/main/java/run/backend/domain/crew/controller/CrewController.java +++ b/src/main/java/run/backend/domain/crew/controller/CrewController.java @@ -11,11 +11,15 @@ import run.backend.domain.crew.dto.response.*; import run.backend.domain.crew.entity.Crew; import run.backend.domain.crew.service.CrewEventService; +import run.backend.domain.crew.service.CrewRankingService; import run.backend.domain.crew.service.CrewService; import run.backend.domain.member.entity.Member; import run.backend.global.annotation.member.Login; import run.backend.global.annotation.member.MemberCrew; import run.backend.global.common.response.CommonResponse; +import run.backend.global.common.response.PageResponse; + +import javax.sound.midi.Synthesizer; @RestController @RequiredArgsConstructor @@ -25,6 +29,7 @@ public class CrewController { private final CrewService crewService; private final CrewEventService crewEventService; + private final CrewRankingService crewRankingService; @PostMapping @Operation(summary = "크루 생성", description = "크루 생성하는 API 입니다.") @@ -114,10 +119,12 @@ public CommonResponse getUpcomingEvent(@MemberCrew Cr return new CommonResponse<>("크루 다가오는 일정 조회 성공", response); } - @GetMapping("/members") - @Operation(summary = "크루원 조회", description = "크루 내 모든 크루원을 조회하는 API 입니다.") - public CommonResponse getCrewMember(@MemberCrew Crew crew) { + @GetMapping("/rankings") + public CommonResponse> getCrewRankings( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "5") int size) { - return new CommonResponse<>("크루원 조회 성공"); + PageResponse response = crewRankingService.getCrewRanking(page, size); + return new CommonResponse<>("크루 랭킹 조회 성공", response); } } diff --git a/src/main/java/run/backend/domain/crew/repository/CrewRepository.java b/src/main/java/run/backend/domain/crew/repository/CrewRepository.java index e6992eb..6c53f0e 100644 --- a/src/main/java/run/backend/domain/crew/repository/CrewRepository.java +++ b/src/main/java/run/backend/domain/crew/repository/CrewRepository.java @@ -1,5 +1,7 @@ package run.backend.domain.crew.repository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import run.backend.domain.crew.entity.Crew; @@ -8,4 +10,7 @@ public interface CrewRepository extends JpaRepository { Optional findByInviteCode(String inviteCode); + + + Page findAllByOrderByMonthlyScoreTotalDesc(Pageable pageable); } diff --git a/src/main/java/run/backend/domain/crew/service/CrewRankingService.java b/src/main/java/run/backend/domain/crew/service/CrewRankingService.java new file mode 100644 index 0000000..7dccfd6 --- /dev/null +++ b/src/main/java/run/backend/domain/crew/service/CrewRankingService.java @@ -0,0 +1,46 @@ +package run.backend.domain.crew.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import run.backend.domain.crew.dto.response.CrewRankingResponse; +import run.backend.domain.crew.entity.Crew; +import run.backend.domain.crew.repository.CrewRepository; +import run.backend.global.common.response.PageResponse; + +import java.util.List; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class CrewRankingService { + + private final CrewRepository crewRepository; + + public PageResponse getCrewRanking(int page, int size) { + + Pageable pageable = PageRequest.of(page, size); + Page pageResult = crewRepository.findAllByOrderByMonthlyScoreTotalDesc(pageable); + + List content = pageResult.stream() + .map(crew -> new CrewRankingResponse( + crew.getId(), + crew.getName(), + crew.getImage(), + crew.getMonthlyDistanceTotal().intValue() + )) + .toList(); + + return new PageResponse<>( + pageResult.getNumber(), + pageResult.getSize(), + pageResult.getTotalPages(), + pageResult.getTotalElements(), + pageResult.isLast(), + content + ); + } +} From 29e7e5c77d10d0e779e4bf9ab8d4f73e9e2a6d77 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Fri, 25 Jul 2025 00:47:10 +0900 Subject: [PATCH 08/23] =?UTF-8?q?[#26]=20chore:=20validation=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index 0ecbe28..64a5ba1 100644 --- a/build.gradle +++ b/build.gradle @@ -35,6 +35,7 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'org.springframework.boot:spring-boot-starter-validation' // mysql runtimeOnly 'com.mysql:mysql-connector-j' From f8ec81338054689e24821473ecf1bf1947f93ee7 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 13:43:31 +0900 Subject: [PATCH 09/23] =?UTF-8?q?[#26]=20feat:=20Crew=EC=97=90=20=EB=B9=BC?= =?UTF-8?q?=EC=95=97=EC=9D=80=20km=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/run/backend/domain/crew/entity/Crew.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/run/backend/domain/crew/entity/Crew.java b/src/main/java/run/backend/domain/crew/entity/Crew.java index 31b8f2d..214bd01 100644 --- a/src/main/java/run/backend/domain/crew/entity/Crew.java +++ b/src/main/java/run/backend/domain/crew/entity/Crew.java @@ -35,11 +35,14 @@ public class Crew extends BaseEntity { @Column(name = "monthly_distance_total") private BigDecimal monthlyDistanceTotal; + @Column(name = "captured_distance_total") + private BigDecimal capturedDistanceTotal; + @Column(name = "monthly_time_total") private Long monthlyTimeTotal; @Column(name = "monthly_score_total") - private BigDecimal monthlyScoreTotal; + private BigDecimal monthlyScoreTotal; // monthlyDistanceTotal(70%) + capturedDistanceTotal(30%) public void incrementMemberCount() { this.memberCount++; @@ -69,6 +72,7 @@ public Crew ( this.inviteCode = UUID.randomUUID().toString(); this.memberCount = 1L; this.monthlyDistanceTotal = BigDecimal.ZERO; + this.capturedDistanceTotal = BigDecimal.ZERO; this.monthlyTimeTotal = 0L; this.monthlyScoreTotal = BigDecimal.ZERO; } From 43964cf1ae50ca30306860f3bb3185e739222fe6 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 13:43:49 +0900 Subject: [PATCH 10/23] =?UTF-8?q?[#26]=20feat:=20=EB=95=85=EB=94=B0?= =?UTF-8?q?=EB=A8=B9=EA=B8=B0=20=ED=98=84=ED=99=A9=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?api=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/domain/crew/controller/CrewController.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/run/backend/domain/crew/controller/CrewController.java b/src/main/java/run/backend/domain/crew/controller/CrewController.java index 4138a38..d70ac7b 100644 --- a/src/main/java/run/backend/domain/crew/controller/CrewController.java +++ b/src/main/java/run/backend/domain/crew/controller/CrewController.java @@ -19,8 +19,6 @@ import run.backend.global.common.response.CommonResponse; import run.backend.global.common.response.PageResponse; -import javax.sound.midi.Synthesizer; - @RestController @RequiredArgsConstructor @RequestMapping("/api/v1/crews") @@ -127,4 +125,11 @@ public CommonResponse> getCrewRankings( PageResponse response = crewRankingService.getCrewRanking(page, size); return new CommonResponse<>("크루 랭킹 조회 성공", response); } + + @GetMapping("/rankings/status") + public CommonResponse getCrewRankingsStatus(@MemberCrew Crew crew) { + + CrewRankingStatusResponse response = crewRankingService.getCrewRankingStatus(crew); + return new CommonResponse<>("크루 땅따먹기 현황 조회 성공", response); + } } From 8ec74ccee5cfd01e7329320d56c1f417ada31e10 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 13:44:06 +0900 Subject: [PATCH 11/23] =?UTF-8?q?[#26]=20fix:=20CrewMapper=20map-struct=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/crew/mapper/CrewMapper.java | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/main/java/run/backend/domain/crew/mapper/CrewMapper.java b/src/main/java/run/backend/domain/crew/mapper/CrewMapper.java index d58b5d2..447f22c 100644 --- a/src/main/java/run/backend/domain/crew/mapper/CrewMapper.java +++ b/src/main/java/run/backend/domain/crew/mapper/CrewMapper.java @@ -1,15 +1,32 @@ package run.backend.domain.crew.mapper; -import org.springframework.stereotype.Component; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ReportingPolicy; import run.backend.domain.crew.dto.response.CrewBaseInfoResponse; import run.backend.domain.crew.dto.response.CrewProfileResponse; +import run.backend.domain.crew.dto.response.CrewRankingResponse; +import run.backend.domain.crew.dto.response.CrewRankingStatusResponse; import run.backend.domain.crew.entity.Crew; import run.backend.domain.member.entity.Member; -@Component -public class CrewMapper { +import java.util.List; - public Crew toEntity(String imageName, String name, String description) { +@Mapper( + componentModel = "spring", + unmappedTargetPolicy = ReportingPolicy.IGNORE +) +public interface CrewMapper { + + @Mapping(target = "rank", source = "rank") + CrewBaseInfoResponse toCrewBaseInfo(int rank, Crew crew); + + @Mapping(target = "monthlyScoreTotal", expression = "java(crew.getMonthlyScoreTotal().intValue())") + CrewRankingResponse toCrewRankingResponse(Crew crew); + + List toCrewRankingResponseList(List crews); + + default Crew toEntity(String imageName, String name, String description) { return Crew.builder() .image(imageName) .name(name) @@ -17,7 +34,7 @@ public Crew toEntity(String imageName, String name, String description) { .build(); } - public CrewProfileResponse toCrewProfile(Crew crew, Member leader) { + default CrewProfileResponse toCrewProfile(Crew crew, Member leader) { return CrewProfileResponse.builder() .crewImage(crew.getImage()) .crewName(crew.getName()) @@ -28,15 +45,14 @@ public CrewProfileResponse toCrewProfile(Crew crew, Member leader) { .build(); } - public CrewBaseInfoResponse toCrewBaseInfo(int rank, Crew crew) { - return CrewBaseInfoResponse.builder() - .rank(rank) - .image(crew.getImage()) - .name(crew.getName()) - .description(crew.getDescription()) - .memberCount(crew.getMemberCount()) - .monthlyDistanceTotal(crew.getMonthlyDistanceTotal()) - .monthlyTimeTotal(crew.getMonthlyTimeTotal()) - .build(); + default CrewRankingStatusResponse toCrewRankingStatusResponse( + int rank, + Crew crew + ) { + return new CrewRankingStatusResponse( + rank, + crew.getMonthlyDistanceTotal().intValue(), + crew.getCapturedDistanceTotal().intValue() + ); } } From e43fb3bd8b25addef0522944eae42d4b5f43b8b8 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 13:44:29 +0900 Subject: [PATCH 12/23] =?UTF-8?q?[#26]=20fix:=20=ED=95=9C=EB=8B=AC=20?= =?UTF-8?q?=EB=88=84=EC=A0=81=20km=EA=B0=80=20=EC=95=84=EB=8B=8C=20?= =?UTF-8?q?=EA=B3=84=EC=82=B0=EB=90=9C=20score=20km=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B1=B8=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/domain/crew/dto/response/CrewRankingResponse.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/run/backend/domain/crew/dto/response/CrewRankingResponse.java b/src/main/java/run/backend/domain/crew/dto/response/CrewRankingResponse.java index a22800d..d4a660a 100644 --- a/src/main/java/run/backend/domain/crew/dto/response/CrewRankingResponse.java +++ b/src/main/java/run/backend/domain/crew/dto/response/CrewRankingResponse.java @@ -4,6 +4,6 @@ public record CrewRankingResponse( Long crewId, String name, String image, - int monthlyDistanceKm + int monthlyScoreTotal ) { } From 14a6a103a9370037b87959e8d4e2f34864c8b7a8 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 13:44:55 +0900 Subject: [PATCH 13/23] =?UTF-8?q?[#26]=20feat:=20mapper=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9,=20=EB=95=85=EB=94=B0=EB=A9=B1=EA=B8=B0=20=ED=98=84?= =?UTF-8?q?=ED=99=A9=20=EC=A1=B0=ED=9A=8C=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crew/service/CrewRankingService.java | 35 ++++++++----------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/src/main/java/run/backend/domain/crew/service/CrewRankingService.java b/src/main/java/run/backend/domain/crew/service/CrewRankingService.java index 7dccfd6..8001892 100644 --- a/src/main/java/run/backend/domain/crew/service/CrewRankingService.java +++ b/src/main/java/run/backend/domain/crew/service/CrewRankingService.java @@ -3,11 +3,12 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import run.backend.domain.crew.dto.response.CrewRankingResponse; +import run.backend.domain.crew.dto.response.CrewRankingStatusResponse; import run.backend.domain.crew.entity.Crew; +import run.backend.domain.crew.mapper.CrewMapper; import run.backend.domain.crew.repository.CrewRepository; import run.backend.global.common.response.PageResponse; @@ -18,29 +19,21 @@ @Transactional(readOnly = true) public class CrewRankingService { + private final CrewMapper crewMapper; private final CrewRepository crewRepository; public PageResponse getCrewRanking(int page, int size) { - Pageable pageable = PageRequest.of(page, size); - Page pageResult = crewRepository.findAllByOrderByMonthlyScoreTotalDesc(pageable); - - List content = pageResult.stream() - .map(crew -> new CrewRankingResponse( - crew.getId(), - crew.getName(), - crew.getImage(), - crew.getMonthlyDistanceTotal().intValue() - )) - .toList(); - - return new PageResponse<>( - pageResult.getNumber(), - pageResult.getSize(), - pageResult.getTotalPages(), - pageResult.getTotalElements(), - pageResult.isLast(), - content - ); + Page pageResult = crewRepository.findAllByOrderByMonthlyScoreTotalDesc(PageRequest.of(page, size)); + List content = crewMapper.toCrewRankingResponseList(pageResult.getContent()); + + return PageResponse.toPageResponse(pageResult, content); + } + + public CrewRankingStatusResponse getCrewRankingStatus(Crew crew) { + + int rank = 0; // [TODO] : 스케줄링 rank 계산 구현 수정 예정 + + return crewMapper.toCrewRankingStatusResponse(rank, crew); } } From 738b4d81d9cd877a2af37612bc63ee39eacad2e6 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 13:45:49 +0900 Subject: [PATCH 14/23] =?UTF-8?q?[#26]=20feat:=20PageResponse=EC=97=90=20t?= =?UTF-8?q?o=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../global/common/response/PageResponse.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/main/java/run/backend/global/common/response/PageResponse.java b/src/main/java/run/backend/global/common/response/PageResponse.java index 7db9403..eadf645 100644 --- a/src/main/java/run/backend/global/common/response/PageResponse.java +++ b/src/main/java/run/backend/global/common/response/PageResponse.java @@ -1,5 +1,7 @@ package run.backend.global.common.response; +import org.springframework.data.domain.Page; + import java.util.List; public record PageResponse( @@ -9,4 +11,16 @@ public record PageResponse( long totalElements, boolean isLast, List content -) {} \ No newline at end of file +) { + + public static PageResponse toPageResponse(Page page, List content) { + return new PageResponse<>( + page.getNumber(), + content.size(), + page.getTotalPages(), + page.getTotalElements(), + page.isLast(), + content + ); + } +} \ No newline at end of file From 23a6b1ca3b6a1942b477d931c784f76dd69a013c Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 13:46:07 +0900 Subject: [PATCH 15/23] =?UTF-8?q?[#26]=20feat:=20CrewRankingStatusResponse?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crew/dto/response/CrewRankingStatusResponse.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/run/backend/domain/crew/dto/response/CrewRankingStatusResponse.java diff --git a/src/main/java/run/backend/domain/crew/dto/response/CrewRankingStatusResponse.java b/src/main/java/run/backend/domain/crew/dto/response/CrewRankingStatusResponse.java new file mode 100644 index 0000000..cc14e5a --- /dev/null +++ b/src/main/java/run/backend/domain/crew/dto/response/CrewRankingStatusResponse.java @@ -0,0 +1,8 @@ +package run.backend.domain.crew.dto.response; + +public record CrewRankingStatusResponse( + int ranking, + int totalDistanceKm, + int capturedDistanceKm +) { +} From 39a2b870bad4ab9cbef87e2f255f87ec4946de33 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 13:46:20 +0900 Subject: [PATCH 16/23] =?UTF-8?q?[#26]=20feat:=20Querydsl=20config=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/config/QuerydslConfig.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/main/java/run/backend/global/config/QuerydslConfig.java diff --git a/src/main/java/run/backend/global/config/QuerydslConfig.java b/src/main/java/run/backend/global/config/QuerydslConfig.java new file mode 100644 index 0000000..61f1736 --- /dev/null +++ b/src/main/java/run/backend/global/config/QuerydslConfig.java @@ -0,0 +1,18 @@ +package run.backend.global.config; + +import com.querydsl.jpa.impl.JPAQueryFactory; +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class QuerydslConfig { + @PersistenceContext + private EntityManager entityManager; + + @Bean + public JPAQueryFactory jpaQueryFactory() { + return new JPAQueryFactory(entityManager); + } +} \ No newline at end of file From f6a15af5b600df639b1787b0d6ba08213b4e5f90 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 15:31:27 +0900 Subject: [PATCH 17/23] =?UTF-8?q?[#26]=20fix:=20pageResponse=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/run/backend/global/common/response/PageResponse.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/run/backend/global/common/response/PageResponse.java b/src/main/java/run/backend/global/common/response/PageResponse.java index eadf645..980c921 100644 --- a/src/main/java/run/backend/global/common/response/PageResponse.java +++ b/src/main/java/run/backend/global/common/response/PageResponse.java @@ -5,8 +5,8 @@ import java.util.List; public record PageResponse( - int page, - int size, + int curPage, + int curElements, int totalPages, long totalElements, boolean isLast, From bbb68a7bdebf5ed6f52848ecee2795dea66f5077 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 15:31:46 +0900 Subject: [PATCH 18/23] =?UTF-8?q?[#26]=20feat:=20CrewRankingServiceTest=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crew/service/CrewRankingServiceTest.java | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 src/test/java/run/backend/domain/crew/service/CrewRankingServiceTest.java diff --git a/src/test/java/run/backend/domain/crew/service/CrewRankingServiceTest.java b/src/test/java/run/backend/domain/crew/service/CrewRankingServiceTest.java new file mode 100644 index 0000000..e993458 --- /dev/null +++ b/src/test/java/run/backend/domain/crew/service/CrewRankingServiceTest.java @@ -0,0 +1,127 @@ +package run.backend.domain.crew.service; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import run.backend.domain.crew.dto.response.CrewRankingResponse; +import run.backend.domain.crew.dto.response.CrewRankingStatusResponse; +import run.backend.domain.crew.entity.Crew; +import run.backend.domain.crew.mapper.CrewMapper; +import run.backend.domain.crew.repository.CrewRepository; +import run.backend.global.common.response.PageResponse; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@DisplayName("Crew Ranking 서비스 테스트") +@ExtendWith(MockitoExtension.class) +public class CrewRankingServiceTest { + + @InjectMocks + private CrewRankingService crewRankingService; + + @Mock + private CrewRepository crewRepository; + + @Mock + private CrewMapper crewMapper; + + private Crew crew1; + private Crew crew2; + private Crew crew3; + private Crew crew4; + private Crew crew5; + + @BeforeEach + void setUp() { + crew1 = Crew.builder() + .name("crew1") + .build(); + crew2 = Crew.builder() + .name("crew2") + .build(); + crew3 = Crew.builder() + .name("crew3") + .build(); + crew4 = Crew.builder() + .name("crew4") + .build(); + crew5 = Crew.builder() + .name("crew5") + .build(); + } + + @Nested + @DisplayName("getCrewRanking 메서드는") + class getCrewRankingTest { + + @Test + @DisplayName("페이지 정보를 기반으로 Crew 랭킹 응답을 반환한다") + void getCrewRanking_whenValidPageRequest_thenReturnsPageResponse() { + + // given + int page = 0; + int size = 5; + + List crews = List.of(crew1, crew2, crew3, crew4, crew5); + Page crewPage = new PageImpl<>(crews, PageRequest.of(page, size), crews.size()); + + List responseList = List.of( + new CrewRankingResponse(1L, "name1", "image1", 5), + new CrewRankingResponse(2L, "name2", "image2", 4), + new CrewRankingResponse(3L, "name3", "image3", 3), + new CrewRankingResponse(4L, "name4", "image4", 2), + new CrewRankingResponse(5L, "name5", "image5", 1) + ); + + when(crewRepository.findAllByOrderByMonthlyScoreTotalDesc(PageRequest.of(page, size))) + .thenReturn(crewPage); + when(crewMapper.toCrewRankingResponseList(crewPage.getContent())) + .thenReturn(responseList); + + // when + PageResponse result = crewRankingService.getCrewRanking(page, size); + + // then + assertThat(result.curPage()).isEqualTo(0); + assertThat(result.curElements()).isEqualTo(5); + assertThat(result.totalPages()).isEqualTo(1); + assertThat(result.totalElements()).isEqualTo(5); + assertThat(result.isLast()).isTrue(); + } + } + + @Nested + @DisplayName("getCrewRankingStatus 메서드는") + class getCrewRankingStatusTest { + + @Test + @DisplayName("정상적으로 Crew 랭킹 상태를 반환한다") + void getCrewRankingStatus_whenValidCrewGiven_thenReturnsStatusResponse() { + + // given + int rank = 0; + CrewRankingStatusResponse expectedResponse = new CrewRankingStatusResponse(rank, 0, 0); + + when(crewMapper.toCrewRankingStatusResponse(rank, crew1)) + .thenReturn(expectedResponse); + + // when + CrewRankingStatusResponse result = crewRankingService.getCrewRankingStatus(crew1); + + // then + assertThat(result).isEqualTo(expectedResponse); + } + } +} From 8952c384f4e657b246d803dca927aa2af109f0ba Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 16:44:32 +0900 Subject: [PATCH 19/23] =?UTF-8?q?[#26]=20feat:=20CrewMapper=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/crew/mapper/CrewMapperTest.java | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 src/test/java/run/backend/domain/crew/mapper/CrewMapperTest.java diff --git a/src/test/java/run/backend/domain/crew/mapper/CrewMapperTest.java b/src/test/java/run/backend/domain/crew/mapper/CrewMapperTest.java new file mode 100644 index 0000000..6a10698 --- /dev/null +++ b/src/test/java/run/backend/domain/crew/mapper/CrewMapperTest.java @@ -0,0 +1,101 @@ +package run.backend.domain.crew.mapper; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import run.backend.domain.crew.dto.response.CrewProfileResponse; +import run.backend.domain.crew.dto.response.CrewRankingStatusResponse; +import run.backend.domain.crew.entity.Crew; +import run.backend.domain.member.entity.Member; +import run.backend.domain.member.enums.Gender; +import run.backend.domain.member.enums.OAuthType; + +import java.math.BigDecimal; + +import static org.assertj.core.api.Assertions.assertThat; + +@DisplayName("CrewMapper 테스트") +public class CrewMapperTest { + + private final CrewMapper crewMapper = new CrewMapperImpl(); + + private Crew crew1; + + @BeforeEach + void setUp() { + + crew1 = new Crew( + 1L, + "crew_name", + "crew_description", + "crew_image", + "invitecode", + 2L, + BigDecimal.valueOf(123), + BigDecimal.valueOf(30), + 50L, + BigDecimal.valueOf(70) + ); + } + + @Test + @DisplayName("정상적으로 Crew로 매핑된다") + void toCrewEntity_mapsCorrectly() { + + // given + String imageName = "image_url"; + String name = "crew_name"; + String description = "crew_description"; + + // when + Crew result = crewMapper.toEntity(imageName, name, description); + + // then + assertThat(result.getImage()).isEqualTo(imageName); + assertThat(result.getName()).isEqualTo(name); + assertThat(result.getDescription()).isEqualTo(description); + } + + @Test + @DisplayName("정상적으로 CrewProfileResponse로 매핑된다") + void toCrewProfileResponse_mapsCorrectly() { + + // given + Member leader = Member.builder() + .username("leader123") + .nickname("철수") + .gender(Gender.MALE) + .age(30) + .oauthId("oauth-123") + .oauthType(OAuthType.GOOGLE) + .profileImage("leader.png") + .build(); + + // when + CrewProfileResponse result = crewMapper.toCrewProfile(crew1, leader); + + // then + assertThat(result.crewImage()).isEqualTo(crew1.getImage()); + assertThat(result.crewName()).isEqualTo(crew1.getName()); + assertThat(result.crewDescription()).isEqualTo(crew1.getDescription()); + assertThat(result.memberCount()).isEqualTo(crew1.getMemberCount()); + assertThat(result.leaderImage()).isEqualTo(leader.getProfileImage()); + assertThat(result.leaderName()).isEqualTo(leader.getNickname()); + } + + @Test + @DisplayName("정상적으로 CrewRankingStatusResponse로 매핑된다") + void toCrewRankingStatusResponse_mapsCorrectly() { + + // given + int rank = 1; + + // when + CrewRankingStatusResponse result = crewMapper.toCrewRankingStatusResponse(rank, crew1); + + // then + assertThat(result.ranking()).isEqualTo(1); + assertThat(result.totalDistanceKm()).isEqualTo(123); + assertThat(result.capturedDistanceKm()).isEqualTo(30); + } +} From f14b1905265483d946ede19758df0c271df0e9a5 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 16:44:55 +0900 Subject: [PATCH 20/23] =?UTF-8?q?[#26]=20feat:=20Crew=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EC=97=90=20=EC=A0=84=EC=B2=B4=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EC=9E=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/run/backend/domain/crew/entity/Crew.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/run/backend/domain/crew/entity/Crew.java b/src/main/java/run/backend/domain/crew/entity/Crew.java index 214bd01..40ee194 100644 --- a/src/main/java/run/backend/domain/crew/entity/Crew.java +++ b/src/main/java/run/backend/domain/crew/entity/Crew.java @@ -1,10 +1,7 @@ package run.backend.domain.crew.entity; import jakarta.persistence.*; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import run.backend.global.common.BaseEntity; import java.math.BigDecimal; @@ -13,6 +10,7 @@ @Entity @Getter @Table(name = "crews") +@AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Crew extends BaseEntity { From 466a675038f75c11354886add082a502876bde5b Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 16:45:14 +0900 Subject: [PATCH 21/23] =?UTF-8?q?[#26]=20delete:=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=EC=97=86=EB=8A=94=20Import=EB=AC=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../run/backend/domain/crew/service/CrewRankingServiceTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/run/backend/domain/crew/service/CrewRankingServiceTest.java b/src/test/java/run/backend/domain/crew/service/CrewRankingServiceTest.java index e993458..3ef643b 100644 --- a/src/test/java/run/backend/domain/crew/service/CrewRankingServiceTest.java +++ b/src/test/java/run/backend/domain/crew/service/CrewRankingServiceTest.java @@ -21,7 +21,6 @@ import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @DisplayName("Crew Ranking 서비스 테스트") From 00f9d3df7d3658ef024e5aee1afe292258726ada Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 16:59:32 +0900 Subject: [PATCH 22/23] =?UTF-8?q?[#26]=20fix:=20updateMemberInfo=EC=97=90?= =?UTF-8?q?=20file=20handle=20Image=20Update=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/service/MemberService.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main/java/run/backend/domain/member/service/MemberService.java b/src/main/java/run/backend/domain/member/service/MemberService.java index 5d43975..e6e82a9 100644 --- a/src/main/java/run/backend/domain/member/service/MemberService.java +++ b/src/main/java/run/backend/domain/member/service/MemberService.java @@ -37,18 +37,8 @@ public MemberInfoResponse getMemberInfo(Member member) { @Transactional public void updateMemberInfo(Member member, String imageStatus, MultipartFile image, MemberInfoRequest data) { - switch (imageStatus) { - - case "updated" : - fileService.deleteImage(member.getProfileImage()); // 기존 이미지 지우기 - String newImageName = fileService.saveProfileImage(image); // 새로운 이미지 저장 - member.updateImage(newImageName); - break ; - case "removed" : - fileService.deleteImage(member.getProfileImage()); - member.updateImage("default-profile-image.png"); - break ; - } + String newImageName = fileService.handleImageUpdate(imageStatus, member.getProfileImage(), image); + member.updateImage(newImageName); if (data.gender() != null) member.updateGender(data.gender()); From a4315a07e4a835ff6e40f2cad0da9f2091ec9808 Mon Sep 17 00:00:00 2001 From: choiseoji Date: Wed, 30 Jul 2025 19:07:33 +0900 Subject: [PATCH 23/23] =?UTF-8?q?[#26]=20test:=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EA=B0=80=EC=9E=85=EB=90=9C=20=ED=81=AC=EB=A3=A8=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=20=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=ED=95=B4?= =?UTF-8?q?=EB=8A=94=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/service/MemberServiceTest.java | 52 ++++++++----------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/src/test/java/run/backend/domain/member/service/MemberServiceTest.java b/src/test/java/run/backend/domain/member/service/MemberServiceTest.java index 180e580..c206c79 100644 --- a/src/test/java/run/backend/domain/member/service/MemberServiceTest.java +++ b/src/test/java/run/backend/domain/member/service/MemberServiceTest.java @@ -10,9 +10,12 @@ import org.springframework.mock.web.MockMultipartFile; import org.springframework.web.multipart.MultipartFile; import run.backend.domain.crew.entity.Crew; +import run.backend.domain.crew.entity.JoinCrew; import run.backend.domain.crew.enums.JoinStatus; +import run.backend.domain.crew.repository.JoinCrewRepository; import run.backend.domain.file.service.FileService; import run.backend.domain.member.dto.request.MemberInfoRequest; +import run.backend.domain.member.dto.response.MemberCrewStatusResponse; import run.backend.domain.member.dto.response.MemberInfoResponse; import run.backend.domain.member.entity.Member; import run.backend.domain.member.enums.Gender; @@ -31,7 +34,7 @@ public class MemberServiceTest { private MemberRepository memberRepository; @Mock - private FileService fileService; + private JoinCrewRepository joinCrewRepository; @InjectMocks private MemberService memberService; @@ -83,43 +86,32 @@ public void getMemberInfoTest() { } @Test - @DisplayName("회원 정보 수정 - 이미지 업로드") - public void updateMemberInfo_whenImageUpdated() { + void getMembersCrewExists_shouldReturnNone_whenNoJoinCrew() { - // given - String imageStatus = "updated"; - String newImagename = "newImage"; - MultipartFile image = new MockMultipartFile( - "newImage", - "newImage.png", - "image/png", - "dummy image content".getBytes()); - - when(fileService.saveProfileImage(image)).thenReturn(newImagename); + // Given + Member member = mock(Member.class); + when(joinCrewRepository.findByMember(member)).thenReturn(Optional.empty()); - // when - memberService.updateMemberInfo(testMember, imageStatus, image, data); + // When + MemberCrewStatusResponse response = memberService.getMembersCrewExists(member); - // then - verify(fileService).deleteImage("test image"); - verify(fileService).saveProfileImage(image); - verify(testMember).updateImage(newImagename); - verify(memberRepository).save(testMember); + // Then + assertEquals("NONE", response.status()); } @Test - @DisplayName("회원 정보 수정 - 이미지 삭제") - public void updateMemberInfo_whenImageRemoved() { + void getMembersCrewExists_shouldReturnJoinStatus_whenJoinCrewExists() { - // given - String imageStatus = "removed"; + // Given + Member member = mock(Member.class); + JoinCrew joinCrew = mock(JoinCrew.class); + when(joinCrew.getJoinStatus()).thenReturn(JoinStatus.APPROVED); + when(joinCrewRepository.findByMember(member)).thenReturn(Optional.of(joinCrew)); - // when - memberService.updateMemberInfo(testMember, imageStatus, null, data); + // When + MemberCrewStatusResponse response = memberService.getMembersCrewExists(member); - // then - verify(fileService).deleteImage("test image"); - verify(testMember).updateImage("default-profile-image.png"); - verify(memberRepository).save(testMember); + // Then + assertEquals("APPROVED", response.status()); } }