diff --git a/build.gradle b/build.gradle index cefb9db..ece2966 100755 --- a/build.gradle +++ b/build.gradle @@ -88,6 +88,10 @@ dependencies { // implementation 'org.springframework.boot:spring-boot-starter-actuator' + + //hibernate + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.hibernate.common:hibernate-commons-annotations:6.0.6.Final' } diff --git a/src/main/java/stackpot/stackpot/feed/service/FeedCommandServiceImpl.java b/src/main/java/stackpot/stackpot/feed/service/FeedCommandServiceImpl.java index 129deb2..b581047 100644 --- a/src/main/java/stackpot/stackpot/feed/service/FeedCommandServiceImpl.java +++ b/src/main/java/stackpot/stackpot/feed/service/FeedCommandServiceImpl.java @@ -1,6 +1,5 @@ package stackpot.stackpot.feed.service; -import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; @@ -13,6 +12,7 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import stackpot.stackpot.apiPayload.code.status.ErrorStatus; import stackpot.stackpot.apiPayload.exception.handler.FeedHandler; import stackpot.stackpot.apiPayload.exception.handler.UserHandler; diff --git a/src/main/java/stackpot/stackpot/pot/converter/PotConverter.java b/src/main/java/stackpot/stackpot/pot/converter/PotConverter.java index 67eae43..56f355d 100644 --- a/src/main/java/stackpot/stackpot/pot/converter/PotConverter.java +++ b/src/main/java/stackpot/stackpot/pot/converter/PotConverter.java @@ -131,10 +131,12 @@ public PotSearchResponseDto toSearchDto(Pot pot) { .build(); } - public PotSummaryDto toDto(Pot pot) { + public PotSummaryDto toDto(Pot pot, Boolean isMember) { return PotSummaryDto.builder() .summary(pot.getPotSummary()) .potLan(splitLanguages(pot.getPotLan())) + .potName(pot.getPotName()) + .isMember(isMember) .build(); } diff --git a/src/main/java/stackpot/stackpot/pot/converter/PotMemberConverter.java b/src/main/java/stackpot/stackpot/pot/converter/PotMemberConverter.java index b0b59bd..252c8bd 100644 --- a/src/main/java/stackpot/stackpot/pot/converter/PotMemberConverter.java +++ b/src/main/java/stackpot/stackpot/pot/converter/PotMemberConverter.java @@ -48,23 +48,22 @@ public PotMemberAppealResponseDto toDto(PotMember entity) { } public PotMemberInfoResponseDto toKaKaoCreatorDto(PotMember entity) { - String creatorRole = RoleNameMapper.mapRoleName(entity.getUser().getRole().name()); + String creatorRole = RoleNameMapper.mapRoleName(entity.getRoleName().name()); String nicknameWithRole = entity.getUser().getNickname() + " " + creatorRole; return PotMemberInfoResponseDto.builder() .potMemberId(entity.getPotMemberId()) .nickname(nicknameWithRole) .potRole(entity.getRoleName().name()) - .owner(true) + .owner(entity.isOwner()) // true 고정 대신 실제 owner 여부 반영 추천 .build(); } public PotMemberInfoResponseDto toKaKaoMemberDto(PotMember entity) { - String roleName = entity.getPotApplication() != null - ? entity.getPotApplication().getPotRole().name() + String roleName = entity.getRoleName() != null + ? entity.getRoleName().name() : "멤버"; - String nicknameWithRole; if (entity.getUser() == null || entity.getUser().isDeleted()) { diff --git a/src/main/java/stackpot/stackpot/pot/dto/PotSummaryDto.java b/src/main/java/stackpot/stackpot/pot/dto/PotSummaryDto.java index 9047c6e..5037e12 100644 --- a/src/main/java/stackpot/stackpot/pot/dto/PotSummaryDto.java +++ b/src/main/java/stackpot/stackpot/pot/dto/PotSummaryDto.java @@ -11,5 +11,7 @@ public class PotSummaryDto { private String summary; private List potLan; + private String potName; + private Boolean isMember; } diff --git a/src/main/java/stackpot/stackpot/pot/service/pot/MyPotService.java b/src/main/java/stackpot/stackpot/pot/service/pot/MyPotService.java index 33f5700..9702cfe 100644 --- a/src/main/java/stackpot/stackpot/pot/service/pot/MyPotService.java +++ b/src/main/java/stackpot/stackpot/pot/service/pot/MyPotService.java @@ -13,6 +13,7 @@ public interface MyPotService { // 사용자의 진행 중인 팟 조회 List getMyPots(); AppealContentDto getAppealContent(Long potId); + AppealContentDto getUserAppealContent(Long potId, Long targetUserId); PotSummaryDto getPotSummary(Long potId); List getCompletedPotsWithBadges(); List getUserCompletedPotsWithBadges(Long userId); diff --git a/src/main/java/stackpot/stackpot/pot/service/pot/MyPotServiceImpl.java b/src/main/java/stackpot/stackpot/pot/service/pot/MyPotServiceImpl.java index 1fa24fa..8be2297 100644 --- a/src/main/java/stackpot/stackpot/pot/service/pot/MyPotServiceImpl.java +++ b/src/main/java/stackpot/stackpot/pot/service/pot/MyPotServiceImpl.java @@ -72,6 +72,45 @@ public List getMyOngoingPots() { } + @Transactional + @Override + public AppealContentDto getUserAppealContent(Long potId, Long targetUserId) { + // 1) 팟 조회 + Pot pot = potRepository.findById(potId) + .orElseThrow(() -> new PotHandler(ErrorStatus.POT_NOT_FOUND)); + + // 2) 상태 체크 + if (!"COMPLETED".equals(pot.getPotStatus())) { + log.error("해당 팟은 COMPLETED 상태가 아닙니다."); + throw new GeneralException(ErrorStatus._BAD_REQUEST); + } + + // 3) 대상 유저의 PotMember 조회 + PotMember potMember = potMemberRepository + .findByPot_PotIdAndUser_Id(potId, targetUserId) + .orElse(null); + + String appealContent = (potMember != null) ? potMember.getAppealContent() : null; + + // 4) 역할 조회 (한글명 매핑) + String userPotRole = potMemberRepository.findRoleByUserId(potId, targetUserId) + .map(role -> RoleNameMapper.getKoreanRoleName(role.name())) + .orElse(null); + + // 5) 뱃지 조회 + List myBadges = potMemberBadgeRepository + .findByPotMember_Pot_PotIdAndPotMember_User_Id(potId, targetUserId) + .stream() + .map(pmb -> new BadgeDto( + pmb.getBadge().getBadgeId(), + pmb.getBadge().getName() + )) + .collect(Collectors.toList()); + + // 6) DTO 변환 + return potDetailConverter.toCompletedPotDetailDto(appealContent, userPotRole, myBadges); + } + @Transactional @Override public AppealContentDto getAppealContent(Long potId) { @@ -116,8 +155,10 @@ public AppealContentDto getAppealContent(Long potId) { public PotSummaryDto getPotSummary(Long potId) { Pot pot = potRepository.findById(potId) .orElseThrow(() -> new PotHandler(ErrorStatus.POT_NOT_FOUND)); + User user = authService.getCurrentUser(); + boolean isMember = potMemberRepository.existsByPotAndUser(pot, user); - return potConverter.toDto(pot); + return potConverter.toDto(pot, isMember); } @Transactional @@ -266,34 +307,31 @@ private List getAllInvolvedPotsByUser(User user, String d .collect(Collectors.toList()); } - @Override - public String patchDelegate(Long potId, Long memberId) { + public String patchDelegate(Long potId, Long potMemberId) { User user = authService.getCurrentUser(); Pot pot = potRepository.findById(potId) .orElseThrow(() -> new PotHandler(ErrorStatus.POT_NOT_FOUND)); - // 기존 Owner PotMember 찾기 PotMember prevOwner = potMemberRepository.findByPot_PotIdAndOwnerTrue(potId); - if (!prevOwner.getUser().equals(user)) { - throw new PotHandler(ErrorStatus.POT_FORBIDDEN); // 권한 없음 + if (prevOwner == null) { + throw new PotHandler(ErrorStatus.POT_NOT_FOUND); + } + if (!prevOwner.getUser().getId().equals(user.getId())) { + throw new PotHandler(ErrorStatus.POT_FORBIDDEN); } - // 기존 Owner PotMember의 owner = false - prevOwner.updateOwner(false); + PotMember newOwner = potMemberRepository.findById(potMemberId) + .orElseThrow(() -> new PotHandler(ErrorStatus.INVALID_MEMBER)); - // 새로운 Owner PotMember의 owner = true - PotMember newOwner = potMemberRepository.findByPotIdAndUserId(potId, memberId); - if (newOwner == null) { - throw new PotHandler(ErrorStatus.INVALID_MEMBER); // 존재하지 않는 멤버 + // 안전장치: 같은 팟인지 확인 + if (!newOwner.getPot().getPotId().equals(potId)) { + throw new PotHandler(ErrorStatus.INVALID_MEMBER); } - // 새로운 오너 PotMember의 owner = true + prevOwner.updateOwner(false); newOwner.updateOwner(true); - - // Pot의 user 외래키 수정 pot.setUser(newOwner.getUser()); - // 저장 potMemberRepository.save(prevOwner); potMemberRepository.save(newOwner); potRepository.save(pot); diff --git a/src/main/java/stackpot/stackpot/user/controller/UserController.java b/src/main/java/stackpot/stackpot/user/controller/UserController.java index 2cc20af..7e0627e 100644 --- a/src/main/java/stackpot/stackpot/user/controller/UserController.java +++ b/src/main/java/stackpot/stackpot/user/controller/UserController.java @@ -315,7 +315,22 @@ public ResponseEntity> getAppealContent( return ResponseEntity.ok(ApiResponse.onSuccess(response)); } - @GetMapping("/potSummary{pot_id}") + @GetMapping("/potAppealContent/{pot_id}/{user_id}") + @Operation( + summary = "다른 사람 마이페이지 '여기서 저는요' 모달 조회 API", + description = "'끓인 팟 상세보기 모달'에 쓰이는 Role, Badge, Appeal Content를 반환합니다." + ) + @ApiErrorCodeExamples({ + ErrorStatus.USER_NOT_FOUND, + }) + public ResponseEntity> getAppealContent( + @PathVariable(name = "pot_id") Long potId, + @PathVariable(name = "user_id") Long userId) { + AppealContentDto response = myPotService.getUserAppealContent(potId, userId); + return ResponseEntity.ok(ApiResponse.onSuccess(response)); + } + + @GetMapping("/potSummary/{pot_id}") @Operation( summary = "끓인 팟 AI 요약 모달 조회 API", description = "끓인 팟을 상세보기할 때 쓰이는 PotSummary, potLan을 반환합니다." diff --git a/src/main/java/stackpot/stackpot/user/service/UserCommandServiceImpl.java b/src/main/java/stackpot/stackpot/user/service/UserCommandServiceImpl.java index a2e1656..0669d83 100644 --- a/src/main/java/stackpot/stackpot/user/service/UserCommandServiceImpl.java +++ b/src/main/java/stackpot/stackpot/user/service/UserCommandServiceImpl.java @@ -1,12 +1,12 @@ package stackpot.stackpot.user.service; -import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.jpa.repository.Modifying; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import stackpot.stackpot.apiPayload.code.status.ErrorStatus; import stackpot.stackpot.apiPayload.exception.GeneralException; import stackpot.stackpot.apiPayload.exception.handler.PotHandler;