diff --git a/src/main/java/stackpot/stackpot/apiPayload/code/status/ErrorStatus.java b/src/main/java/stackpot/stackpot/apiPayload/code/status/ErrorStatus.java index a671b7a0..9f1d7064 100755 --- a/src/main/java/stackpot/stackpot/apiPayload/code/status/ErrorStatus.java +++ b/src/main/java/stackpot/stackpot/apiPayload/code/status/ErrorStatus.java @@ -66,7 +66,7 @@ public enum ErrorStatus implements BaseErrorCode { // Enum 관련 에러 INVALID_POT_STATUS(HttpStatus.BAD_REQUEST, "POT_STATUS4000", "Pot Status 형식이 올바르지 않습니다 (RECRUITING / ONGOING / COMPLETED)"), INVALID_POT_MODE_OF_OPERATION(HttpStatus.BAD_REQUEST, "MODE_OF_OPERATION4000", "Pot ModeOfOperation 형식이 올바르지 않습니다 (ONLINE / OFFLINE / HYBRID)"), - INVALID_ROLE(HttpStatus.BAD_REQUEST, "ROLE4000", "Role 형식이 올바르지 않습니다 (FRONTEND / DESIGN / BACKEND / PLANNING)"), + INVALID_ROLE(HttpStatus.BAD_REQUEST, "ROLE4000", "Role 형식이 올바르지 않습니다 (FRONTEND / DESIGN / BACKEND / PLAN)"), // Taskboard 관련 에러 TASKBOARD_NOT_FOUND(HttpStatus.NOT_FOUND, "TASKBOARD4004", "해당 Task를 찾을 수 없습니다."), diff --git a/src/main/java/stackpot/stackpot/common/util/RoleNameMapper.java b/src/main/java/stackpot/stackpot/common/util/RoleNameMapper.java index a7f37f74..b225dbda 100644 --- a/src/main/java/stackpot/stackpot/common/util/RoleNameMapper.java +++ b/src/main/java/stackpot/stackpot/common/util/RoleNameMapper.java @@ -10,7 +10,7 @@ public final class RoleNameMapper { "BACKEND", "양파", "FRONTEND", "버섯", "DESIGN", "브로콜리", - "PLANNING", "당근" + "PLAN", "당근" ); public static String getKoreanRoleName(String role) { @@ -18,7 +18,7 @@ public static String getKoreanRoleName(String role) { "BACKEND", "백엔드", "FRONTEND", "프론트엔드", "DESIGN", "디자인", - "PLANNING", "기획" + "PLAN", "기획" ); return roleToKoreanMap.getOrDefault(role, "알 수 없음"); } diff --git a/src/main/java/stackpot/stackpot/feed/controller/FeedController.java b/src/main/java/stackpot/stackpot/feed/controller/FeedController.java index ce1db099..f70278bf 100644 --- a/src/main/java/stackpot/stackpot/feed/controller/FeedController.java +++ b/src/main/java/stackpot/stackpot/feed/controller/FeedController.java @@ -33,7 +33,7 @@ public class FeedController { @PostMapping("") @Operation(summary = "Feed 생성 API", description = "Feed를 생성하는 API입니다.\n" + - "- categories: 다중 선택 가능하며 enum입니다. [ALL, BACKEND, FRONTEND, DESIGN, PLANNING] \n" + + "- categories: 다중 선택 가능하며 enum입니다. [ALL, BACKEND, FRONTEND, DESIGN, PLAN] \n" + "- interests: 다중 선택 가능하며 enum입니다. [SIDE_PROJECT(사이드 프로젝트), SOLO_DEVELOPMENT(1인 개발), COMPETITION(공모전), STARTUP(창업), NETWORKING(네트워킹 행사)]\n" + "- seriesId: 저장할 시리즈의 Id를 입력해 주시면 됩니다. 선택하지 않을 경우 null을 보내주세요. \n") @ApiErrorCodeExamples({ @@ -50,7 +50,7 @@ public ResponseEntity> createFeeds( @GetMapping("") @Operation(summary = "Feed 전체 조회 API", description = "category와 sort에 따라 정렬하여 Feed를 보여줍니다. 커서 기반 페이지페니션으로 응답합니다.", parameters = { - @Parameter(name = "category", description = "ALL : 전체 보기, PLANNING/DESIGN/FRONTEND/BACKEND : 역할별로 보기 ", example = "BACKEND"), + @Parameter(name = "category", description = "ALL : 전체 보기, PLAN/DESIGN/FRONTEND/BACKEND : 역할별로 보기 ", example = "BACKEND"), @Parameter(name = "sort", description = "new : 최신순, old : 오래된순, popular : 인기순(좋아요)", example = "old"), @Parameter(name = "cursor", description = "현재 페이지의 마지막 값"), @Parameter(name = "limit", description = "요청에 불러올 Feed 수", example = "10") @@ -88,7 +88,7 @@ public ResponseEntity> getDetailF @Operation( summary = "Feed 수정 API", description = "요청된 feedId의 feed 내용을 수정합니다. 수정 사항이 없다면 null 값을 넣어주세요\n" + - "- categories: 다중 선택 가능하며 enum입니다. [ALL, BACKEND, FRONTEND, DESIGN, PLANNING] \n" + + "- categories: 다중 선택 가능하며 enum입니다. [ALL, BACKEND, FRONTEND, DESIGN, PLAN] \n" + "- interests: 다중 선택 가능하며 enum입니다. [SIDE_PROJECT(사이드 프로젝트), SOLO_DEVELOPMENT(1인 개발), COMPETITION(공모전), STARTUP(창업), NETWORKING(네트워킹 행사)]\n" + "- seriesId: 저장할 시리즈의 Id를 입력해 주시면 됩니다. 선택하지 않을 경우 null을 보내주세요.\n", parameters = { diff --git a/src/main/java/stackpot/stackpot/feed/converter/FeedConverter.java b/src/main/java/stackpot/stackpot/feed/converter/FeedConverter.java index 6b42cb6c..b38845a4 100644 --- a/src/main/java/stackpot/stackpot/feed/converter/FeedConverter.java +++ b/src/main/java/stackpot/stackpot/feed/converter/FeedConverter.java @@ -9,19 +9,15 @@ import stackpot.stackpot.feed.entity.Feed; import stackpot.stackpot.feed.dto.FeedRequestDto; import stackpot.stackpot.feed.dto.FeedResponseDto; -import stackpot.stackpot.feed.dto.FeedSearchResponseDto; import stackpot.stackpot.feed.entity.Series; import stackpot.stackpot.feed.entity.enums.Interest; import stackpot.stackpot.feed.repository.FeedCommentRepository; -import stackpot.stackpot.feed.repository.FeedLikeRepository; import stackpot.stackpot.user.entity.User; import java.util.ArrayList; import java.util.Map; import java.util.stream.Collectors; -import static stackpot.stackpot.common.util.RoleNameMapper.mapRoleName; - @RequiredArgsConstructor @Component public class FeedConverter{ @@ -37,7 +33,7 @@ public FeedResponseDto.FeedDto feedDto(Feed feed, Boolean isOwner, Boolean isLik .feedId(feed.getFeedId()) .writerId(feed.getUser().getId()) .writer(writerNickname) - .writerRole(feed.getUser().getRole()) + .writerRoles(feed.getUser().getRoleNames()) .title(feed.getTitle()) .content(feed.getContent()) .likeCount(feed.getLikeCount()) @@ -67,7 +63,7 @@ public FeedResponseDto.CreatedFeedDto createFeedDto(Feed feed) { .content(feed.getContent()) .writerId(feed.getUser().getId()) .writer(feed.getUser().getNickname()+ " 새싹") - .writerRole(feed.getUser().getRole()) + .writerRoles(feed.getUser().getRoleNames()) .categories(feed.getCategories().stream() .map(Enum::name) .collect(Collectors.toList())) @@ -89,20 +85,6 @@ public Feed toFeed(FeedRequestDto.createDto dto, Series series) { .build(); } - public FeedSearchResponseDto toSearchDto(Feed feed) { - - return FeedSearchResponseDto.builder() - .userId(feed.getUser().getId()) - .feedId(feed.getFeedId()) - .title(feed.getTitle()) - .content(feed.getContent()) - .creatorNickname(feed.getUser().getNickname()+" 새싹") - .creatorRole(mapRoleName(String.valueOf(feed.getUser().getRole()))) - .createdAt(DateFormatter.koreanFormatter(feed.getCreatedAt())) - .likeCount(feed.getLikeCount()) // 좋아요 개수 포함 - .build(); - } - public FeedResponseDto.AuthorizedFeedDto toAuthorizedFeedDto(Feed feed, boolean isOwner, boolean isLiked, boolean isSaved, Long commentCount) { Map seriesMap = null; if (feed.getSeries() != null) { @@ -116,7 +98,7 @@ public FeedResponseDto.AuthorizedFeedDto toAuthorizedFeedDto(Feed feed, boolean .feedId(feed.getFeedId()) .writerId(feed.getUser().getId()) .writer(writerNickname) - .writerRole(feed.getUser().getRole()) + .writerRoles(feed.getUser().getRoleNames()) .title(feed.getTitle()) .content(feed.getContent()) .createdAt(DateFormatter.koreanFormatter(feed.getCreatedAt())) @@ -145,7 +127,7 @@ public FeedResponseDto.FeedDto toFeedDtoFromCache(FeedCacheDto feed, boolean isL .feedId(feed.getFeedId()) .writerId(feed.getUserId()) .writer(feed.getWriter()) - .writerRole(feed.getWriterRole()) + .writerRoles(feed.getWriterRoles()) .title(feed.getTitle()) .content(feed.getContent()) .likeCount(likeCount) @@ -162,7 +144,7 @@ public FeedCacheDto toFeedCacheDto(Feed feed) { .feedId(feed.getFeedId()) .userId(feed.getUser().getId()) .writer(feed.getUser().getNickname()) - .writerRole(feed.getUser().getRole()) + .writerRoles(feed.getUser().getRoleNames()) .title(feed.getTitle()) .content(feed.getContent()) .createdAt(feed.getCreatedAt().toString()) diff --git a/src/main/java/stackpot/stackpot/feed/dto/FeedCacheDto.java b/src/main/java/stackpot/stackpot/feed/dto/FeedCacheDto.java index b8dd4b4b..4bba2e9e 100644 --- a/src/main/java/stackpot/stackpot/feed/dto/FeedCacheDto.java +++ b/src/main/java/stackpot/stackpot/feed/dto/FeedCacheDto.java @@ -4,6 +4,7 @@ import stackpot.stackpot.user.entity.enums.Role; import java.time.LocalDateTime; +import java.util.List; @Getter @Setter @@ -16,7 +17,7 @@ public class FeedCacheDto { private String content; private Long userId; private String writer; - private Role writerRole; + private List writerRoles; private String createdAt; } diff --git a/src/main/java/stackpot/stackpot/feed/dto/FeedResponseDto.java b/src/main/java/stackpot/stackpot/feed/dto/FeedResponseDto.java index 8f8fdcfc..ec5b077f 100644 --- a/src/main/java/stackpot/stackpot/feed/dto/FeedResponseDto.java +++ b/src/main/java/stackpot/stackpot/feed/dto/FeedResponseDto.java @@ -38,7 +38,7 @@ public static class FeedDto { private Long feedId; private Long writerId; private String writer; - private Role writerRole; + private List writerRoles; private String title; private String content; private Long likeCount; @@ -59,7 +59,7 @@ public static class CreatedFeedDto { private Long feedId; private Long writerId; private String writer; - private Role writerRole; + private List writerRoles; private String title; private String content; private String createdAt; diff --git a/src/main/java/stackpot/stackpot/feed/dto/FeedSearchResponseDto.java b/src/main/java/stackpot/stackpot/feed/dto/FeedSearchResponseDto.java index d1f3f655..e47bf72c 100644 --- a/src/main/java/stackpot/stackpot/feed/dto/FeedSearchResponseDto.java +++ b/src/main/java/stackpot/stackpot/feed/dto/FeedSearchResponseDto.java @@ -2,6 +2,8 @@ import lombok.*; +import java.util.List; + @Getter @Setter @Builder @@ -11,7 +13,7 @@ public class FeedSearchResponseDto { private Long feedId; private Long userId; - private String creatorRole; + private List creatorRole; private Boolean isLiked; private String title; private String content; diff --git a/src/main/java/stackpot/stackpot/pot/controller/PotController.java b/src/main/java/stackpot/stackpot/pot/controller/PotController.java index 33fe6228..a2c155e0 100644 --- a/src/main/java/stackpot/stackpot/pot/controller/PotController.java +++ b/src/main/java/stackpot/stackpot/pot/controller/PotController.java @@ -38,7 +38,7 @@ public class PotController { - potStatus: RECRUITING / ONGOING / COMPLETED - potStartDate, potEndDate: yyyy.MM 형식 (예: 2025.08) - potModeOfOperation: ONLINE / OFFLINE / HYBRID - - Role: FRONTEND / BACKEND / DESIGN / PLANNING + - Role: FRONTEND / BACKEND / DESIGN / PLAN """) @PostMapping public ResponseEntity> createPot(@RequestBody @Valid PotRequestDto requestDto) { diff --git a/src/main/java/stackpot/stackpot/pot/converter/PotConverter.java b/src/main/java/stackpot/stackpot/pot/converter/PotConverter.java index 56f355d0..c27f1c3e 100644 --- a/src/main/java/stackpot/stackpot/pot/converter/PotConverter.java +++ b/src/main/java/stackpot/stackpot/pot/converter/PotConverter.java @@ -16,14 +16,9 @@ import org.springframework.stereotype.Component; -import java.time.LocalDate; -import java.time.temporal.ChronoUnit; -import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import java.time.format.DateTimeFormatter; - @Component public class PotConverter{ @@ -71,7 +66,7 @@ public PotPreviewResponseDto toPrviewDto(User user, Pot pot, List recrui return PotPreviewResponseDto.builder() .userId(user.getId()) - .userRole(user.getRole().name()) + .userRoles(user.getRoleNames()) .userNickname(user.getNickname() + " 새싹") .potId(pot.getPotId()) .potName(pot.getPotName()) @@ -87,7 +82,7 @@ public PotPreviewResponseDto toPrviewDto(User user, Pot pot, List recrui public CompletedPotResponseDto toCompletedPotResponseDto(Pot pot, String formattedMembers, Role userPotRole) { Map roleCountMap = pot.getPotMembers().stream() .collect(Collectors.groupingBy( - member -> member.getRoleName().name(), + member -> member.getRoleName().getKoreanName(), Collectors.reducing(0, e -> 1, Integer::sum) )); @@ -104,33 +99,6 @@ public CompletedPotResponseDto toCompletedPotResponseDto(Pot pot, String formatt .build(); } - public PotSearchResponseDto toSearchDto(Pot pot) { - String roleName = (pot.getUser() != null && pot.getUser().getRole() != null) - ? pot.getUser().getRole().name() - : "멤버"; - - String nicknameWithRole = (pot.getUser() != null && pot.getUser().getNickname() != null) - ? pot.getUser().getNickname() + " " + RoleNameMapper.mapRoleName(roleName) - : "Unknown 멤버"; - - return PotSearchResponseDto.builder() - .potId(pot.getPotId()) - .potName(pot.getPotName()) - .potContent(pot.getPotContent()) - .creatorNickname(nicknameWithRole) - .creatorRole(roleName) - .recruitmentPart( - pot.getRecruitmentDetails() != null - ? pot.getRecruitmentDetails().stream() - .filter(rd -> rd.getRecruitmentRole() != null) - .map(rd -> rd.getRecruitmentRole().name()) - .collect(Collectors.joining(", ")) - : "없음" - ) - .recruitmentDeadline(pot.getPotRecruitmentDeadline()) - .build(); - } - public PotSummaryDto toDto(Pot pot, Boolean isMember) { return PotSummaryDto.builder() .summary(pot.getPotSummary()) diff --git a/src/main/java/stackpot/stackpot/pot/converter/PotDetailConverter.java b/src/main/java/stackpot/stackpot/pot/converter/PotDetailConverter.java index 0921d76c..23aac33a 100644 --- a/src/main/java/stackpot/stackpot/pot/converter/PotDetailConverter.java +++ b/src/main/java/stackpot/stackpot/pot/converter/PotDetailConverter.java @@ -4,7 +4,6 @@ import stackpot.stackpot.common.util.DateFormatter; import stackpot.stackpot.common.util.DdayCounter; import stackpot.stackpot.common.util.OperationModeMapper; -import stackpot.stackpot.common.util.RoleNameMapper; import stackpot.stackpot.pot.entity.Pot; import stackpot.stackpot.pot.entity.PotRecruitmentDetails; import stackpot.stackpot.user.entity.User; @@ -28,7 +27,7 @@ public AppealContentDto toCompletedPotDetailDto(String appealContent, String use .build(); } - public PotDetailResponseDto toPotDetailResponseDto(User user, Pot pot, String recruitmentDetails, Boolean isOwner, Boolean isApplied, Boolean isSaved, Long commentCount) { + public PotDetailResponseDto toPotDetailResponseDto(User user, Pot pot, String recruitmentDetails, Boolean isOwner, Boolean isApplied, Boolean isSaved, Long commentCount, String creatorRoleName) { String dDay = DdayCounter.dDayCount(pot.getPotRecruitmentDeadline()); Map recruitingMembers = pot.getRecruitmentDetails().stream() @@ -39,7 +38,7 @@ public PotDetailResponseDto toPotDetailResponseDto(User user, Pot pot, String re return PotDetailResponseDto.builder() .userId(user.getId()) - .userRole(user.getRole().name()) + .userRole(creatorRoleName) .userNickname(user.getNickname() + " 새싹") .isOwner(isOwner) .potId(pot.getPotId()) diff --git a/src/main/java/stackpot/stackpot/pot/converter/PotMemberConverter.java b/src/main/java/stackpot/stackpot/pot/converter/PotMemberConverter.java index 252c8bde..59debfcb 100644 --- a/src/main/java/stackpot/stackpot/pot/converter/PotMemberConverter.java +++ b/src/main/java/stackpot/stackpot/pot/converter/PotMemberConverter.java @@ -18,7 +18,11 @@ public PotMember toEntity(User user, Pot pot, PotApplication application, Boolea .user(user) .pot(pot) .potApplication(application) - .roleName(application != null ? application.getPotRole() : user.getRole()) // PotRole Enum 그대로 사용 + .roleName( + application != null + ? application.getPotRole() + : (user.getRoles().isEmpty() ? Role.UNKNOWN : user.getRoles().get(0)) + ) .owner(isOwner) .appealContent(null) .build(); @@ -81,4 +85,12 @@ public PotMemberInfoResponseDto toKaKaoMemberDto(PotMember entity) { .potRole(roleName) .build(); } + public PotMember toCreatorEntity(User user, Pot pot, String potRole) { + return PotMember.builder() + .user(user) + .pot(pot) + .roleName(Role.fromString(potRole)) + .owner(true) + .build(); + } } \ No newline at end of file diff --git a/src/main/java/stackpot/stackpot/pot/dto/PotPreviewResponseDto.java b/src/main/java/stackpot/stackpot/pot/dto/PotPreviewResponseDto.java index 8d83a372..374c06bc 100644 --- a/src/main/java/stackpot/stackpot/pot/dto/PotPreviewResponseDto.java +++ b/src/main/java/stackpot/stackpot/pot/dto/PotPreviewResponseDto.java @@ -11,7 +11,7 @@ @Builder public class PotPreviewResponseDto { private Long userId; - protected String userRole; + protected List userRoles; private String userNickname; private Long potId; private String potName; diff --git a/src/main/java/stackpot/stackpot/pot/dto/PotRecruitmentRequestDto.java b/src/main/java/stackpot/stackpot/pot/dto/PotRecruitmentRequestDto.java index 7930e60c..a59be6c6 100644 --- a/src/main/java/stackpot/stackpot/pot/dto/PotRecruitmentRequestDto.java +++ b/src/main/java/stackpot/stackpot/pot/dto/PotRecruitmentRequestDto.java @@ -1,5 +1,6 @@ package stackpot.stackpot.pot.dto; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Builder; import lombok.Getter; import lombok.Setter; @@ -10,6 +11,8 @@ @Builder public class PotRecruitmentRequestDto { @ValidRole + @Schema(description = "모집 역할", example = "BACKEND") private String recruitmentRole; + @Schema(description = "역할 별 모집 인원 수", example = "1") private Integer recruitmentCount; } diff --git a/src/main/java/stackpot/stackpot/pot/repository/PotMemberRepository.java b/src/main/java/stackpot/stackpot/pot/repository/PotMemberRepository.java index 9f9b559a..3f147c88 100644 --- a/src/main/java/stackpot/stackpot/pot/repository/PotMemberRepository.java +++ b/src/main/java/stackpot/stackpot/pot/repository/PotMemberRepository.java @@ -12,6 +12,7 @@ import stackpot.stackpot.user.entity.User; import stackpot.stackpot.user.entity.enums.Role; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; @@ -96,4 +97,13 @@ public interface PotMemberRepository extends JpaRepository { long countByPot_PotId(Long potId); Optional findByPot_PotIdAndUser_Id(Long potId, Long userId); + @Query(""" +select pm.user.id as userId, pm.roleName as role +from PotMember pm +where pm.pot.potId = :potId + and pm.user.id in :userIds +""") + List findCreatorRolesByPotAndUserIds(@Param("potId") Long potId, + @Param("userIds") Collection userIds); + } diff --git a/src/main/java/stackpot/stackpot/pot/service/pot/PotCommandServiceImpl.java b/src/main/java/stackpot/stackpot/pot/service/pot/PotCommandServiceImpl.java index 4189b64a..1d31481f 100644 --- a/src/main/java/stackpot/stackpot/pot/service/pot/PotCommandServiceImpl.java +++ b/src/main/java/stackpot/stackpot/pot/service/pot/PotCommandServiceImpl.java @@ -75,6 +75,8 @@ public PotResponseDto createPotWithRecruitments(PotRequestDto requestDto) { pot.setPotStatus("RECRUITING"); Pot savedPot = potRepository.save(pot); + PotMember creator = potMemberConverter.toCreatorEntity(user, savedPot, String.valueOf(requestDto.getPotRole())); + potMemberRepository.save(creator); List recruitmentDetails = requestDto.getRecruitmentDetails().stream() .map(dto -> PotRecruitmentDetails.builder() diff --git a/src/main/java/stackpot/stackpot/pot/service/pot/PotQueryServiceImpl.java b/src/main/java/stackpot/stackpot/pot/service/pot/PotQueryServiceImpl.java index 50928554..99e51b7a 100644 --- a/src/main/java/stackpot/stackpot/pot/service/pot/PotQueryServiceImpl.java +++ b/src/main/java/stackpot/stackpot/pot/service/pot/PotQueryServiceImpl.java @@ -21,7 +21,6 @@ import stackpot.stackpot.pot.dto.*; import stackpot.stackpot.pot.entity.Pot; import stackpot.stackpot.pot.entity.mapping.PotApplication; -import stackpot.stackpot.pot.entity.mapping.PotMember; import stackpot.stackpot.pot.repository.PotCommentRepository; import stackpot.stackpot.pot.repository.PotMemberRepository; import stackpot.stackpot.pot.repository.PotRepository; @@ -68,12 +67,11 @@ public CursorPageResponse getMyCompletedPots(Long curso roleCount -> ((Long) roleCount[1]).intValue() )); String formattedMembers = roleCountsMap.entrySet().stream() - .map(entry -> RoleNameMapper.mapRoleName(entry.getKey()) + "(" + entry.getValue() + ")") + .map(entry -> RoleNameMapper.getKoreanRoleName(entry.getKey()) + "(" + entry.getValue() + ")") .collect(Collectors.joining(", ")); - Role userPotRole = pot.getUser().getId().equals(user.getId()) - ? pot.getUser().getRole() - : potMemberRepository.findRoleByUserId(pot.getPotId(), user.getId()).orElse(pot.getUser().getRole()); + Role userPotRole = potMemberRepository.findRoleByUserId(pot.getPotId(), user.getId()) + .orElse(null); return potConverter.toCompletedPotResponseDto(pot, formattedMembers, userPotRole); }) @@ -95,12 +93,16 @@ public PotDetailResponseDto getPotDetails(Long potId) { boolean isSaved = potSaveRepository.existsByUserAndPot_PotId(user, potId); String recruitmentDetails = pot.getRecruitmentDetails().stream() - .map(rd -> RoleNameMapper.mapRoleName(rd.getRecruitmentRole().name()) + "(" + rd.getRecruitmentCount() + ")") + .map(rd -> RoleNameMapper.getKoreanRoleName(rd.getRecruitmentRole().name()) + "(" + rd.getRecruitmentCount() + ")") .collect(Collectors.joining(", ")); Long countComment = potCommentRepository.countByPotId(potId); + Role creatorRole = potMemberRepository + .findRoleByUserId(pot.getPotId(), pot.getUser().getId()) + .orElse(null); - return potDetailConverter.toPotDetailResponseDto(pot.getUser(), pot, recruitmentDetails, isOwner, isApplied, isSaved, countComment); + String creatorRoleName = creatorRole != null ? creatorRole.name() : "UNKNOWN"; + return potDetailConverter.toPotDetailResponseDto(pot.getUser(), pot, recruitmentDetails, isOwner, isApplied, isSaved, countComment, creatorRoleName); } @Override @@ -234,9 +236,8 @@ public CursorPageResponse getUserCompletedPots(Long use .map(entry -> RoleNameMapper.mapRoleName(entry.getKey()) + "(" + entry.getValue() + ")") .collect(Collectors.joining(", ")); - Role userPotRole = pot.getUser().getId().equals(user.getId()) - ? pot.getUser().getRole() - : potMemberRepository.findRoleByUserId(pot.getPotId(), user.getId()).orElse(pot.getUser().getRole()); + Role userPotRole = potMemberRepository.findRoleByUserId(pot.getPotId(), user.getId()) + .orElse(null); // or throw if required return potConverter.toCompletedPotResponseDto(pot, formattedMembers, userPotRole); }) diff --git a/src/main/java/stackpot/stackpot/pot/service/potApplication/PotApplicationCommandServiceImpl.java b/src/main/java/stackpot/stackpot/pot/service/potApplication/PotApplicationCommandServiceImpl.java index f58e4a25..62697b9e 100644 --- a/src/main/java/stackpot/stackpot/pot/service/potApplication/PotApplicationCommandServiceImpl.java +++ b/src/main/java/stackpot/stackpot/pot/service/potApplication/PotApplicationCommandServiceImpl.java @@ -26,6 +26,7 @@ import java.time.LocalDateTime; import java.util.Optional; import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -80,10 +81,9 @@ public void cancelApplication(Long potId) { private void sendSupportEmailAsync(User user, Pot pot, PotApplication application) { String appliedRole = application.getPotRole().name(); String appliedRoleName = RoleNameMapper.mapRoleName(appliedRole); - String applicantRole = Optional.ofNullable(user.getRole()) + String applicantRole = user.getRoles().stream() .map(role -> RoleNameMapper.mapRoleName(role.name())) - .orElse("멤버"); - + .collect(Collectors.joining(", ")); CompletableFuture.runAsync(() -> emailService.sendSupportNotification( pot.getUser().getEmail(), pot.getPotName(), diff --git a/src/main/java/stackpot/stackpot/pot/service/potApplication/PotApplicationQueryServiceImpl.java b/src/main/java/stackpot/stackpot/pot/service/potApplication/PotApplicationQueryServiceImpl.java index 2623dc96..cc5a40d4 100644 --- a/src/main/java/stackpot/stackpot/pot/service/potApplication/PotApplicationQueryServiceImpl.java +++ b/src/main/java/stackpot/stackpot/pot/service/potApplication/PotApplicationQueryServiceImpl.java @@ -14,11 +14,9 @@ import stackpot.stackpot.pot.dto.PotDetailWithApplicantsResponseDto; import stackpot.stackpot.pot.entity.Pot; import stackpot.stackpot.pot.entity.mapping.PotApplication; -import stackpot.stackpot.pot.repository.PotApplicationRepository; -import stackpot.stackpot.pot.repository.PotCommentRepository; -import stackpot.stackpot.pot.repository.PotRepository; -import stackpot.stackpot.pot.repository.PotSaveRepository; +import stackpot.stackpot.pot.repository.*; import stackpot.stackpot.user.entity.User; +import stackpot.stackpot.user.entity.enums.Role; import stackpot.stackpot.user.repository.UserRepository; import java.util.Collections; @@ -32,6 +30,7 @@ public class PotApplicationQueryServiceImpl implements PotApplicationQueryServic private final PotCommentRepository potCommentRepository; private final PotApplicationRepository potApplicationRepository; private final PotRepository potRepository; + private final PotMemberRepository potMemberRepository; private final UserRepository userRepository; private final PotApplicationConverter potApplicationConverter; private final PotDetailConverter potDetailConverter; @@ -68,12 +67,16 @@ public PotDetailWithApplicantsResponseDto getPotDetailsAndApplicants(Long potId) boolean isSaved = potSaveRepository.existsByUserAndPot_PotId(user, potId); String recruitmentDetails = pot.getRecruitmentDetails().stream() - .map(rd -> RoleNameMapper.mapRoleName(rd.getRecruitmentRole().name()) + "(" + rd.getRecruitmentCount() + ")") + .map(rd -> RoleNameMapper.getKoreanRoleName(rd.getRecruitmentRole().name()) + "(" + rd.getRecruitmentCount() + ")") .collect(Collectors.joining(", ")); Long commentCount = potCommentRepository.countByPotId(potId); + Role creatorRole = potMemberRepository + .findRoleByUserId(pot.getPotId(), pot.getUser().getId()) + .orElse(null); - PotDetailResponseDto potDetailDto = potDetailConverter.toPotDetailResponseDto(pot.getUser(), pot, recruitmentDetails, isOwner, isApplied, isSaved, commentCount); + String creatorRoleName = creatorRole != null ? creatorRole.name() : "UNKNOWN"; + PotDetailResponseDto potDetailDto = potDetailConverter.toPotDetailResponseDto(pot.getUser(), pot, recruitmentDetails, isOwner, isApplied, isSaved, commentCount, creatorRoleName); List applicants = Collections.emptyList(); if (isOwner && "RECRUITING".equals(pot.getPotStatus())) { diff --git a/src/main/java/stackpot/stackpot/pot/service/potMember/PotMemberCommandServiceImpl.java b/src/main/java/stackpot/stackpot/pot/service/potMember/PotMemberCommandServiceImpl.java index dc313cb5..ae8e19e6 100644 --- a/src/main/java/stackpot/stackpot/pot/service/potMember/PotMemberCommandServiceImpl.java +++ b/src/main/java/stackpot/stackpot/pot/service/potMember/PotMemberCommandServiceImpl.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import java.util.stream.Stream; @Service @RequiredArgsConstructor @@ -76,23 +77,31 @@ public List addMembersToPot(Long potId, PotMemberReq potApplicationRepository.save(application); } - PotMember member = potMemberConverter.toEntity(potCreatUser, pot, null, true); - newMembers.add(member); + // 생성자 PotMember는 이미 createPot 단계에서 저장됨 → 새로 만들지 말고 조회 + PotMember creatorPM = potMemberRepository.findByPotIdAndUserId(potId, potCreator.getId()); + if (creatorPM == null) { + throw new PotHandler(ErrorStatus.POT_MEMBER_NOT_FOUND); + } + // 승인된 멤버들만 저장 List savedMembers = potMemberRepository.saveAll(newMembers); // ChatRoom + ChatRoomInfo 생성 Long chatRoomId = chatRoomCommandService.createChatRoom(pot.getPotName(), pot); - List potMemberIds = savedMembers.stream() - .map(PotMember::getPotMemberId) - .collect(Collectors.toList()); + List potMemberIds = Stream.concat( + Stream.of(creatorPM.getPotMemberId()), + savedMembers.stream().map(PotMember::getPotMemberId) + ).toList(); + chatRoomInfoCommandService.createChatRoomInfo(potMemberIds, chatRoomId); - return savedMembers.stream() + // 응답 DTO = 생성자 + 신규 승인자 모두 포함 + return Stream.concat(Stream.of(creatorPM), savedMembers.stream()) .map(potMemberConverter::toDto) - .collect(Collectors.toList()); + .toList(); } + @Transactional @Override public void updateAppealContent(Long potId, String appealContent) { @@ -106,7 +115,7 @@ public void updateAppealContent(Long potId, String appealContent) { } @Override - @org.springframework.transaction.annotation.Transactional + @Transactional public void removeMemberFromPot(Long potId) { User user = authService.getCurrentUser(); Pot pot = potRepository.findById(potId) diff --git a/src/main/java/stackpot/stackpot/task/converter/TaskBoardConverter.java b/src/main/java/stackpot/stackpot/task/converter/TaskBoardConverter.java index 807cf942..6f7143fd 100644 --- a/src/main/java/stackpot/stackpot/task/converter/TaskBoardConverter.java +++ b/src/main/java/stackpot/stackpot/task/converter/TaskBoardConverter.java @@ -33,21 +33,26 @@ public Taskboard toTaskboard(Pot pot, MyPotTaskRequestDto.create request, User u .user(user) .build(); } - public MyPotTaskResponseDto toDTO(Taskboard taskboard, List participants) { + public MyPotTaskResponseDto toDTO(Taskboard taskboard, List participants, + Role creatorRole) { + + String creatorRoleKo = RoleNameMapper.mapRoleName(creatorRole.name()); + String creatorNickname = taskboard.getUser().getNickname() + " " + creatorRoleKo; return MyPotTaskResponseDto.builder() .taskboardId(taskboard.getTaskboardId()) .title(taskboard.getTitle()) .creatorUserId(taskboard.getUser().getUserId()) - .creatorNickname(taskboard.getUser().getNickname() + " " + RoleNameMapper.mapRoleName(String.valueOf(taskboard.getUser().getRole()))) - .creatorRole(taskboard.getUser().getRole()) + .creatorNickname(creatorNickname) + .creatorRole(Role.valueOf(creatorRole.name())) .deadLine(DateFormatter.dotFormatter(taskboard.getDeadLine())) .dDay(DdayCounter.dDayCount(taskboard.getDeadLine())) .description(taskboard.getDescription()) .status(taskboard.getStatus()) .potId(taskboard.getPot().getPotId()) - .participants(toParticipantDtoList(participants)) // 참여자 리스트 변환 + .participants(toParticipantDtoList(participants)) .build(); } + public List toParticipantDtoList(List participants) { return participants.stream() .map(this::toParticipantDto) @@ -64,21 +69,26 @@ public MyPotTaskResponseDto.Participant toParticipantDto(PotMember participant) .build(); } - public MyPotTaskPreViewResponseDto toDto(Taskboard taskboard, List participants) { + public MyPotTaskPreViewResponseDto toDto(Taskboard taskboard, + List participants, + Role creatorRole) { + String creatorRoleKo = RoleNameMapper.mapRoleName(creatorRole.name()); + return MyPotTaskPreViewResponseDto.builder() .taskboardId(taskboard.getTaskboardId()) .title(taskboard.getTitle()) - .creatorNickname(taskboard.getUser().getNickname() + " " + RoleNameMapper.mapRoleName(String.valueOf(taskboard.getUser().getRole()))) - .creatorRole(taskboard.getUser().getRole()) + .creatorNickname(taskboard.getUser().getNickname() + " " + creatorRoleKo) // 닉네임 + 한글 + .creatorRole(Role.valueOf(creatorRole.name())) .dDay(DdayCounter.dDayCount(taskboard.getDeadLine())) .description(taskboard.getDescription()) - .category(determineCategories(participants)) // 카테고리 설정 - .status(taskboard.getStatus()) // OPEN, IN_PROGRESS, CLOSED + .category(determineCategories(participants)) + .status(taskboard.getStatus()) .deadLine(DateFormatter.dotFormatter(taskboard.getDeadLine())) - .participants(toParticipantDtoList(participants)) // 참여자 리스트 변환 + .participants(toParticipantDtoList(participants)) .build(); } + public MyPotTaskStatusResponseDto toTaskStatusDto(Taskboard taskboard, TaskboardStatus taskboardStatus) { return MyPotTaskStatusResponseDto.builder() .taskboardId(taskboard.getTaskboardId()) diff --git a/src/main/java/stackpot/stackpot/task/repository/TaskRepository.java b/src/main/java/stackpot/stackpot/task/repository/TaskRepository.java index 63d5e906..eb54e958 100644 --- a/src/main/java/stackpot/stackpot/task/repository/TaskRepository.java +++ b/src/main/java/stackpot/stackpot/task/repository/TaskRepository.java @@ -10,6 +10,7 @@ import stackpot.stackpot.task.entity.Taskboard; import stackpot.stackpot.task.entity.mapping.Task; +import java.util.Collection; import java.util.List; @Repository @@ -32,4 +33,9 @@ public interface TaskRepository extends JpaRepository { "GROUP BY t.potMember.potMemberId " + "ORDER BY count(t) DESC") List getTop2TaskCountByPotMemberId(@Param("potMemberIds") List potMemberIds, Pageable pageable); + + List findByTaskboardIn(Collection taskboards); + + + } diff --git a/src/main/java/stackpot/stackpot/task/repository/TaskboardRepository.java b/src/main/java/stackpot/stackpot/task/repository/TaskboardRepository.java index 1312a742..83a8de1b 100644 --- a/src/main/java/stackpot/stackpot/task/repository/TaskboardRepository.java +++ b/src/main/java/stackpot/stackpot/task/repository/TaskboardRepository.java @@ -42,4 +42,5 @@ List findByPotPotIdAndDeadLine( @Param("date") LocalDate date ); + } diff --git a/src/main/java/stackpot/stackpot/task/service/TaskCommandServiceImpl.java b/src/main/java/stackpot/stackpot/task/service/TaskCommandServiceImpl.java index 51fd4bc1..ab97211e 100644 --- a/src/main/java/stackpot/stackpot/task/service/TaskCommandServiceImpl.java +++ b/src/main/java/stackpot/stackpot/task/service/TaskCommandServiceImpl.java @@ -19,6 +19,7 @@ import stackpot.stackpot.task.repository.TaskRepository; import stackpot.stackpot.task.repository.TaskboardRepository; import stackpot.stackpot.user.entity.User; +import stackpot.stackpot.user.entity.enums.Role; import java.util.List; import java.util.stream.Collectors; @@ -57,7 +58,11 @@ public MyPotTaskResponseDto createTask(Long potId, MyPotTaskRequestDto.create re this.createAndSaveTasks(taskboard, participants); List participantDtos = taskboardConverter.toParticipantDtoList(participants); - MyPotTaskResponseDto response = taskboardConverter.toDTO(taskboard, participants); + Role creatorRole = potMemberRepository + .findRoleByUserId(taskboard.getPot().getPotId(), taskboard.getUser().getUserId()) + .orElse(Role.UNKNOWN); + + MyPotTaskResponseDto response = taskboardConverter.toDTO(taskboard, participants,creatorRole); response.setParticipants(participantDtos); return response; @@ -87,7 +92,12 @@ public MyPotTaskResponseDto modifyTask(Long potId, Long taskBoardId, MyPotTaskRe if (!participants.isEmpty()) createAndSaveTasks(taskboard, participants); List participantDtos = taskboardConverter.toParticipantDtoList(participants); - MyPotTaskResponseDto response = taskboardConverter.toDTO(taskboard, participants); + + Role creatorRole = potMemberRepository + .findRoleByUserId(taskboard.getPot().getPotId(), taskboard.getUser().getUserId()) + .orElse(Role.UNKNOWN); + + MyPotTaskResponseDto response = taskboardConverter.toDTO(taskboard, participants,creatorRole); response.setParticipants(participantDtos); return response; diff --git a/src/main/java/stackpot/stackpot/task/service/TaskQueryServiceImpl.java b/src/main/java/stackpot/stackpot/task/service/TaskQueryServiceImpl.java index 103d12fc..321b43e7 100644 --- a/src/main/java/stackpot/stackpot/task/service/TaskQueryServiceImpl.java +++ b/src/main/java/stackpot/stackpot/task/service/TaskQueryServiceImpl.java @@ -21,10 +21,13 @@ import stackpot.stackpot.task.repository.TaskRepository; import stackpot.stackpot.task.repository.TaskboardRepository; import stackpot.stackpot.user.entity.User; +import stackpot.stackpot.user.entity.enums.Role; import java.time.LocalDate; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; @Slf4j @@ -52,7 +55,10 @@ public Map> preViewTask(Long .map(Task::getPotMember) .distinct() .collect(Collectors.toList()); - return taskboardConverter.toDto(taskboard, participants); + Role creatorRole = potMemberRepository + .findRoleByUserId(taskboard.getPot().getPotId(), taskboard.getUser().getUserId()) + .orElse(Role.UNKNOWN); + return taskboardConverter.toDto(taskboard, participants,creatorRole ); }) .toList(); @@ -72,31 +78,82 @@ public MyPotTaskResponseDto viewDetailTask(Long potId, Long taskBoardId) { .map(Task::getPotMember) .distinct() .collect(Collectors.toList()); - return taskboardConverter.toDTO(taskboard, participants); + Role creatorRole = potMemberRepository + .findRoleByUserId(taskboard.getPot().getPotId(), taskboard.getUser().getUserId()) + .orElse(Role.UNKNOWN); + return taskboardConverter.toDTO(taskboard, participants,creatorRole); } +// @Override +// public List getTasksFromDate(Long potId, LocalDate date) { +// User user = authService.getCurrentUser(); +// +// potMemberRepository.findByPotPotIdAndUser(potId, user) +// .orElseThrow(() -> new PotHandler(ErrorStatus.POT_MEMBER_NOT_FOUND)); +// +// List taskboards = taskboardRepository.findByPotPotIdAndDeadLine(potId, date); +// +// return taskboards.stream() +// .map(taskboard -> { +// List tasks = taskRepository.findByTaskboard(taskboard); // Task 조회 +// List participants = tasks.stream() +// .map(Task::getPotMember) // Task에서 PotMember 추출 +// .distinct() +// .collect(Collectors.toList()); +// Role creatorRole = potMemberRepository +// .findRoleByUserId(taskboard.getPot().getPotId(), taskboard.getUser().getUserId()) +// .orElse(Role.UNKNOWN); +// return taskboardConverter.toDto(taskboard, participants,creatorRole); +// }) +// .collect(Collectors.toList()); +// } @Override + @Transactional public List getTasksFromDate(Long potId, LocalDate date) { - User user = authService.getCurrentUser(); + User me = authService.getCurrentUser(); - potMemberRepository.findByPotPotIdAndUser(potId, user) + // 1) 멤버십 체크 + potMemberRepository.findByPotPotIdAndUser(potId, me) .orElseThrow(() -> new PotHandler(ErrorStatus.POT_MEMBER_NOT_FOUND)); - List taskboards = taskboardRepository.findByPotPotIdAndDeadLine(potId, date); - - return taskboards.stream() - .map(taskboard -> { - List tasks = taskRepository.findByTaskboard(taskboard); // Task 조회 - List participants = tasks.stream() - .map(Task::getPotMember) // Task에서 PotMember 추출 - .distinct() - .collect(Collectors.toList()); - - return taskboardConverter.toDto(taskboard, participants); - }) - .collect(Collectors.toList()); + // 2) Taskboard 배치 조회 + List boards = taskboardRepository.findByPotPotIdAndDeadLine(potId, date); + if (boards.isEmpty()) return List.of(); + + // 3) Task 배치 조회 후 그룹핑 + List allTasks = taskRepository.findByTaskboardIn(boards); + Map> tasksByBoardId = allTasks.stream() + .collect(Collectors.groupingBy(t -> t.getTaskboard().getTaskboardId())); + + // 4) 작성자 역할 배치 조회 + Set creatorIds = boards.stream() + .map(tb -> tb.getUser().getUserId()) + .collect(Collectors.toSet()); + + Map creatorRoleMap = potMemberRepository + .findCreatorRolesByPotAndUserIds(potId, creatorIds).stream() + .collect(Collectors.toMap( + row -> (Long) row[0], + row -> (Role) row[1], + (a, b) -> a + )); + + // 5) DTO 변환 + return boards.stream().map(tb -> { + // participants: Task → PotMember → id distinct + List tasks = tasksByBoardId.getOrDefault(tb.getTaskboardId(), List.of()); + List participants = tasks.stream() + .map(Task::getPotMember) + .collect(Collectors.toMap(PotMember::getPotMemberId, pm -> pm, (a, b) -> a)) + .values().stream().toList(); + + Role creatorRole = creatorRoleMap.getOrDefault(tb.getUser().getUserId(), Role.UNKNOWN); + + return taskboardConverter.toDto(tb, participants, creatorRole); + }).toList(); } + @Override public List getMonthlyTasks(Long potId, int year, int month) { User user = authService.getCurrentUser(); diff --git a/src/main/java/stackpot/stackpot/user/converter/UserConverter.java b/src/main/java/stackpot/stackpot/user/converter/UserConverter.java index 466e8a5d..3094c3f6 100644 --- a/src/main/java/stackpot/stackpot/user/converter/UserConverter.java +++ b/src/main/java/stackpot/stackpot/user/converter/UserConverter.java @@ -1,32 +1,18 @@ package stackpot.stackpot.user.converter; -import stackpot.stackpot.user.entity.Interest; import stackpot.stackpot.user.entity.TempUser; import stackpot.stackpot.user.entity.User; -import stackpot.stackpot.user.entity.enums.Role; -import stackpot.stackpot.user.dto.request.UserRequestDto; import stackpot.stackpot.user.dto.response.UserResponseDto; import stackpot.stackpot.user.dto.response.UserSignUpResponseDto; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; public class UserConverter { - public static User toUser(UserRequestDto.JoinDto request) { - List interests = request.getInterest(); - - return User.builder() - .interests(interests) - .role(Role.valueOf(String.valueOf(request.getRole()))) - .build(); - } public static UserSignUpResponseDto toUserSignUpResponseDto(TempUser user) { return UserSignUpResponseDto.builder() .id(user.getId()) - .role(user.getRole()) + .roles(user.getRoleNames()) .build(); } @@ -45,7 +31,7 @@ public static UserResponseDto.Userdto toDto(User user) { .nickname(nicknameWithRole) .email(user.getEmail()) .kakaoId(user.getKakaoId()) - .role(user.getRole()) + .roles(user.getRoleNames()) .interest(interests) .userTemperature(user.getUserTemperature()) .userDescription(user.getUserDescription()) @@ -66,7 +52,7 @@ public static UserResponseDto.UserInfoDto toUserInfo(User user) { return UserResponseDto.UserInfoDto.builder() .id(user.getId()) .nickname(nicknameWithRole) - .role(user.getRole()) + .roles(user.getRoleNames()) .interest(interests) .userTemperature(user.getUserTemperature()) .userIntroduction(user.getUserIntroduction()) diff --git a/src/main/java/stackpot/stackpot/user/dto/request/UserRequestDto.java b/src/main/java/stackpot/stackpot/user/dto/request/UserRequestDto.java index b6a65ccb..832da9d7 100644 --- a/src/main/java/stackpot/stackpot/user/dto/request/UserRequestDto.java +++ b/src/main/java/stackpot/stackpot/user/dto/request/UserRequestDto.java @@ -16,7 +16,7 @@ public class UserRequestDto { @NoArgsConstructor public static class JoinDto { @Schema(description = "역할") - Role role; + private List roles; @Schema(description = "관심사", example = "[\"사이드 프로젝트\", \"1인 개발\"]") List interest; diff --git a/src/main/java/stackpot/stackpot/user/dto/request/UserUpdateRequestDto.java b/src/main/java/stackpot/stackpot/user/dto/request/UserUpdateRequestDto.java index 01ee00b5..22032f78 100644 --- a/src/main/java/stackpot/stackpot/user/dto/request/UserUpdateRequestDto.java +++ b/src/main/java/stackpot/stackpot/user/dto/request/UserUpdateRequestDto.java @@ -17,7 +17,7 @@ public class UserUpdateRequestDto { @ValidRole @Schema(description = "역할") - private Role role; + private List roles; @Schema(description = "관심사", example = "[\"사이드 프로젝트\", \"1인 개발\"]") private List interest; diff --git a/src/main/java/stackpot/stackpot/user/dto/response/UserResponseDto.java b/src/main/java/stackpot/stackpot/user/dto/response/UserResponseDto.java index 6ef3c4fd..11a94a6a 100644 --- a/src/main/java/stackpot/stackpot/user/dto/response/UserResponseDto.java +++ b/src/main/java/stackpot/stackpot/user/dto/response/UserResponseDto.java @@ -18,7 +18,7 @@ public static class Userdto{ private Long id; private String email; // 이메일 private String nickname; // 닉네임 - private Role role; // 역할 + private List roles; // 역할 @Schema(description = "관심사", example = "[\"사이드 프로젝트\", \"1인 개발\"]") private List interest; // 관심사 private Integer userTemperature; // 유저 온도 @@ -38,7 +38,7 @@ public static class loginDto { private TokenServiceResponse tokenServiceResponse; @Schema(description = "역할") - private final Role role; + private List roles; @Schema(description = "신규 유저 여부") private Boolean isNewUser; @@ -51,7 +51,7 @@ public static class loginDto { public static class UserInfoDto { private Long id; private String nickname; - private Role role; + private List roles; private List interest; // 관심사 private Integer userTemperature; private String userIntroduction; diff --git a/src/main/java/stackpot/stackpot/user/dto/response/UserSignUpResponseDto.java b/src/main/java/stackpot/stackpot/user/dto/response/UserSignUpResponseDto.java index bd042295..31bdbc8a 100644 --- a/src/main/java/stackpot/stackpot/user/dto/response/UserSignUpResponseDto.java +++ b/src/main/java/stackpot/stackpot/user/dto/response/UserSignUpResponseDto.java @@ -6,6 +6,8 @@ import lombok.Setter; import stackpot.stackpot.user.entity.enums.Role; +import java.util.List; + @Getter @Setter @Builder @@ -15,5 +17,5 @@ public class UserSignUpResponseDto { private Long id; @Schema(description = "역할") - private Role role; + private List roles; } diff --git a/src/main/java/stackpot/stackpot/user/entity/TempUser.java b/src/main/java/stackpot/stackpot/user/entity/TempUser.java index b5d98198..d3a72461 100644 --- a/src/main/java/stackpot/stackpot/user/entity/TempUser.java +++ b/src/main/java/stackpot/stackpot/user/entity/TempUser.java @@ -7,8 +7,11 @@ import stackpot.stackpot.user.entity.enums.Provider; import stackpot.stackpot.user.entity.enums.Role; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; @Entity @Getter @@ -28,9 +31,11 @@ public class TempUser extends BaseEntity{ @Column(nullable = false) private String providerId; + @ElementCollection(targetClass = Role.class) @Enumerated(EnumType.STRING) - @Column(nullable = true, length = 255) - private Role role; // 역할 + @CollectionTable(name = "temp_user_roles", joinColumns = @JoinColumn(name = "temp_user_id")) + @Column(name = "role") + private List roles = new ArrayList<>(); @Column(nullable = true) @ElementCollection @@ -42,5 +47,11 @@ public class TempUser extends BaseEntity{ @Column(nullable = true) private String kakaoId; + public List getRoleNames() { + return roles != null + ? roles.stream().map(Enum::name).collect(Collectors.toList()) + : Collections.emptyList(); + } + } diff --git a/src/main/java/stackpot/stackpot/user/entity/User.java b/src/main/java/stackpot/stackpot/user/entity/User.java index 75d646aa..01ea77f2 100644 --- a/src/main/java/stackpot/stackpot/user/entity/User.java +++ b/src/main/java/stackpot/stackpot/user/entity/User.java @@ -14,7 +14,9 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; @Entity @@ -48,9 +50,11 @@ public class User extends BaseEntity implements UserDetails{ @Column(nullable = true, length = 255) private String nickname; // 닉네임 + @ElementCollection(targetClass = Role.class) + @CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id")) @Enumerated(EnumType.STRING) - @Column(nullable = true, length = 255) - private Role role; // 역할 + @Column(name = "role") + private List roles = new ArrayList<>(); @ElementCollection @CollectionTable(name = "user_interests", joinColumns = @JoinColumn(name = "user_id")) @@ -104,13 +108,20 @@ public Long getUserId() { public void deleteUser() { this.isDeleted = true; this.nickname = "(알 수 없음)"; // 표시용 변경 - this.role = Role.UNKNOWN; + + if (this.roles != null) { + this.roles.clear(); // 기존 역할 초기화 + } + this.roles = new ArrayList<>(); + this.roles.add(Role.UNKNOWN); // UNKNOWN 역할로 변경 + this.kakaoId = null; if (this.interests != null) { this.interests.clear(); // 기존 관심사 목록 비우기 } this.interests = new ArrayList<>(); // 새로운 관심사 목록 생성 this.interests.add("UNKNOWN"); // "UNKNOWN"을 관심사 목록에 추가 + if (this.seriesList != null) { this.seriesList.clear(); // 연관된 시리즈 비우기 } @@ -125,6 +136,11 @@ public void deleteUser() { public void updateUserDescription(String userDescription) { this.userDescription = userDescription; } + public List getRoleNames() { + return roles != null + ? roles.stream().map(Enum::name).collect(Collectors.toList()) + : Collections.emptyList(); + } } diff --git a/src/main/java/stackpot/stackpot/user/entity/enums/Role.java b/src/main/java/stackpot/stackpot/user/entity/enums/Role.java index dc356dc1..c534b7cd 100644 --- a/src/main/java/stackpot/stackpot/user/entity/enums/Role.java +++ b/src/main/java/stackpot/stackpot/user/entity/enums/Role.java @@ -7,7 +7,7 @@ public enum Role { BACKEND("양파", "백엔드"), FRONTEND("버섯", "프론트엔드"), DESIGN("브로콜리", "디자인"), - PLANNING("당근", "기획"), + PLAN("당근", "기획"), DEFAULT("새싹", "새싹"), UNKNOWN("UNKNOWN", "알 수 없음"); diff --git a/src/main/java/stackpot/stackpot/user/repository/TempUserRepository.java b/src/main/java/stackpot/stackpot/user/repository/TempUserRepository.java index 1cc1ef51..19689890 100644 --- a/src/main/java/stackpot/stackpot/user/repository/TempUserRepository.java +++ b/src/main/java/stackpot/stackpot/user/repository/TempUserRepository.java @@ -1,12 +1,23 @@ package stackpot.stackpot.user.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import stackpot.stackpot.user.entity.TempUser; import java.time.LocalDateTime; +import java.util.Optional; @Repository public interface TempUserRepository extends JpaRepository { int deleteByCreatedAtBefore(LocalDateTime time); + + @Query(""" + select tu + from TempUser tu + left join fetch tu.roles + where tu.id = :id + """) + Optional findWithRolesById(@Param("id") Long id); } diff --git a/src/main/java/stackpot/stackpot/user/service/UserCommandServiceImpl.java b/src/main/java/stackpot/stackpot/user/service/UserCommandServiceImpl.java index b53b5e18..d79eeab6 100644 --- a/src/main/java/stackpot/stackpot/user/service/UserCommandServiceImpl.java +++ b/src/main/java/stackpot/stackpot/user/service/UserCommandServiceImpl.java @@ -51,6 +51,8 @@ import stackpot.stackpot.user.repository.UserRepository; import stackpot.stackpot.common.service.EmailService; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -94,7 +96,7 @@ public UserSignUpResponseDto joinUser(UserRequestDto.JoinDto request) { .orElseThrow(() -> new UserHandler(ErrorStatus.USER_NOT_FOUND)); tempUser.setInterest(request.getInterest()); - tempUser.setRole(request.getRole()); + tempUser.setRoles(request.getRoles()); tempUserRepository.save(tempUser); @@ -114,7 +116,7 @@ public UserResponseDto.loginDto isnewUser(Provider provider, String providerId, return UserResponseDto.loginDto.builder() .tokenServiceResponse(token) .isNewUser(false) - .role(user.getRole()) + .roles(user.getRoleNames()) .build(); } else { @@ -130,7 +132,7 @@ public UserResponseDto.loginDto isnewUser(Provider provider, String providerId, return UserResponseDto.loginDto.builder() .tokenServiceResponse(token) .isNewUser(true) // 신규 유저임을 표시 - .role(null) + .roles(Collections.emptyList()) .build(); } } @@ -155,7 +157,7 @@ public UserResponseDto.loginDto isnewUser(Provider provider, String providerId, @Override public UserResponseDto.UserInfoDto getMyUsers() { User user = authService.getCurrentUser(); - if(user.getRole() == Role.UNKNOWN){ + if (user.getRoles().contains(Role.UNKNOWN)){ log.error("탈퇴한 유저에 대한 요청입니다. {}",user.getUserId()); throw new UserHandler(ErrorStatus.USER_NOT_FOUND); } @@ -168,7 +170,7 @@ public UserResponseDto.UserInfoDto getUsers(Long UserId) { .orElseThrow(() -> new UserHandler(ErrorStatus.USER_NOT_FOUND)); //탈퇴한 사용자 - if(user.getRole() == Role.UNKNOWN){ + if (user.getRoles().contains(Role.UNKNOWN)){ log.error("탈퇴한 유저에 대한 요청입니다. {}",user.getUserId()); throw new UserHandler(ErrorStatus.USER_ALREADY_WITHDRAWN); } @@ -180,7 +182,7 @@ public UserMyPageResponseDto getMypages() { User user = authService.getCurrentUser(); // 탈퇴한 사용자 - if (user.getRole() == Role.UNKNOWN) { + if (user.getRoles().contains(Role.UNKNOWN)) { log.error("탈퇴한 유저에 대한 요청입니다. {}", user.getUserId()); throw new UserHandler(ErrorStatus.USER_ALREADY_WITHDRAWN); } @@ -196,7 +198,7 @@ private UserMyPageResponseDto getMypageByUser(Long userId) { User user = userRepository.findById(userId) .orElseThrow(() -> new UserHandler(ErrorStatus.USER_NOT_FOUND)); - if (user.getRole() == Role.UNKNOWN) { + if (user.getRoles().contains(Role.UNKNOWN)) { throw new UserHandler(ErrorStatus.USER_ALREADY_WITHDRAWN); } @@ -215,9 +217,10 @@ public UserResponseDto.Userdto updateUserProfile(UserUpdateRequestDto requestDto User user = authService.getCurrentUser(); // 업데이트할 필드 적용 - if (requestDto.getRole() != null) { - user.setRole(requestDto.getRole()); + if (requestDto.getRoles() != null && !requestDto.getRoles().isEmpty()) { + user.setRoles(requestDto.getRoles()); // } + if (requestDto.getInterest() != null && !requestDto.getInterest().isEmpty()) { user.setInterests(requestDto.getInterest()); } @@ -259,33 +262,48 @@ public NicknameResponseDto createNickname() { @Override @Transactional public TokenServiceResponse saveNickname(String nickname) { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - TempUser tempUser = (TempUser) authentication.getPrincipal(); - log.info("tempUser {} ",tempUser.getId()); + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + Long tempUserId = ((TempUser) auth.getPrincipal()).getId(); + + TempUser tempUser = tempUserRepository.findWithRolesById(tempUserId) + .orElseThrow(() -> new UserHandler(ErrorStatus.USER_NOT_FOUND)); nickname = trimNickname(nickname); + String intro = tempUser.getRoles().isEmpty() + ? nickname + " 새싹입니다." + : tempUser.getRoles().stream() + .map(Role::getKoreanName) + .collect(Collectors.joining(", ")) + "에 관심있는 " + nickname + " 새싹입니다."; + + List interests = tempUser.getInterest() != null + ? new ArrayList<>(tempUser.getInterest()) + : new ArrayList<>(); + + List roles = tempUser.getRoles() != null + ? new ArrayList<>(tempUser.getRoles()) + : new ArrayList<>(); + User user = User.builder() .email(tempUser.getEmail()) .nickname(nickname) .userType(UserType.USER) - .interests(tempUser.getInterest()) - .userIntroduction(tempUser.getRole() + "에 관심있는 " + nickname + " " + "새싹입니다.") + .interests(interests) + .userIntroduction(intro) + .roles(roles) .userTemperature(33) .kakaoId(tempUser.getKakaoId()) .provider(tempUser.getProvider()) .providerId(tempUser.getProviderId()) - .role(tempUser.getRole()) .build(); - userRepository.save(user); // DB에 저장 - tempUserRepository.delete(tempUser); - - TokenServiceResponse tokenServiceResponse = jwtTokenProvider.createToken(user.getUserId(), user.getProvider(), user.getUserType(), user.getEmail()); + userRepository.save(user); + tempUserRepository.deleteById(tempUser.getId()); - return tokenServiceResponse; + return jwtTokenProvider.createToken(user.getUserId(), user.getProvider(), user.getUserType(), user.getEmail()); } + private String trimNickname(String nickname) { // 앞뒤 공백 유지 log.info("닉네임 생성 전 닉네임: {}", nickname); @@ -317,8 +335,6 @@ public String deleteUser(String accessToken) { log.info("회원 탈퇴 시작 id:{}", user.getUserId()); try { - // 토큰 블랙리스트 처리 - blacklistRepository.addToBlacklist(token, jwtTokenProvider.getExpiration(token)); // Feed 관련 데이터 삭제 // deleteFeedRelatedData(user.getId()); @@ -339,6 +355,8 @@ public String deleteUser(String accessToken) { } else { handleNormalUserPotDeletion(user); } + // 토큰 블랙리스트 처리 + blacklistRepository.addToBlacklist(token, jwtTokenProvider.getExpiration(token)); return "회원 탈퇴가 완료되었습니다."; } catch (Exception e) {