From 0bbcf066497cc493dafb809ad12bcb4cf8a6ed9b Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 02:41:51 +0900 Subject: [PATCH 01/18] =?UTF-8?q?refactor:=20=ED=95=99=EC=83=9D=ED=9A=8C?= =?UTF-8?q?=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=EC=9D=84=20=ED=95=99=EC=83=9D?= =?UTF-8?q?=ED=9A=8C=20=ED=83=80=EC=9E=85=EB=B3=84=20=EC=A0=9C=ED=9C=B4/?= =?UTF-8?q?=ED=96=89=EC=82=AC=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95(?= =?UTF-8?q?=ED=95=99=EC=83=9D=ED=9A=8C=20=ED=83=80=EC=9E=85,=20=EA=B2=8C?= =?UTF-8?q?=EC=8B=9C=EA=B8=80=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20?= =?UTF-8?q?=EB=B3=84=20=ED=95=84=ED=84=B0=EB=A7=81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mapper/StudentCouncilPostMapper.java | 4 ++-- .../service/StudentCouncilPostService.java | 19 +++++++++------ .../StudentCouncilPostRepository.java | 14 +++++++++++ .../StudentCouncilPostController.java | 23 ++++++++----------- 4 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java b/src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java index 29c7ae89..df2329d2 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java @@ -19,7 +19,7 @@ @RequiredArgsConstructor public class StudentCouncilPostMapper { - public PostListItemResponse toPostListItemResponse(StudentCouncilPost post, Long currentUserId) { + public PostListItemResponse toPostListItemResponse(StudentCouncilPost post, Long councilId) { return new PostListItemResponse( post.getId(), post.getCategory(), @@ -30,7 +30,7 @@ public PostListItemResponse toPostListItemResponse(StudentCouncilPost post, Long : post.getEndDateTime(), post.getThumbnailImageUrl(), post.getThumbnailIcon(), - post.isWrittenByCouncil(currentUserId) + post.isWrittenByCouncil(councilId) ); } diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java index 207c06b7..5242bb76 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java @@ -12,6 +12,7 @@ import org.springframework.transaction.annotation.Transactional; import com.campus.campus.domain.council.application.exception.StudentCouncilNotFoundException; +import com.campus.campus.domain.council.domain.entity.CouncilType; import com.campus.campus.domain.council.domain.entity.StudentCouncil; import com.campus.campus.domain.council.domain.repository.StudentCouncilRepository; import com.campus.campus.domain.councilpost.application.dto.response.NormalizedDateTime; @@ -100,16 +101,20 @@ public PostResponse findById(Long postId, Long currentUserId) { } @Transactional(readOnly = true) - public Page findAll(PostCategory category, int page, int size, Long currentUserId) { + public Page findPostListByCouncilTypeForCouncil(Long councilId, PostCategory category, + CouncilType councilType, int page, int size) { + StudentCouncil studentCouncil = studentCouncilRepository + .findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId) + .orElseThrow(StudentCouncilNotFoundException::new); + + Long schoolId = studentCouncil.getSchool().getSchoolId(); + Pageable pageable = PageRequest.of(Math.max(page - 1, 0), size, Sort.by(Sort.Direction.DESC, "createdAt")); - Page posts = (category == null) - ? postRepository.findAll(pageable) - : postRepository.findAllByCategory(category, pageable); + Page posts = postRepository.findPostsBySchoolAndFilters(schoolId, councilType, category, + pageable); - return posts.map(post -> - studentCouncilPostMapper.toPostListItemResponse(post, currentUserId) - ); + return posts.map(post -> studentCouncilPostMapper.toPostListItemResponse(post, councilId)); } @Transactional(readOnly = true) diff --git a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java index 85517579..863d455a 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java +++ b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java @@ -5,10 +5,12 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; +import com.campus.campus.domain.council.domain.entity.CouncilType; import com.campus.campus.domain.councilpost.domain.entity.PostCategory; import com.campus.campus.domain.councilpost.domain.entity.StudentCouncilPost; @@ -37,4 +39,16 @@ Page findUpcomingEvents( Pageable pageable ); + @EntityGraph(attributePaths = {"writer", "writer.school", "writer.college", "writer.major"}) + @Query(""" + SELECT p FROM StudentCouncilPost p + JOIN p.writer w + WHERE w.school.schoolId = :schoolId + AND (:councilType IS NULL OR w.councilType = :councilType) + AND (:category IS NULL OR p.category = :category) + AND w.deletedAt IS NULL + """) + Page findPostsBySchoolAndFilters(@Param("schoolId") Long schoolId, + @Param("councilType") CouncilType councilType, @Param("category") PostCategory category, Pageable pageable + ); } diff --git a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java index 70db8fb5..b191ad19 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java +++ b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java @@ -12,6 +12,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import com.campus.campus.domain.council.domain.entity.CouncilType; import com.campus.campus.domain.councilpost.application.dto.request.PostRequest; import com.campus.campus.domain.councilpost.application.dto.response.PostListItemResponse; import com.campus.campus.domain.councilpost.application.dto.response.PostResponse; @@ -21,7 +22,6 @@ import com.campus.campus.global.common.response.CommonResponse; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; import io.swagger.v3.oas.annotations.media.Schema; @@ -142,24 +142,19 @@ public CommonResponse getPost(@PathVariable Long postId, @CurrentC } @GetMapping - @Operation( - summary = "학생회 게시글 목록 조회 (필터링 포함)", - description = "전체 게시글 혹은 제휴(PARTNERSHIP), 행사(EVENT) 카테고리별로 필터링하여 목록을 조회합니다." - ) - public CommonResponse> getPostList( - @Parameter( - description = "필터링할 카테고리 (미선택 시 전체 조회)", - example = "PARTNERSHIP", - schema = @Schema(implementation = PostCategory.class) - ) + @Operation(summary = "학생회 타입별 제휴/행사 목록 조회 (학생회 유저 전용 로직)") + @PreAuthorize("hasRole('COUNCIL')") + public CommonResponse> getPostListByCouncilTypeForCouncil( @RequestParam(required = false) PostCategory category, + @RequestParam(required = false) CouncilType councilType, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "3") int size, - @CurrentCouncilId(required = false) Long councilId + @CurrentCouncilId Long councilId ) { - Page responseDto = postService.findAll(category, page, size, councilId); + Page response = postService.findPostListByCouncilTypeForCouncil(councilId, category, + councilType, page, size); - return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, responseDto); + return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, response); } @GetMapping("/events/upcoming") From fd3ba18bdbfa54dbf4a0bc692895bb3d52b8854b Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 03:30:13 +0900 Subject: [PATCH 02/18] =?UTF-8?q?refactor:=2072=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EC=9D=B4=EB=82=B4=20=ED=96=89=EC=82=AC=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=EC=9D=84=20=ED=95=99=EC=83=9D=ED=9A=8C=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=EB=B3=84=20=ED=95=84=ED=84=B0=EB=A7=81=20?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GetPostListForCouncilResponse.java | 32 +++++++++++++++++++ ...etUpcomingEventListForCouncilResponse.java | 29 +++++++++++++++++ .../mapper/StudentCouncilPostMapper.java | 28 +++++++++++++++- .../service/StudentCouncilPostService.java | 30 ++++++++++------- .../StudentCouncilPostRepository.java | 19 +++++++---- .../StudentCouncilPostController.java | 23 +++++++------ 6 files changed, 133 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostListForCouncilResponse.java create mode 100644 src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetUpcomingEventListForCouncilResponse.java diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostListForCouncilResponse.java b/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostListForCouncilResponse.java new file mode 100644 index 00000000..ce101d58 --- /dev/null +++ b/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostListForCouncilResponse.java @@ -0,0 +1,32 @@ +package com.campus.campus.domain.councilpost.application.dto.response; + +import java.time.LocalDateTime; + +import com.campus.campus.domain.councilpost.domain.entity.PostCategory; +import com.campus.campus.domain.councilpost.domain.entity.ThumbnailIcon; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record GetPostListForCouncilResponse( + @Schema(description = "게시글 id", example = "1") + Long postId, + + @Schema(description = "게시글 카테고리", example = "EVENT") + PostCategory category, + + @Schema(description = "게시글 이름", example = "중간고사 간식생사") + String title, + + @Schema(description = "장소", example = "310관 1층") + String place, + + @Schema(description = "시간(끝나는 시간 or 행사날짜", example = "2026-01-10T18:00:00") + LocalDateTime DateTime, + + @Schema(description = "썸네일 image url", example = "https://www.example.com.png") + String thumbnailImageUrl, + + @Schema(description = "썸네일 아이콘", example = "FOOD") + ThumbnailIcon thumbnailIcon +) { +} diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetUpcomingEventListForCouncilResponse.java b/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetUpcomingEventListForCouncilResponse.java new file mode 100644 index 00000000..c9c69b05 --- /dev/null +++ b/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetUpcomingEventListForCouncilResponse.java @@ -0,0 +1,29 @@ +package com.campus.campus.domain.councilpost.application.dto.response; + +import java.time.LocalDateTime; + +import com.campus.campus.domain.councilpost.domain.entity.PostCategory; +import com.campus.campus.domain.councilpost.domain.entity.ThumbnailIcon; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record GetUpcomingEventListForCouncilResponse( + @Schema(description = "게시글 id", example = "1") + Long postId, + + @Schema(description = "게시글 카테고리", example = "EVENT") + PostCategory category, + + @Schema(description = "게시글 이름", example = "중간고사 간식생사") + String title, + + @Schema(description = "장소", example = "310관 1층") + String place, + + @Schema(description = "시간(끝나는 시간 or 행사날짜", example = "2026-01-10T18:00:00") + LocalDateTime DateTime, + + @Schema(description = "썸네일 아이콘", example = "FOOD") + ThumbnailIcon thumbnailIcon +) { +} diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java b/src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java index df2329d2..e0cf7ccf 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java @@ -7,6 +7,8 @@ import org.springframework.stereotype.Component; import com.campus.campus.domain.council.domain.entity.StudentCouncil; +import com.campus.campus.domain.councilpost.application.dto.response.GetPostListForCouncilResponse; +import com.campus.campus.domain.councilpost.application.dto.response.GetUpcomingEventListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.PostListItemResponse; import com.campus.campus.domain.councilpost.application.dto.request.PostRequest; import com.campus.campus.domain.councilpost.application.dto.response.PostResponse; @@ -34,6 +36,29 @@ public PostListItemResponse toPostListItemResponse(StudentCouncilPost post, Long ); } + public GetPostListForCouncilResponse toGetPostListForCouncilResponse(StudentCouncilPost post) { + return new GetPostListForCouncilResponse( + post.getId(), + post.getCategory(), + post.getTitle(), + post.getPlace(), + post.isEvent() ? post.getStartDateTime() : post.getEndDateTime(), + post.getThumbnailImageUrl(), + post.getThumbnailIcon() + ); + } + + public GetUpcomingEventListForCouncilResponse toGetUpcomingEventListForCouncilResponse(StudentCouncilPost post) { + return new GetUpcomingEventListForCouncilResponse( + post.getId(), + post.getCategory(), + post.getTitle(), + post.getPlace(), + post.getStartDateTime(), + post.getThumbnailIcon() + ); + } + public PostResponse toPostResponse(StudentCouncilPost post, List images, Long currentUserId) { var writer = post.getWriter(); var builder = PostResponse.builder() @@ -59,7 +84,8 @@ public PostResponse toPostResponse(StudentCouncilPost post, List images, return builder.build(); } - public StudentCouncilPost createStudentCouncilPost(StudentCouncil writer, PostRequest dto, LocalDateTime startDateTime, + public StudentCouncilPost createStudentCouncilPost(StudentCouncil writer, PostRequest dto, + LocalDateTime startDateTime, LocalDateTime endDateTime) { return StudentCouncilPost.builder() .writer(writer) diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java index 5242bb76..59855e40 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java @@ -15,6 +15,8 @@ import com.campus.campus.domain.council.domain.entity.CouncilType; import com.campus.campus.domain.council.domain.entity.StudentCouncil; import com.campus.campus.domain.council.domain.repository.StudentCouncilRepository; +import com.campus.campus.domain.councilpost.application.dto.response.GetPostListForCouncilResponse; +import com.campus.campus.domain.councilpost.application.dto.response.GetUpcomingEventListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.NormalizedDateTime; import com.campus.campus.domain.councilpost.application.dto.response.PostListItemResponse; import com.campus.campus.domain.councilpost.application.dto.request.PostRequest; @@ -101,7 +103,8 @@ public PostResponse findById(Long postId, Long currentUserId) { } @Transactional(readOnly = true) - public Page findPostListByCouncilTypeForCouncil(Long councilId, PostCategory category, + public Page findPostListByCouncilTypeForCouncil(Long councilId, + PostCategory category, CouncilType councilType, int page, int size) { StudentCouncil studentCouncil = studentCouncilRepository .findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId) @@ -114,25 +117,28 @@ public Page findPostListByCouncilTypeForCouncil(Long counc Page posts = postRepository.findPostsBySchoolAndFilters(schoolId, councilType, category, pageable); - return posts.map(post -> studentCouncilPostMapper.toPostListItemResponse(post, councilId)); + return posts.map(studentCouncilPostMapper::toGetPostListForCouncilResponse); } @Transactional(readOnly = true) - public Page findUpcomingEvents(int page, int size, Long currentUserId) { - Pageable pageable = PageRequest.of( - Math.max(page - 1, 0), - size, - Sort.by(Sort.Direction.ASC, "startDateTime") - ); + public Page findUpcomingEventsByCouncilTypeForCouncil(Long councilId, + CouncilType councilType, int page, int size) { + Pageable pageable = PageRequest.of(Math.max(page - 1, 0), size, + Sort.by(Sort.Direction.ASC, "startDateTime")); + + StudentCouncil studentCouncil = studentCouncilRepository + .findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId) + .orElseThrow(StudentCouncilNotFoundException::new); + + Long schoolId = studentCouncil.getSchool().getSchoolId(); LocalDateTime now = LocalDateTime.now(); LocalDateTime limit = now.plusHours(UPCOMING_EVENT_WINDOW_HOURS); - Page posts = postRepository.findUpcomingEvents(PostCategory.EVENT, now, limit, pageable); + Page posts = postRepository.findUpcomingEventsBySchoolAndFilters(schoolId, councilType, + PostCategory.EVENT, now, limit, pageable); - return posts.map(post -> - studentCouncilPostMapper.toPostListItemResponse(post, currentUserId) - ); + return posts.map(studentCouncilPostMapper::toGetUpcomingEventListForCouncilResponse); } @Transactional diff --git a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java index 863d455a..cad3f651 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java +++ b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java @@ -26,13 +26,20 @@ public interface StudentCouncilPostRepository extends JpaRepository findByIdWithFullInfo(@Param("postId") Long postId); + @EntityGraph(attributePaths = {"writer", "writer.school", "writer.college", "writer.major"}) @Query(""" - SELECT p - FROM StudentCouncilPost p - WHERE p.category = :category - AND p.startDateTime BETWEEN :now AND :limit - """) - Page findUpcomingEvents( + SELECT p FROM StudentCouncilPost p + JOIN p.writer w + WHERE w.school.schoolId = :schoolId + AND (:councilType IS NULL OR w.councilType = :councilType) + AND p.category = :category + AND p.startDateTime BETWEEN :now AND :limit + AND w.deletedAt IS NULL + ORDER BY p.startDateTime ASC + """) + Page findUpcomingEventsBySchoolAndFilters( + @Param("schoolId") Long schoolId, + @Param("councilType") CouncilType councilType, @Param("category") PostCategory category, @Param("now") LocalDateTime now, @Param("limit") LocalDateTime limit, diff --git a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java index b191ad19..146830d9 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java +++ b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java @@ -14,6 +14,8 @@ import com.campus.campus.domain.council.domain.entity.CouncilType; import com.campus.campus.domain.councilpost.application.dto.request.PostRequest; +import com.campus.campus.domain.councilpost.application.dto.response.GetPostListForCouncilResponse; +import com.campus.campus.domain.councilpost.application.dto.response.GetUpcomingEventListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.PostListItemResponse; import com.campus.campus.domain.councilpost.application.dto.response.PostResponse; import com.campus.campus.domain.councilpost.application.service.StudentCouncilPostService; @@ -144,28 +146,31 @@ public CommonResponse getPost(@PathVariable Long postId, @CurrentC @GetMapping @Operation(summary = "학생회 타입별 제휴/행사 목록 조회 (학생회 유저 전용 로직)") @PreAuthorize("hasRole('COUNCIL')") - public CommonResponse> getPostListByCouncilTypeForCouncil( + public CommonResponse> getPostListByCouncilTypeForCouncil( @RequestParam(required = false) PostCategory category, @RequestParam(required = false) CouncilType councilType, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "3") int size, @CurrentCouncilId Long councilId ) { - Page response = postService.findPostListByCouncilTypeForCouncil(councilId, category, - councilType, page, size); + Page response = postService.findPostListByCouncilTypeForCouncil(councilId, + category, councilType, page, size); return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, response); } @GetMapping("/events/upcoming") - @Operation(summary = "72시간 이내 행사 게시글 조회") - public CommonResponse> getUpcomingEvents( + @Operation(summary = "학생회 타입별 72시간 이내 행사 게시글 조회 (학생회 유저 전용 로직)") + @PreAuthorize("hasRole('COUNCIL')") + public CommonResponse> getUpcomingEvents( + @RequestParam(required = false) CouncilType councilType, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "3") int size, - @CurrentCouncilId(required = false) Long councilId + @CurrentCouncilId Long councilId ) { - return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, - postService.findUpcomingEvents(page, size, councilId) - ); + Page response = postService.findUpcomingEventsByCouncilTypeForCouncil( + councilId, councilType, page, size); + + return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, response); } } From 4043201aee86bc1c8744b87f9d7effa67813ca4a Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 03:38:33 +0900 Subject: [PATCH 03/18] =?UTF-8?q?refactor:=20=ED=95=99=EC=83=9D=ED=9A=8C?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=EB=B3=84=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=ED=96=89=EC=82=AC=EB=A9=B4=20=ED=96=89=EC=82=AC=EB=82=A0,=20?= =?UTF-8?q?=EC=A0=9C=ED=9C=B4=EB=A9=B4=20=EB=81=9D=EB=82=98=EB=8A=94=20?= =?UTF-8?q?=EB=82=A0=EC=9D=B4=20=EA=B0=80=EA=B9=8C=EC=9A=B4=20=EC=88=9C?= =?UTF-8?q?=EC=84=9C=EB=A1=9C=20=EC=A0=95=EB=A0=AC=EB=90=98=EA=B2=8C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/StudentCouncilPostService.java | 9 ++++++++- .../presentation/StudentCouncilPostController.java | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java index 59855e40..21ebc87b 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java @@ -112,7 +112,14 @@ public Page findPostListByCouncilTypeForCouncil(L Long schoolId = studentCouncil.getSchool().getSchoolId(); - Pageable pageable = PageRequest.of(Math.max(page - 1, 0), size, Sort.by(Sort.Direction.DESC, "createdAt")); + Sort sort; + if (category == PostCategory.EVENT) { + sort = Sort.by(Sort.Direction.ASC, "startDateTime"); + } else { + sort = Sort.by(Sort.Direction.ASC, "endDateTime"); + } + + Pageable pageable = PageRequest.of(Math.max(page - 1, 0), size, sort); Page posts = postRepository.findPostsBySchoolAndFilters(schoolId, councilType, category, pageable); diff --git a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java index 146830d9..a4047f4e 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java +++ b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java @@ -162,7 +162,7 @@ public CommonResponse> getPostListByCouncilT @GetMapping("/events/upcoming") @Operation(summary = "학생회 타입별 72시간 이내 행사 게시글 조회 (학생회 유저 전용 로직)") @PreAuthorize("hasRole('COUNCIL')") - public CommonResponse> getUpcomingEvents( + public CommonResponse> getUpcomingEventsByCouncilTypeForCouncil( @RequestParam(required = false) CouncilType councilType, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "3") int size, From a938340d33ad006e05a8f7509569e494ccd991a3 Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 03:44:02 +0900 Subject: [PATCH 04/18] =?UTF-8?q?refactor:=20=ED=95=99=EC=83=9D=ED=9A=8C?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=EB=B3=84=20=EA=B2=8C=EC=8B=9C=EA=B8=80=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EC=95=84=EC=A7=81=20?= =?UTF-8?q?=EC=A7=80=EB=82=98=EC=A7=80=20=EC=95=8A=EC=9D=80=20=EC=A0=9C?= =?UTF-8?q?=ED=9C=B4=EB=82=98=20=ED=96=89=EC=82=AC=EB=A7=8C=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/StudentCouncilPostService.java | 4 +++- .../repository/StudentCouncilPostRepository.java | 11 ++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java index 21ebc87b..3630ed80 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java @@ -121,8 +121,10 @@ public Page findPostListByCouncilTypeForCouncil(L Pageable pageable = PageRequest.of(Math.max(page - 1, 0), size, sort); + LocalDateTime now = LocalDateTime.now(); + Page posts = postRepository.findPostsBySchoolAndFilters(schoolId, councilType, category, - pageable); + now, pageable); return posts.map(studentCouncilPostMapper::toGetPostListForCouncilResponse); } diff --git a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java index cad3f651..ab01d262 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java +++ b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java @@ -53,9 +53,18 @@ Page findUpcomingEventsBySchoolAndFilters( WHERE w.school.schoolId = :schoolId AND (:councilType IS NULL OR w.councilType = :councilType) AND (:category IS NULL OR p.category = :category) + AND ( + (p.category = com.campus.campus.domain.councilpost.domain.entity.PostCategory.EVENT + AND p.startDateTime >= :now) + OR (p.category = com.campus.campus.domain.councilpost.domain.entity.PostCategory.PARTNERSHIP + AND p.endDateTime >= :now) + ) AND w.deletedAt IS NULL """) Page findPostsBySchoolAndFilters(@Param("schoolId") Long schoolId, - @Param("councilType") CouncilType councilType, @Param("category") PostCategory category, Pageable pageable + @Param("councilType") CouncilType councilType, + @Param("category") PostCategory category, + @Param("now") LocalDateTime now, + Pageable pageable ); } From 74ba3b0858c28cd181d7e32bc8637abb14e1a4aa Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 06:15:09 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat:=20=ED=95=99=EC=83=9D=ED=9A=8C=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=EB=B3=84=20=EC=9D=B4=EC=9A=A9=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=ED=95=9C=20=EC=A0=9C=ED=9C=B4=20=EB=AA=A9=EB=A1=9D=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84(?= =?UTF-8?q?=EC=9D=BC=EB=B0=98=20=EC=9C=A0=EC=A0=80=20=EC=A0=84=EC=9A=A9,?= =?UTF-8?q?=20=ED=99=88=20=ED=99=94=EB=A9=B4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...tActivePartnershipListForUserResponse.java | 18 +++++++++ .../mapper/StudentCouncilPostMapper.java | 10 +++++ .../service/StudentCouncilPostService.java | 24 ++++++++++++ .../StudentCouncilPostRepository.java | 38 ++++++++++++++----- .../StudentCouncilPostController.java | 14 +++++++ 5 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetActivePartnershipListForUserResponse.java diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetActivePartnershipListForUserResponse.java b/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetActivePartnershipListForUserResponse.java new file mode 100644 index 00000000..ef1091e3 --- /dev/null +++ b/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetActivePartnershipListForUserResponse.java @@ -0,0 +1,18 @@ +package com.campus.campus.domain.councilpost.application.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record GetActivePartnershipListForUserResponse( + @Schema(description = "제휴글 id", example = "1") + Long postId, + + @Schema(description = "제휴글 이름", example = "투썸 제휴") + String title, + + @Schema(description = "제휴 장소", example = "투썸 플레이스") + String place, + + @Schema(description = "썸네일 image url", example = "https://www.example.com.png") + String thumbnailImageUrl +) { +} diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java b/src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java index e0cf7ccf..895f99ef 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java @@ -9,6 +9,7 @@ import com.campus.campus.domain.council.domain.entity.StudentCouncil; import com.campus.campus.domain.councilpost.application.dto.response.GetPostListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.GetUpcomingEventListForCouncilResponse; +import com.campus.campus.domain.councilpost.application.dto.response.GetActivePartnershipListForUserResponse; import com.campus.campus.domain.councilpost.application.dto.response.PostListItemResponse; import com.campus.campus.domain.councilpost.application.dto.request.PostRequest; import com.campus.campus.domain.councilpost.application.dto.response.PostResponse; @@ -59,6 +60,15 @@ public GetUpcomingEventListForCouncilResponse toGetUpcomingEventListForCouncilRe ); } + public GetActivePartnershipListForUserResponse toGetActivePartnershipListForUserResponse(StudentCouncilPost post) { + return new GetActivePartnershipListForUserResponse( + post.getId(), + post.getTitle(), + post.getPlace(), + post.getThumbnailImageUrl() + ); + } + public PostResponse toPostResponse(StudentCouncilPost post, List images, Long currentUserId) { var writer = post.getWriter(); var builder = PostResponse.builder() diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java index 3630ed80..5bbb18af 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java @@ -15,6 +15,7 @@ import com.campus.campus.domain.council.domain.entity.CouncilType; import com.campus.campus.domain.council.domain.entity.StudentCouncil; import com.campus.campus.domain.council.domain.repository.StudentCouncilRepository; +import com.campus.campus.domain.councilpost.application.dto.response.GetActivePartnershipListForUserResponse; import com.campus.campus.domain.councilpost.application.dto.response.GetPostListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.GetUpcomingEventListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.NormalizedDateTime; @@ -32,6 +33,9 @@ import com.campus.campus.domain.councilpost.domain.entity.StudentCouncilPost; import com.campus.campus.domain.councilpost.domain.repository.PostImageRepository; import com.campus.campus.domain.councilpost.domain.repository.StudentCouncilPostRepository; +import com.campus.campus.domain.user.application.exception.UserNotFoundException; +import com.campus.campus.domain.user.domain.entity.User; +import com.campus.campus.domain.user.domain.repository.UserRepository; import com.campus.campus.global.oci.application.service.PresignedUrlService; import lombok.RequiredArgsConstructor; @@ -42,6 +46,7 @@ @RequiredArgsConstructor public class StudentCouncilPostService { + private final UserRepository userRepository; private final StudentCouncilPostRepository postRepository; private final StudentCouncilRepository studentCouncilRepository; private final PostImageRepository postImageRepository; @@ -150,6 +155,25 @@ public Page findUpcomingEventsByCouncilT return posts.map(studentCouncilPostMapper::toGetUpcomingEventListForCouncilResponse); } + @Transactional(readOnly = true) + public List findActivePartnershipForUser(CouncilType councilType, + Long userId) { + User user = userRepository.findByIdAndDeletedAtIsNull(userId) + .orElseThrow(UserNotFoundException::new); + + Long schoolId = user.getSchool().getSchoolId(); + + LocalDateTime now = LocalDateTime.now(); + Pageable partnershipCount = PageRequest.of(0, 3); + + List partnerships = postRepository.findRandomActivePartnerships(schoolId, councilType, + PostCategory.PARTNERSHIP, now, partnershipCount); + + return partnerships.stream() + .map(studentCouncilPostMapper::toGetActivePartnershipListForUserResponse) + .toList(); + } + @Transactional public void delete(Long councilId, Long postId) { studentCouncilRepository.findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId) diff --git a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java index ab01d262..8340d4dd 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java +++ b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java @@ -1,6 +1,7 @@ package com.campus.campus.domain.councilpost.domain.repository; import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; import org.springframework.data.domain.Page; @@ -28,15 +29,15 @@ public interface StudentCouncilPostRepository extends JpaRepository findUpcomingEventsBySchoolAndFilters( @Param("schoolId") Long schoolId, @Param("councilType") CouncilType councilType, @@ -67,4 +68,23 @@ Page findPostsBySchoolAndFilters(@Param("schoolId") Long sch @Param("now") LocalDateTime now, Pageable pageable ); + + @EntityGraph(attributePaths = {"writer", "writer.school", "writer.college", "writer.major"}) + @Query(""" + SELECT p FROM StudentCouncilPost p + JOIN p.writer w + WHERE w.school.schoolId = :schoolId + AND w.councilType = :councilType + AND p.category = :category + AND :now BETWEEN p.startDateTime AND p.endDateTime + AND w.deletedAt IS NULL + ORDER BY function('RAND') + """) + List findRandomActivePartnerships( + @Param("schoolId") Long schoolId, + @Param("councilType") CouncilType councilType, + @Param("category") PostCategory category, + @Param("now") LocalDateTime now, + Pageable pageable + ); } diff --git a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java index a4047f4e..a826d697 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java +++ b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java @@ -1,5 +1,7 @@ package com.campus.campus.domain.councilpost.presentation; +import java.util.List; + import org.springframework.data.domain.Page; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.DeleteMapping; @@ -14,6 +16,7 @@ import com.campus.campus.domain.council.domain.entity.CouncilType; import com.campus.campus.domain.councilpost.application.dto.request.PostRequest; +import com.campus.campus.domain.councilpost.application.dto.response.GetActivePartnershipListForUserResponse; import com.campus.campus.domain.councilpost.application.dto.response.GetPostListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.GetUpcomingEventListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.PostListItemResponse; @@ -21,6 +24,7 @@ import com.campus.campus.domain.councilpost.application.service.StudentCouncilPostService; import com.campus.campus.domain.councilpost.domain.entity.PostCategory; import com.campus.campus.global.annotation.CurrentCouncilId; +import com.campus.campus.global.annotation.CurrentUserId; import com.campus.campus.global.common.response.CommonResponse; import io.swagger.v3.oas.annotations.Operation; @@ -173,4 +177,14 @@ public CommonResponse> getUpcomingE return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, response); } + + @GetMapping("/partnerships/active") + @Operation(summary = "학생회 타입별 현재 이용 가능한 제휴 (일반 유저 전용 로직, 홈 화면)") + public CommonResponse> getActivePartnershipListForUser( + @RequestParam CouncilType councilType, @CurrentUserId Long userId) { + List responses = postService.findActivePartnershipForUser(councilType, + userId); + + return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, responses); + } } From fa7d37fad85c5cedb43f91bdf3c6484f461b9c03 Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 06:52:00 +0900 Subject: [PATCH 06/18] =?UTF-8?q?feat:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=EC=99=B8=20=EC=84=9C=EB=B9=84=EC=8A=A4=20?= =?UTF-8?q?=EB=82=B4=20=EB=8B=89=EB=84=A4=EC=9E=84=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=EC=9D=B4=20=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=BB=AC=EB=9F=BC=20=EB=B0=8F=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(=EB=8B=89=EB=84=A4=EC=9E=84=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/CampusNicknameUpdateRequest.java | 11 +++++++++++ .../dto/request/UserWithdrawRequest.java | 6 +++--- .../user/application/exception/ErrorCode.java | 3 ++- .../exception/NicknameAlreadyExistsException.java | 9 +++++++++ .../user/application/service/UserService.java | 15 +++++++++++++++ .../campus/domain/user/domain/entity/User.java | 7 +++++++ .../user/domain/repository/UserRepository.java | 2 ++ .../domain/user/presentation/AuthController.java | 2 +- .../domain/user/presentation/UserController.java | 10 ++++++++++ .../user/presentation/UserResponseCode.java | 1 + 10 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/campus/campus/domain/user/application/dto/request/CampusNicknameUpdateRequest.java create mode 100644 src/main/java/com/campus/campus/domain/user/application/exception/NicknameAlreadyExistsException.java diff --git a/src/main/java/com/campus/campus/domain/user/application/dto/request/CampusNicknameUpdateRequest.java b/src/main/java/com/campus/campus/domain/user/application/dto/request/CampusNicknameUpdateRequest.java new file mode 100644 index 00000000..1e4a71aa --- /dev/null +++ b/src/main/java/com/campus/campus/domain/user/application/dto/request/CampusNicknameUpdateRequest.java @@ -0,0 +1,11 @@ +package com.campus.campus.domain.user.application.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; + +public record CampusNicknameUpdateRequest( + @Schema(description = "서비스 내 닉네임", example = "캠퍼스러버") + @NotBlank + String campusNickname +) { +} diff --git a/src/main/java/com/campus/campus/domain/user/application/dto/request/UserWithdrawRequest.java b/src/main/java/com/campus/campus/domain/user/application/dto/request/UserWithdrawRequest.java index b21498a1..150882bb 100644 --- a/src/main/java/com/campus/campus/domain/user/application/dto/request/UserWithdrawRequest.java +++ b/src/main/java/com/campus/campus/domain/user/application/dto/request/UserWithdrawRequest.java @@ -4,8 +4,8 @@ import jakarta.validation.constraints.NotBlank; public record UserWithdrawRequest( - @Schema(description = "닉네임", example = "한승현") - @NotBlank(message = "닉네임을 입력해주세요.") - String nickname + @Schema(description = "카카오 이름", example = "한승현") + @NotBlank(message = "카카오 이름을 입력해주세요.") + String kakaoName ) { } diff --git a/src/main/java/com/campus/campus/domain/user/application/exception/ErrorCode.java b/src/main/java/com/campus/campus/domain/user/application/exception/ErrorCode.java index 16364446..f7193fc1 100644 --- a/src/main/java/com/campus/campus/domain/user/application/exception/ErrorCode.java +++ b/src/main/java/com/campus/campus/domain/user/application/exception/ErrorCode.java @@ -13,7 +13,8 @@ public enum ErrorCode implements ErrorCodeInterface { USER_NOT_FOUND(2100, HttpStatus.NOT_FOUND, "존재하지 않는 사용자입니다."), USER_NOT_FIRST_LOGIN(2101, HttpStatus.BAD_REQUEST, "최초 로그인한 사용가 아닙니다."), NICKNAME_NOT_MATCH(2102, HttpStatus.BAD_REQUEST, "닉네임이 일치하지 않습니다."), - USER_SIGNUP_FORBIDDEN_NOW(2103, HttpStatus.FORBIDDEN, "현재 유저 회원가입할 수 없는 계정입니다."); + USER_SIGNUP_FORBIDDEN_NOW(2103, HttpStatus.FORBIDDEN, "현재 유저 회원가입할 수 없는 계정입니다."), + NICKNAME_ALREADY_EXISTS(2104, HttpStatus.CONFLICT, "이미 사용 중인 닉네임입니다."); private final int code; private final HttpStatus status; diff --git a/src/main/java/com/campus/campus/domain/user/application/exception/NicknameAlreadyExistsException.java b/src/main/java/com/campus/campus/domain/user/application/exception/NicknameAlreadyExistsException.java new file mode 100644 index 00000000..d045c132 --- /dev/null +++ b/src/main/java/com/campus/campus/domain/user/application/exception/NicknameAlreadyExistsException.java @@ -0,0 +1,9 @@ +package com.campus.campus.domain.user.application.exception; + +import com.campus.campus.global.common.exception.ApplicationException; + +public class NicknameAlreadyExistsException extends ApplicationException { + public NicknameAlreadyExistsException() { + super(ErrorCode.NICKNAME_ALREADY_EXISTS); + } +} diff --git a/src/main/java/com/campus/campus/domain/user/application/service/UserService.java b/src/main/java/com/campus/campus/domain/user/application/service/UserService.java index 7d7a9346..53439a60 100644 --- a/src/main/java/com/campus/campus/domain/user/application/service/UserService.java +++ b/src/main/java/com/campus/campus/domain/user/application/service/UserService.java @@ -11,8 +11,10 @@ import com.campus.campus.domain.school.domain.entity.School; import com.campus.campus.domain.school.domain.repository.MajorRepository; import com.campus.campus.domain.school.domain.repository.SchoolRepository; +import com.campus.campus.domain.user.application.dto.request.CampusNicknameUpdateRequest; import com.campus.campus.domain.user.application.dto.request.UserProfileRequest; import com.campus.campus.domain.user.application.dto.response.UserFirstProfileResponse; +import com.campus.campus.domain.user.application.exception.NicknameAlreadyExistsException; import com.campus.campus.domain.user.application.exception.UserNotFirstLoginException; import com.campus.campus.domain.user.application.exception.UserNotFoundException; import com.campus.campus.domain.user.application.mapper.UserMapper; @@ -54,4 +56,17 @@ public UserFirstProfileResponse writeUserProfile(Long userId, UserProfileRequest return userMapper.toUserFirstProfileResponse(user); } + + @Transactional + public void updateCampusNickname(Long userId, CampusNicknameUpdateRequest nicknameUpdateRequest) { + User user = userRepository.findByIdAndDeletedAtIsNull(userId) + .orElseThrow(UserNotFoundException::new); + + if (userRepository.existsByNicknameAndIdNot(nicknameUpdateRequest.campusNickname(), userId)) { + throw new NicknameAlreadyExistsException(); + } + + user.updateCampusNickname(nicknameUpdateRequest.campusNickname()); + userRepository.save(user); + } } diff --git a/src/main/java/com/campus/campus/domain/user/domain/entity/User.java b/src/main/java/com/campus/campus/domain/user/domain/entity/User.java index 14afd03a..6cd1957d 100644 --- a/src/main/java/com/campus/campus/domain/user/domain/entity/User.java +++ b/src/main/java/com/campus/campus/domain/user/domain/entity/User.java @@ -46,6 +46,9 @@ public class User extends BaseEntity { @Column(name = "profile_image") private String profileImage; + @Column(name = "campus-nickname") + private String campusNickname; + @Column(name = "deleted_at") private LocalDateTime deletedAt; @@ -67,6 +70,10 @@ public void updateProfile(School school, College college, Major major) { this.major = major; } + public void updateCampusNickname(String campusNickname) { + this.campusNickname = campusNickname; + } + public void delete(LocalDateTime now) { this.deletedAt = now; } diff --git a/src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java b/src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java index 926b4417..7752998e 100644 --- a/src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java +++ b/src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java @@ -17,5 +17,7 @@ public interface UserRepository extends JpaRepository { boolean existsByIdAndDeletedAtIsNull(Long userId); + boolean existsByNicknameAndIdNot(String campusNickname, Long userId); + List findAllByDeletedAtIsNotNullAndDeletedAtBefore(LocalDateTime softDeleteDate); } diff --git a/src/main/java/com/campus/campus/domain/user/presentation/AuthController.java b/src/main/java/com/campus/campus/domain/user/presentation/AuthController.java index a6012080..3162f1e9 100644 --- a/src/main/java/com/campus/campus/domain/user/presentation/AuthController.java +++ b/src/main/java/com/campus/campus/domain/user/presentation/AuthController.java @@ -35,7 +35,7 @@ public CommonResponse kakaoLogin(@RequestParam("token") Stri @Operation(summary = "카카오 유저 회원탈퇴") public CommonResponse withdraw(@CurrentUserId Long userId, @RequestBody @Valid UserWithdrawRequest userWithdrawRequest) { - kakaoOauthService.withdraw(userId, userWithdrawRequest.nickname()); + kakaoOauthService.withdraw(userId, userWithdrawRequest.kakaoName()); return CommonResponse.success(UserResponseCode.WITHDRAW_SUCCESS); } diff --git a/src/main/java/com/campus/campus/domain/user/presentation/UserController.java b/src/main/java/com/campus/campus/domain/user/presentation/UserController.java index d897b950..f5e4b2d7 100644 --- a/src/main/java/com/campus/campus/domain/user/presentation/UserController.java +++ b/src/main/java/com/campus/campus/domain/user/presentation/UserController.java @@ -5,6 +5,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import com.campus.campus.domain.user.application.dto.request.CampusNicknameUpdateRequest; import com.campus.campus.domain.user.application.dto.request.UserProfileRequest; import com.campus.campus.domain.user.application.dto.response.UserFirstProfileResponse; import com.campus.campus.domain.user.application.service.UserService; @@ -29,4 +30,13 @@ public CommonResponse createUserProfile(@CurrentUserId return CommonResponse.success(UserResponseCode.FIRST_PROFILE_WRITE, userFirstProfileResponse); } + + @PatchMapping("/change/nickname") + @Operation(summary = "사용자 서비스 내 닉네임 변경") + public CommonResponse updateCampusNickname(@CurrentUserId Long userId, + @Valid @RequestBody CampusNicknameUpdateRequest nicknameUpdateRequest) { + userService.updateCampusNickname(userId, nicknameUpdateRequest); + + return CommonResponse.success(UserResponseCode.NICKNAME_UPDATE_SUCCESS); + } } diff --git a/src/main/java/com/campus/campus/domain/user/presentation/UserResponseCode.java b/src/main/java/com/campus/campus/domain/user/presentation/UserResponseCode.java index 562ab1bf..b166e828 100644 --- a/src/main/java/com/campus/campus/domain/user/presentation/UserResponseCode.java +++ b/src/main/java/com/campus/campus/domain/user/presentation/UserResponseCode.java @@ -12,6 +12,7 @@ public enum UserResponseCode implements ResponseCodeInterface { LOGIN_SUCCESS(200, HttpStatus.OK, "로그인에 성공했습니다."), FIRST_PROFILE_WRITE(200, HttpStatus.OK, " 프로필(학교 정보) 입력에 성공했습니다."), + NICKNAME_UPDATE_SUCCESS(200, HttpStatus.OK, "닉네임 변경에 성공했습니다."), WITHDRAW_SUCCESS(200, HttpStatus.OK, "회원탈퇴에 성공했습니다."); private final int code; From 3fce97f4483cb5146f13c1e5a55ab149e744981f Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 07:01:45 +0900 Subject: [PATCH 07/18] =?UTF-8?q?feat:=20=EC=82=AC=EC=9A=A9=EC=9E=90=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84(=ED=99=88=20=ED=99=94=EB=A9=B4=EC=97=90=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=20=EC=A0=95=EB=B3=B4=20=EB=85=B8=EC=B6=9C?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=A8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/UserInfoResponse.java | 21 +++++++++++++++++++ .../user/application/mapper/UserMapper.java | 11 ++++++++++ .../user/application/service/UserService.java | 8 +++++++ .../user/presentation/UserController.java | 10 +++++++++ .../user/presentation/UserResponseCode.java | 3 ++- 5 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/campus/campus/domain/user/application/dto/response/UserInfoResponse.java diff --git a/src/main/java/com/campus/campus/domain/user/application/dto/response/UserInfoResponse.java b/src/main/java/com/campus/campus/domain/user/application/dto/response/UserInfoResponse.java new file mode 100644 index 00000000..82e03900 --- /dev/null +++ b/src/main/java/com/campus/campus/domain/user/application/dto/response/UserInfoResponse.java @@ -0,0 +1,21 @@ +package com.campus.campus.domain.user.application.dto.response; + +import io.swagger.v3.oas.annotations.media.Schema; + +public record UserInfoResponse( + @Schema(description = "유저 id", example = "1") + Long userId, + + @Schema(description = "유저 닉네임(campusNickname 설정했으면 campusNickname, 아니면, nickname") + String nickname, + + @Schema(description = "학교 이름", example = "가천대학교") + String schoolName, + + @Schema(description = "단과대 이름", example = "IT융합대학") + String collegeName, + + @Schema(description = "학과 이름", example = "컴퓨터공학과") + String majorName +) { +} diff --git a/src/main/java/com/campus/campus/domain/user/application/mapper/UserMapper.java b/src/main/java/com/campus/campus/domain/user/application/mapper/UserMapper.java index bf399ae7..f237a762 100644 --- a/src/main/java/com/campus/campus/domain/user/application/mapper/UserMapper.java +++ b/src/main/java/com/campus/campus/domain/user/application/mapper/UserMapper.java @@ -3,6 +3,7 @@ import org.springframework.stereotype.Component; import com.campus.campus.domain.user.application.dto.response.UserFirstProfileResponse; +import com.campus.campus.domain.user.application.dto.response.UserInfoResponse; import com.campus.campus.domain.user.domain.entity.User; import lombok.RequiredArgsConstructor; @@ -26,4 +27,14 @@ public UserFirstProfileResponse toUserFirstProfileResponse(User user) { .majorName(user.getMajor().getMajorName()) .build(); } + + public UserInfoResponse toUserInfoResponse(User user) { + return new UserInfoResponse( + user.getId(), + user.getCampusNickname() == null ? user.getNickname() : user.getCampusNickname(), + user.getSchool().getSchoolName(), + user.getCollege().getCollegeName(), + user.getMajor().getMajorName() + ); + } } diff --git a/src/main/java/com/campus/campus/domain/user/application/service/UserService.java b/src/main/java/com/campus/campus/domain/user/application/service/UserService.java index 53439a60..e08a6443 100644 --- a/src/main/java/com/campus/campus/domain/user/application/service/UserService.java +++ b/src/main/java/com/campus/campus/domain/user/application/service/UserService.java @@ -14,6 +14,7 @@ import com.campus.campus.domain.user.application.dto.request.CampusNicknameUpdateRequest; import com.campus.campus.domain.user.application.dto.request.UserProfileRequest; import com.campus.campus.domain.user.application.dto.response.UserFirstProfileResponse; +import com.campus.campus.domain.user.application.dto.response.UserInfoResponse; import com.campus.campus.domain.user.application.exception.NicknameAlreadyExistsException; import com.campus.campus.domain.user.application.exception.UserNotFirstLoginException; import com.campus.campus.domain.user.application.exception.UserNotFoundException; @@ -69,4 +70,11 @@ public void updateCampusNickname(Long userId, CampusNicknameUpdateRequest nickna user.updateCampusNickname(nicknameUpdateRequest.campusNickname()); userRepository.save(user); } + + public UserInfoResponse getUserInfo(Long userId) { + User user = userRepository.findById(userId) + .orElseThrow(UserNotFoundException::new); + + return userMapper.toUserInfoResponse(user); + } } diff --git a/src/main/java/com/campus/campus/domain/user/presentation/UserController.java b/src/main/java/com/campus/campus/domain/user/presentation/UserController.java index f5e4b2d7..7b5e1991 100644 --- a/src/main/java/com/campus/campus/domain/user/presentation/UserController.java +++ b/src/main/java/com/campus/campus/domain/user/presentation/UserController.java @@ -1,5 +1,6 @@ package com.campus.campus.domain.user.presentation; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -8,6 +9,7 @@ import com.campus.campus.domain.user.application.dto.request.CampusNicknameUpdateRequest; import com.campus.campus.domain.user.application.dto.request.UserProfileRequest; import com.campus.campus.domain.user.application.dto.response.UserFirstProfileResponse; +import com.campus.campus.domain.user.application.dto.response.UserInfoResponse; import com.campus.campus.domain.user.application.service.UserService; import com.campus.campus.global.annotation.CurrentUserId; import com.campus.campus.global.common.response.CommonResponse; @@ -39,4 +41,12 @@ public CommonResponse updateCampusNickname(@CurrentUserId Long userId, return CommonResponse.success(UserResponseCode.NICKNAME_UPDATE_SUCCESS); } + + @GetMapping + @Operation(summary = "사용자 정보 조회(홈 화면)") + public CommonResponse getUserInfo(@CurrentUserId Long userId) { + UserInfoResponse userInfoResponse = userService.getUserInfo(userId); + + return CommonResponse.success(UserResponseCode.GET_USER_INFO_SUCCESS, userInfoResponse); + } } diff --git a/src/main/java/com/campus/campus/domain/user/presentation/UserResponseCode.java b/src/main/java/com/campus/campus/domain/user/presentation/UserResponseCode.java index b166e828..e1a8abd9 100644 --- a/src/main/java/com/campus/campus/domain/user/presentation/UserResponseCode.java +++ b/src/main/java/com/campus/campus/domain/user/presentation/UserResponseCode.java @@ -13,7 +13,8 @@ public enum UserResponseCode implements ResponseCodeInterface { LOGIN_SUCCESS(200, HttpStatus.OK, "로그인에 성공했습니다."), FIRST_PROFILE_WRITE(200, HttpStatus.OK, " 프로필(학교 정보) 입력에 성공했습니다."), NICKNAME_UPDATE_SUCCESS(200, HttpStatus.OK, "닉네임 변경에 성공했습니다."), - WITHDRAW_SUCCESS(200, HttpStatus.OK, "회원탈퇴에 성공했습니다."); + WITHDRAW_SUCCESS(200, HttpStatus.OK, "회원탈퇴에 성공했습니다."), + GET_USER_INFO_SUCCESS(200, HttpStatus.OK, "유저 정보 조회에 성공했습니다."); private final int code; private final HttpStatus status; From 4d8b8f5c38142cf066a29e8e7ad38251706fa88e Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 07:33:53 +0900 Subject: [PATCH 08/18] =?UTF-8?q?refactor:=20CouncilType=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/StudentCouncilPostService.java | 42 +++++++++++++++++-- .../StudentCouncilPostRepository.java | 18 ++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java index 5bbb18af..3dd9ca1a 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java @@ -116,6 +116,14 @@ public Page findPostListByCouncilTypeForCouncil(L .orElseThrow(StudentCouncilNotFoundException::new); Long schoolId = studentCouncil.getSchool().getSchoolId(); + Long collegeId = CouncilType.COLLEGE_COUNCIL.equals(councilType) + && studentCouncil.getCollege() != null + ? studentCouncil.getCollege().getCollegeId() + : null; + Long majorId = CouncilType.MAJOR_COUNCIL.equals(councilType) + && studentCouncil.getMajor() != null + ? studentCouncil.getMajor().getMajorId() + : null; Sort sort; if (category == PostCategory.EVENT) { @@ -129,7 +137,7 @@ public Page findPostListByCouncilTypeForCouncil(L LocalDateTime now = LocalDateTime.now(); Page posts = postRepository.findPostsBySchoolAndFilters(schoolId, councilType, category, - now, pageable); + collegeId, majorId, now, pageable); return posts.map(studentCouncilPostMapper::toGetPostListForCouncilResponse); } @@ -145,12 +153,20 @@ public Page findUpcomingEventsByCouncilT .orElseThrow(StudentCouncilNotFoundException::new); Long schoolId = studentCouncil.getSchool().getSchoolId(); + Long collegeId = CouncilType.COLLEGE_COUNCIL.equals(councilType) + && studentCouncil.getCollege() != null + ? studentCouncil.getCollege().getCollegeId() + : null; + Long majorId = CouncilType.MAJOR_COUNCIL.equals(councilType) + && studentCouncil.getMajor() != null + ? studentCouncil.getMajor().getMajorId() + : null; LocalDateTime now = LocalDateTime.now(); LocalDateTime limit = now.plusHours(UPCOMING_EVENT_WINDOW_HOURS); Page posts = postRepository.findUpcomingEventsBySchoolAndFilters(schoolId, councilType, - PostCategory.EVENT, now, limit, pageable); + PostCategory.EVENT, collegeId, majorId, now, limit, pageable); return posts.map(studentCouncilPostMapper::toGetUpcomingEventListForCouncilResponse); } @@ -161,13 +177,33 @@ public List findActivePartnershipForUse User user = userRepository.findByIdAndDeletedAtIsNull(userId) .orElseThrow(UserNotFoundException::new); + if (user.getSchool() == null) { + return List.of(); + } + Long schoolId = user.getSchool().getSchoolId(); + Long collegeId = null; + Long majorId = null; + + if (CouncilType.COLLEGE_COUNCIL.equals(councilType)) { + if (user.getCollege() == null) { + return List.of(); + } + collegeId = user.getCollege().getCollegeId(); + } + + if (CouncilType.MAJOR_COUNCIL.equals(councilType)) { + if (user.getMajor() == null) { + return List.of(); + } + majorId = user.getMajor().getMajorId(); + } LocalDateTime now = LocalDateTime.now(); Pageable partnershipCount = PageRequest.of(0, 3); List partnerships = postRepository.findRandomActivePartnerships(schoolId, councilType, - PostCategory.PARTNERSHIP, now, partnershipCount); + PostCategory.PARTNERSHIP, collegeId, majorId, now, partnershipCount); return partnerships.stream() .map(studentCouncilPostMapper::toGetActivePartnershipListForUserResponse) diff --git a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java index 8340d4dd..3da4bd93 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java +++ b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java @@ -31,8 +31,12 @@ public interface StudentCouncilPostRepository extends JpaRepository findUpcomingEventsBySchoolAndFilters( @Param("schoolId") Long schoolId, @Param("councilType") CouncilType councilType, @Param("category") PostCategory category, + @Param("collegeId") Long collegeId, + @Param("majorId") Long majorId, @Param("now") LocalDateTime now, @Param("limit") LocalDateTime limit, Pageable pageable @@ -51,8 +57,12 @@ Page findUpcomingEventsBySchoolAndFilters( @Query(""" SELECT p FROM StudentCouncilPost p JOIN p.writer w + LEFT JOIN w.college c + LEFT JOIN w.major m WHERE w.school.schoolId = :schoolId AND (:councilType IS NULL OR w.councilType = :councilType) + AND (:collegeId IS NULL OR c.collegeId = :collegeId) + AND (:majorId IS NULL OR m.majorId = :majorId) AND (:category IS NULL OR p.category = :category) AND ( (p.category = com.campus.campus.domain.councilpost.domain.entity.PostCategory.EVENT @@ -65,6 +75,8 @@ Page findUpcomingEventsBySchoolAndFilters( Page findPostsBySchoolAndFilters(@Param("schoolId") Long schoolId, @Param("councilType") CouncilType councilType, @Param("category") PostCategory category, + @Param("collegeId") Long collegeId, + @Param("majorId") Long majorId, @Param("now") LocalDateTime now, Pageable pageable ); @@ -73,8 +85,12 @@ Page findPostsBySchoolAndFilters(@Param("schoolId") Long sch @Query(""" SELECT p FROM StudentCouncilPost p JOIN p.writer w + LEFT JOIN w.college c + LEFT JOIN w.major m WHERE w.school.schoolId = :schoolId AND w.councilType = :councilType + AND (:collegeId IS NULL OR c.collegeId = :collegeId) + AND (:majorId IS NULL OR m.majorId = :majorId) AND p.category = :category AND :now BETWEEN p.startDateTime AND p.endDateTime AND w.deletedAt IS NULL @@ -84,6 +100,8 @@ List findRandomActivePartnerships( @Param("schoolId") Long schoolId, @Param("councilType") CouncilType councilType, @Param("category") PostCategory category, + @Param("collegeId") Long collegeId, + @Param("majorId") Long majorId, @Param("now") LocalDateTime now, Pageable pageable ); From 42d27276b62481a2b2c89091e4a094cf9120e577 Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 07:49:24 +0900 Subject: [PATCH 09/18] =?UTF-8?q?refactor:=20UserWithdrawRequest=20?= =?UTF-8?q?=ED=95=84=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 --- .../user/application/dto/request/UserWithdrawRequest.java | 2 +- .../campus/campus/domain/user/presentation/AuthController.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/campus/campus/domain/user/application/dto/request/UserWithdrawRequest.java b/src/main/java/com/campus/campus/domain/user/application/dto/request/UserWithdrawRequest.java index 150882bb..14f0a49d 100644 --- a/src/main/java/com/campus/campus/domain/user/application/dto/request/UserWithdrawRequest.java +++ b/src/main/java/com/campus/campus/domain/user/application/dto/request/UserWithdrawRequest.java @@ -6,6 +6,6 @@ public record UserWithdrawRequest( @Schema(description = "카카오 이름", example = "한승현") @NotBlank(message = "카카오 이름을 입력해주세요.") - String kakaoName + String nickname ) { } diff --git a/src/main/java/com/campus/campus/domain/user/presentation/AuthController.java b/src/main/java/com/campus/campus/domain/user/presentation/AuthController.java index 3162f1e9..a6012080 100644 --- a/src/main/java/com/campus/campus/domain/user/presentation/AuthController.java +++ b/src/main/java/com/campus/campus/domain/user/presentation/AuthController.java @@ -35,7 +35,7 @@ public CommonResponse kakaoLogin(@RequestParam("token") Stri @Operation(summary = "카카오 유저 회원탈퇴") public CommonResponse withdraw(@CurrentUserId Long userId, @RequestBody @Valid UserWithdrawRequest userWithdrawRequest) { - kakaoOauthService.withdraw(userId, userWithdrawRequest.kakaoName()); + kakaoOauthService.withdraw(userId, userWithdrawRequest.nickname()); return CommonResponse.success(UserResponseCode.WITHDRAW_SUCCESS); } From 23b9d9cdd0349f367b86d5ccd1382b356601ba71 Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 14:39:32 +0900 Subject: [PATCH 10/18] =?UTF-8?q?refactor:=20User=20=EB=82=B4=20=EC=BB=AC?= =?UTF-8?q?=EB=9F=BC=EB=AA=85=20=ED=95=98=EC=9D=B4=ED=94=88=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 --- .../java/com/campus/campus/domain/user/domain/entity/User.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/campus/campus/domain/user/domain/entity/User.java b/src/main/java/com/campus/campus/domain/user/domain/entity/User.java index 6cd1957d..ac97f60f 100644 --- a/src/main/java/com/campus/campus/domain/user/domain/entity/User.java +++ b/src/main/java/com/campus/campus/domain/user/domain/entity/User.java @@ -46,7 +46,7 @@ public class User extends BaseEntity { @Column(name = "profile_image") private String profileImage; - @Column(name = "campus-nickname") + @Column(name = "campus_nickname") private String campusNickname; @Column(name = "deleted_at") From d8ee8e3e15dabc9beba7a8b6c275aee537c4e5c5 Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 14:40:51 +0900 Subject: [PATCH 11/18] =?UTF-8?q?refactor:=20getUserInfo=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=EB=90=9C=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../campus/domain/user/application/service/UserService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/campus/campus/domain/user/application/service/UserService.java b/src/main/java/com/campus/campus/domain/user/application/service/UserService.java index e08a6443..f77b178c 100644 --- a/src/main/java/com/campus/campus/domain/user/application/service/UserService.java +++ b/src/main/java/com/campus/campus/domain/user/application/service/UserService.java @@ -72,7 +72,7 @@ public void updateCampusNickname(Long userId, CampusNicknameUpdateRequest nickna } public UserInfoResponse getUserInfo(Long userId) { - User user = userRepository.findById(userId) + User user = userRepository.findByIdAndDeletedAtIsNull(userId) .orElseThrow(UserNotFoundException::new); return userMapper.toUserInfoResponse(user); From 0161c88b7c901564965be2859762f0c55a937b40 Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 14:49:10 +0900 Subject: [PATCH 12/18] =?UTF-8?q?refactor:=20=EB=8B=89=EB=84=A4=EC=9E=84?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=20=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../campus/domain/user/application/service/UserService.java | 2 +- .../campus/domain/user/domain/repository/UserRepository.java | 2 +- .../campus/global/auth/application/dto/OauthLoginResponse.java | 3 +++ .../campus/global/auth/application/mapper/LoginMapper.java | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/campus/campus/domain/user/application/service/UserService.java b/src/main/java/com/campus/campus/domain/user/application/service/UserService.java index f77b178c..9d8ecfcd 100644 --- a/src/main/java/com/campus/campus/domain/user/application/service/UserService.java +++ b/src/main/java/com/campus/campus/domain/user/application/service/UserService.java @@ -63,7 +63,7 @@ public void updateCampusNickname(Long userId, CampusNicknameUpdateRequest nickna User user = userRepository.findByIdAndDeletedAtIsNull(userId) .orElseThrow(UserNotFoundException::new); - if (userRepository.existsByNicknameAndIdNot(nicknameUpdateRequest.campusNickname(), userId)) { + if (userRepository.existsByCampusNicknameAndIdNot(nicknameUpdateRequest.campusNickname(), userId)) { throw new NicknameAlreadyExistsException(); } diff --git a/src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java b/src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java index 7752998e..16632acb 100644 --- a/src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java +++ b/src/main/java/com/campus/campus/domain/user/domain/repository/UserRepository.java @@ -17,7 +17,7 @@ public interface UserRepository extends JpaRepository { boolean existsByIdAndDeletedAtIsNull(Long userId); - boolean existsByNicknameAndIdNot(String campusNickname, Long userId); + boolean existsByCampusNicknameAndIdNot(String campusNickname, Long userId); List findAllByDeletedAtIsNotNullAndDeletedAtBefore(LocalDateTime softDeleteDate); } diff --git a/src/main/java/com/campus/campus/global/auth/application/dto/OauthLoginResponse.java b/src/main/java/com/campus/campus/global/auth/application/dto/OauthLoginResponse.java index edcf5cd0..bc25bdb9 100644 --- a/src/main/java/com/campus/campus/global/auth/application/dto/OauthLoginResponse.java +++ b/src/main/java/com/campus/campus/global/auth/application/dto/OauthLoginResponse.java @@ -12,6 +12,9 @@ public record OauthLoginResponse( @Schema(description = "사용자 이름(닉네임)", example = "김잇타") String nickname, + @Schema(description = "사용자 서비스 내 닉네임", example = "망포동 피바라기") + String campusNickname, + @Schema(description = "사용자 id", example = "1") Long userId, diff --git a/src/main/java/com/campus/campus/global/auth/application/mapper/LoginMapper.java b/src/main/java/com/campus/campus/global/auth/application/mapper/LoginMapper.java index 3333a0af..b7e9cf70 100644 --- a/src/main/java/com/campus/campus/global/auth/application/mapper/LoginMapper.java +++ b/src/main/java/com/campus/campus/global/auth/application/mapper/LoginMapper.java @@ -17,6 +17,7 @@ public OauthLoginResponse toOauthLoginResponse(User user, String accessToken, St accessToken, refreshToken, user.getNickname(), + user.getCampusNickname(), user.getId(), user.getKakaoId(), user.getEmail(), From 1dee857a58ccbb7ce7ea86e8559eabdddbe9773d Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Thu, 8 Jan 2026 14:51:17 +0900 Subject: [PATCH 13/18] =?UTF-8?q?refactor:=20dto=20=ED=95=84=EB=93=9C=20?= =?UTF-8?q?=EB=AA=85=EB=AA=85=EB=B2=95=20camelCase=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/response/GetPostListForCouncilResponse.java | 2 +- .../dto/response/GetUpcomingEventListForCouncilResponse.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostListForCouncilResponse.java b/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostListForCouncilResponse.java index ce101d58..294333fd 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostListForCouncilResponse.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetPostListForCouncilResponse.java @@ -21,7 +21,7 @@ public record GetPostListForCouncilResponse( String place, @Schema(description = "시간(끝나는 시간 or 행사날짜", example = "2026-01-10T18:00:00") - LocalDateTime DateTime, + LocalDateTime dateTime, @Schema(description = "썸네일 image url", example = "https://www.example.com.png") String thumbnailImageUrl, diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetUpcomingEventListForCouncilResponse.java b/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetUpcomingEventListForCouncilResponse.java index c9c69b05..cd62e6ec 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetUpcomingEventListForCouncilResponse.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/dto/response/GetUpcomingEventListForCouncilResponse.java @@ -21,7 +21,7 @@ public record GetUpcomingEventListForCouncilResponse( String place, @Schema(description = "시간(끝나는 시간 or 행사날짜", example = "2026-01-10T18:00:00") - LocalDateTime DateTime, + LocalDateTime dateTime, @Schema(description = "썸네일 아이콘", example = "FOOD") ThumbnailIcon thumbnailIcon From 36b30badf3781fa3ab8ce606673fa6a528a8621b Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Fri, 9 Jan 2026 18:51:30 +0900 Subject: [PATCH 14/18] =?UTF-8?q?refactor:=20=EC=9C=A0=EC=A0=80=EC=A0=84?= =?UTF-8?q?=EC=9A=A9=20=ED=95=99=EC=83=9D=ED=9A=8C=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EC=A1=B0=ED=9A=8C=20service,controller=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EB=AA=85=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20?= =?UTF-8?q?=EC=95=A4=EB=93=9C=ED=8F=AC=EC=9D=B8=ED=8A=B8=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD,=20=ED=8A=B8=EB=9E=9C=EC=9E=AD=EC=85=98=20=EC=96=B4?= =?UTF-8?q?=EB=85=B8=ED=85=8C=EC=9D=B4=EC=85=98=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EB=B2=94=EC=9C=84=EB=A1=9C=20=EC=88=98=EC=A0=95,?= =?UTF-8?q?=20=ED=95=99=EC=83=9D=ED=9A=8C=20=EC=A0=84=EC=9A=A9=20service,?= =?UTF-8?q?=20controller=EC=97=90=EC=84=9C=20=ED=8A=B9=EC=A0=95=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=9C=A0=EC=A0=80=EC=A0=84?= =?UTF-8?q?=EC=9A=A9=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ... => StudentCouncilPostForUserService.java} | 49 ++++++++++++++++--- .../service/StudentCouncilPostService.java | 40 --------------- .../StudentCouncilPostRepository.java | 3 +- .../StudentCouncilPostController.java | 28 ++--------- ... StudentCouncilPostForUserController.java} | 28 +++++++++-- 5 files changed, 69 insertions(+), 79 deletions(-) rename src/main/java/com/campus/campus/domain/councilpost/application/service/{UserPostService.java => StudentCouncilPostForUserService.java} (84%) rename src/main/java/com/campus/campus/domain/councilpost/presentation/{UserPostController.java => StudentCouncilPostForUserController.java} (81%) diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/UserPostService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java similarity index 84% rename from src/main/java/com/campus/campus/domain/councilpost/application/service/UserPostService.java rename to src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java index b4d1045c..618df272 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/UserPostService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java @@ -12,6 +12,7 @@ import org.springframework.transaction.annotation.Transactional; import com.campus.campus.domain.council.domain.entity.CouncilType; +import com.campus.campus.domain.councilpost.application.dto.response.GetActivePartnershipListForUserResponse; import com.campus.campus.domain.councilpost.application.dto.response.PostListItemResponse; import com.campus.campus.domain.councilpost.application.dto.response.PostResponse; import com.campus.campus.domain.councilpost.application.exception.CollegeNotSetException; @@ -34,7 +35,8 @@ @Service @RequiredArgsConstructor @Slf4j -public class UserPostService { +@Transactional(readOnly = true) +public class StudentCouncilPostForUserService { private static final int UPCOMING_HOURS = 72; private static final ZoneId KST = ZoneId.of("Asia/Seoul"); @@ -44,7 +46,6 @@ public class UserPostService { private final UserRepository userRepository; private final PostAccessPolicy postAccessPolicy; - @Transactional(readOnly = true) public Page findSchoolPosts(PostCategory category, int page, int size, Long userId) { User user = userRepository.findByIdWithAcademicInfo(userId).orElseThrow(UserNotFoundException::new); @@ -56,7 +57,6 @@ public Page findSchoolPosts(PostCategory category, int pag return posts.map(post -> studentCouncilPostMapper.toPostListItemResponse(post, userId)); } - @Transactional(readOnly = true) public Page findCollegePosts(PostCategory category, int page, int size, Long userId) { User user = userRepository.findByIdWithAcademicInfo(userId).orElseThrow(UserNotFoundException::new); @@ -72,7 +72,6 @@ public Page findCollegePosts(PostCategory category, int pa return posts.map(post -> studentCouncilPostMapper.toPostListItemResponse(post, userId)); } - @Transactional(readOnly = true) public Page findMajorPosts(PostCategory category, int page, int size, Long userId) { User user = userRepository.findByIdWithAcademicInfo(userId).orElseThrow(UserNotFoundException::new); @@ -84,7 +83,6 @@ public Page findMajorPosts(PostCategory category, int page return posts.map(post -> studentCouncilPostMapper.toPostListItemResponse(post, userId)); } - @Transactional(readOnly = true) public Page findUpcomingSchoolEvents72h(int page, int size, Long userId) { User user = userRepository.findByIdWithAcademicInfo(userId).orElseThrow(UserNotFoundException::new); @@ -105,7 +103,6 @@ public Page findUpcomingSchoolEvents72h(int page, int size return posts.map(post -> studentCouncilPostMapper.toPostListItemResponse(post, userId)); } - @Transactional(readOnly = true) public Page findUpcomingCollegeEvents72h(int page, int size, Long userId) { User user = userRepository.findByIdWithAcademicInfo(userId).orElseThrow(UserNotFoundException::new); @@ -134,7 +131,6 @@ public Page findUpcomingCollegeEvents72h(int page, int siz return posts.map(post -> studentCouncilPostMapper.toPostListItemResponse(post, userId)); } - @Transactional(readOnly = true) public Page findUpcomingMajorEvents72h(int page, int size, Long userId) { User user = userRepository.findByIdWithAcademicInfo(userId).orElseThrow(UserNotFoundException::new); @@ -163,7 +159,6 @@ public Page findUpcomingMajorEvents72h(int page, int size, return posts.map(post -> studentCouncilPostMapper.toPostListItemResponse(post, userId)); } - @Transactional(readOnly = true) public PostResponse findById(Long postId, Long userId) { User user = userRepository.findByIdWithAcademicInfo(userId).orElseThrow(UserNotFoundException::new); @@ -180,4 +175,42 @@ public PostResponse findById(Long postId, Long userId) { return studentCouncilPostMapper.toPostResponse(post, imageUrls, userId); } + + public List findActivePartnershipForUser(CouncilType councilType, + Long userId) { + User user = userRepository.findByIdAndDeletedAtIsNull(userId) + .orElseThrow(UserNotFoundException::new); + + if (user.getSchool() == null) { + return List.of(); + } + + Long schoolId = user.getSchool().getSchoolId(); + Long collegeId = null; + Long majorId = null; + + if (CouncilType.COLLEGE_COUNCIL.equals(councilType)) { + if (user.getCollege() == null) { + return List.of(); + } + collegeId = user.getCollege().getCollegeId(); + } + + if (CouncilType.MAJOR_COUNCIL.equals(councilType)) { + if (user.getMajor() == null) { + return List.of(); + } + majorId = user.getMajor().getMajorId(); + } + + LocalDateTime now = LocalDateTime.now(); + Pageable partnershipCount = PageRequest.of(0, 3); + + List partnerships = studentCouncilPostRepository.findRandomActivePartnerships(schoolId, + councilType, PostCategory.PARTNERSHIP, collegeId, majorId, now, partnershipCount); + + return partnerships.stream() + .map(studentCouncilPostMapper::toGetActivePartnershipListForUserResponse) + .toList(); + } } diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java index 3dd9ca1a..1a6b6c44 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java @@ -19,7 +19,6 @@ import com.campus.campus.domain.councilpost.application.dto.response.GetPostListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.GetUpcomingEventListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.NormalizedDateTime; -import com.campus.campus.domain.councilpost.application.dto.response.PostListItemResponse; import com.campus.campus.domain.councilpost.application.dto.request.PostRequest; import com.campus.campus.domain.councilpost.application.dto.response.PostResponse; import com.campus.campus.domain.councilpost.application.exception.NotPostWriterException; @@ -171,45 +170,6 @@ public Page findUpcomingEventsByCouncilT return posts.map(studentCouncilPostMapper::toGetUpcomingEventListForCouncilResponse); } - @Transactional(readOnly = true) - public List findActivePartnershipForUser(CouncilType councilType, - Long userId) { - User user = userRepository.findByIdAndDeletedAtIsNull(userId) - .orElseThrow(UserNotFoundException::new); - - if (user.getSchool() == null) { - return List.of(); - } - - Long schoolId = user.getSchool().getSchoolId(); - Long collegeId = null; - Long majorId = null; - - if (CouncilType.COLLEGE_COUNCIL.equals(councilType)) { - if (user.getCollege() == null) { - return List.of(); - } - collegeId = user.getCollege().getCollegeId(); - } - - if (CouncilType.MAJOR_COUNCIL.equals(councilType)) { - if (user.getMajor() == null) { - return List.of(); - } - majorId = user.getMajor().getMajorId(); - } - - LocalDateTime now = LocalDateTime.now(); - Pageable partnershipCount = PageRequest.of(0, 3); - - List partnerships = postRepository.findRandomActivePartnerships(schoolId, councilType, - PostCategory.PARTNERSHIP, collegeId, majorId, now, partnershipCount); - - return partnerships.stream() - .map(studentCouncilPostMapper::toGetActivePartnershipListForUserResponse) - .toList(); - } - @Transactional public void delete(Long councilId, Long postId) { studentCouncilRepository.findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId) diff --git a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java index c379f2ce..fc27beaa 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java +++ b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java @@ -17,8 +17,6 @@ public interface StudentCouncilPostRepository extends JpaRepository { - Page findAllByCategory(PostCategory category, Pageable pageable); - @Query(""" SELECT p FROM StudentCouncilPost p JOIN FETCH p.writer w @@ -108,6 +106,7 @@ List findRandomActivePartnerships( @Param("now") LocalDateTime now, Pageable pageable ); + @EntityGraph(attributePaths = {"writer", "writer.school", "writer.college", "writer.major"}) @Query(""" diff --git a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java index a826d697..29826597 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java +++ b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java @@ -1,7 +1,5 @@ package com.campus.campus.domain.councilpost.presentation; -import java.util.List; - import org.springframework.data.domain.Page; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.DeleteMapping; @@ -16,15 +14,12 @@ import com.campus.campus.domain.council.domain.entity.CouncilType; import com.campus.campus.domain.councilpost.application.dto.request.PostRequest; -import com.campus.campus.domain.councilpost.application.dto.response.GetActivePartnershipListForUserResponse; import com.campus.campus.domain.councilpost.application.dto.response.GetPostListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.GetUpcomingEventListForCouncilResponse; -import com.campus.campus.domain.councilpost.application.dto.response.PostListItemResponse; import com.campus.campus.domain.councilpost.application.dto.response.PostResponse; import com.campus.campus.domain.councilpost.application.service.StudentCouncilPostService; import com.campus.campus.domain.councilpost.domain.entity.PostCategory; import com.campus.campus.global.annotation.CurrentCouncilId; -import com.campus.campus.global.annotation.CurrentUserId; import com.campus.campus.global.common.response.CommonResponse; import io.swagger.v3.oas.annotations.Operation; @@ -36,7 +31,8 @@ import lombok.RequiredArgsConstructor; @RestController -@RequestMapping("/student-council/posts") +@RequestMapping("/student-councils/posts") +@PreAuthorize("hasRole('COUNCIL')") @Tag(name = "Student Council Post", description = "학생회(COUNCIL) 권한 전용 제휴/행사 게시글 관리 API") @RequiredArgsConstructor public class StudentCouncilPostController { @@ -44,7 +40,6 @@ public class StudentCouncilPostController { private final StudentCouncilPostService postService; @PostMapping - @PreAuthorize("hasRole('COUNCIL')") @Operation( summary = "학생회 제휴/행사 게시글 생성", description = @@ -117,7 +112,6 @@ public CommonResponse createPost( } @PatchMapping("/{postId}") - @PreAuthorize("hasRole('COUNCIL')") @Operation(summary = "학생회 게시글 수정") public CommonResponse updatePost( @CurrentCouncilId Long councilId, @@ -130,7 +124,6 @@ public CommonResponse updatePost( } @DeleteMapping("/{postId}") - @PreAuthorize("hasRole('COUNCIL')") @Operation(summary = "학생회 게시글 삭제") public CommonResponse deletePost(@CurrentCouncilId Long councilId, @PathVariable Long postId) { postService.delete(councilId, postId); @@ -139,17 +132,15 @@ public CommonResponse deletePost(@CurrentCouncilId Long councilId, @PathVa } @GetMapping("/{postId}") - @Operation(summary = "학생회 게시글 단건 조회") + @Operation(summary = "학생회 게시글 상세 조회") public CommonResponse getPost(@PathVariable Long postId, @CurrentCouncilId Long councilId) { - PostResponse responseDto = - postService.findById(postId, councilId); + PostResponse responseDto = postService.findById(postId, councilId); return CommonResponse.success(StudentCouncilPostResponseCode.POST_READ_SUCCESS, responseDto); } @GetMapping @Operation(summary = "학생회 타입별 제휴/행사 목록 조회 (학생회 유저 전용 로직)") - @PreAuthorize("hasRole('COUNCIL')") public CommonResponse> getPostListByCouncilTypeForCouncil( @RequestParam(required = false) PostCategory category, @RequestParam(required = false) CouncilType councilType, @@ -165,7 +156,6 @@ public CommonResponse> getPostListByCouncilT @GetMapping("/events/upcoming") @Operation(summary = "학생회 타입별 72시간 이내 행사 게시글 조회 (학생회 유저 전용 로직)") - @PreAuthorize("hasRole('COUNCIL')") public CommonResponse> getUpcomingEventsByCouncilTypeForCouncil( @RequestParam(required = false) CouncilType councilType, @RequestParam(defaultValue = "1") int page, @@ -177,14 +167,4 @@ public CommonResponse> getUpcomingE return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, response); } - - @GetMapping("/partnerships/active") - @Operation(summary = "학생회 타입별 현재 이용 가능한 제휴 (일반 유저 전용 로직, 홈 화면)") - public CommonResponse> getActivePartnershipListForUser( - @RequestParam CouncilType councilType, @CurrentUserId Long userId) { - List responses = postService.findActivePartnershipForUser(councilType, - userId); - - return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, responses); - } } diff --git a/src/main/java/com/campus/campus/domain/councilpost/presentation/UserPostController.java b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java similarity index 81% rename from src/main/java/com/campus/campus/domain/councilpost/presentation/UserPostController.java rename to src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java index 9a082d51..51e43a60 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/presentation/UserPostController.java +++ b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostForUserController.java @@ -1,15 +1,20 @@ package com.campus.campus.domain.councilpost.presentation; +import java.util.List; + import org.springframework.data.domain.Page; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import com.campus.campus.domain.council.domain.entity.CouncilType; +import com.campus.campus.domain.councilpost.application.dto.response.GetActivePartnershipListForUserResponse; import com.campus.campus.domain.councilpost.application.dto.response.PostListItemResponse; import com.campus.campus.domain.councilpost.application.dto.response.PostResponse; -import com.campus.campus.domain.councilpost.application.service.UserPostService; +import com.campus.campus.domain.councilpost.application.service.StudentCouncilPostForUserService; import com.campus.campus.domain.councilpost.domain.entity.PostCategory; import com.campus.campus.global.annotation.CurrentUserId; import com.campus.campus.global.common.response.CommonResponse; @@ -20,13 +25,14 @@ import lombok.extern.slf4j.Slf4j; @RestController -@RequestMapping("/users/posts") -@Tag(name = "Student Post", description = "사용자가 속한 대학/단과대/전공의 제휴/행사 게시글 목록 조회 API") +@PreAuthorize("hasRole('USER')") +@RequestMapping("/users/student-council/posts") +@Tag(name = "Student Council Post For User", description = "사용자(USER) 권한 전용 사용자가 속한 대학/단과대/전공의 제휴/행사 게시글 목록 조회 API") @RequiredArgsConstructor @Slf4j -public class UserPostController { +public class StudentCouncilPostForUserController { - private final UserPostService postService; + private final StudentCouncilPostForUserService postService; @GetMapping("/school") @Operation( @@ -128,4 +134,16 @@ public CommonResponse> getUpcomingMajorEvents( Page responseDto = postService.findUpcomingMajorEvents72h(page, size, userId); return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, responseDto); } + + @GetMapping("/partnerships/active") + @Operation(summary = "학생회 타입별 현재 이용 가능한 제휴 (일반 유저 전용 로직, 홈 화면)") + public CommonResponse> getActivePartnershipListForUser( + @RequestParam CouncilType councilType, + @CurrentUserId Long userId + ) { + List responses = postService.findActivePartnershipForUser(councilType, + userId); + + return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, responses); + } } From 49e67a7c603bd654b3a311c7ecbe978b15998f85 Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Fri, 9 Jan 2026 21:25:07 +0900 Subject: [PATCH 15/18] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=ED=83=80=EC=9E=84=EC=A1=B4=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 --- .../service/StudentCouncilPostForUserService.java | 2 +- .../application/service/StudentCouncilPostService.java | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java index 618df272..2190f2dc 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java @@ -203,7 +203,7 @@ public List findActivePartnershipForUse majorId = user.getMajor().getMajorId(); } - LocalDateTime now = LocalDateTime.now(); + LocalDateTime now = LocalDateTime.now(KST); Pageable partnershipCount = PageRequest.of(0, 3); List partnerships = studentCouncilPostRepository.findRandomActivePartnerships(schoolId, diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java index 1a6b6c44..eb257ad1 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java @@ -15,7 +15,6 @@ import com.campus.campus.domain.council.domain.entity.CouncilType; import com.campus.campus.domain.council.domain.entity.StudentCouncil; import com.campus.campus.domain.council.domain.repository.StudentCouncilRepository; -import com.campus.campus.domain.councilpost.application.dto.response.GetActivePartnershipListForUserResponse; import com.campus.campus.domain.councilpost.application.dto.response.GetPostListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.GetUpcomingEventListForCouncilResponse; import com.campus.campus.domain.councilpost.application.dto.response.NormalizedDateTime; @@ -32,9 +31,6 @@ import com.campus.campus.domain.councilpost.domain.entity.StudentCouncilPost; import com.campus.campus.domain.councilpost.domain.repository.PostImageRepository; import com.campus.campus.domain.councilpost.domain.repository.StudentCouncilPostRepository; -import com.campus.campus.domain.user.application.exception.UserNotFoundException; -import com.campus.campus.domain.user.domain.entity.User; -import com.campus.campus.domain.user.domain.repository.UserRepository; import com.campus.campus.global.oci.application.service.PresignedUrlService; import lombok.RequiredArgsConstructor; @@ -45,7 +41,6 @@ @RequiredArgsConstructor public class StudentCouncilPostService { - private final UserRepository userRepository; private final StudentCouncilPostRepository postRepository; private final StudentCouncilRepository studentCouncilRepository; private final PostImageRepository postImageRepository; From 843d1d0242e537c36a1ec3af46c15554faf5506f Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Fri, 9 Jan 2026 22:00:58 +0900 Subject: [PATCH 16/18] =?UTF-8?q?refactor:=20findActivePartnershipForUser?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=EC=9E=90=20=EC=A1=B0=ED=9A=8C=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/service/StudentCouncilPostForUserService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java index 2190f2dc..f5f788bc 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostForUserService.java @@ -178,7 +178,7 @@ public PostResponse findById(Long postId, Long userId) { public List findActivePartnershipForUser(CouncilType councilType, Long userId) { - User user = userRepository.findByIdAndDeletedAtIsNull(userId) + User user = userRepository.findByIdWithAcademicInfo(userId) .orElseThrow(UserNotFoundException::new); if (user.getSchool() == null) { From 887371a5d1c8d89d20f6511bc08cfe95a92791ca Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Fri, 9 Jan 2026 22:15:06 +0900 Subject: [PATCH 17/18] =?UTF-8?q?refactor:=20=ED=95=99=EC=83=9D=ED=9A=8C?= =?UTF-8?q?=20=EC=A0=9C=ED=9C=B4/=ED=96=89=EC=82=AC=20=EB=AA=A9=EB=A1=9D?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=ED=95=99=EC=83=9D?= =?UTF-8?q?=ED=9A=8C=20=ED=83=80=EC=9E=85=20=ED=95=84=ED=84=B0=EB=A7=81=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=ED=95=9C=20=EB=B3=B8=EC=9D=B8=EC=9D=B4=20=EC=9E=91=EC=84=B1?= =?UTF-8?q?=ED=95=9C=20=EA=B8=80=EB=A7=8C=20=EC=A1=B0=ED=9A=8C=EB=90=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/StudentCouncilPostService.java | 22 +++++-------------- .../StudentCouncilPostRepository.java | 12 ++-------- .../StudentCouncilPostController.java | 9 ++++---- 3 files changed, 11 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java index eb257ad1..bd4f7842 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java @@ -102,23 +102,11 @@ public PostResponse findById(Long postId, Long currentUserId) { } @Transactional(readOnly = true) - public Page findPostListByCouncilTypeForCouncil(Long councilId, - PostCategory category, - CouncilType councilType, int page, int size) { - StudentCouncil studentCouncil = studentCouncilRepository - .findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId) + public Page findPostListForCouncil(Long councilId, PostCategory category, + int page, int size) { + studentCouncilRepository.findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId) .orElseThrow(StudentCouncilNotFoundException::new); - Long schoolId = studentCouncil.getSchool().getSchoolId(); - Long collegeId = CouncilType.COLLEGE_COUNCIL.equals(councilType) - && studentCouncil.getCollege() != null - ? studentCouncil.getCollege().getCollegeId() - : null; - Long majorId = CouncilType.MAJOR_COUNCIL.equals(councilType) - && studentCouncil.getMajor() != null - ? studentCouncil.getMajor().getMajorId() - : null; - Sort sort; if (category == PostCategory.EVENT) { sort = Sort.by(Sort.Direction.ASC, "startDateTime"); @@ -130,8 +118,8 @@ public Page findPostListByCouncilTypeForCouncil(L LocalDateTime now = LocalDateTime.now(); - Page posts = postRepository.findPostsBySchoolAndFilters(schoolId, councilType, category, - collegeId, majorId, now, pageable); + Page posts = postRepository.findPostsByCouncilAndFilters(councilId, category, now, + pageable); return posts.map(studentCouncilPostMapper::toGetPostListForCouncilResponse); } diff --git a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java index fc27beaa..6b9d629e 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java +++ b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java @@ -58,12 +58,7 @@ Page findUpcomingEventsBySchoolAndFilters( @Query(""" SELECT p FROM StudentCouncilPost p JOIN p.writer w - LEFT JOIN w.college c - LEFT JOIN w.major m - WHERE w.school.schoolId = :schoolId - AND (:councilType IS NULL OR w.councilType = :councilType) - AND (:collegeId IS NULL OR c.collegeId = :collegeId) - AND (:majorId IS NULL OR m.majorId = :majorId) + WHERE w.id = :councilId AND (:category IS NULL OR p.category = :category) AND ( (p.category = com.campus.campus.domain.councilpost.domain.entity.PostCategory.EVENT @@ -73,11 +68,8 @@ Page findUpcomingEventsBySchoolAndFilters( ) AND w.deletedAt IS NULL """) - Page findPostsBySchoolAndFilters(@Param("schoolId") Long schoolId, - @Param("councilType") CouncilType councilType, + Page findPostsByCouncilAndFilters(@Param("councilId") Long councilId, @Param("category") PostCategory category, - @Param("collegeId") Long collegeId, - @Param("majorId") Long majorId, @Param("now") LocalDateTime now, Pageable pageable ); diff --git a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java index 29826597..4cfbc72d 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java +++ b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java @@ -140,16 +140,15 @@ public CommonResponse getPost(@PathVariable Long postId, @CurrentC } @GetMapping - @Operation(summary = "학생회 타입별 제휴/행사 목록 조회 (학생회 유저 전용 로직)") - public CommonResponse> getPostListByCouncilTypeForCouncil( + @Operation(summary = "학생회 제휴/행사 목록 조회 (학생회 홈화면, 로그인한 학생회 작성한 글만)") + public CommonResponse> getPostListForCouncil( @RequestParam(required = false) PostCategory category, - @RequestParam(required = false) CouncilType councilType, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "3") int size, @CurrentCouncilId Long councilId ) { - Page response = postService.findPostListByCouncilTypeForCouncil(councilId, - category, councilType, page, size); + Page response = postService.findPostListForCouncil(councilId, + category, page, size); return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, response); } From ef6943d549b445ec486cf0adcfbd9b6399df0afa Mon Sep 17 00:00:00 2001 From: 1winhyun Date: Fri, 9 Jan 2026 22:20:27 +0900 Subject: [PATCH 18/18] =?UTF-8?q?refactor:=20=ED=95=99=EC=83=9D=ED=9A=8C?= =?UTF-8?q?=2072=EC=8B=9C=EA=B0=84=20=EC=9D=B4=EB=82=B4=20=ED=96=89?= =?UTF-8?q?=EC=82=AC=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=ED=95=99?= =?UTF-8?q?=EC=83=9D=ED=9A=8C=20=ED=83=80=EC=9E=85=20=ED=95=84=ED=84=B0?= =?UTF-8?q?=EB=A7=81=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=EB=B3=B8=EC=9D=B8=EC=9D=B4=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=ED=95=9C=20=EA=B8=80=EB=A7=8C=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/StudentCouncilPostService.java | 21 +++++-------------- .../StudentCouncilPostRepository.java | 14 +++---------- .../StudentCouncilPostController.java | 9 ++++---- 3 files changed, 12 insertions(+), 32 deletions(-) diff --git a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java index bd4f7842..446edee7 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java +++ b/src/main/java/com/campus/campus/domain/councilpost/application/service/StudentCouncilPostService.java @@ -125,30 +125,19 @@ public Page findPostListForCouncil(Long councilId } @Transactional(readOnly = true) - public Page findUpcomingEventsByCouncilTypeForCouncil(Long councilId, - CouncilType councilType, int page, int size) { + public Page findUpcomingEventsForCouncil(Long councilId, + int page, int size) { Pageable pageable = PageRequest.of(Math.max(page - 1, 0), size, Sort.by(Sort.Direction.ASC, "startDateTime")); - StudentCouncil studentCouncil = studentCouncilRepository - .findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId) + studentCouncilRepository.findByIdAndManagerApprovedIsTrueAndDeletedAtIsNull(councilId) .orElseThrow(StudentCouncilNotFoundException::new); - Long schoolId = studentCouncil.getSchool().getSchoolId(); - Long collegeId = CouncilType.COLLEGE_COUNCIL.equals(councilType) - && studentCouncil.getCollege() != null - ? studentCouncil.getCollege().getCollegeId() - : null; - Long majorId = CouncilType.MAJOR_COUNCIL.equals(councilType) - && studentCouncil.getMajor() != null - ? studentCouncil.getMajor().getMajorId() - : null; - LocalDateTime now = LocalDateTime.now(); LocalDateTime limit = now.plusHours(UPCOMING_EVENT_WINDOW_HOURS); - Page posts = postRepository.findUpcomingEventsBySchoolAndFilters(schoolId, councilType, - PostCategory.EVENT, collegeId, majorId, now, limit, pageable); + Page posts = postRepository.findUpcomingEventsByCouncil(councilId, PostCategory.EVENT, + now, limit, pageable); return posts.map(studentCouncilPostMapper::toGetUpcomingEventListForCouncilResponse); } diff --git a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java index 6b9d629e..1898f73b 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java +++ b/src/main/java/com/campus/campus/domain/councilpost/domain/repository/StudentCouncilPostRepository.java @@ -32,23 +32,15 @@ public interface StudentCouncilPostRepository extends JpaRepository findUpcomingEventsBySchoolAndFilters( - @Param("schoolId") Long schoolId, - @Param("councilType") CouncilType councilType, + Page findUpcomingEventsByCouncil( + @Param("councilId") Long councilId, @Param("category") PostCategory category, - @Param("collegeId") Long collegeId, - @Param("majorId") Long majorId, @Param("now") LocalDateTime now, @Param("limit") LocalDateTime limit, Pageable pageable diff --git a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java index 4cfbc72d..81099218 100644 --- a/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java +++ b/src/main/java/com/campus/campus/domain/councilpost/presentation/StudentCouncilPostController.java @@ -154,15 +154,14 @@ public CommonResponse> getPostListForCouncil } @GetMapping("/events/upcoming") - @Operation(summary = "학생회 타입별 72시간 이내 행사 게시글 조회 (학생회 유저 전용 로직)") - public CommonResponse> getUpcomingEventsByCouncilTypeForCouncil( - @RequestParam(required = false) CouncilType councilType, + @Operation(summary = "학생회 72시간 이내 행사 조회 (학생회 홈화면, 로그인한 학생회 작성한 글만)") + public CommonResponse> getUpcomingEventsForCouncil( @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "3") int size, @CurrentCouncilId Long councilId ) { - Page response = postService.findUpcomingEventsByCouncilTypeForCouncil( - councilId, councilType, page, size); + Page response = postService.findUpcomingEventsForCouncil(councilId, + page, size); return CommonResponse.success(StudentCouncilPostResponseCode.POST_LIST_READ_SUCCESS, response); }