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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/java/bst/bobsoolting/BobsooltingApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@SpringBootApplication(exclude = {
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration.class
})

public class BobsooltingApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package bst.bobsoolting.post.command.application.controller;

import bst.bobsoolting.post.command.application.dto.PostDTO;
import bst.bobsoolting.post.command.application.service.PostCommandService;
import bst.bobsoolting.post.command.vo.request.RequestUpdatePostVO;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/post/command")
@RequiredArgsConstructor
public class PostCommandController {

private final PostCommandService postCommandService;

/**
* 게시글 생성
*/
@PostMapping("/")
public ResponseEntity<PostDTO> createPost(@RequestBody PostDTO postDTO) {
PostDTO created = postCommandService.createPost(postDTO);
return ResponseEntity.ok(created);
}

/**
* 게시글 수정
*/
@PutMapping("/{postId}")
public ResponseEntity<PostDTO> updatePost(@PathVariable Long postId,
@RequestBody RequestUpdatePostVO postDTO) {
postDTO.setPostId(postId);
PostDTO updated = postCommandService.updatePost(postDTO);
return ResponseEntity.ok(updated);
}

/**
* 게시글 삭제 (소프트 딜리트)
*/
@PatchMapping("/{postId}")
public ResponseEntity<Void> softDeletePost(@PathVariable("postId") Long postId) {
postCommandService.deletePost(postId);
return ResponseEntity.noContent().build();
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package bst.bobsoolting.post.command.application.dto;

import lombok.*;
import java.time.LocalDate;
import java.time.LocalDateTime;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
public class PostDTO {
private Long postId;
private String category; // Enum을 문자열로 전달 (Converter에서 변환)
private String title;
private String content;
private String images;
private Integer maxParticipants;
private String participants;
private String recruitmentStatus;
private LocalDate date;
private String location;
private Boolean postStatus;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private String memberId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package bst.bobsoolting.post.command.application.mapper;

import bst.bobsoolting.post.command.application.dto.PostDTO;
import bst.bobsoolting.post.command.domain.aggregate.Category;
import bst.bobsoolting.post.command.domain.aggregate.Post;
import org.springframework.stereotype.Component;

@Component
public class PostConverter {

// DTO -> Entity 변환
public Post toEntity(PostDTO dto) {
if (dto == null) return null;
return Post.builder()
.postId(dto.getPostId())
.category(dto.getCategory() != null ? Category.valueOf(dto.getCategory()) : null)
.title(dto.getTitle())
.content(dto.getContent())
.images(dto.getImages())
.maxParticipants(dto.getMaxParticipants())
.participants(dto.getParticipants())
.recruitmentStatus(dto.getRecruitmentStatus())
.date(dto.getDate())
.location(dto.getLocation())
.postStatus(dto.getPostStatus())
.createdAt(dto.getCreatedAt())
.updatedAt(dto.getUpdatedAt())
.memberId(dto.getMemberId())
.build();
}

// Entity -> DTO 변환
public PostDTO toDTO(Post post) {
if (post == null) return null;
return PostDTO.builder()
.postId(post.getPostId())
.category(post.getCategory() != null ? post.getCategory().name() : null)
.title(post.getTitle())
.content(post.getContent())
.images(post.getImages())
.maxParticipants(post.getMaxParticipants())
.participants(post.getParticipants())
.recruitmentStatus(post.getRecruitmentStatus())
.date(post.getDate())
.location(post.getLocation())
.postStatus(post.getPostStatus())
.createdAt(post.getCreatedAt())
.updatedAt(post.getUpdatedAt())
.memberId(post.getMemberId())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package bst.bobsoolting.post.command.application.service;

import bst.bobsoolting.post.command.application.dto.PostDTO;
import bst.bobsoolting.post.command.vo.request.RequestUpdatePostVO;

public interface PostCommandService {

PostDTO createPost(PostDTO postDTO);

PostDTO updatePost(RequestUpdatePostVO updateVO);

void deletePost(Long postId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package bst.bobsoolting.post.command.application.service;

import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
import bst.bobsoolting.member.command.application.service.MemberCommandServiceImpl;
import bst.bobsoolting.member.command.domain.aggregate.entity.Member;
import bst.bobsoolting.member.query.repository.MemberMapper;
import bst.bobsoolting.post.command.application.dto.PostDTO;
import bst.bobsoolting.post.command.application.mapper.PostConverter;
import bst.bobsoolting.post.command.domain.aggregate.Post;
import bst.bobsoolting.post.command.domain.repository.PostRepository;
import bst.bobsoolting.post.command.vo.request.RequestUpdatePostVO;
import bst.bobsoolting.post.query.repository.PostMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;

@Slf4j
@Service
@RequiredArgsConstructor
public class PostCommandServiceImpl implements PostCommandService {

private final PostRepository postRepository;
private final PostMapper postMapper;
private final PostConverter postConverter; //

@Override
@Transactional
public PostDTO createPost(PostDTO postDTO) {
postDTO.setPostStatus(true);
postDTO.setCreatedAt(LocalDateTime.now());
postDTO.setUpdatedAt(LocalDateTime.now());

// static 메서드 대신 인스턴스 메서드 호출
Post post = postConverter.toEntity(postDTO);
postRepository.save(post);

return postConverter.toDTO(post);
}

@Override
@Transactional
public PostDTO updatePost(RequestUpdatePostVO updateVO) {
Post existing = postMapper.findByPostId(updateVO.getPostId());
if (existing == null) {
throw new RuntimeException("Post not found with id: " + updateVO.getPostId());
}

Post updatedPost = Post.builder()
.postId(existing.getPostId())
.category(existing.getCategory())
.title(updateVO.getTitle() != null ? updateVO.getTitle() : existing.getTitle())
.content(updateVO.getContent() != null ? updateVO.getContent() : existing.getContent())
.images(updateVO.getImages() != null ? updateVO.getImages() : existing.getImages())
.maxParticipants(updateVO.getMaxParticipants() != null ? updateVO.getMaxParticipants() : existing.getMaxParticipants())
.participants(existing.getParticipants()) // 참여자 목록은 그대로 유지
.recruitmentStatus(updateVO.getRecruitmentStatus() != null ? updateVO.getRecruitmentStatus() : existing.getRecruitmentStatus())
.date(updateVO.getDate() != null ? updateVO.getDate() : existing.getDate())
.location(updateVO.getLocation() != null ? updateVO.getLocation() : existing.getLocation())
.postStatus(existing.getPostStatus()) // 이 값은 변경하지 않는다고 가정
.createdAt(existing.getCreatedAt())
.updatedAt(LocalDateTime.now())
.memberId(existing.getMemberId())
.build();

postRepository.save(updatedPost);

return postConverter.toDTO(updatedPost);
}

@Override
@Transactional
public void deletePost(Long postId) {
Post existing = postMapper.findByPostId(postId);
if(existing == null) {
throw new RuntimeException("Post not found with id: " + postId);
}

Post updatedPost = Post.builder()
.postId(existing.getPostId())
.category(existing.getCategory())
.title(existing.getTitle())
.content(existing.getContent())
.images(existing.getImages())
.maxParticipants(existing.getMaxParticipants())
.participants(existing.getParticipants())
.recruitmentStatus(existing.getRecruitmentStatus())
.date(existing.getDate())
.location(existing.getLocation())
.postStatus(false) // 소프트 딜리트를 위해 false로 설정
.createdAt(existing.getCreatedAt())
.updatedAt(LocalDateTime.now())
.memberId(existing.getMemberId())
.build();

postRepository.save(updatedPost);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package bst.bobsoolting.post.command.domain.aggregate;

public enum Category {
BOB,
SOOL,
TING
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package bst.bobsoolting.post.command.domain.aggregate;

import lombok.*;

import java.time.LocalDate;
import java.time.LocalDateTime;

import jakarta.persistence.*;


@Entity
@Table(name = "post")
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Builder
public class Post {


@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long postId; // PK (AUTO_INCREMENT)

@Enumerated(EnumType.STRING)
private Category category; // Enum (예: STUDY, MEETUP 등)

private String title;
private String content;
private String images; // JSON 컬럼
private Integer maxParticipants;
private String participants; // JSON 컬럼
private String recruitmentStatus;
private LocalDate date;
private String location;
private Boolean postStatus;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private String memberId; // 외래키

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package bst.bobsoolting.post.command.domain.repository;

import bst.bobsoolting.post.command.domain.aggregate.Post;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;



@Repository
public interface PostRepository extends JpaRepository<Post, Long> {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package bst.bobsoolting.post.command.vo.request;

import java.time.LocalDateTime;

import com.fasterxml.jackson.annotation.JsonProperty;


import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ToString
public class RequestDeletePostVO {
// 업데이트할 게시글의 식별자 (수정 대상 확인용)
@JsonProperty("postId")
private Long postId;

// 사용자가 수정 가능한 필드들
@JsonProperty("postStatus")
private Boolean postStatus;

@JsonProperty("UpdatedAt")
private LocalDateTime UpdatedAt;
}
Loading
Loading