Skip to content

Commit

Permalink
Merge pull request #154 from fresh-trash-project/feature/#153-update-…
Browse files Browse the repository at this point in the history
…alarm-feature
  • Loading branch information
JadeKim042386 authored Jun 3, 2024
2 parents 5db17dc + ed21c4a commit 9123b35
Show file tree
Hide file tree
Showing 15 changed files with 109 additions and 82 deletions.
1 change: 1 addition & 0 deletions database/mariadb/initdb.d/create_table.sql
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ CREATE TABLE `alarms`
`message` varchar(255) NOT NULL,
`created_at` datetime NOT NULL,
`read_at` datetime,
`deleted_at` datetime,
PRIMARY KEY (`id`),
foreign key (`member_id`) references members (id) on delete cascade
) ENGINE = InnoDB
Expand Down
54 changes: 30 additions & 24 deletions database/mariadb/initdb.d/insert_data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -143,29 +143,35 @@ values (5, 1, 2, now()),
(19, 1, 2, now()),
(20, 1, 2, now());

insert into alarms(member_id, alarm_type, alarm_args, message, created_at, read_at)
values (1, 'CHAT', json_object('fromMemberId', 2, 'targetId', 1), '거래 완료되었습니다.', now(), null),
(1, 'CHAT', json_object('fromMemberId', 1, 'targetId', 2), '알람 테스트', now(), null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 1), 'user123님이 예약중으로 변경하였습니다.', now(), null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 1), 'user123님이 판매중으로 변경하였습니다.', now(), null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 1), 'user123님이 예약중으로 변경하였습니다.', now(), null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 1), 'user123님이 판매중으로 변경하였습니다.', now(), null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 1), '거래 완료되었습니다.', now(), null),
(1, 'TRANSACTION', json_object('fromMemberId', 4, 'targetId', 4), '예약중으로 변경하였습니다.', now(), null),
(1, 'TRANSACTION', json_object('fromMemberId', 4, 'targetId', 4), '거래 완료되었습니다.', now(), null),
insert into alarms(member_id, alarm_type, alarm_args, message, created_at, read_at, deleted_at)
values (1, 'CHAT', json_object('fromMemberId', 2, 'targetId', 1), '거래 완료되었습니다.', now(), null, null),
(1, 'CHAT', json_object('fromMemberId', 1, 'targetId', 2), '알람 테스트', now(), null, null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 1), 'user123님이 예약중으로 변경하였습니다.', now(), null, null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 1), 'user123님이 판매중으로 변경하였습니다.', now(), null, null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 1), 'user123님이 예약중으로 변경하였습니다.', now(), null, null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 1), 'user123님이 판매중으로 변경하였습니다.', now(), null, null),
(1, 'CHAT', json_object('fromMemberId', 2, 'targetId', 5), '거래 완료되었습니다.', now(), now(), null),
(1, 'CHAT', json_object('fromMemberId', 1, 'targetId', 6), '알람 테스트', now(), now(), null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 5), 'user123님이 예약중으로 변경하였습니다.', now(), now(), null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 5), 'user123님이 판매중으로 변경하였습니다.', now(), now(), null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 5), 'user123님이 예약중으로 변경하였습니다.', now(), now(), null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 5), 'user123님이 판매중으로 변경하였습니다.', now(), now(), null),
(1, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 1), '거래 완료되었습니다.', now(), null, null),
(1, 'TRANSACTION', json_object('fromMemberId', 4, 'targetId', 4), '예약중으로 변경하였습니다.', now(), null, null),
(1, 'TRANSACTION', json_object('fromMemberId', 4, 'targetId', 4), '거래 완료되었습니다.', now(), null, null),
(1, 'FLAG', json_object('fromMemberId', 1, 'targetId', 1),
'1번 신고받은 내역이 있습니다. 신고받은 횟수가 10번이상 되면 서비스를 이용하실 수 없습니다.', now(), null),
(2, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), 'abc님이 판매중으로 변경하였습니다.', now(), null),
(2, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), '판매 완료되었습니다.', now(), null),
(2, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), '판매 완료되었습니다.', now(), null),
'1번 신고받은 내역이 있습니다. 신고받은 횟수가 10번이상 되면 서비스를 이용하실 수 없습니다.', now(), null, null),
(2, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), 'abc님이 판매중으로 변경하였습니다.', now(), null, null),
(2, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), '판매 완료되었습니다.', now(), null, null),
(2, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), '판매 완료되었습니다.', now(), null, null),
(2, 'FLAG', json_object('fromMemberId', 1, 'targetId', 1),
'1번 신고받은 내역이 있습니다. 신고받은 횟수가 10번이상 되면 서비스를 이용하실 수 없습니다.', now(), null),
(2, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 2), 'user123님이 예약중으로 변경했습니다.', now(), null),
(2, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 2), '판매 완료되었습니다.', now(), null),
(2, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 1), '판매 완료되었습니다.', now(), null),
(3, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), 'abc님이 판매중으로 변경하였습니다.', now(), null),
(3, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), 'abc님이 예약중으로 변경하였습니다.', now(), null),
(3, 'TRANSACTION', json_object('fromMemberId', 2, 'targetId', 1), '판매 완료되었습니다.', now(), null),
(4, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), 'abc님이 판매중으로 변경하였습니다.', now(), null),
(5, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), 'abc님이 판매중으로 변경하였습니다.', now(), null);

