Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.campus.campus.domain.councilpost.application.dto.response;

import java.time.LocalDateTime;

import io.swagger.v3.oas.annotations.media.Schema;

public record GetLikedPostResponse(
@Schema(description = "게시글 id", example = "1")
Long postId,

@Schema(description = "게시글 이름", example = "투썸 제휴")
String title,

@Schema(description = "게시글 장소", example = "투썸 플레이스")
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
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.campus.campus.domain.councilpost.application.dto.response;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

import com.campus.campus.domain.councilpost.domain.entity.PostCategory;
import com.campus.campus.domain.councilpost.domain.entity.ThumbnailIcon;
import com.fasterxml.jackson.annotation.JsonInclude;

import lombok.Builder;

@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public record GetPostForUserResponse(

Long id,
Long writerId,
String writerName,

PostCategory category,
String title,
String content,
String place,
LocalDate startDate,
LocalDate endDate,
LocalDateTime startDateTime,

String thumbnailImageUrl,
ThumbnailIcon thumbnailIcon,

boolean isLiked,

List<String> images
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
public record PostResponse(
public record GetPostResponse(

Long id,
Long writerId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.campus.campus.domain.councilpost.application.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;

public record LikePostResponse(
@Schema(description = "좋아요 누른 사용자 id", example = "1")
Long userId,

@Schema(description = "좋아요 누를 게시글의 id", example = "1")
Long postId,

@Schema(description = "좋아요 여부", example = "true")
boolean liked
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ public record PostListItemResponse(
LocalDateTime endDateTime,
String thumbnailImageUrl,
ThumbnailIcon thumbnailIcon,
Boolean isWriter
boolean liked
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,35 @@
import org.springframework.stereotype.Component;

import com.campus.campus.domain.council.domain.entity.StudentCouncil;
import com.campus.campus.domain.councilpost.application.dto.response.GetLikedPostResponse;
import com.campus.campus.domain.councilpost.application.dto.response.GetPostResponse;
import com.campus.campus.domain.councilpost.application.dto.response.LikePostResponse;
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;
import com.campus.campus.domain.councilpost.application.dto.response.GetPostForUserResponse;
import com.campus.campus.domain.councilpost.domain.entity.LikePost;
import com.campus.campus.domain.councilpost.domain.entity.PostImage;
import com.campus.campus.domain.councilpost.domain.entity.StudentCouncilPost;
import com.campus.campus.domain.user.domain.entity.User;

import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
public class StudentCouncilPostMapper {

public PostListItemResponse toPostListItemResponse(StudentCouncilPost post, Long councilId) {
public PostListItemResponse toPostListItemResponse(StudentCouncilPost post, boolean isLiked) {
return new PostListItemResponse(
post.getId(),
post.getCategory(),
post.getTitle(),
post.getPlace(),
post.isEvent()
? post.getStartDateTime()
: post.getEndDateTime(),
post.isEvent() ? post.getStartDateTime() : post.getEndDateTime(),
post.getThumbnailImageUrl(),
post.getThumbnailIcon(),
post.isWrittenByCouncil(councilId)
isLiked
);
}

Expand Down Expand Up @@ -69,19 +71,45 @@ public GetActivePartnershipListForUserResponse toGetActivePartnershipListForUser
);
}

public PostResponse toPostResponse(StudentCouncilPost post, List<String> images, Long currentUserId) {
public GetPostResponse toGetPostResponse(StudentCouncilPost post, List<String> images, Long currentCouncilId) {
var writer = post.getWriter();
var builder = GetPostResponse.builder()
.id(post.getId())
.writerId(writer.getId())
.writerName(writer.getCouncilName())
.isWriter(post.isWrittenByCouncil(currentCouncilId))
.category(post.getCategory())
.title(post.getTitle())
.content(post.getContent())
.place(post.getPlace())
.thumbnailImageUrl(post.getThumbnailImageUrl())
.thumbnailIcon(post.getThumbnailIcon())
.images(images != null ? images : Collections.emptyList());

if (post.isEvent()) {
builder.startDateTime(post.getStartDateTime());
} else {
builder.startDate(post.getDisplayStartDate());
builder.endDate(post.getDisplayEndDate());
}

return builder.build();
}
Comment on lines +74 to +97
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

게시글 작성자 null 체크 필요

Line 75에서 post.getWriter()의 결과를 null 체크 없이 사용하고 있습니다. StudentCouncilPost 엔티티의 writer 필드는 @ManyToOne으로 정의되어 있으나 @NotNull 제약이 없어 null일 가능성이 있습니다. Lines 78-79에서 writer.getId()writer.getCouncilName() 호출 시 NPE가 발생할 수 있습니다.

🛡️ NPE 방지를 위한 수정 제안
 public GetPostResponse toGetPostResponse(StudentCouncilPost post, List<String> images, Long currentCouncilId) {
 	var writer = post.getWriter();
+	if (writer == null) {
+		throw new IllegalStateException("게시글에 작성자 정보가 없습니다.");
+	}
 	var builder = GetPostResponse.builder()
 		.id(post.getId())
 		.writerId(writer.getId())

또는 서비스 계층에서 writer가 항상 존재함을 보장하는 경우, 엔티티에 @NotNull 제약을 추가하는 것을 권장합니다.

🤖 Prompt for AI Agents
In
@src/main/java/com/campus/campus/domain/councilpost/application/mapper/StudentCouncilPostMapper.java
around lines 74 - 97, The toGetPostResponse method uses post.getWriter() without
null-check, which can NPE on writer.getId() / writer.getCouncilName(); update
toGetPostResponse to safely handle a null writer by fetching var writer =
post.getWriter(); then check if writer == null and set builder.writerId(null)
and builder.writerName(null) or a sensible default (e.g., "Unknown") before
calling writer.getId() / writer.getCouncilName(); ensure
isWriter(post.isWrittenByCouncil(currentCouncilId)) still works when writer is
null (guard or compute using post.isWrittenByCouncil which should tolerate
null), and return builder.build().


public GetPostForUserResponse toGetPostForUserResponse(StudentCouncilPost post, List<String> images,
Long currentUserId, boolean isLiked) {
var writer = post.getWriter();
var builder = PostResponse.builder()
var builder = GetPostForUserResponse.builder()
.id(post.getId())
.writerId(writer.getId())
.writerName(writer.getCouncilName())
.isWriter(post.isWrittenByCouncil(currentUserId))
.category(post.getCategory())
.title(post.getTitle())
.content(post.getContent())
.place(post.getPlace())
.thumbnailImageUrl(post.getThumbnailImageUrl())
.thumbnailIcon(post.getThumbnailIcon())
.isLiked(isLiked)
.images(images != null ? images : Collections.emptyList());

if (post.isEvent()) {
Expand All @@ -94,9 +122,26 @@ public PostResponse toPostResponse(StudentCouncilPost post, List<String> images,
return builder.build();
}

public LikePostResponse toLikePostResponse(User user, StudentCouncilPost post, boolean liked) {
return new LikePostResponse(
user.getId(),
post.getId(),
liked
);
}

public GetLikedPostResponse toGetLikedPostResponse(StudentCouncilPost post) {
return new GetLikedPostResponse(
post.getId(),
post.getTitle(),
post.getPlace(),
post.isEvent() ? post.getStartDateTime() : post.getEndDateTime(),
post.getThumbnailImageUrl()
);
}

public StudentCouncilPost createStudentCouncilPost(StudentCouncil writer, PostRequest dto,
LocalDateTime startDateTime,
LocalDateTime endDateTime) {
LocalDateTime startDateTime, LocalDateTime endDateTime) {
return StudentCouncilPost.builder()
.writer(writer)
.category(dto.category())
Expand All @@ -116,4 +161,11 @@ public PostImage createPostImage(StudentCouncilPost post, String imageUrl) {
.imageUrl(imageUrl)
.build();
}

public LikePost createLikePost(User user, StudentCouncilPost post) {
return LikePost.builder()
.post(post)
.user(user)
.build();
}
}
Loading