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

feat: push notification #73

Merged
merged 5 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
4 changes: 4 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ jobs:
cd ./resources
touch ./application.yml
echo "${{ secrets.APPLICATION_YML }}" | base64 --decode >> application.yml
mkdir firebase
cd ./firebase
touch ./firebase_key.json
echo "${{ secrets.FIREBASE_KEY }}" | base64 --decode >> firebase_key.json

# λΉŒλ“œ κΆŒν•œ λΆ€μ—¬
- name: Grant execute permission for gradlew
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ out/
.vscode/

application.yml
firebase_key.json
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ dependencies {

// Thymeleaf μ„€μ •
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'

// firebase μ„€μ •
implementation 'com.google.firebase:firebase-admin:9.2.0'
}

tasks.named('test') {
Expand Down
Copy link
Member

Choose a reason for hiding this comment

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

μ €λ²ˆ 이메일 인증과 λ™μΌν•˜κ²Œ ν‘Έμ‹œμ•Œλ¦Ό λ˜ν•œ μ™ΈλΆ€μ‹œμŠ€ν…œμ„ 톡해 λ³΄λ‚΄λŠ” 것이기 λ•Œλ¬Έμ— λ„λ©”μΈμ„œλΉ„μŠ€μ— μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ •μ˜ν•˜κ³  infra μ˜μ—­μ—μ„œ PushMessageClientκ³Ό 같은 객체λ₯Ό λ§Œλ“€μ–΄ sendNotification() ν•¨μˆ˜λ₯Ό κ΅¬ν˜„ν•˜λŠ” 것도 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€!

Copy link
Member Author

Choose a reason for hiding this comment

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

λ°˜μ˜μ™„λ£Œν–ˆμŠ΅λ‹ˆλ‹€!

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.fullcar.carpool.application.Alarm;

import com.fullcar.core.exception.CustomException;
import com.fullcar.core.response.ErrorCode;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.Notification;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
public class AlarmService {

private final FirebaseMessaging firebaseMessaging;

public void sendNotification(String nickname, String deviceToken, String title, String body) {
Notification notification = Notification.builder()
.setTitle(nickname + "λ‹˜! " + title)
.setBody(body)
.build();

Message message = Message.builder()
.setToken(deviceToken)
.setNotification(notification)
.build();

try {
firebaseMessaging.send(message);
}
catch (FirebaseMessagingException e){
System.out.println(e);
throw new CustomException(ErrorCode.FAILED_TO_SEND_NOTIFICATION);
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

ν‘Έμ‹œμ•Œλ¦Όμ€ μ™ΈλΆ€μ‹œμŠ€ν…œμ„ 톡해 λ³΄λ‚΄λŠ” 것이기 λ•Œλ¬Έμ— FormService와 κ²°ν•©λœ ν˜•νƒœλ‘œ κ°œλ°œλœλ‹€λ©΄ μ™ΈλΆ€μ‹œμŠ€ν…œμ— μ˜μ‘΄ν•˜κ²Œ 될 κ°€λŠ₯성이 μžˆλ‹€κ³  μƒκ°ν•©λ‹ˆλ‹€! λ”°λΌμ„œ Spring Eventλ₯Ό ν™œμš©ν•˜μ—¬ 결합도λ₯Ό λŠμŠ¨ν•˜κ²Œ ν•˜λŠ” λ°©μ•ˆλ„ 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€!!

Copy link
Member Author

Choose a reason for hiding this comment

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

ToDo둜 이슈 νŒŒλ†“μ„κ²Œμš”!

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.fullcar.carpool.application.form;

import com.fullcar.carpool.application.Alarm.AlarmService;
import com.fullcar.carpool.domain.carpool.Carpool;
import com.fullcar.carpool.domain.carpool.CarpoolId;
import com.fullcar.carpool.domain.carpool.CarpoolRepository;
Expand Down Expand Up @@ -28,10 +29,12 @@ public class FormService {
private final CarpoolRepository carpoolRepository;
private final MemberRepository memberRepository; //TODO: Event 기반으둜 λ³€κ²½ ν•„μš”.
private final FormMapper formMapper;
private final AlarmService alarmService;

@Transactional
public FormResponseDto requestForm(Member member, CarpoolId carpoolId, FormRequestDto formRequestDto) {
Carpool carpool = carpoolRepository.findByCarpoolIdAndIsDeletedOrThrow(carpoolId, false);
Member driver = memberRepository.findByIdAndIsDeletedOrThrow(carpool.getDriver().getMemberId(), false);

if (carpool.isMyCarpool(member.getId())) {
throw new CustomException(ErrorCode.CANNOT_SEND_TO_OWN_CARPOOL);
Expand All @@ -50,6 +53,7 @@ public FormResponseDto requestForm(Member member, CarpoolId carpoolId, FormReque
}

Form form = formMapper.toEntity(member, carpoolId, formRequestDto);
alarmService.sendNotification(driver.getNickname(), driver.getDeviceToken(), "νƒ‘μŠΉ μš”μ²­μ΄ λ“€μ–΄μ™”μ–΄μš”!", "νƒ‘μŠΉμž 정보λ₯Ό ν™•μΈν•˜κ³  μŠΉμΈν•΄ μ£Όμ„Έμš”πŸš˜");

return formMapper.toDto(
formRepository.saveAndFlush(form),
Expand Down Expand Up @@ -93,12 +97,26 @@ public FormResponseDto.FormDetailDto updateForm(Member member, FormId formId, Fo
Form form = formRepository.findByFormIdAndIsDeletedOrThrow(formId, false);
form.changeFormState(formUpdateDto);

Member passenger = memberRepository.findByIdAndIsDeletedOrThrow(form.getPassenger().getMemberId(), false);
Carpool carpool = carpoolRepository.findByCarpoolIdAndIsDeletedOrThrow(form.getCarpoolId(), false);

if (!carpool.isMyCarpool(member.getId())) {
throw new CustomException(ErrorCode.CANNOT_CHANGE_FORM_STATE);
}

System.out.println(passenger.getDeviceToken());

if (formUpdateDto.getFormState() == FormState.ACCEPT) {
String title = "μΉ΄ν’€ 맀칭에 μ„±κ³΅ν–ˆμ–΄μš”!";
String body = "μš΄μ „μž 정보λ₯Ό 확인해 μ£Όμ„Έμš”πŸš˜";
alarmService.sendNotification(passenger.getNickname(), passenger.getDeviceToken(), title, body);
}
else if (formUpdateDto.getFormState() == FormState.REJECT) {
String title = "μΉ΄ν’€ 맀칭에 μ‹€νŒ¨ν–ˆμ–΄μš”.";
String body = "λ‹€λ₯Έ 카풀을 μ°Ύμ•„λ³ΌκΉŒμš”?πŸ₯Ή";
alarmService.sendNotification(passenger.getNickname(), passenger.getDeviceToken(), title, body);
}

return formMapper.toDetailDto(
formRepository.saveAndFlush(form),
memberRepository.findByIdAndIsDeletedOrThrow(
Expand All @@ -107,5 +125,4 @@ public FormResponseDto.FormDetailDto updateForm(Member member, FormId formId, Fo
)
);
}

}
1 change: 1 addition & 0 deletions src/main/java/com/fullcar/carpool/domain/form/Form.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ else if (formState == FormState.REJECT) {
throw new CustomException(ErrorCode.INVALID_FORM_STATE);
}
}

public void accept(String contact, String toPassenger) {
this.formState = FormState.ACCEPT;
this.resultMessage = ResultMessage.builder()
Expand Down
37 changes: 37 additions & 0 deletions src/main/java/com/fullcar/core/config/FirebaseConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.fullcar.core.config;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.FirebaseMessaging;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;

@Configuration
public class FirebaseConfig {

private final ClassPathResource firebaseResource = new ClassPathResource("firebase/firebase_key.json");

@Bean
FirebaseApp firebaseApp() throws IOException {
FirebaseOptions options = FirebaseOptions.builder()
.setCredentials(GoogleCredentials.fromStream(
firebaseResource.getInputStream()))
.build();

if (FirebaseApp.getApps().isEmpty()) {
return FirebaseApp.initializeApp(options);
}
else {
return FirebaseApp.getApps().get(0);
}
}

@Bean
FirebaseMessaging firebaseMessaging() throws IOException {
return FirebaseMessaging.getInstance(firebaseApp());
}
}
5 changes: 4 additions & 1 deletion src/main/java/com/fullcar/core/response/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ public enum ErrorCode {
NOT_EXIST_USER(NOT_FOUND, "μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ‚¬μš©μžμž…λ‹ˆλ‹€."),
NOT_EXIST_CARPOOL(NOT_FOUND, "μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μΉ΄ν’€μž…λ‹ˆλ‹€."),
NOT_EXIST_CAR(NOT_FOUND, "μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ°¨λŸ‰μž…λ‹ˆλ‹€."),
NOT_EXIST_FORM(NOT_FOUND, "μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ‹ μ²­μ„œμž…λ‹ˆλ‹€.");
NOT_EXIST_FORM(NOT_FOUND, "μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μ‹ μ²­μ„œμž…λ‹ˆλ‹€."),

/* 500 INTERNAL SERVER ERROR */
FAILED_TO_SEND_NOTIFICATION(INTERNAL_SERVER_ERROR, "ν‘Έμ‹œμ•Œλ¦Ό 전솑 μ‹€νŒ¨");

private final HttpStatus status;
private final String message;
Expand Down
Loading