Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ dependencies {

//
implementation 'org.springframework.boot:spring-boot-starter-actuator'

Comment on lines 90 to +91
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

요약과 불일치: Actuator가 제거되지 않았습니다

PR 설명/요약에는 Actuator 제거로 되어 있으나, 현재 spring-boot-starter-actuator가 남아 있습니다. 의도대로라면 삭제하세요.

-	implementation 'org.springframework.boot:spring-boot-starter-actuator'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
implementation 'org.springframework.boot:spring-boot-starter-actuator'
🤖 Prompt for AI Agents
In build.gradle around lines 90 to 91, the PR summary says Actuator was removed
but the dependency implementation
'org.springframework.boot:spring-boot-starter-actuator' is still present; remove
that dependency line (or if Actuator should stay, update the PR summary to
reflect that) so the code and PR description are consistent.

//hibernate
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.hibernate.common:hibernate-commons-annotations:6.0.6.Final'
Comment on lines +92 to +94
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

JPA 의존성 중복 선언(앞에서 이미 추가됨) — 정리 필요

이미 2635 라인에 spring-boot-starter-data-jpa가 선언되어 있는데, 9294에서 다시 추가되었습니다. 중복은 해석 순서/버전 충돌, 의존성 그래프 혼선을 유발합니다. 아래처럼 후반부 항목을 제거하세요.

-	//hibernate
-	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
-	implementation 'org.hibernate.common:hibernate-commons-annotations:6.0.6.Final'
+	// Hibernate Commons (필요 시에만)
+	implementation 'org.hibernate.common:hibernate-commons-annotations:6.0.6.Final'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
//hibernate
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.hibernate.common:hibernate-commons-annotations:6.0.6.Final'
// Hibernate Commons (필요 시에만)
implementation 'org.hibernate.common:hibernate-commons-annotations:6.0.6.Final'
🤖 Prompt for AI Agents
In build.gradle around lines 92 to 94, there is a duplicate declaration of
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' that was
already declared at lines 26–35; remove the later duplicate line (leave the
hibernate-commons-annotations line if still needed) so the JPA starter is
declared only once, then run a quick dependency refresh/Gradle sync to ensure no
version/graph issues remain.

}


Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Comment on lines +51 to 59
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

toKaKaoCreatorDto: roleName/user 널 가드 추가 권장

entity.getRoleName().name()entity.getUser().getNickname()은 널일 경우 NPE를 유발합니다. toDto와 동일한 방식으로 방어 코드를 추가하세요. 또한 “추천” 코멘트는 구현에 반영되었으므로 주석 정리도 제안드립니다.

-        String creatorRole = RoleNameMapper.mapRoleName(entity.getRoleName().name());
-        String nicknameWithRole = entity.getUser().getNickname() + " " + creatorRole;
+        String rawRole = entity.getRoleName() != null ? entity.getRoleName().name() : "멤버";
+        String creatorRole = RoleNameMapper.mapRoleName(rawRole);
+        String baseNickname = (entity.getUser() != null) ? entity.getUser().getNickname() : "(알 수 없음)";
+        String nicknameWithRole = baseNickname + " " + creatorRole;
...
-                .potRole(entity.getRoleName().name())
-                .owner(entity.isOwner()) // true 고정 대신 실제 owner 여부 반영 추천
+                .potRole(rawRole)
+                .owner(entity.isOwner())
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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();
String rawRole = entity.getRoleName() != null
? entity.getRoleName().name()
: "멤버";
String creatorRole = RoleNameMapper.mapRoleName(rawRole);
String baseNickname = (entity.getUser() != null)
? entity.getUser().getNickname()
: "(알 수 없음)";
String nicknameWithRole = baseNickname + " " + creatorRole;
return PotMemberInfoResponseDto.builder()
.potMemberId(entity.getPotMemberId())
.nickname(nicknameWithRole)
.potRole(rawRole)
.owner(entity.isOwner())
.build();
🤖 Prompt for AI Agents
In src/main/java/stackpot/stackpot/pot/converter/PotMemberConverter.java around
lines 51 to 59, add null guards like in toDto so calling
entity.getRoleName().name() and entity.getUser().getNickname() cannot NPE: check
entity.getRoleName() and entity.getUser() for null, compute potRole =
entity.getRoleName() != null ? entity.getRoleName().name() : "UNKNOWN" (or other
default), compute nickname = entity.getUser() != null &&
entity.getUser().getNickname() != null ? entity.getUser().getNickname() + " " +
RoleNameMapper.mapRoleName(potRole) : RoleNameMapper.mapRoleName(potRole) (or
use safe defaults), set potRole using the safe value, and remove the leftover
inline Korean "추천" comment so the code is clean.

}

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()) {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/stackpot/stackpot/pot/dto/PotSummaryDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@
public class PotSummaryDto {
private String summary;
private List<String> potLan;
private String potName;
private Boolean isMember;
}

Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public interface MyPotService {
// 사용자의 진행 중인 팟 조회
List<OngoingPotResponseDto> getMyPots();
AppealContentDto getAppealContent(Long potId);
AppealContentDto getUserAppealContent(Long potId, Long targetUserId);
PotSummaryDto getPotSummary(Long potId);
List<CompletedPotBadgeResponseDto> getCompletedPotsWithBadges();
List<CompletedPotBadgeResponseDto> getUserCompletedPotsWithBadges(Long userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,45 @@ public List<OngoingPotResponseDto> 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<BadgeDto> 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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -266,34 +307,31 @@ private List<OngoingPotResponseDto> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,22 @@ public ResponseEntity<ApiResponse<AppealContentDto>> 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<ApiResponse<AppealContentDto>> 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을 반환합니다."
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down