Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#153] 실시간 알람 연결, (크루 알람,게임 알람) 생성, 수정, 삭제, 미확인 알람 확인 구현 #190

Merged
merged 78 commits into from
Nov 26, 2023
Merged
Show file tree
Hide file tree
Changes from 77 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
6e386db
feat: Crew, Game 알람 도메인 구현
jay-so Nov 24, 2023
473eb81
feat: 통합 알람 도메인 구현
jay-so Nov 19, 2023
a1660fa
feat:알람 상태, 알람 타입 Converter 구현
jay-so Nov 19, 2023
6ae6963
feat: Crew알람, Game알람, 통합 알람 리포지토리 구현
jay-so Nov 19, 2023
bc5c2e6
feat: 통합 알림 예외처리 구현
jay-so Nov 19, 2023
7de8236
refactor: Crew 알림, Game 알림 도메인 리팩토링
jay-so Nov 19, 2023
226b231
feat: 통합 알림(Crew알람,Game알림 통합) 리팩토링
jay-so Nov 19, 2023
dbbdb23
refactor: 알람 ExceptionCode 리팩토링
jay-so Nov 19, 2023
95cca67
feat: Crew 관련 알림 Event 및 핸들러 구현
jay-so Nov 19, 2023
706653a
feat: Game관련 Event 및 핸들러 구현
jay-so Nov 19, 2023
408db12
feat: 크루 관련 알람, 게임 관련 알람 비동기 처리 로직 추가
jay-so Nov 19, 2023
9fa0f5f
feat: Sse 연결 로직 및 SseEmitters 클래스 구현
jay-so Nov 20, 2023
ef138c8
style: Alarm 오타 수정
jay-so Nov 20, 2023
34de4d1
feat:CrewAlarm,GameAlarm,Alarm Repository 구현
jay-so Nov 20, 2023
04af8bd
feat: CrewAlarm,GameAlarm Response 구현
jay-so Nov 20, 2023
91c3663
feat: GameAlarmService (게임 관련 이벤트를 통한 게임 알람 생성 로직 구현)
jay-so Nov 20, 2023
77e9a07
feat:CrewAlarmService(크루 관련 이벤트를 통해, 크루 생성 알람) 구현
jay-so Nov 20, 2023
ad72030
feat:Crew 알람, Game 알람 Update DTO 구현
jay-so Nov 20, 2023
b431b60
feat: Alarm관련 에러 코드 업데이트
jay-so Nov 20, 2023
0c7e29f
style: alarm 오타 수정
jay-so Nov 20, 2023
5216572
feat: Crew,Game 알람 deleteAll 메소드. updateAlarmStatus 메소드, findAlarm 메소…
jay-so Nov 20, 2023
f380b0f
feat: Crew,Game 알람 Repository 읽음 상태 변화 메소드 구현
jay-so Nov 20, 2023
67e96b9
feat: 게임 도메인 해당 게임 host 체크 로직 추가
jay-so Nov 21, 2023
88a8ff8
feat: 게임 관련 에러 코드 업데이트
jay-so Nov 21, 2023
f264593
style: MemberRepository import문 설정 코드 컨벤션 적용
jay-so Nov 20, 2023
0bb2074
style: 코드 컨벤션에 따른 메소드 명 수정
jay-so Nov 20, 2023
8c6c3a9
refactor: Crew 알람, Game알람 updateStatus 리팩토링
jay-so Nov 20, 2023
11fadde
style: 불필요한 개행 삭제
jay-so Nov 20, 2023
165921c
feat: 통합 알림 서비스 deleteAllAlarms,updateAlarmById,checkUnReadAlarms,sub…
jay-so Nov 20, 2023
ba28f92
feat: 통합 알람 Controller sse연결, 미확인 알람 체크, 수정, 삭제 로직 구현
jay-so Nov 20, 2023
2a542ee
refactor: CrewAlarmResponse,GameAlarmResponse 직렬화 리팩토링
jay-so Nov 20, 2023
fbc0948
refactor: CrewAlarmEventHander,GameAlarmEventHandler 리팩토링
jay-so Nov 20, 2023
cdbc934
refactor: SseEmitters 관련 리팩토링
jay-so Nov 20, 2023
c9238f5
feat: 크루장 sse 실시간 알람, 크루원 sse 실시간 알람 로직 구현
jay-so Nov 20, 2023
56a2adf
refactor: AlarmTypeConverter클래스 @Converter 어노테이션 추가
jay-so Nov 20, 2023
a518f80
feat: 호스트 sse 실시간 알람, 게스트 sse 실시간 알람 로직 구현
jay-so Nov 21, 2023
b994014
chore: 패키지명 오타 수정
jay-so Nov 21, 2023
16efc0b
style: 팀 코드 컨벤션 스타일 적용
jay-so Nov 21, 2023
c085b41
fix: 순환참조 문제 해결
jay-so Nov 21, 2023
3f4ce55
feat: 통합 알람 커서 기반 페이징 구현
jay-so Nov 24, 2023
fa9d715
fix: 실시간 알람 Handler 비동기 상황 시 발생되는오류 수정
jay-so Nov 21, 2023
79473ac
feat: 통합 알람 커서 기반 페이징 처리
jay-so Nov 21, 2023
6dabcc0
style: 불필요한 주석 삭제
jay-so Nov 22, 2023
e95316c
refactor: 이벤트 발생 시 해당 크루, 해당 게임을 통해 호스트 정보를 가져오도록 리팩토링
jay-so Nov 22, 2023
576a5fe
style: 불필요한 주석 제거
jay-so Nov 22, 2023
543ff01
style: 코드 컨벤션 수정
jay-so Nov 22, 2023
f5b2f45
refactor: 통합 알림 타입을 크루 알림, 게임 알림 타입 각각으로 구분
jay-so Nov 22, 2023
c5609bd
style: 불필요한 주석 제거
jay-so Nov 22, 2023
1473d74
feat: Crew 알람 수정 기능 구현
jay-so Nov 22, 2023
2c39923
feat:게임 알람 수정 기능 구현
jay-so Nov 22, 2023
704f6d6
refactor: 게임 관련 알람 생성, 수정, 삭제 구현 & 실시간 게임 관련 알람 응답 메시지 리팩토링
jay-so Nov 22, 2023
f0e490a
refactor: 크루 알람 생성, 수정, 삭제 부분 구현 & 실시간 크루 관련 알람 응답 메시지 리팩토링
jay-so Nov 22, 2023
1d26c29
refactor: 실시간 알람 연결 및 해당 사용자의 모든 알람 여부 확인, 삭제 리팩토링 및 불필요한 주석 제거
jay-so Nov 22, 2023
b97e336
refactor: 크루 알람, 게임 알람 리포지토리 리팩토링
jay-so Nov 22, 2023
146eb30
style: AlarmController 불필요한 주석 제거
jay-so Nov 22, 2023
ddf1089
feat: AlarmExistStatus Response DTO 구현
jay-so Nov 23, 2023
a5ffa15
chore: http 내 토큰 제거
jay-so Nov 23, 2023
f75859a
style: api명 복수형으로 코드 컨벤션 수정
jay-so Nov 23, 2023
2edb980
refactor: 커서 페이징 관련 Response 클래스 및 관련 내용 삭제
jay-so Nov 23, 2023
ef5fb41
style: 정적 팩토리 네이밍 팀 컨벤션 적용
jay-so Nov 23, 2023
9aa923a
style: 에러 코드, 불필요한 개행 수정
jay-so Nov 23, 2023
d6ad7d5
refactor: thread pool 설정 리팩토링
jay-so Nov 24, 2023
a76d0bd
style: 크루 알람, 게임알람 응답 알람 id명을 명확하게 하기 위한 crewAlarmId,gameAlarmId로 수정
jay-so Nov 24, 2023
5a25655
refactor: Game 알람 이벤트 lombok을 사용하여 @Getter,@Builder을 사용하게 리팩토링
jay-so Nov 24, 2023
0f301a3
refactor: Crew 이벤트 lombok을 사용하여 @Getter,@Builder을 사용하게 리팩토링
jay-so Nov 24, 2023
167d198
refactor: CrewEvent 하나로 통일하도록 리팩토링
jay-so Nov 24, 2023
22160dd
refactor: GameEvent 하나로 통일하도록 리팩토링
jay-so Nov 24, 2023
6970b6d
refactor: SseEmitters를 SseEmitterRepository,SseEmitterRepositoryImpl …
jay-so Nov 24, 2023
2e48511
refactor: GameAlarmService,CrewAlarmService 누락된 @Transactional 어노테이션 …
jay-so Nov 24, 2023
cd53999
refactor: CrewAlarmService,GameAlarmService에서의 SseMessage및 생성 부분 SseE…
jay-so Nov 24, 2023
9fb98ab
fix: Game Event 동일 타입으로 인식으로 인한 오류 수정
jay-so Nov 24, 2023
ff6d07e
fix: Crew Event 동일 타입으로 인식으로 인한 오류 수정
jay-so Nov 24, 2023
e9b7a2b
fix: 서버 측 SseEmitter 인스턴스 timout 시간 최대 설정
jay-so Nov 24, 2023
bf364ef
refactor:SseEmitterLocalRepository 클래스 명 변경 및 Repository 내 notify 메소드 삭제
jay-so Nov 25, 2023
99c3c19
refactor: SseEmitterService 리팩토링 및 불필요한 메소드 삭제
jay-so Nov 25, 2023
7b60bf2
refactor: SseEmitterLocalRepsoitory 리팩토링(eventCache 변수 네이밍 변경 ,findEm…
jay-so Nov 25, 2023
877c80d
refactor: SseEmitterService 리팩토링( notify메소드 삭제, member 도메인 의존성 삭제, 이벤…
jay-so Nov 25, 2023
b0bdc91
fix: 크루 거절 알람, 게임 거절 알람 트랜잭션 문제로 인한 크루 알람, 게임 알람 이벤트 핸들러 리팩토링
jay-so Nov 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/main/http/alram/alarm.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
### 해당 사용자 알람 sse 연결
GET http://localhost:8080/alarms/subscribe
Content-Type: text/event-stream
Authorization:

### 해당 사용자 읽지 않은 알림 확인
GET http://localhost:8080/alarms/unread
Authorization:
Content-Type: application/json

### 해당 사용자의 모든 알람 삭제
DELETE http://localhost:8080/alarms
Authorization:
Content-Type: application/json
9 changes: 9 additions & 0 deletions src/main/http/alram/crew-alarm.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

### 해당 사용자 크루 관련 알람 상태 변경
POST http://localhost:8080/crew-alarm/{crewAlarmId}
Authorization:
Content-Type: application/json

{
"isRead": true
}
9 changes: 9 additions & 0 deletions src/main/http/alram/game-alarm.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

### 게임 관련 알람 상태 수정
POST http://localhost:8080/game-alarm/{gameAlarmId}
Authorization:
Content-Type: application/json

{
"isRead": true
}
10 changes: 5 additions & 5 deletions src/main/http/crew/crew.http
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
### 크루 생성
POST http://localhost:8080/crews
Authorization: Bearer {{token1}}
Authorization:
Content-Type: application/json

{
"name": "땅콩 크루",
"content": "안녕하세요, 땅콩 크루입니다. 백둥체육관 201호에서 진행합니다.",
"name": "밥솥 크루",
"content": "안녕하세요, 밭솥 크루입니다. 백둥체육관 201호에서 진행합니다.",
"maxMemberCount": 15,
"addressDepth1": "서울시",
"addressDepth2": "강남구"
Expand All @@ -15,8 +15,8 @@ Content-Type: application/json
GET http://localhost:8080/crews/15

### 크루가입 신청
POST http://localhost:8080/crews/15/members
Authorization: Bearer {{token2}}
POST http://localhost:8080/crews/11/members
Authorization:
Content-Type: application/json


Expand Down
2 changes: 1 addition & 1 deletion src/main/http/game/game.http
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Content-Type: application/json
Authorization:

### 게스트 모집 참여 신청 수락
PATCH http://localhost:8080/games/38/members/2
PATCH http://localhost:8080/games/2/members/3
Content-Type: application/json
Authorization:

Expand Down
60 changes: 60 additions & 0 deletions src/main/java/kr/pickple/back/alarm/config/AsynConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package kr.pickple.back.alarm.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
@EnableAsync
public class AsynConfig {

@Bean
public TaskExecutor taskExecutor() {
return CustomThreadPoolTaskExecutor.builder()
.corePoolSize(30)
.maxPoolSize(50)
.queueCapacity(70)
.build();
}
Comment on lines +16 to +20
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 매직넘버 사용 지양 원칙을 위반합니다.
이런 설정값은 보통 application.yml에 작성하고, property 객체로 받아오는게 일반적입니다.

Copy link
Member Author

@jay-so jay-so Nov 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 문제에 착각을 했었습니다! 매직넘버가 있어, 해당 부분을 설정 파일로 빼었을때, 기존 크루 거절, 게임 거절의 경우 해당 알람이 발생되지 않았었기에 해당 PR에서는 수정하지 못했었습니다.
살펴보니 제 기존 로직에 대해서 다음과 같은 문제가 있었습니다.

현재 PR의 크루 거절, 게임 멤버 거절 알람

            deleteCrewMember(crewMember);

            eventPublisher.publishEvent(CrewMemberRejectedEvent.builder()
                    .crewId(crewId)
                    .memberId(memberId)
                    .build());
            return;
        }

먼저 크루원 테이블, 게임 멤버 테이블에서 거절 시 하드 delete가 되어 알람 대상자가 미리 삭제되어 알람 발송이 불가하였습니다. 따라서 먼저 거절 시 알람을 보내고, 크루원 및 게임 멤버 테이블에 삭제되어야 했었네요.

이후의 PR에서 리팩토링한 부분

       eventPublisher.publishEvent(CrewMemberRejectedEvent.builder()
                    .crewId(crewId)
                    .memberId(memberId)
                    .build());

            deleteCrewMember(crewMember);
            return;

해당 부분을 놓치고 있었네요! 다음 PR에서 바로 수정을 했습니다!


static class CustomThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {
private CustomThreadPoolTaskExecutor(final int corePoolSize, final int maxPoolSize, final int queueCapacity) {
super();
this.setCorePoolSize(corePoolSize);
this.setMaxPoolSize(maxPoolSize);
this.setQueueCapacity(queueCapacity);
this.initialize();
}

public static Builder builder() {
return new Builder();
}

public static class Builder {
private int corePoolSize;
private int maxPoolSize;
private int queueCapacity;

public Builder corePoolSize(final int corePoolSize) {
this.corePoolSize = corePoolSize;
return this;
}

public Builder maxPoolSize(final int maxPoolSize) {
this.maxPoolSize = maxPoolSize;
return this;
}

public Builder queueCapacity(final int queueCapacity) {
this.queueCapacity = queueCapacity;
return this;
}

public CustomThreadPoolTaskExecutor build() {
return new CustomThreadPoolTaskExecutor(corePoolSize, maxPoolSize, queueCapacity);
}
}
}
hanjo8813 marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package kr.pickple.back.alarm.controller;

import kr.pickple.back.alarm.dto.response.AlarmExistStatusResponse;
import kr.pickple.back.alarm.service.AlarmService;
import kr.pickple.back.alarm.service.SseEmitterService;
import kr.pickple.back.auth.config.resolver.Login;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import static org.springframework.http.HttpStatus.NO_CONTENT;
import static org.springframework.http.HttpStatus.OK;

@RestController
@RequiredArgsConstructor
@RequestMapping("/alarms")
public class AlarmController {

private final AlarmService alarmService;
private final SseEmitterService sseEmitterService;

@GetMapping(value = "/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public ResponseEntity<SseEmitter> subscribeToSse(
@Login final Long loggedInMemberId
) {
final SseEmitter emitter = alarmService.subscribeToSse(loggedInMemberId);
sseEmitterService.sendCachedEventToUser(loggedInMemberId, emitter);

return ResponseEntity.status(OK)
.header("X-Accel-Buffering", "no")
.body(emitter);
Comment on lines +34 to +36
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nginx 설정 중 프록시 버퍼링 옵션을 off로 바꾸면되지 않나요?

Copy link
Member Author

@jay-so jay-so Nov 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분을 정말 정말 많이 고민했었습니다.

  • SSE 통신에서 서버에서 동적으로 생성된 컨텐츠를 보내주기 때문에 때문에 본문의 크기를 미리 알 수 없기에 기본적으로 응답에 Transfer-Encoding: chunked를 사용하는데요.
  • Nginx는 서버의 응답을 버퍼에 저장해두었다가 버퍼가 차거나 서버가 응답 데이터를 모두 보내면 클라이언트로 전송하게 됩니다. - 이에 버퍼링 기능을 활성화하면 SSE 통신 시 원하는대로 동작하지 않거나 실시간성이 떨어지게 되어 SSE 응답에 대해서는 proxy buffering 설정을 비활성화 해주는 것이 좋지만 다음의 문제가 발생합니다.

해당 방식으로 인한 발생하는 또다른 문제: Nginx의 설정 파일에서 버퍼링을 비활성화하면 다른 모든 API 응답에 대해서도 버퍼링을 하지 않기 때문에 비효율적이기 때문이라는 문제점이 발생됩니다.

  • 따라서 nginx의 X-accel 기능을 활용하면 좋다고 합니다.
  • 백엔드의 응답 헤더에 X-accel로 시작하는 헤더가 있으면 Nginx가 이 정보를 이용해 내부적인 처리를 따로 하도록 만들 수 있도록 하는 방식이 SSE 응답만 버퍼링을 하지 않도록 설정할 수 있다고 합니다.
  • 따라서 헤더에다가 X-accel 기능을 사용하였습니다.

참고자료: 우테코 SSE 연결 (https://tecoble.techcourse.co.kr/post/2022-10-11-server-sent-events/)

}

@GetMapping("/unread")
public ResponseEntity<AlarmExistStatusResponse> findUnreadAlarm(
@Login final Long loggedInMemberId
) {
AlarmExistStatusResponse response = alarmService.checkUnReadAlarms(loggedInMemberId);

return ResponseEntity
.status(OK)
.body(response);
}

@DeleteMapping
public ResponseEntity<Void> deleteAllAlarms(
@Login final Long loggedInMemberId
) {
alarmService.deleteAllAlarms(loggedInMemberId);

return ResponseEntity.status(NO_CONTENT)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package kr.pickple.back.alarm.controller;

import jakarta.validation.Valid;
import kr.pickple.back.alarm.dto.request.CrewAlarmUpdateStatusRequest;
import kr.pickple.back.alarm.service.CrewAlarmService;
import kr.pickple.back.auth.config.resolver.Login;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import static org.springframework.http.HttpStatus.NO_CONTENT;

@RestController
@RequiredArgsConstructor
@RequestMapping("/crew-alarms")
public class CrewAlarmController {

private final CrewAlarmService crewAlarmService;

@PatchMapping("/{crewAlarmId}")
public ResponseEntity<Void> updateCrewAlarmStatus(
@Login final Long loggedInMemberId,
@PathVariable final Long crewAlarmId,
@Valid @RequestBody final CrewAlarmUpdateStatusRequest crewAlarmUpdateStatusRequest
hanjo8813 marked this conversation as resolved.
Show resolved Hide resolved
) {
crewAlarmService.updateCrewAlarmById(loggedInMemberId, crewAlarmId, crewAlarmUpdateStatusRequest);

return ResponseEntity.status(NO_CONTENT)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package kr.pickple.back.alarm.controller;

import jakarta.validation.Valid;
import kr.pickple.back.alarm.dto.request.GameAlarmUpdateStatusRequest;
import kr.pickple.back.alarm.service.GameAlarmService;
import kr.pickple.back.auth.config.resolver.Login;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import static org.springframework.http.HttpStatus.NO_CONTENT;

@RestController
@RequiredArgsConstructor
@RequestMapping("/game-alarms")
public class GameAlarmController {

private final GameAlarmService gameAlarmService;

@PatchMapping("/{gameAlarmId}")
public ResponseEntity<Void> updateGameAlarmStatus(
@Login final Long loggedInMemberId,
@PathVariable final Long gameAlarmId,
@Valid @RequestBody final GameAlarmUpdateStatusRequest gameAlarmUpdateStatusRequest
) {
gameAlarmService.updateGameAlarmById(loggedInMemberId, gameAlarmId, gameAlarmUpdateStatusRequest);

return ResponseEntity.status(NO_CONTENT)
.build();
}
}
42 changes: 42 additions & 0 deletions src/main/java/kr/pickple/back/alarm/domain/AlarmExistsStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package kr.pickple.back.alarm.domain;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import kr.pickple.back.crew.exception.CrewException;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static kr.pickple.back.alarm.exception.AlarmExceptionCode.ALARM_EXISTS_STATUS_NOT_FOUND;

@Getter
@RequiredArgsConstructor
public enum AlarmExistsStatus {

EXISTS("읽지 않은 알람이 있음", true),
NOT_EXISTS("읽지 않은 알람이 없음", false);

private static final Map<String, AlarmExistsStatus> alarmExistsStatusMap = Collections.unmodifiableMap(Stream.of(values())
.collect(Collectors.toMap(AlarmExistsStatus::getDescription, Function.identity())));

private final String description;
private final Boolean booleanValue;

@JsonCreator
public static AlarmExistsStatus from(final String description) {
if (alarmExistsStatusMap.containsKey(description)) {
return alarmExistsStatusMap.get(description);
}
throw new CrewException(ALARM_EXISTS_STATUS_NOT_FOUND, description);
}

@JsonValue
public Boolean getBooleanValue() {
return booleanValue;
}
}
42 changes: 42 additions & 0 deletions src/main/java/kr/pickple/back/alarm/domain/AlarmStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package kr.pickple.back.alarm.domain;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import kr.pickple.back.crew.exception.CrewException;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static kr.pickple.back.alarm.exception.AlarmExceptionCode.ALARM_STATUS_NOT_FOUND;

@Getter
@RequiredArgsConstructor
public enum AlarmStatus {

TRUE("읽음", true),
FALSE("읽지 않음", false);

private static final Map<Boolean, AlarmStatus> alarmStatusMap = Collections.unmodifiableMap(Stream.of(values())
.collect(Collectors.toMap(AlarmStatus::getBooleanValue, Function.identity())));

private final String description;
private final Boolean booleanValue;

@JsonCreator
public static AlarmStatus from(final Boolean booleanValue) {
if (alarmStatusMap.containsKey(booleanValue)) {
return alarmStatusMap.get(booleanValue);
}
throw new CrewException(ALARM_STATUS_NOT_FOUND, booleanValue);
}

@JsonValue
public Boolean getBooleanValue() {
return booleanValue;
}
}
58 changes: 58 additions & 0 deletions src/main/java/kr/pickple/back/alarm/domain/CrewAlarm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package kr.pickple.back.alarm.domain;

import jakarta.persistence.*;
import jakarta.validation.constraints.NotNull;
import kr.pickple.back.alarm.util.AlarmStatusConverter;
import kr.pickple.back.alarm.util.CrewAlarmTypeConverter;
import kr.pickple.back.common.domain.BaseEntity;
import kr.pickple.back.crew.domain.Crew;
import kr.pickple.back.member.domain.Member;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import static kr.pickple.back.alarm.domain.AlarmStatus.FALSE;

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class CrewAlarm extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@NotNull
@Column(length = 10)
@Convert(converter = AlarmStatusConverter.class)
private AlarmStatus isRead = FALSE;

@NotNull
@Column(length = 30)
@Convert(converter = CrewAlarmTypeConverter.class)
private CrewAlarmType crewAlarmType;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "crew_id")
private Crew crew;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;

@Builder
private CrewAlarm(
final CrewAlarmType crewAlarmType,
final Crew crew,
final Member member
) {
this.crewAlarmType = crewAlarmType;
this.crew = crew;
this.member = member;
}

public void updateStatus(final AlarmStatus status) {
this.isRead = status;
}
}
Loading