Skip to content

Commit

Permalink
v1.0.0 배포 (#89)
Browse files Browse the repository at this point in the history
* feat: user 도메인 추가 [#1]

* chore: 설정 파일 properties에서 yml로 변경 [#1]

* chore: gitignore 수정 [#1]

* feat: OAuth 로 리다이렉트하는 uri 추가 [#1]

* feat: google oauth client 추가 [#1]

* test: 인증 redirect uri 생성 테스트 추가 [#1]

* refactor: 소셜 인증 redirect 메서드 이름 변경 [#1]

* feat: id token 가져오는 기능 추가 [#1]

* test: id token 가져오는 기능 테스트 코드 추가 [#1]

* feat: id token에서 유저 정보 가져오는 기능 추가 [#1]

* fix: json 매핑 오류 수정 [#1]

* test: id token에서 유저 정보 추출 테스트 코드 추가 [#1]

* refactor: id token 이미지 받는 기능 제거 및 id token 받는 dto 이름 변경 [#1]

* feat: id token의 정보로 유저 엔티티 만드는 기능 추가 [#1]

* test: id token 정보로 유저 엔티티 만드는 기능 테스트 코드 추가 [#1]

* feat: jwt 토큰 생성 및 파싱 기능 추가 [#1]

* test: jwtProvider 테스트 코드 추가 [#1]

* refactor: User 도메인 이름 Member로 변경 [#1]

* chore: jwt 의존성 추가 [#1]

* feat: 로그인 service 추가 [#1]

* chore: gitignore 수정 [#1]

* test: fixture 추가 및 로그인 service 테스트 코드 추가 [#1]

* test: 테스트 코드 fixture 로 변경 [#1]

* feat: 로그인 controller 추가 [#1]

* refactor: user 패키지 이름 member로 변경 [#1]

* feat: 유저 인증 인터셉터 기능 추가 [#1]

* test: 인증 헤더 파싱 테스트 코드 추가 [#1]

* feat: 예외 관련 처리 구체화 및 advice 추가 [#1]

* test: controller 테스트 코드 추가 [#1]

* refactor: member 도메인 이름 변경 [#3]

* refactor: 예외 구조 변경 [#3]

* fix: auth transaction 추가 [#1]

* feat: 로컬 이미지 저장 기능 추가 [#3]

* feat: 피드 생성 service 기능 추가 [#3]

* chore: 개발용 초기화 sql 추가 [#3]

* test: 피드 생성 service 테스트 코드 추가 [#3]

* fix: 이미지 저장 안되는 버그 수정 [#3]

* chore: gitignore 수정 [#3]

* feat: 피드 저장 controller 추가 [#3]

* test: controller 단위 테스트 용 super class 추가 및 fixture 수정 [#3]

* test: 이미지 업로드 단위 테스트 추가 [#3]

* chore: memberRepository 패키지 위치 변경 [#3]

* style: 안 쓰는 코드 제거 [#3]

* feat: 피드 수정 기능 추가 (#6)

* test: authControllerTest 단위 테스트로 변경

* feat: 피드 수정 service 추가

* test: 피드 관련 테스트 코드 추가

Closed #5

* feat: 팔로우 언팔로우 기능 추가 (#8)

* feat: 팔로우기능 service 추가

* test: 팔로우 service 테스트 코드 추가

* feat: 팔로우 controller 추가

* feat: 언팔로우 기능 service 추가

* test: 언팔로우 serivce 테스트 코드 추가

* feat: 팔로우/언팔로우 controller 추가

* test: 팔로우/언팔로우 controller 테스트 코드 추가

* feat: 피드 수정 기능 추가 (#9)

* test: authControllerTest 단위 테스트로 변경

* feat: 피드 수정 service 추가

* test: 피드 관련 테스트 코드 추가

* feat: 피드 수정 controller 추가

* test: 피드 수정 controller 테스트 코드 추가

* feat: 피드 삭제 기능 추가 (#11)

* feat: 피드 삭제 기능 추가

* test: 피드 삭제 테스트 코드 추가

* feat: 댓글 생성 기능 추가 (#13)

* feat: 댓글 생성 기능 service 추가

* test: 댓글 생성 service 테스트 코드 추가

* feat: 댓글 생성 controller 추가

* test: 댓글 생성 controller 테스트 코드 추가

* fix: 댓글 양방향 관계 추가 및 삭제 시 함께 삭제 되도록 수정 (#16)

* 피드 게시물 이름 구분 (#23)

refactor: 피드 게시물 이름 구분

* 게시글 업로드 리팩터링 (#25)

* refactor: 게시글 업로드 리팩터링

* refactor: 게시글 피드 이름 변경

* test: 테스트 코드를 위한 기능 추가

* refactor: 댓글 생성 리팩터링 (#27)

* refactor: 댓글 생성 검증 위치 이동

* refactor: 생성자 수정 및 양방향 매핑 추가

* refactor: 피드 게시글 이름 변경

* test: 테스트 깨지는 부분 disable 및 수정

* fix: 댓글 생성 공백 제거 후 내용 없으면 예외 처리

* test: 댓글 생성 테스트 코드 추가

* feat: 댓글 삭제 기능 추가 (#29)

* feat: 댓글 작성자 검증 기능 추가

* test: 댓글 작성자 검증 테스트 코드 추가

* refactor: 이름 member에서 author로 구체화

* test: 피드 게시글 이름 변경

* feat: 댓글 삭제 service 추가

* test: 댓글 삭제 service 테스트 코드 추가

* feat: 댓글 삭제 controller 추가

* test: 댓글 삭제 controller 테스트 코드 추가

* refactor: 컨트롤러 관련 리팩터링 (#31)

* test: 컨트롤러 테스트 부분 토큰 검증 추가

* refactor: 피드 게시글 이름 변경

* refactor: 필요 없는 응답 제거

* test: 테스트 코드 컨트롤러 응답 부분 수정

* refactor: 예외 구조 간단하게 변경

* modify: 댓글 생성 API URL 변경 (#33)

* modify: 변경에 맞게 댓글 생성 API url 수정

* test: 테스트 코드 수정

* refactor: 팔로우/언팔로우 리팩터링 (#35)

* refactor: 팔로우 도메인 패키지 이동

* refactor: follow 도메인 분리

* fix: mappedBy 잘못 되어있던 부분 수정

* refactor: 팔로우 검증 기능 도메인으로 이동

* test: 팔로우 테스트 코드 수정

* test: 팔로우 테스트 코드 추가

* refactor: 언팔로우 리팩터링

* test: 언팔로우 테스트 코드 수정

* modify: 팔로우 API URL 변경

* feat: 프로필 조회 기능 추가 (#37)

* feat: member에 followers 필드 추가

* refactor: nickname 이름 수정

* feat: 유저 프로필 조회 service 추가

* test: member 도메인 테스트 추가

* test: 프로필 조회 테스트 코드 추가

* feat: 프로필 조회 controller 추가

* test: profile 조회 테스트 코드 추가

* feat: 좋아요 추가/취소 기능 추가 (#39)

* fix: 프로필 URL 수정

* feat: like 도메인 추가

* feat: 좋아요 serivce 기능 추가

* feat: 좋아요 테스트 코드 추가

* feat: 좋아요 controller 추가

* test: 좋아요 테스트 코드 추가

* feat: 좋아요 취소 service 추가

* test: 좋아요 취소 테스트 코드 추가

* feat: 좋아요 취소 controller 추가

* test: 좋아요 취소 테스트 코드 추가

* feat: 특정 게시글 조회 기능 추가 (#41)

* refactor: 생성 날짜 필드 이름 변경

* feat: post 도메인에 시간 엔티티 상속 추가

* feat: 게시글 조회 service 추가

* test: 게시글 조회 테스트 코드 추가

* feat: 게시글 작성자 기능 추가

* refactor: 게시글 작성자 검증 리팩터링

* style: 안 쓰는 코드 제거

* feat: 게시글 조회 controller 추가

* test: 게시글 조회 테스트 코드 추가

* feat: 피드 조회 기능 추가 (#43)

* chore: ignore 추가

* feat: 유저 피드 조회 service 추가

* test: 유저 피드 조회 테스트 코드 추가

* feat: 유저 피드 조회 controller 추가

* test: 유저 피드 조회 테스트 코드 추가

* fix: 페이징에 필요한 데이터 추가

* test: 페이징 부분 테스트 추가

* feat: 최신 피드 조회 service 추가

* test: 최신 피드 조회 테스트 코드 추가

* feat: 최신 피드 조회 controller 추가

* test: 최신 피드 조회 테스트 코드 추가

* refactor: 게시글 Command Query 분리, 작성자 서브 도메인 분리, 테스트 동등성 검증 수정 (#45)

* refactor: Author 서브 도메인 분리

* fix: 게시글 조회 API 수정

* style: 안 쓰는 코드 제거

* refactor: 게시글 queryService 분리

* test: 테스트 코드 분리

* refactor: 게시글 조회와 검증 중복 제거

* refactor: 게시글 commandService 이름 변경

* refactor: 댓글 작성자 분리

* fix: 작성자가 아닐 경우 예외 다르게 표시 되도록 수정

* refactor: 메서드 및 예외 이름 구체화

* test: 게시글 조회 테스트 전체 값 검증으로 변경

* fix: 유저 피드 게시글 최신 순서로 되도록 수정

* test: 유저 피드 조회 테스트 전체 값 검증으로 변경

* test: 최신 피드 조회 테스트 전체 값 검증으로 변경

* modify: 댓글 API url 수정 (#47)

* feat: 특정 게시글의 댓글 조회 기능 추가 (#49)

* refactor: 댓글 CommandService로 이름 변경

* style: 코드 순서 수정

* feat: 게시글 댓글 조회 service 추가

* test: 게시글 댓글 조회 테스트 코드 추가

* refactor: response dto 위치 수정

* style: 안 쓰는 코드 제거

* fix: 조회 API 생성 날짜 시간까지 반환하도록 수정

* �feat: 게시글 이미지 s3 저장 기능 추가 (#52)

* chore: aws 의존성 추가

* feat: s3 이미지 업로드 기능 추가

* refactor: 로컬 파일 저장 기능 패키지 수정

* feat: 게시글 이미지 s3 저장 기능 추가

* style: 필요 없는 코드 제거

* fix: 스프링 자동 주입을 위한 s3 키 이름 변경

* test: 테스트 깨지는 부분 수정

* test: S3PostImageStore 테스트 코드 추가

* test: s3 이미지 저장 코드 추가

* test: 게시글 등록 테스트 코드 예외 추가

* style: 안 쓰는 코드 제거

* feat: 멤버 정보 조회 기능 추가 (#54)

* refactor: memberQueryService로 이름 변경

* feat: 멤버 정보 조회 service 추가

* test: 멤버 정보 조회 테스트 코드 추가

* feat: 멤버 정보 조회 controller 추가

* test: 멤버 정보 조회 테스트 코드 추가

* feat: 유저 정보 수정 기능 추가 (#57)

* feat: 멤버 정보 수정 기능 추가

* test: fixture 추가 및 이름 수정

* test: 멤버 정보 수정 테스트 코드 추가

* refactor: imageStore 예외 리팩터링

* feat: s3프로필 저장 기능 추가

* feat: 유저 정보 수정 service 추가

* test: 유저 정보 수정 테스트 코드 추가

* test: s3프로필 저장 테스트 코드 추가

* test: 유저 정보 수정 controller 추가

* test: 유저 정보 수정 테스트 코드 추가

* test: 게시글 관련 fixture 수정

* feat: 유저 닉네임 검색 기능 추가 (#59)

* feat: 유저 닉네임으로 검색 service 추가

* test: 테스트 시 유저 초기화 하는 부분 개별 테스트로 분리

* test: 유저 닉네임 검색 테스트 코드 추가

* feat: 유저 닉네임 검색 controller 추가

* test: 유저 닉네임 검색 테스트 코드 추가

* style: 안 쓰는 코드 제거

* feat: 알람 기능 추가 (#61)

* feat: 팔로우시 발생하는 알람 도메인 추가

* feat: 팔로우 알람 이벤트 추가

* feat: 알람에 text 프로퍼티 추가

* feat: 팔로우시 event 발생시키는 기능 추가

* fix: 알람 도메인 하나로 통합 및 팔로잉 되는 유저에게 알람 가도록 수정

* chore: fcm 의존성 추가

* fix: 알람 전송 인터페이스 수정

* feat: fcm 알람 기능 추가

* feat: 유저 device 도메인 추가

* chore: gitignore 추가

* chore: fcmConfig 실행 프로필 및 테스트 설정값 추가

* feat: 비동기 thread pool 설정 추가

* fix: 쿼리 수정 및 device fetch join 대상 수정

* test: 팔로우 이벤트 테스트 코드 추가

* chore: gitignore 추가

* remove: 개발용 파일 이미지 저장 기능 제거

* test: 테스트 컴파일 되도록 수정

* test: 비동기로 인한 테스트 독립성 깨짐 수정

* feat: 게시글 작성 알람 이벤트 추가

* refactor: 공통 Infra 패키지 생성

* refactor: 알람 공통 infra 리팩터링

* refactor: 이벤트 각 도메인으로 패키지 변경

* refactor: 이벤트 전달 방식 dto로 변경

* test: 테스트 코드 수정 및 추가

* feat: 알람 조회 기능 추가 (#63)

* style: 안 쓰는 코드 제거

* refactor: 변수 이름 변경

* feat: 알람 조회 service 추가

* test: 알람 조회 테스트 코드 추가

* refactor: 이름 형식 맞춤

* feat: 알람 조회 controller 추가

* test: 알람 조회 테스트 코드 추가

* refactor: 알람 전송, 저장 분리 리팩터링 (#66)

* refactor: asyncConfigure 구현하는 방식으로 설정 변경

* refactor: transaction 제거

* feat: �알람 읽기 기능 추가 (#68)

* feat: 알람 도메인 읽기 기능 추가

* test: 알람 읽기, 받은 유저 검증 테스트 코드 추가

* refactor: 알람 받은 사람 receiver로 이름 변경

* feat: 알람 읽기 service 추가

* test: 알람 읽기 테스트 코드 추가

* feat: 알람 읽기 controller 추가

* test: 알람 읽기 테스트 코드 추가

* feat: 디바이스 토큰 추가 기능 추가 (#70)

* feat: 디바이스 토큰 추가 기능 추가

* test: 디바이스 토큰 추가 테스트 코드 추가

* chore: CI/CD 워크플로우 추가 (#71)

* chore: ci 워크플로우 추가

* chore: cd 워크플로우 추가

* chore: deploy.sh 추가

* chore: 도커 관련 파일 추가

* chore: gitignore 추가

* chore: docker 레포 이름 수정

* chore: dockerfile 실행 경로 수정

* Revert "chore: dockerfile 실행 경로 수정"

This reverts commit 0d53422.

* chore: dockerfile 위치 수정

* chore: dockerfile 수정

* fix: prod환경에서 firebasekey 외부 파일 읽도록 수정

* refactor: ManyToOne fetchType Lazy로 변경 (#74)

* refactor: ManyToOne fetchType Lazy로 수정

* chore: test osiv false 설정

* feat: request valid 추가 (#77)

* refactor: ManyToOne fetchType Lazy로 수정

* chore: test osiv false 설정

* chore: spring validation 추가

* feat: device token valid 추가

* feat: member update valid 추가

* feat: new comment valid 추가

* feat: post update valid 추가

* feat: post upload valid 추가

* feat: valid advice 추가

* fix: 보낼 메시지 없는 경우 안보내도록 수정 (#78)

fix: 보낼 메시지 없는 경우 안보내도록 수정

* refactor: 예외 관련 리팩터링 (#81)

* feat: 최상위 에러 처리 advice 추가

* refactor: 클라이언트 관련 예외 레벨 info 수준으로 변경

* fix: 보낼 메시지 없는 경우 안보내도록 수정 (#78)

fix: 보낼 메시지 없는 경우 안보내도록 수정

* refactor: 클라이언트 관련 예외 레벨 info 수준으로 변경

* feat: 최상위 예외 처리 기능 추가

* chore: develop 관련 코드 및 파일 추가 (#82)

* chore: ngrinder docker compose 파일 추가

* chore: deploy 관련 스크립트 이동

* chore: 개발용 sql 추가

* feat: develop 용 코드 추가

* refacotr: 좋아요 예외 구체적으로 변경

* refactor: 타임라인 defaultPageable 적용

* feat: health check API 추가 (#84)

* feat: heath check API 추가

* chore: deploy스크립트 health check url 수정

* fix: health check 관련 오타 및 인증 부분 수정

* refactor: 네이밍, 서브 도메인 클래스 분리 등 리팩터링 (#86)

* refactor: 패키지 이름 변경

* refactor: 생성자 접근 제한자 수정

* refactor: 회원가입 접근 제한자 수정 및 Transaction 제거

* refactor: jwtProvider 패키지 변경

* style: 코드 정리

* fix: base time entity 빠진 부분 추가

* fix: Long 타입에 not empty로 된 부분 수정

* refactor: followings casacade 변경

* refactor: 필요 없는 request 파라미터 제거

* refactor: nickname에서 username으로 변경

* style: 안 쓰는 코드 제거

* test: 테스트 꼬인 부분 수정

* refactor: response 정적 팩터리 메서드 이름 통일

* refactor: 유저 social info와 detailed info 분리

* chore: prod DB 의존성 추가 및 배포 스크립트 수정 (#88)

* chore: sql 스크립트 추가 및 수정

* refactor: member 생성시 기본 값 추가

* chore: prod db 의존성 추가

* chore: deploy 스크립트 잘못된 부분 수정

* fix: 유저 생성 안 되는 오류 수정

* chore: 배포 관련 스크립트 추가 및 수정
  • Loading branch information
wlsh44 authored May 18, 2023
1 parent eb20581 commit 8d0b664
Show file tree
Hide file tree
Showing 195 changed files with 9,298 additions and 1,702 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Sns-CD

on:
push:
branches: [ "main" ]

permissions:
contents: read

jobs:
deploy:

runs-on: ubuntu-latest

steps:
- name: Deploy application
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }}
username: ${{ secrets.USERNAME }}
script: |
echo "${{ secrets.APPLICATION_PROD }}" > /config/application-prod.yml
/script/deploy.sh
44 changes: 44 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Sns-CI

on:
pull_request:
branches: [ "main" ]

permissions:
contents: read

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'

- name: Gradle Caching
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Build with Gradle
run: ./gradlew bootJar

- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push docker image
run: |
docker build -t ${{ secrets.DOCKERHUB_USERNAME }}/sns .
docker push ${{ secrets.DOCKERHUB_USERNAME }}/sns
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,9 @@ gen
.DS_Store
src/main/resources/static
src/main/resources/application.yml
data-dev.sql
data-dev.sql
.run/
/src/main/resources/sns-project-377909-firebase-adminsdk-nza69-533f79c5dc.json

src/test/resources/application-integration-test.yml
*key.pem
7 changes: 7 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
FROM openjdk:17

ARG JAR_FILE=build/libs/*.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "/app.jar", "&"]
8 changes: 7 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.boot:spring-boot-starter-validation'

implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.4'
implementation 'com.google.firebase:firebase-admin:9.1.1'

implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

testRuntimeOnly 'com.h2database:h2'
implementation 'mysql:mysql-connector-java:8.0.32'

compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.example.sns.alarm.application;

import com.example.sns.alarm.domain.Alarm;
import com.example.sns.alarm.domain.AlarmRepository;
import com.example.sns.alarm.exception.AlarmNotFoundException;
import com.example.sns.alarm.exception.NotAlarmReceiverException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional
@RequiredArgsConstructor
public class AlarmCommandService {

private final AlarmRepository alarmRepository;

public void readAlarm(Long memberId, Long alarmId) {
Alarm alarm = alarmRepository.findById(alarmId)
.orElseThrow(() -> new AlarmNotFoundException(alarmId));
validateAlarmReceiver(memberId, alarm);

alarm.read();
}

private void validateAlarmReceiver(Long memberId, Alarm alarm) {
if (!alarm.isReceiver(memberId)) {
throw new NotAlarmReceiverException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.example.sns.alarm.application;

import com.example.sns.alarm.domain.Alarm;
import com.example.sns.alarm.domain.AlarmRepository;
import com.example.sns.alarm.presentation.dto.AlarmListResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class AlarmQueryService {

private final AlarmRepository alarmRepository;

public AlarmListResponse findAlarms(Long memberId, Pageable pageable) {
Slice<Alarm> alarmSlice = alarmRepository.findByMemberId(memberId, pageable);

return AlarmListResponse.from(alarmSlice.getContent(), alarmSlice.hasNext(), alarmSlice.getNumber());
}
}
63 changes: 63 additions & 0 deletions src/main/java/com/example/sns/alarm/domain/Alarm.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.example.sns.alarm.domain;

import com.example.sns.alarm.exception.AlreadyReadAlarmException;
import com.example.sns.common.entity.BaseTimeEntity;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

import java.util.List;

import static com.example.sns.alarm.domain.AlarmType.FOLLOW;
import static com.example.sns.alarm.domain.AlarmType.POST_UPLOAD;

@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Alarm extends BaseTimeEntity {

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

private Long memberId;

private boolean read;

private String text;

private AlarmType type;

private Alarm(Long memberId, String text, AlarmType type) {
this.memberId = memberId;
this.text = text;
this.type = type;
this.read = false;
}

public static Alarm createFollowedAlarm(Long targetId, String text) {
return new Alarm(targetId, text, FOLLOW);
}

public static List<Alarm> createPostUploadedAlarms(List<Long> targetIds, String text) {
return targetIds.stream()
.map(targetId -> new Alarm(targetId, text, POST_UPLOAD))
.toList();
}

public boolean isReceiver(Long memberId) {
return this.memberId.equals(memberId);
}

public void read() {
if (this.isRead()) {
throw new AlreadyReadAlarmException();
}
this.read = true;
}
}
11 changes: 11 additions & 0 deletions src/main/java/com/example/sns/alarm/domain/AlarmRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.sns.alarm.domain;

import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface AlarmRepository extends JpaRepository<Alarm, Long> {
Slice<Alarm> findByMemberId(Long memberId, Pageable pageable);
}
16 changes: 16 additions & 0 deletions src/main/java/com/example/sns/alarm/domain/AlarmType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.example.sns.alarm.domain;

public enum AlarmType {
FOLLOW("%s님이 회원님을 팔로우하기 시작했습니다."),
POST_UPLOAD("%s님이 게시글을 작성했습니다.");

private final String text;

AlarmType(String text) {
this.text = text;
}

public String getText(String param) {
return String.format(text, param);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.sns.alarm.exception;

import com.example.sns.common.exception.SnsException;
import org.springframework.http.HttpStatus;

public class AlarmNotFoundException extends SnsException {

public static final String ERROR_MSG = "존재하지 않는 알람입니다. id = %d";

public AlarmNotFoundException(Long alarmId) {
super(String.format(ERROR_MSG, alarmId), HttpStatus.BAD_REQUEST);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.sns.alarm.exception;

import com.example.sns.common.exception.SnsException;
import org.springframework.http.HttpStatus;

public class AlreadyReadAlarmException extends SnsException {

public static final String ERROR_MSG = "이미 읽은 알람입니다.";

public AlreadyReadAlarmException() {
super(ERROR_MSG, HttpStatus.BAD_REQUEST);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.sns.alarm.exception;

import com.example.sns.common.exception.SnsException;
import org.springframework.http.HttpStatus;

public class NotAlarmReceiverException extends SnsException {

public static final String ERROR_MSG = "알람을 받은 유저가 아닙니다.";

public NotAlarmReceiverException() {
super(ERROR_MSG, HttpStatus.BAD_REQUEST);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.example.sns.alarm.presentation;

import com.example.sns.alarm.application.AlarmCommandService;
import com.example.sns.alarm.application.AlarmQueryService;
import com.example.sns.alarm.presentation.dto.AlarmListResponse;
import com.example.sns.auth.presentation.Authenticated;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

private final AlarmQueryService alarmQueryService;
private final AlarmCommandService alarmCommandService;

@GetMapping("")
public ResponseEntity<AlarmListResponse> findAlarms(@Authenticated Long memberId, @PageableDefault Pageable pageable) {
AlarmListResponse response = alarmQueryService.findAlarms(memberId, pageable);

return ResponseEntity.ok(response);
}

@PostMapping("/{alarmId}/read")
public ResponseEntity<Void> readAlarm(@Authenticated Long memberId, @PathVariable Long alarmId) {
alarmCommandService.readAlarm(memberId, alarmId);

return ResponseEntity.ok().build();
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/example/sns/alarm/presentation/dto/AlarmDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.sns.alarm.presentation.dto;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class AlarmDto {

private final Long alarmId;
private final String text;
private final boolean read;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.example.sns.alarm.presentation.dto;

import com.example.sns.alarm.domain.Alarm;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.List;

@Getter
@RequiredArgsConstructor
public class AlarmListResponse {

private final List<AlarmDto> alarms;
private final boolean last;
private final int offset;

public static AlarmListResponse from(List<Alarm> alarms, boolean hasNext, int offset) {
List<AlarmDto> alarmDtoList = alarms.stream()
.map(alarm -> new AlarmDto(alarm.getId(), alarm.getText(), alarm.isRead()))
.toList();
return new AlarmListResponse(alarmDtoList, !hasNext, offset);
}
}
Loading

0 comments on commit 8d0b664

Please sign in to comment.