diff --git a/.idea/dbnavigator.xml b/.idea/dbnavigator.xml
deleted file mode 100644
index bde6588..0000000
--- a/.idea/dbnavigator.xml
+++ /dev/null
@@ -1,531 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/java/dongguk/osori/domain/goal/GoalRepository.java b/src/main/java/dongguk/osori/domain/goal/GoalRepository.java
deleted file mode 100644
index e4c8c47..0000000
--- a/src/main/java/dongguk/osori/domain/goal/GoalRepository.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package dongguk.osori.domain.goal;
-
-import dongguk.osori.domain.goal.entity.Goal;
-import dongguk.osori.domain.user.entity.User;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.stereotype.Repository;
-
-import java.util.Optional;
-
-@Repository
-public interface GoalRepository extends JpaRepository {
- Optional findByGoalIdAndUser(Long goalId, User user);
-}
diff --git a/src/main/java/dongguk/osori/domain/goal/controller/FeedController.java b/src/main/java/dongguk/osori/domain/goal/controller/FeedController.java
new file mode 100644
index 0000000..eb3388d
--- /dev/null
+++ b/src/main/java/dongguk/osori/domain/goal/controller/FeedController.java
@@ -0,0 +1,50 @@
+package dongguk.osori.domain.goal.controller;
+
+import dongguk.osori.domain.goal.dto.FeedGoalDto;
+import dongguk.osori.domain.goal.dto.GoalDetailResponseDto;
+import dongguk.osori.domain.goal.service.GoalService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import io.swagger.v3.oas.annotations.responses.ApiResponses;
+import jakarta.servlet.http.HttpSession;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Optional;
+
+@RequiredArgsConstructor
+@RestController
+@RequestMapping("/api/feeds")
+public class FeedController {
+
+ private final GoalService goalService;
+
+ @Operation(summary = "팔로우한 사람들의 오늘 날짜 목표 조회", description = "팔로우한 사람들의 오늘 날짜 목표를 피드에서 조회합니다.")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "목표 조회 성공"),
+ @ApiResponse(responseCode = "401", description = "인증되지 않은 사용자")
+ })
+ @GetMapping
+ public ResponseEntity> getTodayFeedGoals(HttpSession session) {
+ Long userId = (Long) session.getAttribute("userId");
+ if (userId == null) {
+ return ResponseEntity.status(401).build();
+ }
+ List feedGoals = goalService.getTodayFeedGoals(userId);
+ return ResponseEntity.ok(feedGoals);
+ }
+
+ @Operation(summary = "단일 목표 상세 조회", description = "단일 목표와 그에 달린 모든 댓글을 조회합니다.")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "목표 상세 조회 성공"),
+ @ApiResponse(responseCode = "404", description = "목표를 찾을 수 없음")
+ })
+ @GetMapping("/{goalId}")
+ public ResponseEntity getGoalDetailsWithComments(@PathVariable("goalId") Long goalId) {
+ Optional goalDetails = goalService.getGoalDetailsWithComments(goalId);
+ return goalDetails.map(ResponseEntity::ok)
+ .orElseGet(() -> ResponseEntity.notFound().build());
+ }
+}
diff --git a/src/main/java/dongguk/osori/domain/goal/controller/GoalCommentController.java b/src/main/java/dongguk/osori/domain/goal/controller/GoalCommentController.java
new file mode 100644
index 0000000..fc7e3e6
--- /dev/null
+++ b/src/main/java/dongguk/osori/domain/goal/controller/GoalCommentController.java
@@ -0,0 +1,41 @@
+package dongguk.osori.domain.goal.controller;
+
+import dongguk.osori.domain.goal.dto.GoalCommentDto;
+import dongguk.osori.domain.goal.dto.GoalCommentResponseDto;
+import dongguk.osori.domain.goal.service.GoalCommentService;
+import jakarta.servlet.http.HttpSession;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/api/goals/{goalId}/comments")
+@RequiredArgsConstructor
+public class GoalCommentController {
+
+ private final GoalCommentService goalCommentService;
+
+ // 댓글 추가
+ @PostMapping
+ public ResponseEntity addComment(
+ @PathVariable("goalId") Long goalId,
+ @RequestBody GoalCommentDto commentDto,
+ HttpSession session) {
+ Long userId = (Long) session.getAttribute("userId");
+ if (userId == null) {
+ return ResponseEntity.status(401).build();
+ }
+ GoalCommentResponseDto response = goalCommentService.addComment(goalId, userId, commentDto);
+ return ResponseEntity.ok(response);
+ }
+
+ // 댓글 삭제
+ @DeleteMapping("/{commentId}")
+ public ResponseEntity deleteComment(
+ @PathVariable("goalId") Long goalId,
+ @PathVariable("commentId") Long commentId,
+ @RequestAttribute("userId") Long userId) {
+ goalCommentService.deleteComment(commentId, userId);
+ return ResponseEntity.noContent().build();
+ }
+}
diff --git a/src/main/java/dongguk/osori/domain/goal/GoalController.java b/src/main/java/dongguk/osori/domain/goal/controller/GoalController.java
similarity index 67%
rename from src/main/java/dongguk/osori/domain/goal/GoalController.java
rename to src/main/java/dongguk/osori/domain/goal/controller/GoalController.java
index 1e1608b..ea21dd2 100644
--- a/src/main/java/dongguk/osori/domain/goal/GoalController.java
+++ b/src/main/java/dongguk/osori/domain/goal/controller/GoalController.java
@@ -1,8 +1,6 @@
-package dongguk.osori.domain.goal;
+package dongguk.osori.domain.goal.controller;
-import dongguk.osori.domain.goal.dto.GoalCompletionDto;
-import dongguk.osori.domain.goal.dto.GoalDto;
-import dongguk.osori.domain.goal.entity.Goal;
+import dongguk.osori.domain.goal.dto.*;
import dongguk.osori.domain.goal.service.GoalService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -31,27 +29,45 @@ public class GoalController {
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자")
})
@GetMapping
- public ResponseEntity> getUserGoals(HttpSession session) {
+ public ResponseEntity> getUserGoals(HttpSession session) {
Long userId = (Long) session.getAttribute("userId");
if (userId == null) {
return ResponseEntity.status(401).build();
}
- List goals = goalService.getUserGoals(userId);
+ List goals = goalService.getUserGoals(userId);
return ResponseEntity.ok(goals);
}
+ @Operation(summary = "목표 단일 조회", description = "특정 목표의 내용을 조회하고 연관된 댓글을 함께 보여줍니다.")
+ @ApiResponses(value = {
+ @ApiResponse(responseCode = "200", description = "목표 조회 성공"),
+ @ApiResponse(responseCode = "401", description = "인증되지 않은 사용자"),
+ @ApiResponse(responseCode = "404", description = "목표를 찾을 수 없음")
+ })
+ @GetMapping("/{goalId}")
+ public ResponseEntity getGoalById(@PathVariable("goalId") Long goalId, HttpSession session) {
+ Long userId = (Long) session.getAttribute("userId");
+ if (userId == null) {
+ return ResponseEntity.status(401).build();
+ }
+ Optional goalDetail = goalService.getGoalDetailsWithComments(goalId);
+ return goalDetail.map(ResponseEntity::ok)
+ .orElseGet(() -> ResponseEntity.notFound().build());
+ }
+
+
@Operation(summary = "로그인한 사용자의 목표 생성", description = "로그인한 사용자의 새로운 목표를 생성합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "목표 생성 성공"),
@ApiResponse(responseCode = "401", description = "인증되지 않은 사용자")
})
@PostMapping
- public ResponseEntity createGoal(@RequestBody GoalDto goalDto, HttpSession session) {
+ public ResponseEntity createGoal(@RequestBody GoalDto goalDto, HttpSession session) {
Long userId = (Long) session.getAttribute("userId");
if (userId == null) {
return ResponseEntity.status(401).build();
}
- Goal createdGoal = goalService.createGoal(goalDto, userId);
+ GoalResponseDto createdGoal = goalService.createGoal(goalDto, userId);
return ResponseEntity.ok(createdGoal);
}
@@ -62,12 +78,12 @@ public ResponseEntity createGoal(@RequestBody GoalDto goalDto, HttpSession
@ApiResponse(responseCode = "404", description = "목표를 찾을 수 없음")
})
@PatchMapping("/{goalId}")
- public ResponseEntity updateGoal(@PathVariable("goalId") Long goalId, @RequestBody GoalDto goalDto, HttpSession session) {
+ public ResponseEntity updateGoal(@PathVariable("goalId") Long goalId, @RequestBody GoalDto goalDto, HttpSession session) {
Long userId = (Long) session.getAttribute("userId");
if (userId == null) {
return ResponseEntity.status(401).build();
}
- Optional updatedGoal = goalService.updateGoal(goalId, goalDto, userId);
+ Optional updatedGoal = goalService.updateGoal(goalId, goalDto, userId);
return updatedGoal.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.notFound().build());
}
@@ -83,7 +99,7 @@ public ResponseEntity deleteGoal(@PathVariable("goalId") Long goalId, Http
if (userId == null) {
return ResponseEntity.status(401).build();
}
- goalService.deleteGoal(goalId, userId);
+ goalService.deleteGoal(userId, goalId);
return ResponseEntity.noContent().build();
}
@@ -94,12 +110,12 @@ public ResponseEntity deleteGoal(@PathVariable("goalId") Long goalId, Http
@ApiResponse(responseCode = "404", description = "목표를 찾을 수 없음")
})
@PatchMapping("/{goalId}/completion")
- public ResponseEntity updateGoalCompletion(@PathVariable("goalId") Long goalId, @RequestBody GoalCompletionDto goalCompletionDto, HttpSession session) {
+ public ResponseEntity updateGoalCompletion(@PathVariable("goalId") Long goalId, @RequestBody GoalCompletionDto goalCompletionDto, HttpSession session) {
Long userId = (Long) session.getAttribute("userId");
if (userId == null) {
return ResponseEntity.status(401).build();
}
- Optional updatedGoal = goalService.updateGoalCompletionStatus(goalId, goalCompletionDto, userId);
+ Optional updatedGoal = goalService.updateGoalCompletionStatus(goalId, goalCompletionDto, userId);
return updatedGoal.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.notFound().build());
}
diff --git a/src/main/java/dongguk/osori/domain/goal/dto/FeedGoalDto.java b/src/main/java/dongguk/osori/domain/goal/dto/FeedGoalDto.java
new file mode 100644
index 0000000..5275161
--- /dev/null
+++ b/src/main/java/dongguk/osori/domain/goal/dto/FeedGoalDto.java
@@ -0,0 +1,17 @@
+package dongguk.osori.domain.goal.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.time.LocalDateTime;
+
+@Getter
+@AllArgsConstructor
+public class FeedGoalDto {
+ private Long goalId;
+ private String authorNickname;
+ private LocalDateTime createdAt;
+ private boolean completed;
+ private String content;
+ private int commentCount;
+}
diff --git a/src/main/java/dongguk/osori/domain/goal/dto/GoalCommentDto.java b/src/main/java/dongguk/osori/domain/goal/dto/GoalCommentDto.java
new file mode 100644
index 0000000..4088121
--- /dev/null
+++ b/src/main/java/dongguk/osori/domain/goal/dto/GoalCommentDto.java
@@ -0,0 +1,18 @@
+package dongguk.osori.domain.goal.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public class GoalCommentDto {
+ private Long commentId;
+ private String nickname;
+ private String content;
+ private LocalDateTime createdAt;
+ private int emoji;
+}
diff --git a/src/main/java/dongguk/osori/domain/goal/dto/GoalCommentResponseDto.java b/src/main/java/dongguk/osori/domain/goal/dto/GoalCommentResponseDto.java
new file mode 100644
index 0000000..8581fd5
--- /dev/null
+++ b/src/main/java/dongguk/osori/domain/goal/dto/GoalCommentResponseDto.java
@@ -0,0 +1,17 @@
+package dongguk.osori.domain.goal.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.time.LocalDateTime;
+
+@Getter
+@AllArgsConstructor
+public class GoalCommentResponseDto {
+ private Long commentId;
+ private String nickname;
+ private String content;
+ private LocalDateTime createdAt;
+ private int emoji;
+}
+
diff --git a/src/main/java/dongguk/osori/domain/goal/dto/GoalDetailResponseDto.java b/src/main/java/dongguk/osori/domain/goal/dto/GoalDetailResponseDto.java
new file mode 100644
index 0000000..d5867dd
--- /dev/null
+++ b/src/main/java/dongguk/osori/domain/goal/dto/GoalDetailResponseDto.java
@@ -0,0 +1,18 @@
+package dongguk.osori.domain.goal.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Getter
+@AllArgsConstructor
+public class GoalDetailResponseDto {
+ private Long goalId;
+ private String content;
+ private String nickname;
+ private LocalDateTime createdAt;
+ private boolean completed;
+ private List comments;
+}
diff --git a/src/main/java/dongguk/osori/domain/goal/dto/GoalDto.java b/src/main/java/dongguk/osori/domain/goal/dto/GoalDto.java
index 1894437..447c9fe 100644
--- a/src/main/java/dongguk/osori/domain/goal/dto/GoalDto.java
+++ b/src/main/java/dongguk/osori/domain/goal/dto/GoalDto.java
@@ -8,5 +8,5 @@
@AllArgsConstructor
@NoArgsConstructor
public class GoalDto {
- private String context;
+ private String content;
}
diff --git a/src/main/java/dongguk/osori/domain/goal/dto/GoalResponseDto.java b/src/main/java/dongguk/osori/domain/goal/dto/GoalResponseDto.java
new file mode 100644
index 0000000..527a094
--- /dev/null
+++ b/src/main/java/dongguk/osori/domain/goal/dto/GoalResponseDto.java
@@ -0,0 +1,17 @@
+package dongguk.osori.domain.goal.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class GoalResponseDto {
+ private Long goalId;
+ private String content;
+ private LocalDateTime createdAt;
+ private boolean completed;
+}
diff --git a/src/main/java/dongguk/osori/domain/goal/entity/Goal.java b/src/main/java/dongguk/osori/domain/goal/entity/Goal.java
index 969f412..fd6e60c 100644
--- a/src/main/java/dongguk/osori/domain/goal/entity/Goal.java
+++ b/src/main/java/dongguk/osori/domain/goal/entity/Goal.java
@@ -6,6 +6,8 @@
import lombok.*;
import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
@AllArgsConstructor
@NoArgsConstructor
@@ -14,11 +16,12 @@
@Entity
public class Goal {
- @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long goalId;
@Column(nullable = false)
- private String context;
+ private String content;
@Column(nullable = false)
private LocalDateTime createdAt;
@@ -31,10 +34,20 @@ public class Goal {
@JsonIgnore
private User user;
+ @OneToMany(mappedBy = "goal", cascade = CascadeType.ALL, orphanRemoval = true)
+ private List comments = new ArrayList<>();
- public void updateContext(String context) { this.context = context; }
- public void markAsCompleted() { this.completed = true; }
- public void markAsIncomplete() { this.completed = false; }
+ public void updateContent(String content) {
+ this.content = content;
+ }
+
+ public void markAsCompleted() {
+ this.completed = true;
+ }
+
+ public void markAsIncomplete() {
+ this.completed = false;
+ }
@PrePersist
public void prePersist() {
@@ -43,9 +56,10 @@ public void prePersist() {
}
@Builder
- public Goal(String context, User user) {
- this.context = context;
+ public Goal(String content, User user) {
+ this.content = content;
this.user = user;
+ this.createdAt = LocalDateTime.now();
+ this.completed = false;
}
-
}
diff --git a/src/main/java/dongguk/osori/domain/goal/entity/GoalComment.java b/src/main/java/dongguk/osori/domain/goal/entity/GoalComment.java
new file mode 100644
index 0000000..53a91f3
--- /dev/null
+++ b/src/main/java/dongguk/osori/domain/goal/entity/GoalComment.java
@@ -0,0 +1,71 @@
+package dongguk.osori.domain.goal.entity;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import dongguk.osori.domain.user.entity.User;
+import jakarta.persistence.*;
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Entity
+public class GoalComment {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long commentId;
+
+ @Column(nullable = false)
+ private String content;
+
+ @Column(nullable = false)
+ private int emoji;
+
+ @Column(nullable = false)
+ private LocalDateTime createdAt;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "goal_id")
+ @JsonIgnore
+ private Goal goal;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "user_id")
+ @JsonIgnore
+ private User user;
+
+ @Builder
+ public GoalComment(String content, int emoji, Goal goal, User user) {
+ this.content = content;
+ this.emoji = emoji;
+ this.goal = goal;
+ this.user = user;
+ this.createdAt = LocalDateTime.now();
+ }
+
+ public void updateCreatedAt(LocalDateTime createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public void updateCommentId(Long commentId) {
+ this.commentId = commentId;
+ }
+
+ public void updateContent(String content) {
+ this.content = content;
+ }
+
+ public void updateEmoji(int emoji) {
+ this.emoji = emoji;
+ }
+
+ public void updateGoal(Goal goal) {
+ this.goal = goal;
+ }
+
+ public void updateUser(User user) {
+ this.user = user;
+ }
+}
diff --git a/src/main/java/dongguk/osori/domain/goal/repository/GoalCommentRepository.java b/src/main/java/dongguk/osori/domain/goal/repository/GoalCommentRepository.java
new file mode 100644
index 0000000..178e6a4
--- /dev/null
+++ b/src/main/java/dongguk/osori/domain/goal/repository/GoalCommentRepository.java
@@ -0,0 +1,12 @@
+package dongguk.osori.domain.goal.repository;
+
+import dongguk.osori.domain.goal.entity.GoalComment;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+@Repository
+public interface GoalCommentRepository extends JpaRepository {
+ List findByGoal_GoalId(Long goalId);
+}
diff --git a/src/main/java/dongguk/osori/domain/goal/repository/GoalRepository.java b/src/main/java/dongguk/osori/domain/goal/repository/GoalRepository.java
new file mode 100644
index 0000000..ce1be2d
--- /dev/null
+++ b/src/main/java/dongguk/osori/domain/goal/repository/GoalRepository.java
@@ -0,0 +1,19 @@
+package dongguk.osori.domain.goal.repository;
+
+import dongguk.osori.domain.goal.entity.Goal;
+import dongguk.osori.domain.user.entity.User;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface GoalRepository extends JpaRepository {
+ Optional findByGoalId(Long goalId);
+ Optional findByGoalIdAndUser(Long goalId, User user);
+ List findByUserInAndCreatedAtBetween(List users, LocalDateTime start, LocalDateTime end);
+
+}
+
diff --git a/src/main/java/dongguk/osori/domain/goal/service/GoalCommentService.java b/src/main/java/dongguk/osori/domain/goal/service/GoalCommentService.java
new file mode 100644
index 0000000..a724d9a
--- /dev/null
+++ b/src/main/java/dongguk/osori/domain/goal/service/GoalCommentService.java
@@ -0,0 +1,96 @@
+package dongguk.osori.domain.goal.service;
+
+import dongguk.osori.domain.goal.dto.GoalCommentDto;
+import dongguk.osori.domain.goal.dto.GoalCommentResponseDto;
+import dongguk.osori.domain.goal.dto.GoalDetailResponseDto;
+import dongguk.osori.domain.goal.entity.Goal;
+import dongguk.osori.domain.goal.entity.GoalComment;
+import dongguk.osori.domain.goal.repository.GoalCommentRepository;
+import dongguk.osori.domain.goal.repository.GoalRepository;
+import dongguk.osori.domain.user.entity.User;
+import dongguk.osori.domain.user.UserRepository;
+import jakarta.transaction.Transactional;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class GoalCommentService {
+
+ private final GoalCommentRepository goalCommentRepository;
+ private final GoalRepository goalRepository;
+ private final UserRepository userRepository;
+
+ // 댓글 추가
+ @Transactional
+ public GoalCommentResponseDto addComment(Long goalId, Long userId, GoalCommentDto commentDto) {
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new RuntimeException("User not found"));
+ Goal goal = goalRepository.findById(goalId)
+ .orElseThrow(() -> new RuntimeException("Goal not found"));
+
+ GoalComment comment = GoalComment.builder()
+ .content(commentDto.getContent())
+ .emoji(commentDto.getEmoji())
+ .goal(goal)
+ .user(user)
+ .build();
+
+ goalCommentRepository.save(comment);
+
+ return new GoalCommentResponseDto(
+ comment.getCommentId(),
+ user.getNickname(),
+ comment.getContent(),
+ comment.getCreatedAt(),
+ comment.getEmoji()
+ );
+ }
+
+ // 댓글 조회
+ @Transactional
+ public Optional getGoalDetailsWithComments(Long goalId) {
+ return goalRepository.findById(goalId)
+ .map(goal -> {
+ List comments = goal.getComments().stream()
+ .map(comment -> new GoalCommentResponseDto(
+ comment.getCommentId(),
+ comment.getUser().getNickname(),
+ comment.getContent(),
+ comment.getCreatedAt(),
+ comment.getEmoji()
+ ))
+ .collect(Collectors.toList());
+
+ return new GoalDetailResponseDto(
+ goal.getGoalId(),
+ goal.getContent(),
+ goal.getUser().getNickname(),
+ goal.getCreatedAt(),
+ goal.isCompleted(),
+ comments
+ );
+ });
+ }
+
+
+ // 댓글 삭제
+ @Transactional
+ public void deleteComment(Long commentId, Long userId) {
+ GoalComment comment = goalCommentRepository.findById(commentId)
+ .orElseThrow(() -> new RuntimeException("Comment not found"));
+
+ if (!comment.getUser().getUserId().equals(userId)) {
+ throw new RuntimeException("Unauthorized user");
+ }
+
+ goalCommentRepository.deleteById(commentId);
+ }
+}
diff --git a/src/main/java/dongguk/osori/domain/goal/service/GoalService.java b/src/main/java/dongguk/osori/domain/goal/service/GoalService.java
index 3b67549..f829df4 100644
--- a/src/main/java/dongguk/osori/domain/goal/service/GoalService.java
+++ b/src/main/java/dongguk/osori/domain/goal/service/GoalService.java
@@ -1,8 +1,9 @@
package dongguk.osori.domain.goal.service;
-import dongguk.osori.domain.goal.GoalRepository;
-import dongguk.osori.domain.goal.dto.GoalCompletionDto;
-import dongguk.osori.domain.goal.dto.GoalDto;
+import dongguk.osori.domain.goal.dto.*;
+import dongguk.osori.domain.goal.entity.GoalComment;
+import dongguk.osori.domain.goal.repository.GoalCommentRepository;
+import dongguk.osori.domain.goal.repository.GoalRepository;
import dongguk.osori.domain.goal.entity.Goal;
import dongguk.osori.domain.user.entity.User;
import dongguk.osori.domain.user.UserRepository;
@@ -11,8 +12,11 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
+import java.util.stream.Collectors;
@Slf4j
@Service
@@ -21,6 +25,7 @@ public class GoalService {
private final GoalRepository goalRepository;
private final UserRepository userRepository;
+ private final GoalCommentRepository goalCommentRepository;
// 로그인된 사용자 가져오기
private User getLoggedInUser(Long userId) {
@@ -29,47 +34,47 @@ private User getLoggedInUser(Long userId) {
.orElseThrow(() -> new RuntimeException("User not found"));
}
-
// 로그인된 사용자의 모든 목표 조회
- public List getUserGoals(Long userId) {
+ public List getUserGoals(Long userId) {
User user = getLoggedInUser(userId);
log.debug("Goals retrieved: {}", user.getGoals()); // 디버깅용
- return user.getGoals();
+ return user.getGoals().stream()
+ .map(goal -> new GoalResponseDto(goal.getGoalId(), goal.getContent(), goal.getCreatedAt(), goal.isCompleted()))
+ .collect(Collectors.toList());
}
// 로그인된 사용자의 목표 생성
@Transactional
- public Goal createGoal(GoalDto goalDto, Long userId) {
+ public GoalResponseDto createGoal(GoalDto goalDto, Long userId) {
log.debug("Creating goal for user ID: {}", userId); // 디버깅용
User user = getLoggedInUser(userId);
log.debug("User found: {}", user); // 디버깅용
// Goal Builder 사용하여 새로운 목표 생성
Goal goal = Goal.builder()
- .context(goalDto.getContext())
+ .content(goalDto.getContent())
.user(user)
.build();
log.debug("New goal created: {}", goal); // 디버깅용
- user.getGoals().add(goal);
- return goalRepository.save(goal);
+ goalRepository.save(goal);
+ return new GoalResponseDto(goal.getGoalId(), goal.getContent(), goal.getCreatedAt(), goal.isCompleted());
}
// 로그인된 사용자의 목표 수정
@Transactional
- public Optional updateGoal(Long goalId, GoalDto goalDto, Long userId) {
+ public Optional updateGoal(Long goalId, GoalDto goalDto, Long userId) {
User user = getLoggedInUser(userId);
return goalRepository.findByGoalIdAndUser(goalId, user)
.map(goal -> {
- goal.updateContext(goalDto.getContext());
- return goal;
+ goal.updateContent(goalDto.getContent());
+ return new GoalResponseDto(goal.getGoalId(), goal.getContent(), goal.getCreatedAt(), goal.isCompleted());
});
}
-
// 로그인된 사용자의 목표 달성 여부 업데이트
@Transactional
- public Optional updateGoalCompletionStatus(Long goalId, GoalCompletionDto goalCompletionDto, Long userId) {
+ public Optional updateGoalCompletionStatus(Long goalId, GoalCompletionDto goalCompletionDto, Long userId) {
User user = getLoggedInUser(userId);
return goalRepository.findById(goalId)
.filter(goal -> user.getGoals().contains(goal))
@@ -79,7 +84,7 @@ public Optional updateGoalCompletionStatus(Long goalId, GoalCompletionDto
} else {
goal.markAsIncomplete();
}
- return goal;
+ return new GoalResponseDto(goal.getGoalId(), goal.getContent(), goal.getCreatedAt(), goal.isCompleted());
});
}
@@ -88,7 +93,7 @@ public Optional updateGoalCompletionStatus(Long goalId, GoalCompletionDto
public String deleteGoal(Long userId, Long goalId) {
log.debug("Deleting goal ID: {} for user ID: {}", goalId, userId); // 디버깅용
Goal goal = goalRepository.findById(goalId)
- .orElseThrow(() -> new RuntimeException("존재하지 않는 목표"));
+ .orElseThrow(() -> new RuntimeException("존재하지 않는 목표"));
User user = getLoggedInUser(userId);
log.debug("User found: {}, Goal user: {}", user, goal.getUser()); // 디버깅용
@@ -97,11 +102,57 @@ public String deleteGoal(Long userId, Long goalId) {
log.warn("User mismatch: {} is not the owner of goal ID: {}", userId, goalId); // 디버깅용
return "잘못된 사용자, 삭제 실패";
}
- user.getGoals().remove(goal); // User 엔티티에서 해당 목표를 제거
goalRepository.deleteById(goalId); // Goal에서 목표 삭제
log.debug("Goal deleted successfully: {}", goalId); // 디버깅용
- return "아코자국 삭제 완료";
+ return "목표 삭제 완료";
}
+ // 피드에서 오늘 날짜의 팔로우한 사람들의 목표 조회
+ @Transactional
+ public List getTodayFeedGoals(Long userId) {
+ User loggedInUser = getLoggedInUser(userId); // 로그인된 사용자 가져오기
+ LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
+ LocalDateTime endOfDay = startOfDay.plusDays(1);
+
+ List followingUsers = loggedInUser.getFollowingUsers(); // 팔로우한 사용자 목록 가져오기
+
+ // 오늘 날짜에 생성된 팔로우한 사용자의 목표 조회
+ return goalRepository.findByUserInAndCreatedAtBetween(followingUsers, startOfDay, endOfDay)
+ .stream()
+ .map(goal -> new FeedGoalDto(
+ goal.getGoalId(),
+ goal.getUser().getNickname(),
+ goal.getCreatedAt(),
+ goal.isCompleted(),
+ goal.getContent(),
+ goal.getComments().size()
+ ))
+ .collect(Collectors.toList());
+ }
+ // Goal ID를 기준으로 목표 상세 조회 (댓글 포함)
+ @Transactional
+ public Optional getGoalDetailsWithComments(Long goalId) {
+ return goalRepository.findById(goalId)
+ .map(goal -> {
+ List comments = goal.getComments().stream()
+ .map(comment -> new GoalCommentResponseDto(
+ comment.getCommentId(),
+ comment.getUser().getNickname(),
+ comment.getContent(),
+ comment.getCreatedAt(),
+ comment.getEmoji()
+ ))
+ .collect(Collectors.toList());
+
+ return new GoalDetailResponseDto(
+ goal.getGoalId(),
+ goal.getContent(),
+ goal.getUser().getNickname(),
+ goal.getCreatedAt(),
+ goal.isCompleted(),
+ comments
+ );
+ });
+ }
}
diff --git a/src/main/java/dongguk/osori/domain/user/entity/User.java b/src/main/java/dongguk/osori/domain/user/entity/User.java
index ac35c11..378e399 100644
--- a/src/main/java/dongguk/osori/domain/user/entity/User.java
+++ b/src/main/java/dongguk/osori/domain/user/entity/User.java
@@ -8,6 +8,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.stream.Collectors;
@AllArgsConstructor
@NoArgsConstructor
@@ -37,9 +38,8 @@ public class User {
@Column(nullable = true)
private String introduce;
- private Integer balance;
-
+ private Integer balance;
public void updateNickName(String nickname) {
this.nickname = nickname;
@@ -66,5 +66,10 @@ public void updateBalance(int amount) {
@OneToMany(mappedBy = "following", cascade = CascadeType.ALL, orphanRemoval = true)
private List followers = new ArrayList<>();
-
+ // 팔로우하는 사용자 목록 반환
+ public List getFollowingUsers() {
+ return following.stream()
+ .map(Follow::getFollowing)
+ .collect(Collectors.toList());
+ }
}
diff --git a/src/main/java/dongguk/osori/domain/user/service/UserService.java b/src/main/java/dongguk/osori/domain/user/service/UserService.java
index 03858c4..634ca4d 100644
--- a/src/main/java/dongguk/osori/domain/user/service/UserService.java
+++ b/src/main/java/dongguk/osori/domain/user/service/UserService.java
@@ -122,5 +122,16 @@ public User findUserByEmail(String email) {
return userRepository.findByEmail(email)
.orElseThrow(() -> new IllegalArgumentException("User not found with email: " + email));
}
+
+ // 팔로우하는 사용자 목록 반환 메소드
+ @Transactional(readOnly = true)
+ public List getFollowingUsers(Long userId) {
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new IllegalArgumentException("유저 아이디를 찾을 수 없음: " + userId));
+
+ return user.getFollowingUsers().stream()
+ .map(followedUser -> new UserDto(followedUser.getUserId(), followedUser.getNickname(), followedUser.getEmail()))
+ .collect(Collectors.toList());
+ }
}