Skip to content

Commit

Permalink
[알림] 알림 연관 기능 구현(#66) (#74)
Browse files Browse the repository at this point in the history
* feat: 알림 종류 enum 정의(#66)

알림 종류는 다음과 같다.
- 화살 수신
- 채팅 요청 수신
- 요청한 채팅 수락됨

* feat: 알림 엔티티 구성(#66)

* feat: 읽지 않은 알림 개수 조회 로직 정의(#66)

* feat: 읽지 않은 알림 개수 조회 로직 구현(#66)

* feat: 읽지 않은 알림 개수 조회 비즈니스 로직 구성(#66)

* feat: 읽지 않은 알림 개수 응답 DTO 정의(#66)

* feat: 읽지 않은 알림 개수 조회 API 구성(#66)

* feat: 읽지 않은 알림 개수 조회 메서드 파라미터명 수정(#66)

* feat: 알림 목록 조회 로직 정의(#66)

* feat: 알림 목록 조회 로직 구현(#66)

* feat: 알림 목록 응답 DTO 정의(#66)

* feat: 알림 목록 조회 요청 DTO 정의(#66)

* feat: 알림 목록 페이지 응답 DTO 정의(#66)

* feat: 알림 목록 조회 로직 추가(#66)

* feat: 받은 알림 목록 조회 비즈니스 로직 추가(#66)

페이지 형태로 알림 목록 제공, 페이지 사이즈는 고정 9

* feat: 받은 알림 목록 조회 API 추가(#66)

query string 통해 페이지 번호를 받아 받은 알림 목록 제공

* feat: 알림 동의 내역 찾지 못함 예외 정의(#66)

* feat: 알림 동의 내역 엔티티 구성(#66)

- 알림 타입의 경우 외부에서 요청하여 생성하는 것이 아닌 코드에서만 설정
- 열거형 타입으로 정의하여 코드에서 안정적으로 다룰 수 있도록 구성
- permissionStatus의 경우 @Getter 사용시 is... 네이밍으로 형성되어
  의미를 명확히 표현하지 못한다고 생각해 별도의 getter 구성

* feat: 사용자, 알림 종류 기반 알림 동의 내역 조회 로직 정의(#66)

* feat: 사용자 알림 동의 내역 조회 로직 구성(#66)

사용자의 알림 동의 내역중 주어진 알림 종류에 해당하는 내역 조회

* feat: 사용자 알림 동의 내역 조회 비즈니스 로직 구성(#66)

* feat: 사용자 알림 동의 내역 조회 응답 DTO 구성(#66)

* feat: 알림 동의 내역 조회 API 구성(#66)

* feat: 사용자 알림 동의 내역 수정 요청 DTO 수정(#66)

- 요청한 채팅 수락 시 알림 동의 여부 필드 추가
- not null 제약 조건 추가

* feat: 사용자 알림 동의 내역 수정 비즈니스 로직 구성(#66)

더티 체킹 활용, 사용자의 각 종류별 알림 동의 내역의 동의 상태 업데이트

* feat: 알림 동의 내역, 동의 상태 수정 메서드 추가(#66)

* feat: 사용자 알림 동의 내역 수정 API 구성(#66)

* feat: fcm 사용 설정 추가(#66)

* feat: fcm 이용, 알림 전송 로직 추가(#66)

알림 제목, 내용외 부가적인 데이터로 다음과 같은 것들이 포함된다.
- 알림 생성 사용자 ID(화살 보낸 사용자, 채팅 요청한 사용자 등)
- 알림 생성 일시

* feat: 알림 전송 이벤트 DTO 정의(#66)

- 모든 필드가 불변이기에 레코드 활용
- 알림 제목, 내용을 제공하는 편의 메서드 구성

* feat: 알림 내역 저장 로직 추가(#66)

* feat: FCM 커스텀 예외 정의(#66)

* feat: 사용자 엔티티, device token 필드 추가(#66)

- 알림 전송을 위한 device token 필드 추가
- device token 갱신 로직 추가

* feat: 알림 타입 열거형에 알림 메시지 필드 추가(#66)

알림 타입마다 메시지 형식이 정해져 있기에 타입별로 메시지를
함께 관리할 수 있도록 메시지 필드 추가

* feat: 알림 내역 생성시, 읽음 여부 기본 false 설정(#66)

* feat: 알림 내역 저장 로직 추가(#66)

* feat: 알림 전송 이벤트 핸들링 로직 추가(#66)

알림 전송 이벤트 발생시 다음 두 작업을 수행한다.
1. FCM을 이용해 사용자 기기에 푸시 알림 전송
2. 알림 내역 저장

* chore: fcm 관련 리소스 ignore 설정(#66)

* chore: fcm admin sdk 의존성 추가(#66)

* feat: 잘못된 필드명, 변수명 수정(#71)

sentUser -> sender, receivedUser -> receiver로 수정

* feat: 화살 내역 존재 확인 메서드명 수정(#71)

엔티티 필드명 수정에 따른 변경

* feat: 화살 내역 존재 확인 메서드 수정(#71)

엔티티 필드명 수정에 다른 파라미터명, 메서드명 변경

* feat: 엔티티 필드명 수정에 따른 변경(#71)

* feat: 화살 보내기 로직, 파라미터명 수정(#71)

* feat: 읽지 않은 알림 존재 여부 응답 DTO 추가(#66)

* feat: 읽지 않은 알림 존재 여부 조회 로직 추가(#66)

* feat: 읽지 않은 알림 존재 여부 조회 로직 추가(#66)

* feat: 읽지 않은 알림 존재 확인 로직 추가(#66)

* feat: 읽지 않은 알림 존재 확인 API 추가(#66)

* feat: 푸시 알림 전송 요청시, 알림에 표시될 이미지 포함(#66)

* feat: 화살 보내기 로직, 화살 수신 이벤트 발행 코드 추가(#66)

* feat: 알림 읽음 처리 로직 추가(#66)

* feat: 사용자가 받은 알림 목록 조회 로직 추가(#66)

* feat: 사용자가 받은 알림 목록 조회 로직 추가(#66)

- 기존 DTO 형태 조회 로직 삭제
- 엔티티로 받은 알림 목록을 조회하는 로직 추가

* feat: 사용하지 않는 코드 삭제(#66)

* feat: 알림 목록 조회 응답 DTO 수정(#66)

- 알림 ID 필드 추가
- 편의성 팩토리 메서드 추가

* feat: 알림 목록 조회 페이지 응답 DTO, 팩토리 메서드 추가(#66)

* feat: 받은 알림 목록 조회시 조회되는 알림 읽음 처리 추가(#66)

- 받은 알림 목록의 조회할 경우 읽지 않은 알림은 전부 읽음 처리되어야 함
- 이에 따라 비즈니스 로직에 로직 추가

* feat: 알림 관련 API, URL에 /users 접두사 추가(#66)

사용자에 종속된 알림 목록에 접근한다는 점을 잘 표현할 수 있도록 URL 일괄 수정

* feat: device token 업데이트 요청 DTO 추가(#66)

* feat: device token 업데이트 로직 추가(#66)

* feat: device token 업데이트 API 추가(#66)

* feat: 불필요한 FCM 예외 삭제(#66)

* feat: 알림 전송 이벤트 구성 데이터 변경(#66)

* feat: device token not found 예외 추가(#66)

* feat: device token not found 예외 처리 로직 추가(#66)

* feat: 알림 전송 이벤트 변경에 따른 사용 위치 수정(#66)

* feat: 화살 보내기시 알림 발행 로직 수정(#66)

* feat: 비동기 사용 설정 정의(#66)

* feat: 채팅 관련 상수 수정, 알림 메시지 제공 메서드 추가(#66)

* feat: 사용하지 않는 응답 DTO 삭제(#66)

* feat: 필드 타입, 기본 타입으로 변경(#66)

* feat: 알림 메시지 제공 메서드 수정(#66)

알림 메시지 제공 주체 변경에 따라 수정

* feat: badge 조회 로직 추가(#66)

- ios에서 앱에 표기되는 뱃지는 사용자가 확인하지 않은 알림, 메시지 등을 의미
- 기존 읽지 않은 알림 개수를 조회하는 로직은 이를 위해 필요
- 하지만, 뱃지는 별도의 API가 아닌 푸시 알림 데이터에 포함되어야 하는 것으로 파악
- 이에 불필요한 비즈니스 로직을 삭제하고, 단순히 뱃지를 조회하여 제공하는 로직 추가

* feat: 알림 타입 상수 이름 변경에 따른 사용 위치 수정(#66)

* feat: badge 조회 로직 추가(#66)

- 알림 전송 이벤트는 생성자, 수신자, 알림 타입, 알림 생성 일시를 저장
- badge의 경우 fcm을 통해 푸시 알림을 보낼 때만 필요하므로 이벤트 데이터와 분리
- 이벤트 리스너에서 badge로 조회하고 fcm을 통해 푸시 알림 전송시 전달하도록 설계

* feat: 알림 전송 로직 변경(#66)

- Apns를 이용할 때 보다 구체화된 설정을 제공하는 ApnsConfig를 이용하도록 로직 수정
- 알림에 badge 포함, badge의 의미는 알림 수신자가 읽지 않은 알림 개수
- 알림에 이미지 포함하는 로직 삭제
- fcm 이용 도중 발생한 장애가 비즈니스 로직에까지 전파되는 형태는 부적절하다고 판단
- 다만, 외부 서비스와 관련하여 발생한 장애를 클라이언트에게 전달할 필요는 없다고 생각
- 서버 내부에서 관련 예외를 로그로 남겨, 장애 발생시 대응할 수 있는 형태로 예외 처리 구성

* feat: 불필요한 API 삭제(#66)

* feat: 알림 타입 일치 확인 메서드 추가(#66)

* feat: 알림 동의 내역 목록 조회 메서드 추가(#66)

- 불필요한 조회 메서드 삭제
- 알림 동의 내역 목록 조회 메서드 추가

* feat: 사용자 알림 동의 내역 목록 조회 로직 추가(#66)

* feat: 알림 동의 내역 연관 로직 수정(#66)

- 알림 동의 내역 조회 및 수정시 목록 단위 작업토록 변경
- 알림 동의 내역 수정시 특정 알림 타입의 내역이 존재하지 않으면 새로 생성
- 이외에는 기존 동의 내역의 동의 상태를 수정하도록 로직 변경

* feat: 알림 on/off 설정 API, http method PUT으로 변경(#66)

- 해당 API에 요청을 통해 새로운 알림 동의 내역을 생성할 수 있음
- 리소스가 존재하지 않으면 새로 생성한다는 측면에서 PUT으로 http method 변경

* feat: 불필요한 읽음 여부 필드 삭제(#66)

알림 목록 조회시 조회되는 알림들을 전부 읽음 처리하기에 읽음 여부 필드 삭제

* feat: 알림 목록(페이지) 요청 DTO 수정(#66)

- DTO 이름, 알림 목록(페이지)을 요청한다는 의미를 잘 나타내도록 수정
- 요청 바디를 통해 DTO 필드를 매핑함에 따라 생성자 및 필드 명세 수정
- 필드 타입, 기본 타입으로 수정

* feat: 알림 목록 조회 로직 변경(#66)

- 알림 목록을 조회할 시 가장 최근에 받은 알림부터 조회되어야 함
- 생성 일시 내림차순 기준으로 알림 목록을 조회하도록 로직 변경

* feat: 알림 목록 조회 로직 변경(#66)

- 최근에 받은 알림 목록을 조회한다는 의미를 잘 표현하게 메서드명 수정
- 조회 로직 변경에 따라 코드 수정

* feat: 알림 목록 조회 메서드명, 로직 수정(#66)

- 최근에 받은 알림 목록 조회한다는 의미를 표현하도록 메서드명 수정
- 조회 메서드명 변경에 따른 사용 위치 수정

* feat: 알림 목록 조회 API 수정(#66)

- 가장 최근에 받은 알림부터 조회된다는 의미를 표현할 수 있게 메서드명 수정
- 알림 목록 조회시 조회되는 알림들이 읽음 처리됨
- 해당 API가 멱등성을 보장하지 않으므로 PATCH로 http method 변경
- PATCH 사용시 요청 데이터는 request body를 통해 전달되는 방식이 권장됨
- 이에 따라 요청 DTO를 request body를 통해 생성하도록 로직 수정

* feat: 알림 동의 내역 조회 로직 추가(#66)

* feat: 알림 동의 내역 조회 로직 추가(#66)

* feat: 사용자 알림 동의 여부 확인 로직 추가(#66)

* feat: 사용자 알림 동의 여부 확인 로직 추가(#66)

특정 타입의 알림을 사용자가 동의했을 경우만 푸시 알림으로 전달하도록 로직 추가

* feat: 최근 받은 알림 목록 조회 메서드명 수정(#66)

* feat: 알림 동의 내역 업데이트 로직 수정(#66)

알림 타입, 알림 동의 상태로 구성된 Map을 활용하여 중복 로직 제거

* feat: device token null 여부 검증 코드 제거(#66)

* feat: 알림 전송 가능 확인 로직에 device token 검증 로직 추가(#66)

- 기존 코드에서는 fcm 유틸 클래스의 푸시 알림을 보내는 로직에서 device token 검증
- 해당 로직은 비동기로 처리되기 때문에 예외 처리가 여러움
- 클래스 책임면에서 해당 검증은 유틸 클래스에서 수행하는 것이 부적절하다고 판단
- 특정 사용자에게 알림 전송이 가능한 지 확인하는 로직에서 device token 존재 여부도 판단

* feat: 알림 전송 가능 여부 확인 메서드명 변경(#66)

* feat: 알림 동의 내역 저장 로직 추가(#66)

* chore: fcm account key 설정 스텝 추가(#66)

* chore: resources/fcm 디렉토리 생성 커맨드 추가(#66)

* chore: fcm account key base64 decode 과정 추가(#66)

- github actions는 json 파일을 직접 secret으로 관리하는 형태를 권장하지 않음
- json 파일을 base64 인코딩된 형태로 secret으로 관리
- actions 수행시 디코딩하여 사용하는 형식으로 수정

* chore: fcm account key 환경 변수 설정 구절 추가(#66)

* chore: FCM_ACCOUNT_KEY 환경 변수 디코딩 구절 수정(#66)

* feat: 알림 내역 저장 후 푸시 알림 전송토록 수정(#66)

* feat: 응답에 포함된 알림 생성자 이름, 닉네임으로 수정(#66)

* feat: 알림 메시지에 알림 생성자 이름이 아닌 닉네임 포함토록 수정(#66)

* chore: EOF 추가(#66)

* feat: 알림 타입 열거형 패키지 변경(#66)

- 알림 타입 열거형, notification.enums 패키지로 이동
- 사용 위치 수정

* feat: 페이지 번호 nullable하도록 변경(#66)

- 페이지 번호를 nullable하도록 변경
- 페이지 번호를 요청에 포함하지 않을 경우 기본 첫 페이지 제공

* feat: 알림 엔티티 필드명 수정, 읽음 처리 메서드 삭제(#66)

- creator 필드명, sender로 수정
- 사용하지 않는 읽음 처리 메서드 삭제

* feat: ID 기반 알림 읽음 처리 로직 정의(#66)

* feat: ID 기반 알림 읽음 처리 로직 구현(#66)

- 벌크성 수정 쿼리를 통해 특정 알림 ID보다 ID가 작거나 같은 알림들 전부 읽음 처리
- 벌크성 수정 쿼리는 영속성 컨텍스트를 무시, DB와 컨텍스트의 엔티티 상태가 달라질 수 있음
- 이를 방지하기 위해 수정 쿼리 실행 이후 영속성 컨텍스트 초기화

* feat: creator 변수명, sender로 수정(#66)

알림을 '보내는 사용자'라는 의미를 잘 표현하기 위해 변수명 수정

* feat: 첫 페이지, 마지막 페이지 여부 필드 기본 타입으로 변경(#66)

* feat: 커스텀 repository 인터페이스 상속 추가(#66)

* feat: ID 기반 알림 읽음 처리 로직 추가(#66)

* feat: 최근 받은 알림 목록 조회 로직 수정(#66)

- 한 번에 제공되는 알림 데이터 수, 45개로 조정
- 기존 로직의 경우 첫 페이지에서 조회되지 않은 알림이 계속 읽음 처리되지 않을 수 있는 오류 존재
- 알림 목록 조회시 가장 최근에 받은 알림 ID 기반, 그 이전 알림 전부 읽음 처리하도록 로직 수정

* feat: 알림 타입, 동의 여부 맵 제공 로직 추가(#66)

요청 DTO에 포함된 알림 타입별 동의 여부를 맵으로 제공할 수 있는 로직 추가

* feat: 알림 동의 내역 생성시 무조건 동의 처리(#66)

* feat: 알림 동의 내역, 리스트 단위로 저장하도록 로직 변경(#66)

* feat: 동의 처리된 알림 동의 내역 생성 로직 추가(#66)

* feat: 회원 가입시 모든 알림 허용 처리(#66)

- 회원 가입시 모든 알림 허용 처리
- 추후에 사용자가 수정할 수 있게 제공

* feat: 알림 동의 내역 업데이트 로직 수정(#66)

- 요청 DTO를 통해 알림 타입별 동의 여부를 Map 형태로 치환
- 이 Map을 기반으로 조회된 알림 내역들, 타입별로 동의 여부 업데이트

* feat: 알림 on/off 설정 API, PATCH로 수정(#66)

- 기존에는 해당 API를 통해 알림 내역이 없을 경우 생성
- 알림 내역 동의 여부를 수정만 하는 방향으로 로직 변경, 이에 따라 PUT에서 PATCH로 수정

* feat: 알림 동의 내역 조회 로직 수정(#66)

알림 동의 내역 리스트를 바탕으로 알림 타입별 동의 내역 Map을 형성하여 응답하도록
코드를 간결하게 수정

* feat: 불필요한 알림 ID 삭제(#66)

* chore: EOF 추가(#66)

* feat: 알림 읽음 처리 메서드명 변경(#66)

* feat: 알림 읽음 처리 메서드 수정(#66)

- 기존 로직은 주어진 ID보다 작거나 같은 ID를 가진 알림을 전부 읽음 처리
- 모든 사용자가 받은 알림이 읽음 처리되는 잘못된 로직
- 특정 사용자가 받은 알림만 ID를 기준으로 알림 처리할 수 있도록 로직 수정
- 메서드명 변경

* feat: 알림 읽음 처리 로직 수정(#66)

* feat: 최근 알림 목록 조회 로직 수정(#66)

- 페이지 번호가 optional해짐에 따라 요청 DTO 검증 로직 추가
- 읽음 처리 메서드명 변경에 따른 사용 위치 수정

* feat: 최근 받은 알림 목록 조회시 request body optional하게 수정(#66)

* feat: 불필요한 줄바꿈 제거(#66)

* feat: 변수명, 메서드명에서 Map 제외(#66)

* feat: 변수명에서 Map 제외(#66)

* refactor: 코드 정렬 수정(#66)

* feat: 알림 동의 내역 상세 DTO 구성(#66)

* feat: 알림 내역 업데이트 요청 DTO 변경(#66)

- 알림 내역 상세 DTO를 이용도록 변경
- 알림 내역에 초점을 두도록 클래스 이름 변경

* feat: 알림 내역 조회 응답 DTO 변경(#66)

- 알림 내역 상세 DTO를 이용하도록 변경
- 알림에 초점을 두도록 클래스 이름 변경

* feat: 알림 동의 내역 조회, 알림 동의 내역 업데이트 로직 수정(#66)

새로 구성한 알림 동의 내역 상세 DTO를 이용하여 코드를 보다 간결하게 변경

* feat: 요청 동의 여부에 null 값이 포함되는 예외 정의(#66)

* feat: 필드 검증 추가(#66)

* feat: 필드 검증 추가(#66)

* feat: 알림 동의 내역 업데이트 로직 수정(#66)

- 요청 동의 내역 리스트, null 값 포함 여부 검증 추가
- 요청 DTO, 알림 유형 필드 타입 변경에 따른 업데이트 로직 수정

* feat: 알림 on/off 설정 API, 요청 DTO 이름 변경(#66)

* feat: 요청 동의 내역에 null이 포함되는 예외, 메시지 수정(#66)

* feat: 컬렉션 요소들에 대한 검증 추가(#66)

* feat: 알림 유형 필드, 검증 수정(#66)

- 기존 정규식 검증으로도 공백 값 검증 가능
- 공백 값 요청될 시 좀 더 상세한 예외 상황 표현을 위해 @notblank 어노테이션으로 변경
  • Loading branch information
Minjae-An authored May 14, 2024
1 parent 159fb06 commit 970adeb
Show file tree
Hide file tree
Showing 39 changed files with 1,063 additions and 63 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/TestCi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ jobs:
java-version: '17'
distribution: 'adopt'

- name: Set fcm account key
env:
FCM_ACCOUNT_KEY: ${{ secrets.FCM_ACCOUNT_KEY }}
run: |
mkdir -p be/src/main/resources/fcm
touch be/src/main/resources/fcm/fcm-account-key.json
echo "$FCM_ACCOUNT_KEY" | base64 --decode > be/src/main/resources/fcm/fcm-account-key.json
- name: Set application properties
run: |
touch be/src/main/resources/application.properties
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,5 @@ gradle-app.setting

.idea
be/src/main/resources/application.properties
be/src/main/resources/fcm/*

3 changes: 3 additions & 0 deletions be/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ dependencies {
implementation 'javax.xml.bind:jaxb-api:2.3.1'
implementation 'com.sun.xml.bind:jaxb-impl:2.3.3'
implementation 'com.sun.xml.bind:jaxb-core:2.3.0.1'

// fcm
implementation 'com.google.firebase:firebase-admin:9.2.0'
}

tasks.named('bootBuildImage') {
Expand Down
9 changes: 9 additions & 0 deletions be/src/main/java/yeonba/be/arrow/service/ArrowService.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import yeonba.be.arrow.dto.response.UserArrowsResponse;
Expand All @@ -16,6 +17,8 @@
import yeonba.be.exception.ArrowException;
import yeonba.be.exception.GeneralException;
import yeonba.be.exception.UserException;
import yeonba.be.notification.enums.NotificationType;
import yeonba.be.notification.event.NotificationSendEvent;
import yeonba.be.user.entity.User;
import yeonba.be.user.repository.user.UserQuery;

Expand All @@ -26,6 +29,7 @@ public class ArrowService {
private final UserQuery userQuery;
private final ArrowCommand arrowCommand;
private final ArrowQuery arrowQuery;
private final ApplicationEventPublisher eventPublisher;

@Transactional
public boolean dailyCheck(long userId, LocalDate dailyCheckDay) {
Expand Down Expand Up @@ -100,6 +104,11 @@ public void sendArrow(long senderId, long receiverId) {

sender.minusArrow(sendArrow);
receiver.plusArrow(sendArrow);

LocalDateTime createdAt = arrowTransaction.getCreatedAt();
NotificationSendEvent notificationSendEvent =
new NotificationSendEvent(NotificationType.ARROW_RECEIVED, sender, receiver, createdAt);
eventPublisher.publishEvent(notificationSendEvent);
}

@Transactional
Expand Down
10 changes: 10 additions & 0 deletions be/src/main/java/yeonba/be/config/AsyncConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package yeonba.be.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync
public class AsyncConfig {

}
31 changes: 31 additions & 0 deletions be/src/main/java/yeonba/be/config/FcmConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package yeonba.be.config;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.FirebaseMessaging;
import java.io.FileInputStream;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;

@Configuration
public class FcmConfig {

@Value("${FCM_ACCOUNT_KEY_PATH}")
private Resource accountKey;

@Bean
public FirebaseMessaging firebaseMessaging() throws IOException {

FileInputStream serviceAccount = new FileInputStream(accountKey.getFile());

FirebaseOptions firebaseOptions = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build();

return FirebaseMessaging.getInstance(FirebaseApp.initializeApp(firebaseOptions));
}
}
25 changes: 25 additions & 0 deletions be/src/main/java/yeonba/be/exception/NotificationException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package yeonba.be.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum NotificationException implements BaseException {

NOTIFICATION_PERMISSION_NOT_FOUND(
HttpStatus.BAD_REQUEST,
"해당 알림 동의 내역이 존재하지 않습니다."),

DEVICE_TOKEN_NOT_FOUND(
HttpStatus.BAD_REQUEST,
"해당 사용자의 device token이 존재하지 않습니다. token을 먼저 등록해주세요."),

REQUEST_PERMISSIONS_CAN_NOT_CONTAIN_NULL(
HttpStatus.BAD_REQUEST,
"요청되는 동의 내역들엔 null 값이 포함될 수 없습니다.");

private final HttpStatus httpStatus;
private final String reason;
}
22 changes: 4 additions & 18 deletions be/src/main/java/yeonba/be/exception/UtilException.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package yeonba.be.exception;

import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum UtilException implements BaseException {

INVALID_JWT(
Expand All @@ -14,22 +18,4 @@ public enum UtilException implements BaseException {

private final HttpStatus httpStatus;
private final String reason;

UtilException(HttpStatus httpStatus, String reason) {

this.httpStatus = httpStatus;
this.reason = reason;
}

@Override
public HttpStatus getHttpStatus() {

return httpStatus;
}

@Override
public String getReason() {

return reason;
}
}
23 changes: 20 additions & 3 deletions be/src/main/java/yeonba/be/mypage/controller/MyPageController.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import yeonba.be.mypage.dto.request.UserAllowNotificationsRequest;
import yeonba.be.mypage.dto.request.NotificationPermissionsUpdateRequest;
import yeonba.be.mypage.dto.request.UserChangeInactiveStatusRequest;
import yeonba.be.mypage.dto.request.UserUpdateProfileRequest;
import yeonba.be.mypage.dto.request.UserUpdateUnwantedAcquaintancesRequest;
import yeonba.be.mypage.dto.response.BlockedUsersResponse;
import yeonba.be.mypage.dto.response.NotificationPermissionsResponse;
import yeonba.be.mypage.dto.response.UserProfileDetailResponse;
import yeonba.be.mypage.dto.response.UserSimpleProfileResponse;
import yeonba.be.mypage.service.AcquaintanceService;
Expand Down Expand Up @@ -96,11 +97,27 @@ public ResponseEntity<CustomResponse<Void>> updateProfile(
.body(new CustomResponse<>());
}

@Operation(summary = "알림 동의 내역(on/off) 조회", description = "알림 동의 내역을 조회할 수 있습니다.")
@ApiResponse(responseCode = "200", description = "알림 동의 내역 조회 성공")
@GetMapping("/users/notifications/permissions")
public ResponseEntity<CustomResponse<NotificationPermissionsResponse>> getNotificationPermissions(
@RequestAttribute("userId") long userId) {

NotificationPermissionsResponse response = myPageService.getNotificationPermissions(userId);

return ResponseEntity
.ok()
.body(new CustomResponse<>(response));
}

@Operation(summary = "알림 on/off 설정", description = "알림별로 on/off를 설정할 수 있습니다.")
@ApiResponse(responseCode = "200", description = "알림 on/off 설정 정상 처리")
@PatchMapping("/user/notifications")
@PatchMapping("/users/notifications/permissions")
public ResponseEntity<CustomResponse<Void>> allowNotifications(
@RequestBody UserAllowNotificationsRequest request) {
@RequestAttribute("userId") long userId,
@Valid @RequestBody NotificationPermissionsUpdateRequest request) {

myPageService.updateNotificationPermissions(userId, request);

return ResponseEntity
.ok()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package yeonba.be.mypage.dto;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import yeonba.be.notification.entity.NotificationPermission;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class NotificationPermissionDetail {

@Schema(
type = "string",
description = "알림 유형",
example = "ARROW_RECEIVED")
@Pattern(
regexp = "^(ARROW_RECEIVED|CHATTING_REQUESTED|CHATTING_REQUEST_ACCEPTED)$",
message = "알림 유형은 허용된 문자열만 가능합니다.")
@NotBlank(message = "알림 유형은 반드시 입력되어야 합니다.")
private String type;

@Schema(
type = "boolean",
description = "알림 동의 여부",
example = "true")
@JsonProperty("isPermit")
@NotNull(message = "알림 동의 여부는 반드시 입력되어야 합니다.")
private boolean permit;

public static NotificationPermissionDetail of(NotificationPermission notificationPermission) {

return new NotificationPermissionDetail(
notificationPermission.getType().name(),
notificationPermission.getPermissionStatus());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package yeonba.be.mypage.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import java.util.List;
import lombok.Getter;
import lombok.NoArgsConstructor;
import yeonba.be.mypage.dto.NotificationPermissionDetail;

@Getter
@NoArgsConstructor
public class NotificationPermissionsUpdateRequest {

@Schema(
type = "array",
description = "알림 유형별 동의 여부")
@NotEmpty(message = "알림 유형별 동의 여부는 반드시 포함되어야 합니다.")
List<@Valid NotificationPermissionDetail> permissions;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package yeonba.be.mypage.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;
import yeonba.be.mypage.dto.NotificationPermissionDetail;

@Getter
@AllArgsConstructor
public class NotificationPermissionsResponse {

@Schema(
type = "array",
description = "알림 유형별 동의 내역")
List<NotificationPermissionDetail> permissions;
}
Loading

0 comments on commit 970adeb

Please sign in to comment.