diff --git a/src/main/java/com/example/beginnerfitbe/alarm/controller/AlarmController.java b/src/main/java/com/example/beginnerfitbe/alarm/controller/AlarmController.java new file mode 100644 index 0000000..03679c6 --- /dev/null +++ b/src/main/java/com/example/beginnerfitbe/alarm/controller/AlarmController.java @@ -0,0 +1,51 @@ +package com.example.beginnerfitbe.alarm.controller; + +import com.example.beginnerfitbe.alarm.domain.Alarm; +import com.example.beginnerfitbe.alarm.dto.AlarmDTO; +import com.example.beginnerfitbe.alarm.service.AlarmService; +import com.example.beginnerfitbe.jwt.util.JwtUtil; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping("/alarm") +@RequiredArgsConstructor +public class AlarmController { + + private final AlarmService alarmService; + private final JwtUtil jwtUtil; // JwtUtil 주입 + + @GetMapping + public ResponseEntity> getAlarms(HttpServletRequest request) { + // JWT 토큰에서 사용자 ID 추출 + String token = jwtUtil.resolveToken(request); + Long userId = jwtUtil.getUserId(token.substring(7)); + + // 사용자 ID로 알림 조회 + List alarms = alarmService.getAlarmsByUserId(userId); + List alarmDTOs = alarms.stream() + .map(AlarmDTO::fromEntity) + .collect(Collectors.toList()); + return ResponseEntity.ok(alarmDTOs); + } + + // 알림 체크 처리 + @PutMapping("/{alarmId}/check") + public ResponseEntity checkAlarm(HttpServletRequest request, @PathVariable Long alarmId) { + + String token = jwtUtil.resolveToken(request); + Long userId = jwtUtil.getUserId(token.substring(7)); + + // 알림 체크 처리 + alarmService.checkAlarm(userId, alarmId); + + return ResponseEntity.ok().build(); + } + + +} diff --git a/src/main/java/com/example/beginnerfitbe/alarm/domain/Alarm.java b/src/main/java/com/example/beginnerfitbe/alarm/domain/Alarm.java new file mode 100644 index 0000000..1fea645 --- /dev/null +++ b/src/main/java/com/example/beginnerfitbe/alarm/domain/Alarm.java @@ -0,0 +1,44 @@ +package com.example.beginnerfitbe.alarm.domain; + +import com.example.beginnerfitbe.user.domain.User; +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; + +@Entity +@Getter +@Setter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Alarm { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long alarmId; + + @Column(nullable = false) + private boolean alarmChecked; + + @Column(nullable = false) + private LocalDateTime alarmDate; + + @Column(nullable = false) + private String alarmMessage; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "userId") + private User user; + + @Column(nullable = false) + @Enumerated(EnumType.STRING) // Enum 값을 문자열로 저장 + private AlarmType alarmType; // 추가된 enum 필드 + + @Builder + public Alarm(User user, boolean alarmChecked, String alarmMessage, AlarmType alarmType) { + this.user = user; + this.alarmChecked = alarmChecked; + this.alarmDate = LocalDateTime.now(); + this.alarmMessage = alarmMessage; + this.alarmType = alarmType; + } +} diff --git a/src/main/java/com/example/beginnerfitbe/alarm/domain/AlarmType.java b/src/main/java/com/example/beginnerfitbe/alarm/domain/AlarmType.java new file mode 100644 index 0000000..2efe4f4 --- /dev/null +++ b/src/main/java/com/example/beginnerfitbe/alarm/domain/AlarmType.java @@ -0,0 +1,8 @@ +package com.example.beginnerfitbe.alarm.domain; + +public enum AlarmType { + FRIEND_REQUEST, // 친구 요청 + COMMENT_ALARM, // 게시글 댓글 알림 + CHALLENGE_REMINDER, // 오늘의 챌린지 남은 알림 + FRIEND_ACCEPTANCE // 친구 수락 알림 +} \ No newline at end of file diff --git a/src/main/java/com/example/beginnerfitbe/alarm/dto/AlarmDTO.java b/src/main/java/com/example/beginnerfitbe/alarm/dto/AlarmDTO.java new file mode 100644 index 0000000..61303e4 --- /dev/null +++ b/src/main/java/com/example/beginnerfitbe/alarm/dto/AlarmDTO.java @@ -0,0 +1,34 @@ +package com.example.beginnerfitbe.alarm.dto; + +import com.example.beginnerfitbe.alarm.domain.Alarm; +import com.example.beginnerfitbe.alarm.domain.AlarmType; +import lombok.*; + +import java.time.LocalDateTime; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class AlarmDTO { + + private Long alarmId; + private boolean alarmChecked; + private LocalDateTime alarmDate; + private String alarmMessage; + private Long userId; + private AlarmType alarmType; + + public static AlarmDTO fromEntity(Alarm alarm) { + return AlarmDTO.builder() + .alarmId(alarm.getAlarmId()) + .alarmChecked(alarm.isAlarmChecked()) + .alarmDate(alarm.getAlarmDate()) + .alarmMessage(alarm.getAlarmMessage()) + .userId(alarm.getUser().getId()) + .alarmType(alarm.getAlarmType()) + .build(); + } + +} diff --git a/src/main/java/com/example/beginnerfitbe/alarm/repository/AlarmRepository.java b/src/main/java/com/example/beginnerfitbe/alarm/repository/AlarmRepository.java new file mode 100644 index 0000000..fffcc76 --- /dev/null +++ b/src/main/java/com/example/beginnerfitbe/alarm/repository/AlarmRepository.java @@ -0,0 +1,14 @@ +package com.example.beginnerfitbe.alarm.repository; + +import com.example.beginnerfitbe.alarm.domain.Alarm; +import com.example.beginnerfitbe.user.domain.User; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface AlarmRepository extends JpaRepository { + + List findByUser(User user); +} diff --git a/src/main/java/com/example/beginnerfitbe/alarm/service/AlarmService.java b/src/main/java/com/example/beginnerfitbe/alarm/service/AlarmService.java new file mode 100644 index 0000000..2a3520e --- /dev/null +++ b/src/main/java/com/example/beginnerfitbe/alarm/service/AlarmService.java @@ -0,0 +1,52 @@ +package com.example.beginnerfitbe.alarm.service; + +import com.example.beginnerfitbe.alarm.domain.Alarm; +import com.example.beginnerfitbe.alarm.domain.AlarmType; +import com.example.beginnerfitbe.alarm.repository.AlarmRepository; +import com.example.beginnerfitbe.user.domain.User; +import com.example.beginnerfitbe.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional +@RequiredArgsConstructor +public class AlarmService { + + private final AlarmRepository alarmRepository; + private final UserRepository userRepository; + + public void createAlarm(User receiver, String alarmMessage, AlarmType alarmType) { + Alarm alarm = Alarm.builder() + .user(receiver) + .alarmChecked(false) + .alarmMessage(alarmMessage) + .alarmType(alarmType) // 알림 타입 추가 + .build(); + alarmRepository.save(alarm); + } + + public List getAlarmsByUserId(Long userId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new RuntimeException("사용자를 찾을 수 없습니다.")); + return alarmRepository.findByUser(user); + } + + public void checkAlarm(Long userId, Long alarmId) { + // alarmId에 해당하는 알림 객체 찾기 + Alarm alarm = alarmRepository.findById(alarmId) + .orElseThrow(() -> new RuntimeException("해당 알림이 존재하지 않습니다.")); + + // 알림의 userId와 일치하는지 확인 + if (!alarm.getUser().getId().equals(userId)) { + throw new RuntimeException("해당 알림을 가진 사용자를 찾을 수 없습니다."); + } + + // alarmChecked를 true로 변경 + alarm.setAlarmChecked(true); + alarmRepository.save(alarm); // 변경사항 저장 + } +} diff --git a/src/main/java/com/example/beginnerfitbe/challengeparticipant/service/ChallengeParticipantService.java b/src/main/java/com/example/beginnerfitbe/challengeparticipant/service/ChallengeParticipantService.java index c67c07b..60694ea 100644 --- a/src/main/java/com/example/beginnerfitbe/challengeparticipant/service/ChallengeParticipantService.java +++ b/src/main/java/com/example/beginnerfitbe/challengeparticipant/service/ChallengeParticipantService.java @@ -1,5 +1,7 @@ package com.example.beginnerfitbe.challengeparticipant.service; +import com.example.beginnerfitbe.alarm.domain.AlarmType; +import com.example.beginnerfitbe.alarm.service.AlarmService; import com.example.beginnerfitbe.challengeparticipant.domain.ChallengeParticipant; import com.example.beginnerfitbe.challengeparticipant.dto.ChallengeParticipantDTO; import com.example.beginnerfitbe.challengeparticipant.dto.ChallengeRankingDto; @@ -26,6 +28,7 @@ public class ChallengeParticipantService { private final ChallengeParticipantRepository challengeParticipantRepository; private final UserRepository userRepository; private final FriendRepository friendRepository; + private final AlarmService alarmService; public List getAcceptedFriends(Long userId) { // 수락된 친구 목록 가져오기 @@ -175,9 +178,30 @@ public void completeChallenge(Long userId, Long challengeId) { .findByUserIdAndChallenge_ChallengeIdAndChallengeCompletedDate(userId, challengeId, currentDate) .orElseThrow(() -> new RuntimeException("Challenge participant not found")); // 예외 처리 - // isCompleted를 true로 변경 - participant.setCompleted(true); // setter 메서드 필요 + + participant.setCompleted(true); // isCompleted를 true로 변경 challengeParticipantRepository.save(participant); // 변경사항 저장 + + + // 해당 사용자의 오늘 날짜에 해당하는 챌린지 참가자 목록 가져오기 (challengeId 제거) + List participants = challengeParticipantRepository + .findByUserIdAndChallengeCompletedDate(userId, currentDate); + + // isCompleted가 true인 참가자의 수를 세기 + long completedCount = participants.stream() + .filter(ChallengeParticipant::isCompleted) + .count(); + + // completedCount가 2일 경우 알림 생성 + if (completedCount == 2) { + String alarmMessage = ""; + // 알림 생성 + alarmService.createAlarm(participant.getUser(), alarmMessage, AlarmType.CHALLENGE_REMINDER); + } + + + + } public void notcompleteChallenge(Long userId, Long challengeId) { diff --git a/src/main/java/com/example/beginnerfitbe/comment/service/CommentService.java b/src/main/java/com/example/beginnerfitbe/comment/service/CommentService.java index 38d0ef5..1e181b8 100644 --- a/src/main/java/com/example/beginnerfitbe/comment/service/CommentService.java +++ b/src/main/java/com/example/beginnerfitbe/comment/service/CommentService.java @@ -1,5 +1,7 @@ package com.example.beginnerfitbe.comment.service; +import com.example.beginnerfitbe.alarm.domain.AlarmType; +import com.example.beginnerfitbe.alarm.service.AlarmService; import com.example.beginnerfitbe.comment.domain.Comment; import com.example.beginnerfitbe.comment.dto.CommentCreateDto; import com.example.beginnerfitbe.comment.dto.CommentDto; @@ -26,8 +28,9 @@ public class CommentService { private final PostRepository postRepository; private final UserRepository userRepository; private final CommentRepository commentRepository; + private final AlarmService alarmService; - public ResponseEntity create(Long userId, Long postId,CommentCreateDto commentCreateDto){ + public ResponseEntity create(Long userId, Long postId, CommentCreateDto commentCreateDto){ Post post = postRepository.findById(postId).orElseThrow(() -> new IllegalArgumentException("not found post")); User user = userRepository.findById(userId).orElseThrow(() -> new IllegalArgumentException("not found user")); @@ -39,6 +42,10 @@ public ResponseEntity create(Long userId, Long postId,CommentCrea .build(); commentRepository.save(comment); + + String alarmMessage = user.getName() + " "+postId; + alarmService.createAlarm(post.getUser(), alarmMessage, AlarmType.COMMENT_ALARM); // 알림 생성 + return ResponseEntity.ok(StateResponse.builder().code("SUCCESS").message("댓글을 성공적으로 생성했습니다.").build()); } diff --git a/src/main/java/com/example/beginnerfitbe/friend/service/FriendService.java b/src/main/java/com/example/beginnerfitbe/friend/service/FriendService.java index 08fd640..bb0120d 100644 --- a/src/main/java/com/example/beginnerfitbe/friend/service/FriendService.java +++ b/src/main/java/com/example/beginnerfitbe/friend/service/FriendService.java @@ -1,5 +1,7 @@ package com.example.beginnerfitbe.friend.service; +import com.example.beginnerfitbe.alarm.domain.AlarmType; +import com.example.beginnerfitbe.alarm.service.AlarmService; import com.example.beginnerfitbe.friend.domain.Friend; import com.example.beginnerfitbe.friend.dto.FriendDTO; import com.example.beginnerfitbe.friend.repository.FriendRepository; @@ -23,6 +25,7 @@ public class FriendService { private final FriendRepository friendRepository; private final UserRepository userRepository; + private final AlarmService alarmService; @Transactional public FriendDTO sendPathRequest(Long senderId, Long receiverId) { @@ -51,7 +54,6 @@ else if (reexistingFriend.isPresent()) { throw new RuntimeException("이미 친구 요청이 온 사용자입니다. 친구 요청을 수락해주세요."); } - else { Friend friend = Friend.builder() .sender(sender) @@ -61,6 +63,10 @@ else if (reexistingFriend.isPresent()) { .build(); Friend savedFriend = friendRepository.save(friend); + + String alarmMessage = sender.getName(); + alarmService.createAlarm(receiver, alarmMessage, AlarmType.FRIEND_REQUEST); + return FriendDTO.fromEntity(savedFriend); } } @@ -101,6 +107,10 @@ else if (reexistingFriend.isPresent()) { .build(); Friend savedFriend = friendRepository.save(friend); + + String alarmMessage = sender.getName(); + alarmService.createAlarm(receiver, alarmMessage, AlarmType.FRIEND_REQUEST); + return FriendDTO.fromEntity(savedFriend); } @@ -210,6 +220,12 @@ public void acceptFriendRequest(Long senderId, Long receiverId) { friend.accept(); friendRepository.save(friend); + + // 알림 메시지 생성 + String alarmMessage =friend.getReceiver().getName(); + + // 알림 생성 + alarmService.createAlarm(friend.getSender(), alarmMessage, AlarmType.FRIEND_ACCEPTANCE); } public void rejectFriendRequest(Long senderId, Long receiverId) { diff --git a/src/main/java/com/example/beginnerfitbe/user/domain/User.java b/src/main/java/com/example/beginnerfitbe/user/domain/User.java index c2f686b..9718cb6 100644 --- a/src/main/java/com/example/beginnerfitbe/user/domain/User.java +++ b/src/main/java/com/example/beginnerfitbe/user/domain/User.java @@ -1,12 +1,8 @@ package com.example.beginnerfitbe.user.domain; +import com.example.beginnerfitbe.alarm.domain.Alarm; import com.example.beginnerfitbe.attendance.domain.Attendance; import com.example.beginnerfitbe.challengeparticipant.domain.ChallengeParticipant; -import com.example.beginnerfitbe.declaration.domain.Declaration; -import com.example.beginnerfitbe.like.domain.PostLike; -import com.example.beginnerfitbe.playlist.domain.Playlist; -import com.example.beginnerfitbe.post.domain.Post; -import com.example.beginnerfitbe.scrap.domain.Scrap; import com.example.beginnerfitbe.weight.domain.WeightRecord; import jakarta.persistence.*; import lombok.*; @@ -71,6 +67,9 @@ public class User { @OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) private List challengeParticipants = new ArrayList<>(); + @OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) + private List alarms = new ArrayList<>(); + @Builder public User(String email, String name, String password, String profileUrl, double height, double weight, double targetWeight, String date, String targetDate, int exerciseTime, List exerciseIntensity, List exerciseGoals, List concernedAreas) {