'1번 신고받은 내역이 있습니다. 신고받은 횟수가 10번이상 되면 서비스를 이용하실 수 없습니다.', now(), null, null),
(2, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 2), 'user123님이 예약중으로 변경했습니다.', now(), null, null),
(2, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 2), '판매 완료되었습니다.', now(), null, null),
(2, 'TRANSACTION', json_object('fromMemberId', 3, 'targetId', 1), '판매 완료되었습니다.', now(), null, null),
(3, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), 'abc님이 판매중으로 변경하였습니다.', now(), null, null),
(3, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), 'abc님이 예약중으로 변경하였습니다.', now(), null, null),
(3, 'TRANSACTION', json_object('fromMemberId', 2, 'targetId', 1), '판매 완료되었습니다.', now(), null, null),
(4, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), 'abc님이 판매중으로 변경하였습니다.', now(), null, null),
(5, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), 'abc님이 판매중으로 변경하였습니다.', now(), null, null),
(1, 'TRANSACTION', json_object('fromMemberId', 1, 'targetId', 1), 'abc님이 판매중으로 변경하였습니다.', now(), null, now());
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.scheduling.annotation.EnableScheduling;

@ConfigurationPropertiesScan
@SpringBootApplication
@EnableScheduling
public class FreshTrashBackendApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,20 @@

