diff --git a/backend/src/main/java/corea/alarm/domain/Alarm.java b/backend/src/main/java/corea/alarm/domain/Alarm.java new file mode 100644 index 000000000..486362f02 --- /dev/null +++ b/backend/src/main/java/corea/alarm/domain/Alarm.java @@ -0,0 +1,22 @@ +package corea.alarm.domain; + +import corea.member.domain.Member; + +public interface Alarm { + + boolean isStatus(boolean status); + + boolean isNotReceiver(Member member); + + void read(); + + String getActionType(); + + Long getId(); + + Long getActorId(); + + Long getInteractionId(); + + Long getReceiverId(); +} diff --git a/backend/src/main/java/corea/alarm/domain/AlarmActionType.java b/backend/src/main/java/corea/alarm/domain/AlarmActionType.java index eb74aca0a..7de6cbf6a 100644 --- a/backend/src/main/java/corea/alarm/domain/AlarmActionType.java +++ b/backend/src/main/java/corea/alarm/domain/AlarmActionType.java @@ -3,4 +3,7 @@ public enum AlarmActionType { REVIEW_COMPLETE, REVIEW_URGE, + MATCH_COMPLETE, + MATCH_FAIL, + FEEDBACK_CREATED, } diff --git a/backend/src/main/java/corea/alarm/domain/AlarmType.java b/backend/src/main/java/corea/alarm/domain/AlarmType.java index 6a95bc239..c22de62cc 100644 --- a/backend/src/main/java/corea/alarm/domain/AlarmType.java +++ b/backend/src/main/java/corea/alarm/domain/AlarmType.java @@ -2,7 +2,8 @@ public enum AlarmType { // 유저간 상호작용으로 인한 알람 - USER; + USER, + SERVER; public static AlarmType from(String value) { return AlarmType.valueOf(value); diff --git a/backend/src/main/java/corea/alarm/domain/UserAlarmsByActionType.java b/backend/src/main/java/corea/alarm/domain/AlarmsByActionType.java similarity index 76% rename from backend/src/main/java/corea/alarm/domain/UserAlarmsByActionType.java rename to backend/src/main/java/corea/alarm/domain/AlarmsByActionType.java index e6e0846bd..4c3c87386 100644 --- a/backend/src/main/java/corea/alarm/domain/UserAlarmsByActionType.java +++ b/backend/src/main/java/corea/alarm/domain/AlarmsByActionType.java @@ -6,12 +6,13 @@ import java.util.Set; import java.util.stream.Collectors; -public record UserAlarmsByActionType(Map> data) { +public record AlarmsByActionType(Map> data) { + public Set getActorIds() { return data.values() .stream() .flatMap(Collection::stream) - .map(UserToUserAlarm::getActorId) + .map(Alarm::getActorId) .collect(Collectors.toSet()); } @@ -20,11 +21,11 @@ public Set getRoomIds() { .stream() .flatMap(Collection::stream) //.filter(alarm -> alarm.getAlarmActionType() == AlarmActionType.REVIEW_COMPLETE) - .map(UserToUserAlarm::getInteractionId) + .map(Alarm::getInteractionId) .collect(Collectors.toSet()); } - public List getList() { + public List getList() { return data.values() .stream() .flatMap(Collection::stream) diff --git a/backend/src/main/java/corea/alarm/domain/ServerToUserAlarm.java b/backend/src/main/java/corea/alarm/domain/ServerToUserAlarm.java new file mode 100644 index 000000000..0b89cf0ec --- /dev/null +++ b/backend/src/main/java/corea/alarm/domain/ServerToUserAlarm.java @@ -0,0 +1,60 @@ +package corea.alarm.domain; + +import corea.global.BaseTimeEntity; +import corea.member.domain.Member; +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import static jakarta.persistence.GenerationType.IDENTITY; + +@Entity +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter +public class ServerToUserAlarm extends BaseTimeEntity implements Alarm { + + @Id + @GeneratedValue(strategy = IDENTITY) + private Long id; + + @Enumerated(EnumType.STRING) + private AlarmActionType alarmActionType; + + private Long receiverId; + + private Long interactionId; + + private boolean isRead; + + public ServerToUserAlarm(AlarmActionType alarmActionType, long receiverId, long interactionId, boolean isRead) { + this(null, alarmActionType, receiverId, interactionId, isRead); + } + + @Override + public boolean isStatus(boolean status) { + return isRead == status; + } + + @Override + public String getActionType() { + return alarmActionType.name(); + } + + @Override + public Long getActorId() { + return null; + } + + @Override + public boolean isNotReceiver(Member member) { + return !receiverId.equals(member.getId()); + } + + @Override + public void read() { + isRead = true; + } +} diff --git a/backend/src/main/java/corea/alarm/domain/ServerToUserAlarmReader.java b/backend/src/main/java/corea/alarm/domain/ServerToUserAlarmReader.java new file mode 100644 index 000000000..0dafeec1c --- /dev/null +++ b/backend/src/main/java/corea/alarm/domain/ServerToUserAlarmReader.java @@ -0,0 +1,40 @@ +package corea.alarm.domain; + +import corea.alarm.repository.ServerToUserAlarmRepository; +import corea.exception.CoreaException; +import corea.exception.ExceptionType; +import corea.global.annotation.Reader; +import corea.member.domain.Member; +import lombok.RequiredArgsConstructor; + +import java.util.EnumMap; +import java.util.stream.Collectors; + +@Reader +@RequiredArgsConstructor +public class ServerToUserAlarmReader { + + private final ServerToUserAlarmRepository serverToUserAlarmRepository; + + public long countReceivedAlarm(Member member, boolean isRead) { + return serverToUserAlarmRepository.findAllByReceiverId(member.getId()) + .stream() + .filter(alarm -> alarm.isStatus(isRead)) + .count(); + } + + public ServerToUserAlarm find(long actionId) { + return serverToUserAlarmRepository.findById(actionId) + .orElseThrow(() -> new CoreaException(ExceptionType.NOT_RECEIVED_ALARM)); + } + + public AlarmsByActionType findAllByReceiver(Member member) { + return new AlarmsByActionType(serverToUserAlarmRepository.findAllByReceiverId(member.getId()) + .stream() + .collect(Collectors.groupingBy( + ServerToUserAlarm::getAlarmActionType, + () -> new EnumMap<>(AlarmActionType.class), + Collectors.toList() + ))); + } +} diff --git a/backend/src/main/java/corea/alarm/domain/ServerToUserAlarmWriter.java b/backend/src/main/java/corea/alarm/domain/ServerToUserAlarmWriter.java new file mode 100644 index 000000000..3ec87eab4 --- /dev/null +++ b/backend/src/main/java/corea/alarm/domain/ServerToUserAlarmWriter.java @@ -0,0 +1,27 @@ +package corea.alarm.domain; + +import corea.alarm.repository.ServerToUserAlarmRepository; +import corea.exception.CoreaException; +import corea.exception.ExceptionType; +import corea.global.annotation.Writer; +import corea.member.domain.Member; +import lombok.RequiredArgsConstructor; + +@Writer +@RequiredArgsConstructor +public class ServerToUserAlarmWriter { + + private final ServerToUserAlarmRepository serverToUserAlarmRepository; + + public ServerToUserAlarm create(ServerToUserAlarm serverToUserAlarm) { + return serverToUserAlarmRepository.save(serverToUserAlarm); + } + + public ServerToUserAlarm check(Member member, ServerToUserAlarm serverToUserAlarm) { + if (serverToUserAlarm.isNotReceiver(member)) { + throw new CoreaException(ExceptionType.NOT_RECEIVED_ALARM); + } + serverToUserAlarm.read(); + return serverToUserAlarm; + } +} diff --git a/backend/src/main/java/corea/alarm/domain/UserToUserAlarm.java b/backend/src/main/java/corea/alarm/domain/UserToUserAlarm.java index de66bd90a..c525aaa3b 100644 --- a/backend/src/main/java/corea/alarm/domain/UserToUserAlarm.java +++ b/backend/src/main/java/corea/alarm/domain/UserToUserAlarm.java @@ -14,7 +14,7 @@ @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter -public class UserToUserAlarm extends BaseTimeEntity { +public class UserToUserAlarm extends BaseTimeEntity implements Alarm { @Id @GeneratedValue(strategy = IDENTITY) @@ -40,23 +40,27 @@ public UserToUserAlarm(AlarmActionType alarmActionType, long actorId, long recei this(null, alarmActionType, actorId, receiverId, interactionId, isRead); } + public boolean isUrgeAlarm() { + return alarmActionType == AlarmActionType.REVIEW_URGE; + } + + @Override public boolean isStatus(boolean status) { return isRead == status; } + @Override public String getActionType() { return alarmActionType.name(); } + @Override public boolean isNotReceiver(Member member) { return !receiverId.equals(member.getId()); } + @Override public void read() { isRead = true; } - - public boolean isUrgeAlarm() { - return alarmActionType == AlarmActionType.REVIEW_URGE; - } } diff --git a/backend/src/main/java/corea/alarm/domain/UserToUserAlarmReader.java b/backend/src/main/java/corea/alarm/domain/UserToUserAlarmReader.java index d4b0616e5..411ba3f87 100644 --- a/backend/src/main/java/corea/alarm/domain/UserToUserAlarmReader.java +++ b/backend/src/main/java/corea/alarm/domain/UserToUserAlarmReader.java @@ -1,5 +1,6 @@ package corea.alarm.domain; +import corea.alarm.repository.UserToUserAlarmRepository; import corea.exception.CoreaException; import corea.exception.ExceptionType; import corea.global.annotation.Reader; @@ -28,8 +29,8 @@ public UserToUserAlarm find(long actionId) { .orElseThrow(() -> new CoreaException(ExceptionType.NOT_RECEIVED_ALARM)); } - public UserAlarmsByActionType findAllByReceiver(Member member) { - return new UserAlarmsByActionType(userToUserAlarmRepository.findAllByReceiverId(member.getId()) + public AlarmsByActionType findAllByReceiver(Member member) { + return new AlarmsByActionType(userToUserAlarmRepository.findAllByReceiverId(member.getId()) .stream() .collect(Collectors.groupingBy( UserToUserAlarm::getAlarmActionType, diff --git a/backend/src/main/java/corea/alarm/domain/UserToUserAlarmWriter.java b/backend/src/main/java/corea/alarm/domain/UserToUserAlarmWriter.java index 6e441edd1..1f9238a30 100644 --- a/backend/src/main/java/corea/alarm/domain/UserToUserAlarmWriter.java +++ b/backend/src/main/java/corea/alarm/domain/UserToUserAlarmWriter.java @@ -1,5 +1,6 @@ package corea.alarm.domain; +import corea.alarm.repository.UserToUserAlarmRepository; import corea.exception.CoreaException; import corea.exception.ExceptionType; import corea.global.annotation.Writer; @@ -15,7 +16,7 @@ public class UserToUserAlarmWriter { public UserToUserAlarm create(UserToUserAlarm userToUserAlarm) { return userToUserAlarmRepository.save(userToUserAlarm); } - + public UserToUserAlarm check(Member member, UserToUserAlarm userToUserAlarm) { if (userToUserAlarm.isNotReceiver(member)) { throw new CoreaException(ExceptionType.NOT_RECEIVED_ALARM); diff --git a/backend/src/main/java/corea/alarm/dto/AlarmResponse.java b/backend/src/main/java/corea/alarm/dto/AlarmResponse.java index 5739bcea2..f4ac402e5 100644 --- a/backend/src/main/java/corea/alarm/dto/AlarmResponse.java +++ b/backend/src/main/java/corea/alarm/dto/AlarmResponse.java @@ -1,6 +1,7 @@ package corea.alarm.dto; import corea.alarm.domain.AlarmType; +import corea.alarm.domain.ServerToUserAlarm; import corea.alarm.domain.UserToUserAlarm; import corea.member.domain.Member; import corea.room.domain.Room; @@ -31,7 +32,13 @@ public record AlarmResponse( String alarmType ) { - public static AlarmResponse from(UserToUserAlarm alarm, Member member, Room room) { - return new AlarmResponse(alarm.getId(), alarm.getActionType(), MemberResponse.from(member), InteractionResponse.from(room), alarm.isRead(), alarm.getCreateAt(), AlarmType.USER.name()); + public static AlarmResponse of(UserToUserAlarm alarm, Member member, Room room) { + return new AlarmResponse(alarm.getId(), alarm.getActionType(), MemberResponse.from(member), + InteractionResponse.from(room), alarm.isRead(), alarm.getCreateAt(), AlarmType.USER.name()); + } + + public static AlarmResponse of(ServerToUserAlarm alarm, Room room) { + return new AlarmResponse(alarm.getId(), alarm.getActionType(), null, + InteractionResponse.from(room), alarm.isRead(), alarm.getCreateAt(), AlarmType.SERVER.name()); } } diff --git a/backend/src/main/java/corea/alarm/dto/AlarmResponses.java b/backend/src/main/java/corea/alarm/dto/AlarmResponses.java index 00e7ad0c4..50af742fa 100644 --- a/backend/src/main/java/corea/alarm/dto/AlarmResponses.java +++ b/backend/src/main/java/corea/alarm/dto/AlarmResponses.java @@ -1,6 +1,10 @@ package corea.alarm.dto; +import corea.alarm.domain.Alarm; +import corea.alarm.domain.ServerToUserAlarm; import corea.alarm.domain.UserToUserAlarm; +import corea.exception.CoreaException; +import corea.exception.ExceptionType; import corea.member.domain.Member; import corea.room.domain.Room; import io.swagger.v3.oas.annotations.media.Schema; @@ -13,10 +17,18 @@ public record AlarmResponses( @Schema(description = "알림 리스트") List data) { - public static AlarmResponses from(List responses, Map members, Map rooms) { + public static AlarmResponses of(List alarms, Map members, Map userAlarmRooms, Map serverAlarmRooms) { //@formatter:off - return new AlarmResponses(responses.stream() - .map(alarm -> AlarmResponse.from(alarm, members.get(alarm.getActorId()), rooms.get(alarm.getInteractionId()))) + return new AlarmResponses(alarms.stream() + .map(alarm -> { + if (alarm instanceof UserToUserAlarm userAlarm) { + return AlarmResponse.of(userAlarm, members.get(userAlarm.getActorId()), userAlarmRooms.get(userAlarm.getInteractionId())); + } + if (alarm instanceof ServerToUserAlarm serverAlarm){ + return AlarmResponse.of(serverAlarm, serverAlarmRooms.get(serverAlarm.getInteractionId())); + } + throw new CoreaException(ExceptionType.UNDEFINED_ALARM_TYPE); + }) .sorted(Comparator.comparing(AlarmResponse::createAt).reversed()) .toList()); //@formatter:on diff --git a/backend/src/main/java/corea/alarm/dto/CreateServerToUserAlarmInput.java b/backend/src/main/java/corea/alarm/dto/CreateServerToUserAlarmInput.java new file mode 100644 index 000000000..ad498e3af --- /dev/null +++ b/backend/src/main/java/corea/alarm/dto/CreateServerToUserAlarmInput.java @@ -0,0 +1,18 @@ +package corea.alarm.dto; + +import corea.alarm.domain.AlarmActionType; +import corea.alarm.domain.ServerToUserAlarm; + +public record CreateServerToUserAlarmInput(AlarmActionType alarmType, + long receiverId, + long interactionId) { + + public ServerToUserAlarm toEntity() { + return new ServerToUserAlarm( + alarmType, + receiverId, + interactionId, + false + ); + } +} diff --git a/backend/src/main/java/corea/alarm/repository/ServerToUserAlarmRepository.java b/backend/src/main/java/corea/alarm/repository/ServerToUserAlarmRepository.java new file mode 100644 index 000000000..97ab184fd --- /dev/null +++ b/backend/src/main/java/corea/alarm/repository/ServerToUserAlarmRepository.java @@ -0,0 +1,11 @@ +package corea.alarm.repository; + +import corea.alarm.domain.ServerToUserAlarm; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface ServerToUserAlarmRepository extends JpaRepository { + + List findAllByReceiverId(long receiverId); +} diff --git a/backend/src/main/java/corea/alarm/domain/UserToUserAlarmRepository.java b/backend/src/main/java/corea/alarm/repository/UserToUserAlarmRepository.java similarity index 83% rename from backend/src/main/java/corea/alarm/domain/UserToUserAlarmRepository.java rename to backend/src/main/java/corea/alarm/repository/UserToUserAlarmRepository.java index 2e94b0906..e39ca9929 100644 --- a/backend/src/main/java/corea/alarm/domain/UserToUserAlarmRepository.java +++ b/backend/src/main/java/corea/alarm/repository/UserToUserAlarmRepository.java @@ -1,5 +1,6 @@ -package corea.alarm.domain; +package corea.alarm.repository; +import corea.alarm.domain.UserToUserAlarm; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; diff --git a/backend/src/main/java/corea/alarm/service/AlarmService.java b/backend/src/main/java/corea/alarm/service/AlarmService.java index 7e63ff0ff..63f2bb29d 100644 --- a/backend/src/main/java/corea/alarm/service/AlarmService.java +++ b/backend/src/main/java/corea/alarm/service/AlarmService.java @@ -1,18 +1,13 @@ package corea.alarm.service; import corea.alarm.domain.*; -import corea.alarm.dto.AlarmCheckRequest; -import corea.alarm.dto.AlarmCountResponse; -import corea.alarm.dto.AlarmResponses; -import corea.alarm.domain.AlarmActionType; -import corea.alarm.domain.UserAlarmsByActionType; -import corea.alarm.domain.UserToUserAlarmReader; -import corea.alarm.domain.UserToUserAlarmWriter; -import corea.alarm.dto.CreateUserToUserAlarmInput; +import corea.alarm.dto.*; import corea.exception.CoreaException; import corea.exception.ExceptionType; import corea.member.domain.Member; import corea.member.domain.MemberReader; +import corea.participation.domain.Participation; +import corea.participation.domain.ParticipationReader; import corea.room.domain.Room; import corea.room.domain.RoomReader; import lombok.RequiredArgsConstructor; @@ -20,7 +15,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; import java.util.Map; +import java.util.stream.Stream; @Slf4j @Service @@ -32,14 +29,11 @@ public class AlarmService { private final MemberReader memberReader; private final RoomReader roomReader; - private final UserToUserAlarmWriter userToUserAlarmWriter; + private final ParticipationReader participationReader; private final UserToUserAlarmReader userToUserAlarmReader; - - public AlarmCountResponse getUnReadAlarmCount(long userId) { - Member member = memberReader.findOne(userId); - long userToUserAlarmCount = userToUserAlarmReader.countReceivedAlarm(member, UNREAD); - return new AlarmCountResponse(userToUserAlarmCount); - } + private final UserToUserAlarmWriter userToUserAlarmWriter; + private final ServerToUserAlarmReader serverToUserAlarmReader; + private final ServerToUserAlarmWriter serverToUserAlarmWriter; @Transactional public void createReviewAlarm(long reviewerId, long revieweeId, long roomId) { @@ -52,33 +46,93 @@ public void createReviewAlarm(long reviewerId, long revieweeId, long roomId) { } } - public AlarmResponses getAlarm(long userId) { - Member member = memberReader.findOne(userId); - UserAlarmsByActionType userToUserAlarms = userToUserAlarmReader.findAllByReceiver(member); - Map actors = memberReader.findMembersMappedById(userToUserAlarms.getActorIds()); - Map rooms = roomReader.findRoomsMappedById(userToUserAlarms.getRoomIds()); - return AlarmResponses.from(userToUserAlarms.getList(), actors, rooms); + @Transactional + public void createUrgeAlarm(long revieweeId, long reviewerId, long roomId) { + boolean unReadUrgeAlarmExist = userToUserAlarmReader.existUnReadUrgeAlarm(revieweeId, reviewerId, roomId); + if (unReadUrgeAlarmExist) { + log.warn("리뷰 재촉 알림 생성을 실패했습니다. 리뷰어 ID={},리뷰이 ID={},방 ID={}", + reviewerId, revieweeId, roomId); + throw new CoreaException(ExceptionType.SAME_UNREAD_ALARM_EXIST); + } + CreateUserToUserAlarmInput input = new CreateUserToUserAlarmInput(AlarmActionType.REVIEW_URGE, revieweeId, reviewerId, roomId); + userToUserAlarmWriter.create(input.toEntity()); + } + + @Transactional + public void createFeedbackAlarm(long deliverId, long receiverId, long roomId) { + try { + CreateUserToUserAlarmInput input = new CreateUserToUserAlarmInput(AlarmActionType.FEEDBACK_CREATED, deliverId, receiverId, roomId); + userToUserAlarmWriter.create(input.toEntity()); + } catch (Exception e) { + log.warn("피드백 작성 알림 생성을 실패했습니다. 작성자 ID={},수신자 ID={},방 ID={}", + deliverId, receiverId, roomId); + } + } + + @Transactional + public void createMatchingCompletedAlarm(long roomId) { + participationReader.findAllByRoomId(roomId).stream() + .map(Participation::getMember) + .forEach(member -> createMatchingResultAlarm(AlarmActionType.MATCH_COMPLETE, roomId, member.getId())); + } + + @Transactional + public void createMatchingFailedAlarm(long roomId) { + participationReader.findAllByRoomId(roomId).stream() + .map(Participation::getMember) + .forEach(member -> createMatchingResultAlarm(AlarmActionType.MATCH_FAIL, roomId, member.getId())); + } + + private void createMatchingResultAlarm(AlarmActionType alarmActionType, long roomId, long memberId) { + try { + CreateServerToUserAlarmInput input = new CreateServerToUserAlarmInput(alarmActionType, memberId, roomId); + serverToUserAlarmWriter.create(input.toEntity()); + } catch (Exception e) { + log.warn("매칭 결과 알림 생성을 실패했습니다. 참여자 ID={},방 ID={}", memberId, roomId); + } } @Transactional public void checkAlarm(long userId, AlarmCheckRequest request) { Member member = memberReader.findOne(userId); AlarmType alarmType = AlarmType.from(request.alarmType()); + if (alarmType == AlarmType.USER) { UserToUserAlarm userToUserAlarm = userToUserAlarmReader.find(request.alarmId()); userToUserAlarmWriter.check(member, userToUserAlarm); } + if (alarmType == AlarmType.SERVER) { + ServerToUserAlarm serverToUserAlarm = serverToUserAlarmReader.find(request.alarmId()); + serverToUserAlarmWriter.check(member, serverToUserAlarm); + } } - @Transactional - public void createUrgeAlarm(long revieweeId, long reviewerId, long roomId) { - boolean unReadUrgeAlarmExist = userToUserAlarmReader.existUnReadUrgeAlarm(revieweeId, reviewerId, roomId); - if (unReadUrgeAlarmExist) { - log.warn("리뷰 재촉 알림 생성을 실패했습니다. 리뷰어 ID={},리뷰이 ID={},방 ID={}", - reviewerId, revieweeId, roomId); - throw new CoreaException(ExceptionType.SAME_UNREAD_ALARM_EXIST); - } - CreateUserToUserAlarmInput input = new CreateUserToUserAlarmInput(AlarmActionType.REVIEW_URGE, revieweeId, reviewerId, roomId); - userToUserAlarmWriter.create(input.toEntity()); + public AlarmCountResponse getUnReadAlarmCount(long userId) { + Member member = memberReader.findOne(userId); + long userToUserAlarmCount = userToUserAlarmReader.countReceivedAlarm(member, UNREAD); + long serverToUserAlarmCount = serverToUserAlarmReader.countReceivedAlarm(member, UNREAD); + + return new AlarmCountResponse(userToUserAlarmCount + serverToUserAlarmCount); + } + + public AlarmResponses getAlarm(long userId) { + Member member = memberReader.findOne(userId); + AlarmsByActionType userAlarms = userToUserAlarmReader.findAllByReceiver(member); + AlarmsByActionType serverAlarms = serverToUserAlarmReader.findAllByReceiver(member); + + List allAlarms = mergeAlarms(userAlarms, serverAlarms); + + Map actors = memberReader.findMembersMappedById(userAlarms.getActorIds()); + Map userAlarmRooms = roomReader.findRoomsMappedById(userAlarms.getRoomIds()); + Map serverAlarmRooms = roomReader.findRoomsMappedById(serverAlarms.getRoomIds()); + + return AlarmResponses.of(allAlarms, actors, userAlarmRooms, serverAlarmRooms); + } + + private List mergeAlarms(AlarmsByActionType userAlarms, AlarmsByActionType serverAlarms) { + return Stream.concat( + userAlarms.getList().stream(), + serverAlarms.getList().stream() + ).toList(); } } diff --git a/backend/src/main/java/corea/exception/ExceptionType.java b/backend/src/main/java/corea/exception/ExceptionType.java index cab29505c..5479b6dab 100644 --- a/backend/src/main/java/corea/exception/ExceptionType.java +++ b/backend/src/main/java/corea/exception/ExceptionType.java @@ -8,7 +8,7 @@ public enum ExceptionType { ALREADY_PARTICIPATED_ROOM(HttpStatus.BAD_REQUEST, "해당 방에 이미 참여했습니다."), NOT_PARTICIPATED_ROOM(HttpStatus.BAD_REQUEST, "아직 참여하지 않은 방입니다."), - ROOM_STATUS_IS_NOT_PROGRESS(HttpStatus.BAD_REQUEST,"방이 진행중인 상태가 아닙니다."), + ROOM_STATUS_IS_NOT_PROGRESS(HttpStatus.BAD_REQUEST, "방이 진행중인 상태가 아닙니다."), ROOM_STATUS_INVALID(HttpStatus.BAD_REQUEST, "방이 마감되었습니다."), MEMBER_IS_NOT_MANAGER(HttpStatus.BAD_REQUEST, "매니저가 아닙니다."), MEMBER_IS_NOT_REVIEWER(HttpStatus.BAD_REQUEST, "리뷰어로만 참여할 수 없습니다."), @@ -19,8 +19,9 @@ public enum ExceptionType { NOT_MATCHED_MEMBER(HttpStatus.BAD_REQUEST, "매칭된 인원들이 아닙니다."), ALREADY_COMPLETED_REVIEW(HttpStatus.BAD_REQUEST, "이미 리뷰를 완료했습니다."), - NOT_RECEIVED_ALARM(HttpStatus.BAD_REQUEST,"본인이 받은 알람이 아닙니다."), + NOT_RECEIVED_ALARM(HttpStatus.BAD_REQUEST, "본인이 받은 알람이 아닙니다."), SAME_UNREAD_ALARM_EXIST(HttpStatus.BAD_REQUEST, "상대방에게 읽지 않은 같은 알람이 존재합니다."), + UNDEFINED_ALARM_TYPE(HttpStatus.BAD_REQUEST, "정의되지 않은 알람 타입입니다."), ALREADY_COMPLETED_FEEDBACK(HttpStatus.BAD_REQUEST, "이미 작성한 피드백이 존재합니다."), INVALID_CALCULATION_FORMULA(HttpStatus.BAD_REQUEST, "잘못된 계산식입니다."), diff --git a/backend/src/main/java/corea/feedback/service/DevelopFeedbackService.java b/backend/src/main/java/corea/feedback/service/DevelopFeedbackService.java index 55290ad13..2916429a3 100644 --- a/backend/src/main/java/corea/feedback/service/DevelopFeedbackService.java +++ b/backend/src/main/java/corea/feedback/service/DevelopFeedbackService.java @@ -1,5 +1,6 @@ package corea.feedback.service; +import corea.alarm.service.AlarmService; import corea.feedback.domain.DevelopFeedback; import corea.feedback.domain.DevelopFeedbackReader; import corea.feedback.domain.DevelopFeedbackWriter; @@ -18,6 +19,7 @@ @Transactional(readOnly = true) public class DevelopFeedbackService { + private final AlarmService alarmService; private final DevelopFeedbackReader developFeedbackReader; private final DevelopFeedbackWriter developFeedbackWriter; private final MatchResultWriter matchResultWriter; @@ -25,10 +27,14 @@ public class DevelopFeedbackService { @Transactional public DevelopFeedbackResponse create(long roomId, long deliverId, DevelopFeedbackCreateRequest request) { - MatchResult matchResult = matchResultWriter.completeDevelopFeedback(roomId, deliverId, request.receiverId()); + long receiverId = request.receiverId(); + + MatchResult matchResult = matchResultWriter.completeDevelopFeedback(roomId, deliverId, receiverId); DevelopFeedback feedback = request.toEntity(roomId, matchResult.getReviewer(), matchResult.getReviewee()); - DevelopFeedback createdFeedback = developFeedbackWriter.create(feedback, roomId, deliverId, request.receiverId()); + DevelopFeedback createdFeedback = developFeedbackWriter.create(feedback, roomId, deliverId, receiverId); + + alarmService.createFeedbackAlarm(deliverId, receiverId, roomId); return DevelopFeedbackResponse.from(createdFeedback); } diff --git a/backend/src/main/java/corea/feedback/service/SocialFeedbackService.java b/backend/src/main/java/corea/feedback/service/SocialFeedbackService.java index 1ee08055b..79665a037 100644 --- a/backend/src/main/java/corea/feedback/service/SocialFeedbackService.java +++ b/backend/src/main/java/corea/feedback/service/SocialFeedbackService.java @@ -1,5 +1,6 @@ package corea.feedback.service; +import corea.alarm.service.AlarmService; import corea.feedback.domain.SocialFeedback; import corea.feedback.domain.SocialFeedbackReader; import corea.feedback.domain.SocialFeedbackWriter; @@ -18,6 +19,7 @@ @Transactional(readOnly = true) public class SocialFeedbackService { + private final AlarmService alarmService; private final SocialFeedbackReader socialFeedbackReader; private final SocialFeedbackWriter socialFeedbackWriter; private final MatchResultWriter matchResultWriter; @@ -25,10 +27,14 @@ public class SocialFeedbackService { @Transactional public SocialFeedbackResponse create(long roomId, long deliverId, SocialFeedbackCreateRequest request) { - MatchResult matchResult = matchResultWriter.completeSocialFeedback(roomId, deliverId, request.receiverId()); + long receiverId = request.receiverId(); + + MatchResult matchResult = matchResultWriter.completeSocialFeedback(roomId, deliverId, receiverId); SocialFeedback feedback = request.toEntity(roomId, matchResult.getReviewee(), matchResult.getReviewer()); - SocialFeedback createdFeedback = socialFeedbackWriter.create(feedback, roomId, deliverId, request.receiverId()); + SocialFeedback createdFeedback = socialFeedbackWriter.create(feedback, roomId, deliverId, receiverId); + + alarmService.createFeedbackAlarm(deliverId, receiverId, roomId); return SocialFeedbackResponse.from(createdFeedback); } diff --git a/backend/src/main/java/corea/participation/domain/ParticipationReader.java b/backend/src/main/java/corea/participation/domain/ParticipationReader.java index baa55b990..8063432cb 100644 --- a/backend/src/main/java/corea/participation/domain/ParticipationReader.java +++ b/backend/src/main/java/corea/participation/domain/ParticipationReader.java @@ -28,4 +28,8 @@ public List findRevieweeIdsByRoomId(long roomId) { .map(Member::getId) .toList(); } + + public List findAllByRoomId(long roomId) { + return participationRepository.findAllByRoomId(roomId); + } } diff --git a/backend/src/main/java/corea/scheduler/service/MatchingExecutor.java b/backend/src/main/java/corea/scheduler/service/MatchingExecutor.java index ba6e62b31..4d197a8cd 100644 --- a/backend/src/main/java/corea/scheduler/service/MatchingExecutor.java +++ b/backend/src/main/java/corea/scheduler/service/MatchingExecutor.java @@ -1,5 +1,6 @@ package corea.scheduler.service; +import corea.alarm.service.AlarmService; import corea.exception.CoreaException; import corea.matching.domain.PullRequestInfo; import corea.matching.service.MatchingService; @@ -26,6 +27,7 @@ public class MatchingExecutor { private final PrivatePullRequestProvider privatePullRequestProvider; private final PullRequestProvider pullRequestProvider; private final MatchingService matchingService; + private final AlarmService alarmService; private final RoomReader roomReader; private final RoomMatchReader roomMatchReader; private final FailedMatchingRepository failedMatchingRepository; @@ -38,6 +40,7 @@ public void match(long roomId) { try { template.execute(status -> { startMatching(roomId); + createMatchingCompleteAlarm(roomId); return null; }); } catch (CoreaException e) { @@ -52,6 +55,14 @@ private void startMatching(long roomId) { matchingService.match(roomId, pullRequestInfo); } + private void createMatchingCompleteAlarm(long roomId) { + alarmService.createMatchingCompletedAlarm(roomId); + } + + private void createMatchingFailedAlarm(long roomId) { + alarmService.createMatchingFailedAlarm(roomId); + } + private PullRequestInfo getPullRequestInfo(Room room, boolean isPublic) { if (isPublic) { return pullRequestProvider.getUntilDeadline(room.getRepositoryLink(), room.getRecruitmentDeadline()); @@ -65,6 +76,7 @@ private void recordMatchingFailure(long roomId, CoreaException e) { template.execute(status -> { updateRoomStatusToFail(roomId); saveFailedMatching(roomId, e); + createMatchingFailedAlarm(roomId); return null; }); } diff --git a/backend/src/test/java/config/ControllerTest.java b/backend/src/test/java/config/ControllerTest.java index 9fe7c7495..e15bd2277 100644 --- a/backend/src/test/java/config/ControllerTest.java +++ b/backend/src/test/java/config/ControllerTest.java @@ -8,6 +8,7 @@ import java.lang.annotation.RetentionPolicy; @Sql(value = {"/clear.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) +@Sql(value = {"/clear.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @Retention(RetentionPolicy.RUNTIME) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @TestExecutionListeners( diff --git a/backend/src/test/java/config/ServiceTest.java b/backend/src/test/java/config/ServiceTest.java index 65615adec..9ecc81f89 100644 --- a/backend/src/test/java/config/ServiceTest.java +++ b/backend/src/test/java/config/ServiceTest.java @@ -7,6 +7,7 @@ import java.lang.annotation.RetentionPolicy; @Sql(value = {"/clear.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) +@Sql(value = {"/clear.sql"}, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD) @Retention(RetentionPolicy.RUNTIME) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) public @interface ServiceTest { diff --git a/backend/src/test/java/corea/alarm/controller/AlarmControllerTest.java b/backend/src/test/java/corea/alarm/controller/AlarmControllerTest.java index 36a07fc5d..bca34042e 100644 --- a/backend/src/test/java/corea/alarm/controller/AlarmControllerTest.java +++ b/backend/src/test/java/corea/alarm/controller/AlarmControllerTest.java @@ -2,8 +2,8 @@ import config.ControllerTest; import corea.alarm.domain.UserToUserAlarm; -import corea.alarm.domain.UserToUserAlarmRepository; import corea.alarm.dto.AlarmCheckRequest; +import corea.alarm.repository.UserToUserAlarmRepository; import corea.auth.service.TokenService; import corea.fixture.AlarmFixture; import corea.fixture.MemberFixture; diff --git a/backend/src/test/java/corea/alarm/service/AlarmServiceTest.java b/backend/src/test/java/corea/alarm/service/AlarmServiceTest.java index 5fe597aff..4f4e30b35 100644 --- a/backend/src/test/java/corea/alarm/service/AlarmServiceTest.java +++ b/backend/src/test/java/corea/alarm/service/AlarmServiceTest.java @@ -1,20 +1,28 @@ package corea.alarm.service; import config.ServiceTest; +import corea.alarm.domain.AlarmActionType; +import corea.alarm.domain.ServerToUserAlarm; import corea.alarm.domain.UserToUserAlarm; -import corea.alarm.domain.UserToUserAlarmRepository; import corea.alarm.dto.AlarmCheckRequest; import corea.alarm.dto.AlarmCountResponse; import corea.alarm.dto.AlarmResponse; import corea.alarm.dto.AlarmResponses; +import corea.alarm.repository.ServerToUserAlarmRepository; +import corea.alarm.repository.UserToUserAlarmRepository; import corea.exception.CoreaException; import corea.fixture.AlarmFixture; import corea.fixture.MemberFixture; import corea.fixture.RoomFixture; import corea.member.domain.Member; +import corea.member.domain.MemberRole; import corea.member.repository.MemberRepository; +import corea.participation.domain.Participation; +import corea.participation.domain.ParticipationStatus; +import corea.participation.repository.ParticipationRepository; import corea.room.domain.Room; import corea.room.repository.RoomRepository; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -25,6 +33,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; @ServiceTest class AlarmServiceTest { @@ -32,12 +42,18 @@ class AlarmServiceTest { @Autowired AlarmService alarmService; + @Autowired + ServerToUserAlarmRepository serverToUserAlarmRepository; + @Autowired UserToUserAlarmRepository userToUserAlarmRepository; @Autowired MemberRepository memberRepository; + @Autowired + ParticipationRepository participationRepository; + private Member actor; private Member receiver; private Member notReceiver; @@ -56,13 +72,22 @@ void setUp() { interactionId = interaction.getId(); } + @AfterEach + void tearDown() { + serverToUserAlarmRepository.deleteAll(); + } + @Test @DisplayName("자신에게 작성된 알람들을 가져온다.") void get_alarm_count() { userToUserAlarmRepository.save(AlarmFixture.REVIEW_COMPLETE(actor.getId(), receiver.getId(), interactionId)); userToUserAlarmRepository.save(AlarmFixture.REVIEW_COMPLETE(actor.getId(), notReceiver.getId(), interactionId)); + + serverToUserAlarmRepository.save(AlarmFixture.MATCH_COMPLETE(receiver.getId(), interactionId)); + serverToUserAlarmRepository.save(AlarmFixture.MATCH_FAIL(notReceiver.getId(), interactionId)); + AlarmCountResponse response = alarmService.getUnReadAlarmCount(receiver.getId()); - assertThat(response.count()).isEqualTo(1); + assertThat(response.count()).isEqualTo(2); } @Test @@ -70,8 +95,12 @@ void get_alarm_count() { void get_only_not_read_alarm_count() { userToUserAlarmRepository.save(AlarmFixture.REVIEW_COMPLETE(actor.getId(), receiver.getId(), interactionId)); userToUserAlarmRepository.save(AlarmFixture.READ_REVIEW_COMPLETE(actor.getId(), receiver.getId(), interactionId)); + + serverToUserAlarmRepository.save(AlarmFixture.MATCH_COMPLETE(receiver.getId(), interactionId)); + serverToUserAlarmRepository.save(AlarmFixture.READ_MATCH_COMPLETE(receiver.getId(), interactionId)); + AlarmCountResponse response = alarmService.getUnReadAlarmCount(receiver.getId()); - assertThat(response.count()).isEqualTo(1); + assertThat(response.count()).isEqualTo(2); } @Test @@ -79,12 +108,18 @@ void get_only_not_read_alarm_count() { void get_alarm() { UserToUserAlarm alarm1 = userToUserAlarmRepository.save(AlarmFixture.REVIEW_COMPLETE(actor.getId(), receiver.getId(), interactionId)); UserToUserAlarm alarm2 = userToUserAlarmRepository.save(AlarmFixture.READ_REVIEW_COMPLETE(actor.getId(), receiver.getId(), interactionId)); + + ServerToUserAlarm alarm3 = serverToUserAlarmRepository.save(AlarmFixture.MATCH_COMPLETE(receiver.getId(), interactionId)); + ServerToUserAlarm alarm4 = serverToUserAlarmRepository.save(AlarmFixture.MATCH_FAIL(receiver.getId(), interactionId)); + AlarmResponses responses = alarmService.getAlarm(receiver.getId()); - assertThat(responses.data()).hasSize(2) + assertThat(responses.data()).hasSize(4) .usingElementComparatorIgnoringFields("createAt") .containsExactly( - AlarmResponse.from(alarm2, actor, interaction), - AlarmResponse.from(alarm1, actor, interaction) + AlarmResponse.of(alarm4, interaction), + AlarmResponse.of(alarm3, interaction), + AlarmResponse.of(alarm2, actor, interaction), + AlarmResponse.of(alarm1, actor, interaction) ); } @@ -98,7 +133,7 @@ void throw_exception_when_not_receive_alarm() { } @Test - @DisplayName("자신에게 해당된 알람이 아니면 예외를 발생한다.") + @DisplayName("존재하는 알람이 아니면 예외를 발생한다.") void throw_exception_when_not_exist_alarm() { userToUserAlarmRepository.save(AlarmFixture.REVIEW_COMPLETE(actor.getId(), receiver.getId(), interactionId)); @@ -134,4 +169,40 @@ void does_not_create_urge_alarm_when_unread_urge_alarm_exist() { assertThatThrownBy(() -> alarmService.createUrgeAlarm(actor.getId(), receiver.getId(), interactionId)) .isInstanceOf(CoreaException.class); } + + @Test + @DisplayName("매칭 완료 알람을 생성한다.") + void matching_complete_alarm() { + participationRepository.save(new Participation(interaction, receiver, MemberRole.BOTH, ParticipationStatus.PARTICIPATED, 2)); + participationRepository.save(new Participation(interaction, actor, MemberRole.BOTH, ParticipationStatus.PARTICIPATED, 2)); + + alarmService.createMatchingCompletedAlarm(interactionId); + + List matchingAlarmsToReceiver = serverToUserAlarmRepository.findAllByReceiverId(receiver.getId()); + List totalAlarms = serverToUserAlarmRepository.findAll(); + + assertAll( + () -> assertEquals(totalAlarms.size(), 2), + () -> assertEquals(matchingAlarmsToReceiver.size(), 1), + () -> assertEquals(matchingAlarmsToReceiver.get(0).getAlarmActionType(), AlarmActionType.MATCH_COMPLETE) + ); + } + + @Test + @DisplayName("매칭 실패 알람을 생성한다.") + void matching_fail_alarm() { + participationRepository.save(new Participation(interaction, receiver, MemberRole.BOTH, ParticipationStatus.PARTICIPATED, 2)); + participationRepository.save(new Participation(interaction, actor, MemberRole.BOTH, ParticipationStatus.PARTICIPATED, 2)); + + alarmService.createMatchingFailedAlarm(interactionId); + + List matchingAlarmsToReceiver = serverToUserAlarmRepository.findAllByReceiverId(receiver.getId()); + List totalAlarms = serverToUserAlarmRepository.findAll(); + + assertAll( + () -> assertEquals(totalAlarms.size(), 2), + () -> assertEquals(matchingAlarmsToReceiver.size(), 1), + () -> assertEquals(matchingAlarmsToReceiver.get(0).getAlarmActionType(), AlarmActionType.MATCH_FAIL) + ); + } } diff --git a/backend/src/test/java/corea/feedback/controller/DevelopFeedbackFeedbackControllerTest.java b/backend/src/test/java/corea/feedback/controller/DevelopFeedbackFeedbackControllerTest.java index febac80b7..cc7a00a0d 100644 --- a/backend/src/test/java/corea/feedback/controller/DevelopFeedbackFeedbackControllerTest.java +++ b/backend/src/test/java/corea/feedback/controller/DevelopFeedbackFeedbackControllerTest.java @@ -1,6 +1,9 @@ package corea.feedback.controller; import config.ControllerTest; +import corea.alarm.domain.AlarmActionType; +import corea.alarm.domain.AlarmsByActionType; +import corea.alarm.domain.UserToUserAlarmReader; import corea.auth.service.TokenService; import corea.feedback.dto.DevelopFeedbackCreateRequest; import corea.fixture.MatchResultFixture; @@ -13,12 +16,15 @@ import corea.room.repository.RoomRepository; import io.restassured.RestAssured; import io.restassured.http.ContentType; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + @ControllerTest class DevelopFeedbackFeedbackControllerTest { @@ -31,31 +37,58 @@ class DevelopFeedbackFeedbackControllerTest { @Autowired private MatchResultRepository matchResultRepository; + @Autowired + private UserToUserAlarmReader userToUserAlarmReader; + @Autowired private TokenService tokenService; - @Test - @DisplayName("개발(리뷰어 -> 리뷰이) 피드백을 작성한다.") - void create() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - String token = tokenService.createAccessToken(reviewer); + private Member manager; + private Room room; + private Member reviewer; + private Member reviewee; + private String token; + private DevelopFeedbackCreateRequest request; + + @BeforeEach + void setUp() { + manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); + room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); + reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); + reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); + token = tokenService.createAccessToken(reviewer); matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( room.getId(), reviewer, reviewee )); - DevelopFeedbackCreateRequest request = new DevelopFeedbackCreateRequest( + request = new DevelopFeedbackCreateRequest( reviewee.getId(), 4, List.of("방의 목적에 맞게 코드를 작성했어요", "코드를 이해하기 쉬웠어요"), "처음 자바를 접해봤다고 했는데 \n 생각보다 매우 구성되어 있는 코드 였던거 같습니다. ...", 2 ); + } + + @Test + @DisplayName("개발(리뷰어 -> 리뷰이) 피드백을 작성한다.") + void create() { + RestAssured.given() + .auth() + .oauth2(token) + .contentType(ContentType.JSON) + .body(request) + .when() + .post("/rooms/" + room.getId() + "/develop/feedbacks") + .then() + .statusCode(200); + } + @Test + @DisplayName("개발 피드백 작성 시 리뷰이에게 알람이 생성된다.") + void create_alarm() { RestAssured.given() .auth() .oauth2(token) @@ -65,5 +98,15 @@ void create() { .post("/rooms/" + room.getId() + "/develop/feedbacks") .then() .statusCode(200); + + long reviewerAlarmCount = userToUserAlarmReader.countReceivedAlarm(reviewer, false); + long revieweeAlarmCount = userToUserAlarmReader.countReceivedAlarm(reviewee, false); + AlarmsByActionType alarms = userToUserAlarmReader.findAllByReceiver(reviewee); + + assertAll( + () -> assertEquals(reviewerAlarmCount, 0), + () -> assertEquals(revieweeAlarmCount, 1), + () -> assertTrue(alarms.data().containsKey(AlarmActionType.FEEDBACK_CREATED)) + ); } } diff --git a/backend/src/test/java/corea/feedback/controller/SocialFeedbackFeedbackControllerTest.java b/backend/src/test/java/corea/feedback/controller/SocialFeedbackFeedbackControllerTest.java index ed07ced56..b48469ee7 100644 --- a/backend/src/test/java/corea/feedback/controller/SocialFeedbackFeedbackControllerTest.java +++ b/backend/src/test/java/corea/feedback/controller/SocialFeedbackFeedbackControllerTest.java @@ -1,6 +1,9 @@ package corea.feedback.controller; import config.ControllerTest; +import corea.alarm.domain.AlarmActionType; +import corea.alarm.domain.AlarmsByActionType; +import corea.alarm.domain.UserToUserAlarmReader; import corea.auth.service.TokenService; import corea.feedback.dto.SocialFeedbackCreateRequest; import corea.fixture.MatchResultFixture; @@ -13,12 +16,15 @@ import corea.room.repository.RoomRepository; import io.restassured.RestAssured; import io.restassured.http.ContentType; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + @ControllerTest class SocialFeedbackFeedbackControllerTest { @@ -31,30 +37,57 @@ class SocialFeedbackFeedbackControllerTest { @Autowired private MatchResultRepository matchResultRepository; + @Autowired + private UserToUserAlarmReader userToUserAlarmReader; + @Autowired private TokenService tokenService; - @Test - @DisplayName("소셜(리뷰이 -> 리뷰어)에 대한 피드백을 작성한다.") - void create() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - String token = tokenService.createAccessToken(reviewee); + private Member manager; + private Room room; + private Member reviewer; + private Member reviewee; + private String token; + private SocialFeedbackCreateRequest request; + + @BeforeEach + void setUp() { + manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); + room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); + reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); + reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); + token = tokenService.createAccessToken(reviewee); matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( room.getId(), reviewer, reviewee )); - SocialFeedbackCreateRequest request = new SocialFeedbackCreateRequest( + request = new SocialFeedbackCreateRequest( reviewer.getId(), 4, - List.of("방의 목적에 맞게 코드를 작성했어요", "코드를 이해하기 쉬웠어요"), - "유용한 블로그나 아티클도 남겨주시고, \n 사소한 부분까지 잘 챙겨준게 좋았씁니다." + List.of("친절했어요", "리뷰 속도가 빨랐어요"), + "유용한 블로그나 아티클도 남겨주시고, \n 사소한 부분까지 잘 챙겨준게 좋았습니다." ); + } + + @Test + @DisplayName("소셜(리뷰이 -> 리뷰어)에 대한 피드백을 작성한다.") + void create() { + RestAssured.given() + .auth() + .oauth2(token) + .contentType(ContentType.JSON) + .body(request) + .when() + .post("/rooms/" + room.getId() + "/social/feedbacks") + .then() + .statusCode(200); + } + @Test + @DisplayName("소셜 피드백 작성 시 리뷰어에게 알람이 생성된다.") + void create_alarm() { RestAssured.given() .auth() .oauth2(token) @@ -64,5 +97,15 @@ void create() { .post("/rooms/" + room.getId() + "/social/feedbacks") .then() .statusCode(200); + + long reviewerAlarmCount = userToUserAlarmReader.countReceivedAlarm(reviewer, false); + long revieweeAlarmCount = userToUserAlarmReader.countReceivedAlarm(reviewee, false); + AlarmsByActionType alarms = userToUserAlarmReader.findAllByReceiver(reviewer); + + assertAll( + () -> assertEquals(revieweeAlarmCount, 0), + () -> assertEquals(reviewerAlarmCount, 1), + () -> assertTrue(alarms.data().containsKey(AlarmActionType.FEEDBACK_CREATED)) + ); } } diff --git a/backend/src/test/java/corea/feedback/service/DevelopFeedbackServiceTest.java b/backend/src/test/java/corea/feedback/service/DevelopFeedbackServiceTest.java index 4ad8519b6..d51798653 100644 --- a/backend/src/test/java/corea/feedback/service/DevelopFeedbackServiceTest.java +++ b/backend/src/test/java/corea/feedback/service/DevelopFeedbackServiceTest.java @@ -1,6 +1,9 @@ package corea.feedback.service; import config.ServiceTest; +import corea.alarm.domain.AlarmActionType; +import corea.alarm.domain.AlarmsByActionType; +import corea.alarm.domain.UserToUserAlarmReader; import corea.exception.CoreaException; import corea.exception.ExceptionType; import corea.feedback.dto.DevelopFeedbackCreateRequest; @@ -17,6 +20,7 @@ import corea.room.domain.Room; import corea.room.repository.RoomRepository; import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -25,6 +29,8 @@ import java.util.List; import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertTrue; @ServiceTest class DevelopFeedbackServiceTest { @@ -38,42 +44,60 @@ class DevelopFeedbackServiceTest { @Autowired private MatchResultRepository matchResultRepository; + @Autowired + private UserToUserAlarmReader userToUserAlarmReader; + @Autowired private DevelopFeedbackService developFeedbackService; - @Test - @Transactional - @DisplayName("개발(리뷰어->리뷰이) 대한 피드백 내용을 생성한다.") - void create() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member deliver = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member receiver = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - MatchResult matchResult = matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( + private Member manager; + private Room room; + private Member deliver; + private Member receiver; + private MatchResult matchResult; + + @BeforeEach + void setUp() { + manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); + room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); + deliver = memberRepository.save(MemberFixture.MEMBER_PORORO()); + receiver = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); + matchResult = matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( room.getId(), deliver, receiver )); + } + //@Transactional + @Test + @DisplayName("개발(리뷰어->리뷰이) 대한 피드백 내용을 생성한다.") + void create() { assertThatCode(() -> developFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId()))) .doesNotThrowAnyException(); assertThat(matchResult.isReviewerCompletedFeedback()).isTrue(); } - @Transactional + //@Transactional + @Test + @DisplayName("개발 피드백이 작성되면 리뷰이에게 알람이 생성된다.") + void create_alarm() { + assertThatCode(() -> developFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId()))) + .doesNotThrowAnyException(); + + long count = userToUserAlarmReader.countReceivedAlarm(receiver, false); + AlarmsByActionType alarms = userToUserAlarmReader.findAllByReceiver(receiver); + + assertAll( + () -> assertThat(count).isOne(), + () -> assertTrue(alarms.data().containsKey(AlarmActionType.FEEDBACK_CREATED)) + ); + } + + //@Transactional @Test @DisplayName("방이 close 상태가 아닐 때 피드백을 작성하면, 피드백 받은 개수가 증가하지 않는다") void notUpdateFeedbackPoint() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member deliver = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member receiver = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - deliver, - receiver - )); - developFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId())); Profile profile = receiver.getProfile(); @@ -84,17 +108,14 @@ void notUpdateFeedbackPoint() { @Test @DisplayName("방이 close 상태일 때 피드백을 작성하면, 피드백 받은 개수가 바로 증가한다.") void updateFeedbackPoint() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN_WITH_CLOSED(manager)); - Member deliver = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member receiver = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); + Room closedRoom = roomRepository.save(RoomFixture.ROOM_DOMAIN_WITH_CLOSED(manager)); matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), + closedRoom.getId(), deliver, receiver )); - developFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId())); + developFeedbackService.create(closedRoom.getId(), deliver.getId(), createRequest(receiver.getId())); Profile profile = receiver.getProfile(); assertThat(profile.getFeedbackCount()).isEqualTo(1); @@ -103,10 +124,8 @@ void updateFeedbackPoint() { @Test @DisplayName("개발(리뷰어 -> 리뷰이) 에 대한 매칭 결과가 없으면 예외를 발생한다.") void throw_exception_when_not_exist_match_result() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member deliver = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member receiver = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); + Member deliver = memberRepository.save(MemberFixture.MEMBER_ASH()); + Member receiver = memberRepository.save(MemberFixture.MEMBER_MOVIN()); assertThatCode(() -> developFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId()))) .asInstanceOf(InstanceOfAssertFactories.type(CoreaException.class)) @@ -117,16 +136,6 @@ void throw_exception_when_not_exist_match_result() { @Test @DisplayName("개발(리뷰어 -> 리뷰이) 에 대한 피드백이 이미 있다면 피드백을 생성할 때 예외를 발생한다.") void throw_exception_when_already_feedback_exist() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member deliver = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member receiver = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - deliver, - receiver - )); - developFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId())); assertThatCode(() -> developFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId()))) @@ -138,15 +147,6 @@ void throw_exception_when_already_feedback_exist() { @Test @DisplayName("유저네임을 통해 방에 대한 자신의 리뷰이를 검색한다.") void findDevelopFeedback() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member deliver = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member receiver = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - deliver, - receiver - )); developFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId())); DevelopFeedbackResponse response = developFeedbackService.findDevelopFeedback(room.getId(), deliver.getId(), receiver.getUsername()); @@ -156,15 +156,6 @@ void findDevelopFeedback() { @Test @DisplayName("개발(리뷰어 -> 리뷰이) 피드백 내용을 업데이트한다.") void update() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member deliver = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member receiver = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - deliver, - receiver - )); DevelopFeedbackResponse createResponse = developFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId())); DevelopFeedbackResponse updateResponse = developFeedbackService.update(createResponse.feedbackId(), deliver.getId(), updateRequest()); @@ -174,16 +165,6 @@ void update() { @Test @DisplayName("없는 개발(리뷰어 -> 리뷰이) 피드백 내용을 업데이트시 예외를 발생한다.") void throw_exception_when_update_with_not_exist_feedback() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member deliver = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member receiver = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - deliver, - receiver - )); - assertThatThrownBy(() -> developFeedbackService.update(room.getId(), deliver.getId(), updateRequest())) .asInstanceOf(InstanceOfAssertFactories.type(CoreaException.class)) .extracting(CoreaException::getExceptionType) @@ -193,16 +174,6 @@ void throw_exception_when_update_with_not_exist_feedback() { @Test @DisplayName("개발(리뷰어 -> 리뷰이) 피드백 작성자가 아닌 사람이 업데이트시 예외를 발생한다.") void throw_exception_when_anonymous_updates_feedback() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member deliver = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member receiver = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - deliver, - receiver - )); - DevelopFeedbackResponse createResponse = developFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId())); assertThatThrownBy(() -> developFeedbackService.update(createResponse.feedbackId(), receiver.getId(), updateRequest())) diff --git a/backend/src/test/java/corea/feedback/service/SocialFeedbackServiceTest.java b/backend/src/test/java/corea/feedback/service/SocialFeedbackServiceTest.java index 3e91dca0f..b86366817 100644 --- a/backend/src/test/java/corea/feedback/service/SocialFeedbackServiceTest.java +++ b/backend/src/test/java/corea/feedback/service/SocialFeedbackServiceTest.java @@ -1,6 +1,9 @@ package corea.feedback.service; import config.ServiceTest; +import corea.alarm.domain.AlarmActionType; +import corea.alarm.domain.AlarmsByActionType; +import corea.alarm.domain.UserToUserAlarmReader; import corea.exception.CoreaException; import corea.exception.ExceptionType; import corea.feedback.dto.SocialFeedbackCreateRequest; @@ -16,7 +19,9 @@ import corea.member.repository.MemberRepository; import corea.room.domain.Room; import corea.room.repository.RoomRepository; +import org.assertj.core.api.Assertions; import org.assertj.core.api.InstanceOfAssertFactories; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -27,6 +32,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertTrue; @ServiceTest class SocialFeedbackServiceTest { @@ -40,45 +47,63 @@ class SocialFeedbackServiceTest { @Autowired private MatchResultRepository matchResultRepository; + @Autowired + private UserToUserAlarmReader userToUserAlarmReader; + @Autowired private SocialFeedbackService socialFeedbackService; - @Test - @Transactional - @DisplayName("소셜(리뷰이->리뷰어) 대한 피드백 내용을 생성한다.") - void create() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - MatchResult matchResult = matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( + private Member manager; + private Room room; + private Member deliver; + private Member receiver; + private MatchResult matchResult; + + @BeforeEach + void setUp() { + manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); + room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); + deliver = memberRepository.save(MemberFixture.MEMBER_PORORO()); + receiver = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); + matchResult = matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( room.getId(), - reviewer, - reviewee + receiver, + deliver )); + } - assertThatCode(() -> socialFeedbackService.create(room.getId(), reviewee.getId(), createRequest(reviewer.getId()))) + //@Transactional + @Test + @DisplayName("소셜(리뷰이->리뷰어) 대한 피드백 내용을 생성한다.") + void create() { + assertThatCode(() -> socialFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId()))) .doesNotThrowAnyException(); assertThat(matchResult.isRevieweeCompletedFeedback()).isTrue(); } - @Transactional + //@Transactional + @Test + @DisplayName("소셜 피드백이 작성되면 리뷰이에게 알람이 생성된다.") + void create_alarm() { + Assertions.assertThatCode(() -> socialFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId()))) + .doesNotThrowAnyException(); + + long count = userToUserAlarmReader.countReceivedAlarm(receiver, false); + AlarmsByActionType alarms = userToUserAlarmReader.findAllByReceiver(receiver); + + assertAll( + () -> assertThat(count).isOne(), + () -> assertTrue(alarms.data().containsKey(AlarmActionType.FEEDBACK_CREATED)) + ); + } + + //@Transactional @Test @DisplayName("방이 close 상태가 아닐 때 피드백을 작성하면, 피드백 받은 개수가 증가하지 않는다") void notUpdateFeedbackPoint() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - reviewer, - reviewee - )); + socialFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId())); - socialFeedbackService.create(room.getId(), reviewee.getId(), createRequest(reviewer.getId())); - - Profile profile = reviewer.getProfile(); + Profile profile = receiver.getProfile(); assertThat(profile.getFeedbackCount()).isEqualTo(0); } @@ -86,31 +111,26 @@ void notUpdateFeedbackPoint() { @Test @DisplayName("방이 close 상태일 때 피드백을 작성하면, 피드백 받은 개수가 바로 증가한다.") void updateFeedbackPoint() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN_WITH_CLOSED(manager)); - Member reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); + Room closedRoom = roomRepository.save(RoomFixture.ROOM_DOMAIN_WITH_CLOSED(manager)); matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - reviewer, - reviewee + closedRoom.getId(), + receiver, + deliver )); - socialFeedbackService.create(room.getId(), reviewee.getId(), createRequest(reviewer.getId())); + socialFeedbackService.create(closedRoom.getId(), deliver.getId(), createRequest(receiver.getId())); - Profile profile = reviewer.getProfile(); + Profile profile = receiver.getProfile(); assertThat(profile.getFeedbackCount()).isEqualTo(1); } @Test @DisplayName("소셜(리뷰이->리뷰어)에 대한 매칭 결과가 없으면 예외를 발생한다.") void throw_exception_when_not_exist_match_result() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); + Member receiver = memberRepository.save(MemberFixture.MEMBER_MOVIN()); + Member deliver = memberRepository.save(MemberFixture.MEMBER_ASH()); - assertThatThrownBy(() -> socialFeedbackService.create(room.getId(), reviewee.getId(), createRequest(reviewer.getId()))) + assertThatThrownBy(() -> socialFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId()))) .asInstanceOf(InstanceOfAssertFactories.type(CoreaException.class)) .extracting(CoreaException::getExceptionType) .isEqualTo(ExceptionType.NOT_MATCHED_MEMBER); @@ -119,19 +139,9 @@ void throw_exception_when_not_exist_match_result() { @Test @DisplayName("소셜(리뷰이 -> 리뷰어) 에 대한 피드백이 이미 있다면 피드백을 생성할 때 예외를 발생한다.") void throw_exception_when_already_feedback_exist() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - reviewer, - reviewee - )); + socialFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId())); - socialFeedbackService.create(room.getId(), reviewee.getId(), createRequest(reviewer.getId())); - - assertThatCode(() -> socialFeedbackService.create(room.getId(), reviewee.getId(), createRequest(reviewer.getId()))) + assertThatCode(() -> socialFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId()))) .asInstanceOf(InstanceOfAssertFactories.type(CoreaException.class)) .extracting(CoreaException::getExceptionType) .isEqualTo(ExceptionType.ALREADY_COMPLETED_FEEDBACK); @@ -140,35 +150,17 @@ void throw_exception_when_already_feedback_exist() { @Test @DisplayName("유저네임을 통해 방에 대한 자신의 리뷰어를 검색한다.") void findReviewerToReviewee() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - reviewer, - reviewee - )); - socialFeedbackService.create(room.getId(), reviewee.getId(), createRequest(reviewer.getId())); + socialFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId())); - SocialFeedbackResponse response = socialFeedbackService.findSocialFeedback(room.getId(), reviewee.getId(), reviewer.getUsername()); - assertThat(response.receiverId()).isEqualTo(reviewer.getId()); + SocialFeedbackResponse response = socialFeedbackService.findSocialFeedback(room.getId(), deliver.getId(), receiver.getUsername()); + assertThat(response.receiverId()).isEqualTo(receiver.getId()); } @Test @DisplayName("소셜(리뷰이->리뷰어) 피드백 내용을 업데이트한다.") void update() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - reviewer, - reviewee - )); - SocialFeedbackResponse createResponse = socialFeedbackService.create(room.getId(), reviewee.getId(), createRequest(reviewer.getId())); - SocialFeedbackResponse updateResponse = socialFeedbackService.update(createResponse.feedbackId(), reviewee.getId(), updateRequest()); + SocialFeedbackResponse createResponse = socialFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId())); + SocialFeedbackResponse updateResponse = socialFeedbackService.update(createResponse.feedbackId(), deliver.getId(), updateRequest()); assertThat(updateResponse.evaluationPoint()).isEqualTo(2); } @@ -176,36 +168,16 @@ void update() { @Test @DisplayName("없는 소셜(리뷰이->리뷰어) 피드백 내용을 업데이트시 예외를 발생한다.") void throw_exception_when_update_with_not_exist_feedback() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - reviewer, - reviewee - )); - - assertThatThrownBy(() -> socialFeedbackService.update(room.getId(), reviewer.getId(), updateRequest())) + assertThatThrownBy(() -> socialFeedbackService.update(room.getId(), receiver.getId(), updateRequest())) .isInstanceOf(CoreaException.class); } @Test @DisplayName("소셜(리뷰어 -> 리뷰이) 피드백 작성자가 아닌 사람이 업데이트시 예외를 발생한다.") void throw_exception_when_anonymous_updates_feedback() { - Member manager = memberRepository.save(MemberFixture.MEMBER_ROOM_MANAGER_JOYSON()); - Room room = roomRepository.save(RoomFixture.ROOM_DOMAIN(manager)); - Member reviewer = memberRepository.save(MemberFixture.MEMBER_PORORO()); - Member reviewee = memberRepository.save(MemberFixture.MEMBER_YOUNGSU()); - matchResultRepository.save(MatchResultFixture.MATCH_RESULT_DOMAIN( - room.getId(), - reviewer, - reviewee - )); - - SocialFeedbackResponse createResponse = socialFeedbackService.create(room.getId(), reviewee.getId(), createRequest(reviewer.getId())); + SocialFeedbackResponse createResponse = socialFeedbackService.create(room.getId(), deliver.getId(), createRequest(receiver.getId())); - assertThatThrownBy(() -> socialFeedbackService.update(createResponse.feedbackId(), reviewer.getId(), updateRequest())) + assertThatThrownBy(() -> socialFeedbackService.update(createResponse.feedbackId(), receiver.getId(), updateRequest())) .asInstanceOf(InstanceOfAssertFactories.type(CoreaException.class)) .extracting(CoreaException::getExceptionType) .isEqualTo(ExceptionType.FEEDBACK_UPDATE_AUTHORIZATION_ERROR); diff --git a/backend/src/test/java/corea/fixture/AlarmFixture.java b/backend/src/test/java/corea/fixture/AlarmFixture.java index 6daa116be..7f1f01ae1 100644 --- a/backend/src/test/java/corea/fixture/AlarmFixture.java +++ b/backend/src/test/java/corea/fixture/AlarmFixture.java @@ -1,7 +1,8 @@ package corea.fixture; -import corea.alarm.domain.UserToUserAlarm; import corea.alarm.domain.AlarmActionType; +import corea.alarm.domain.ServerToUserAlarm; +import corea.alarm.domain.UserToUserAlarm; public class AlarmFixture { public static UserToUserAlarm REVIEW_COMPLETE(long actorId, long receiverId, long interactionId) { @@ -19,4 +20,16 @@ public static UserToUserAlarm URGE_REVIEW(long actorId, long receiverId, long in public static UserToUserAlarm READ_URGE_REVIEW(long actorId, long receiverId, long interactionId) { return new UserToUserAlarm(AlarmActionType.REVIEW_URGE, actorId, receiverId, interactionId, true); } + + public static ServerToUserAlarm MATCH_COMPLETE(long receiverId, long interactionId) { + return new ServerToUserAlarm(AlarmActionType.MATCH_COMPLETE, receiverId, interactionId, false); + } + + public static ServerToUserAlarm READ_MATCH_COMPLETE(long receiverId, long interactionId) { + return new ServerToUserAlarm(AlarmActionType.MATCH_COMPLETE, receiverId, interactionId, true); + } + + public static ServerToUserAlarm MATCH_FAIL(long receiverId, long interactionId) { + return new ServerToUserAlarm(AlarmActionType.MATCH_FAIL, receiverId, interactionId, false); + } } diff --git a/backend/src/test/java/corea/scheduler/service/MatchingExecutorTest.java b/backend/src/test/java/corea/scheduler/service/MatchingExecutorTest.java index 8efe2a553..c304b9c23 100644 --- a/backend/src/test/java/corea/scheduler/service/MatchingExecutorTest.java +++ b/backend/src/test/java/corea/scheduler/service/MatchingExecutorTest.java @@ -2,6 +2,9 @@ import config.ServiceTest; import config.TestAsyncConfig; +import corea.alarm.domain.AlarmActionType; +import corea.alarm.domain.ServerToUserAlarm; +import corea.alarm.repository.ServerToUserAlarmRepository; import corea.fixture.MemberFixture; import corea.fixture.RoomFixture; import corea.matching.domain.PullRequestInfo; @@ -22,9 +25,7 @@ import corea.room.repository.RoomRepository; import corea.scheduler.domain.AutomaticMatching; import corea.scheduler.repository.AutomaticMatchingRepository; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; @@ -35,6 +36,8 @@ import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @@ -60,6 +63,9 @@ class MatchingExecutorTest { @Autowired private ParticipationRepository participationRepository; + @Autowired + private ServerToUserAlarmRepository serverToUserAlarmRepository; + @MockBean private PullRequestProvider pullRequestProvider; @@ -71,6 +77,7 @@ class MatchingExecutorTest { private Member movin; private Member ten; private Member cho; + @Autowired private RoomMatchInfoRepository roomMatchInfoRepository; @@ -84,7 +91,7 @@ void setUp() { cho = memberRepository.save(MemberFixture.MEMBER_CHOCO()); room = roomRepository.save(RoomFixture.ROOM_DOMAIN(pororo, LocalDateTime.now().plusSeconds(3))); - roomMatchInfoRepository.save(new RoomMatchInfo(room.getId(),true)); + roomMatchInfoRepository.save(new RoomMatchInfo(room.getId(), true)); emptyParticipantRoom = roomRepository.save(RoomFixture.ROOM_DOMAIN(ash, LocalDateTime.now().plusSeconds(3))); participationRepository.save(new Participation(room, pororo, MemberRole.BOTH, room.getMatchingSize())); @@ -122,6 +129,11 @@ private PullRequestInfo getPullRequestInfo(Member pororo, Member ash, Member joy )); } + @AfterEach + void tearDown() { + serverToUserAlarmRepository.deleteAll(); + } + @Test @DisplayName("매칭을 진행한다.") void match() { @@ -133,6 +145,21 @@ void match() { assertThat(matchResults).isNotEmpty(); } + @Test + @DisplayName("매칭이 완료되면 매칭 완료 알람이 생성된다.") + void matchCompleteAlarm() { + AutomaticMatching automaticMatching = automaticMatchingRepository.save(new AutomaticMatching(room.getId(), room.getRecruitmentDeadline())); + + matchingExecutor.match(automaticMatching.getRoomId()); + + List serverToUserAlarms = serverToUserAlarmRepository.findAll(); + + assertAll( + () -> assertThat(serverToUserAlarms).isNotEmpty(), + () -> assertEquals(serverToUserAlarms.get(0).getAlarmActionType(), AlarmActionType.MATCH_COMPLETE) + ); + } + @Transactional @Test @DisplayName("매칭 시도 중 예외가 발생했다면 방 상태를 FAIL로 변경한다.") @@ -143,4 +170,22 @@ void matchFail() { assertThat(emptyParticipantRoom.getStatus()).isEqualTo(RoomStatus.FAIL); } + + // 이 부분 트랜잭션 문제 같은데 정확히 모르겠어서 같이 봐주셨으면 합니당... + @Disabled + @Transactional + @Test + @DisplayName("매칭이 실패하면 매칭 실패 알람이 생성된다.") + void matchFailAlarm() { + AutomaticMatching automaticMatching = automaticMatchingRepository.save(new AutomaticMatching(emptyParticipantRoom.getId(), emptyParticipantRoom.getRecruitmentDeadline())); + + matchingExecutor.match(automaticMatching.getRoomId()); + List serverToUserAlarms = serverToUserAlarmRepository.findAll(); + + assertAll( + () -> assertThat(emptyParticipantRoom.getStatus()).isEqualTo(RoomStatus.FAIL), + () -> assertThat(serverToUserAlarms).isNotEmpty(), + () -> assertEquals(serverToUserAlarms.get(0).getAlarmActionType(), AlarmActionType.MATCH_FAIL) + ); + } }