@Builder
public record AlarmPayload(String message, Long wasteId, Long memberId, Long fromMemberId, AlarmType alarmType) {
public static AlarmPayload ofProductDealByBuyer(String message, ChatRoom chatRoom) {
return ofProductDeal(message, chatRoom)
public static AlarmPayload ofProductDealByBuyer(String message, ChatRoom chatRoom, AlarmType alarmType) {
return ofProductDeal(message, chatRoom, alarmType)
.memberId(chatRoom.getSellerId())
.fromMemberId(chatRoom.getBuyerId())
.build();
}

public static AlarmPayload ofProductDealBySeller(String message, ChatRoom chatRoom) {
return ofProductDeal(message, chatRoom)
public static AlarmPayload ofProductDealBySeller(String message, ChatRoom chatRoom, AlarmType alarmType) {
return ofProductDeal(message, chatRoom, alarmType)
.memberId(chatRoom.getBuyerId())
.fromMemberId(chatRoom.getSellerId())
.build();
}

private static AlarmPayloadBuilder ofProductDeal(String message, ChatRoom chatRoom) {
return AlarmPayload.builder()
.message(message)
.wasteId(chatRoom.getWasteId())
.alarmType(AlarmType.TRANSACTION);
}

public static AlarmPayload ofUserFlag(String message, Long wasteId, Long targetMemberId, Long currentMemberId) {
return AlarmPayload.builder()
.message(message)
Expand All @@ -36,4 +29,11 @@ public static AlarmPayload ofUserFlag(String message, Long wasteId, Long targetM
.alarmType(AlarmType.FLAG)
.build();
}

private static AlarmPayloadBuilder ofProductDeal(String message, ChatRoom chatRoom, AlarmType alarmType) {
return AlarmPayload.builder()
.message(message)
.wasteId(chatRoom.getWasteId())
.alarmType(alarmType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
import freshtrash.freshtrashbackend.entity.constants.AlarmType;
import lombok.Builder;

import java.time.LocalDateTime;

@Builder
public record AlarmResponse(Long id, AlarmType alarmType, AlarmArgs alarmArgs, String message) {
public record AlarmResponse(Long id, AlarmType alarmType, AlarmArgs alarmArgs, String message, LocalDateTime readAt) {
public static AlarmResponse fromEntity(Alarm alarm) {
return AlarmResponse.builder()
.id(alarm.getId())
.alarmType(alarm.getAlarmType())
.alarmArgs(alarm.getAlarmArgs())
.message(alarm.getMessage())
.readAt(alarm.getReadAt())
.build();
}
}
7 changes: 6 additions & 1 deletion src/main/java/freshtrash/freshtrashbackend/entity/Alarm.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import freshtrash.freshtrashbackend.entity.constants.AlarmType;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import lombok.*;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.Where;

import javax.persistence.*;
import java.time.LocalDateTime;
Expand All @@ -20,6 +22,8 @@
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@TypeDef(name = "json", typeClass = JsonType.class)
@SQLDelete(sql = "update alarms set deleted_at = now() where id=?")
@Where(clause = "deleted_at is NULL")
public class Alarm extends CreatedAt {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -37,9 +41,10 @@ public class Alarm extends CreatedAt {
@Column(nullable = false)
private String message;

@Setter
private LocalDateTime readAt;

private LocalDateTime deletedAt;

@ToString.Exclude
@ManyToOne(optional = false, fetch = LAZY)
@JoinColumn(name = "memberId", insertable = false, updatable = false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package freshtrash.freshtrashbackend.entity.constants;

public enum AlarmType {
CHAT,
TRANSACTION,
BIDDING,
PAY,
RECEIVE,
NOT_PAY,
FLAG
CHAT, // 채팅 알림
TRANSACTION, // 거래 상태 변경 시 알림
BOOKING_REQUEST, // 예약 요청 알림
BIDDING, // 최종 입찰자 알림
PAY, // 결제 완료 알림
RECEIVE, // 상품 수령 알림
NOT_PAY, // 미결제 알림
FLAG // 사용자 신고 알림
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

@Transactional(propagation = Propagation.SUPPORTS)
public interface AlarmRepository extends JpaRepository<Alarm, Long> {
Page<Alarm> findAllByMember_IdAndReadAtIsNull(Long memberId, Pageable pageable);
Page<Alarm> findAllByMember_Id(Long memberId, Pageable pageable);

@Query(nativeQuery = true, value = "update alarms a set a.read_at = now() where a.id = ?1")
@Query(nativeQuery = true, value = "update alarms a set a.read_at = now() where a.id = ?1 and a.read_at is null")
void updateReadAtById(Long alarmId);

boolean existsByIdAndMember_Id(Long alarmId, Long memberId);

void deleteAllInBatchByReadAtNotNullAndCreatedAtBefore(LocalDateTime localDateTime);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
import org.springframework.data.domain.Pageable;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

@Slf4j
Expand All @@ -29,18 +32,15 @@
public class AlarmService {
private static final Long SSE_TIMEOUT = TimeUnit.MINUTES.toMillis(30);
private static final String CONNECTED_ALARM_NAME = "connected";
private static final String WASTE_TRANSACTION_ALARM_NAME = "waste-transaction-alarm";
private final EmitterRepository emitterRepository;
private final AlarmRepository alarmRepository;

/**
* 전체 알람 조회
* - 읽지 않은 알람(readAt == null)만 조회
* - 읽지 않은 알람도 같이 조회
*/
public Page<AlarmResponse> getAlarms(Long memberId, Pageable pageable) {
return alarmRepository
.findAllByMember_IdAndReadAtIsNull(memberId, pageable)
.map(AlarmResponse::fromEntity);
return alarmRepository.findAllByMember_Id(memberId, pageable).map(AlarmResponse::fromEntity);
}

/**
Expand All @@ -67,7 +67,7 @@ private void receive(Long memberId, AlarmResponse alarmResponse) {
try {
sseEmitter.send(SseEmitter.event()
.id(String.valueOf(alarmResponse.id()))
.name(WASTE_TRANSACTION_ALARM_NAME)
.name(alarmResponse.alarmType().name())
.data(alarmResponse));
} catch (IOException e) {
emitterRepository.deleteByMemberId(memberId);
Expand Down Expand Up @@ -112,6 +112,19 @@ public void readAlarm(Long alarmId) {
alarmRepository.updateReadAtById(alarmId);
}

/**
* 1개월이 지난 알람 모두 삭제
* - 매달 0시에 수행
*/
@Transactional
@Scheduled(cron = "0 0 0 * * *")
public void deleteAlarms() {
log.debug("delete alarms!!");
alarmRepository.deleteAllInBatchByReadAtNotNullAndCreatedAtBefore(
LocalDateTime.now().minusMonths(1));
log.debug("successfully deleted alarms!!");
}

public boolean isOwnerOfAlarm(Long alarmId, Long memberId) {
return alarmRepository.existsByIdAndMember_Id(alarmId, memberId);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package freshtrash.freshtrashbackend.service.alarm;

import freshtrash.freshtrashbackend.entity.ChatRoom;
import freshtrash.freshtrashbackend.entity.constants.AlarmType;
import freshtrash.freshtrashbackend.entity.constants.SellStatus;
import freshtrash.freshtrashbackend.service.ChatRoomService;
import freshtrash.freshtrashbackend.service.TransactionService;
Expand Down Expand Up @@ -28,7 +29,7 @@ void publishEvent(ChatRoom bookedChatRoom) {
this.chatRoomService
.getNotClosedChatRoomsByWasteId(bookedChatRoom.getWasteId())
.forEach(chatRoom -> {
this.producer.updateSellStatus(chatRoom, message);
this.producer.updateSellStatus(chatRoom, message, AlarmType.TRANSACTION);
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package freshtrash.freshtrashbackend.service.alarm;

import freshtrash.freshtrashbackend.entity.ChatRoom;
import freshtrash.freshtrashbackend.entity.constants.AlarmType;
import freshtrash.freshtrashbackend.entity.constants.SellStatus;
import freshtrash.freshtrashbackend.service.ChatRoomService;
import freshtrash.freshtrashbackend.service.TransactionService;
Expand Down Expand Up @@ -28,7 +29,7 @@ void publishEvent(ChatRoom ongoingChatRoom) {
chatRoomService
.getNotClosedChatRoomsByWasteId(ongoingChatRoom.getWasteId())
.forEach(chatRoom -> {
this.producer.updateSellStatus(chatRoom, message);
this.producer.updateSellStatus(chatRoom, message, AlarmType.BOOKING_REQUEST);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,8 @@ public class ChatProducer {
private final MQPublisher mqPublisher;

public void occurredUserFlag(Long wasteId, Long targetMemberId, Long currentMemberId, String message) {
mqPublisher.publish(generateUserFlagEvent(wasteId, targetMemberId, currentMemberId, message));
}

private AlarmEvent generateUserFlagEvent(Long wasteId, Long targetMemberId, Long currentMemberId, String message) {
return AlarmEvent.of(
mqPublisher.publish(AlarmEvent.of(
WASTE_TRANSACTION_FLAG.getRoutingKey(),
AlarmPayload.ofUserFlag(message, wasteId, targetMemberId, currentMemberId));
AlarmPayload.ofUserFlag(message, wasteId, targetMemberId, currentMemberId)));
}
}
Loading

0 comments on commit 9123b35

Please sign in to comment.