Skip to content

Commit

Permalink
Merge pull request #71 from dev-hooon/feat/#69/notification-service
Browse files Browse the repository at this point in the history
feat : 알림 서비스 로직 구현(Slack API)
  • Loading branch information
dlswns2480 authored Jan 8, 2024
2 parents 3c1394e + 465c0a2 commit f017312
Show file tree
Hide file tree
Showing 10 changed files with 199 additions and 2 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ dependencies {
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5'

implementation group: 'org.json', name: 'json', version: '20231013'


}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.prgrms.catchtable.common;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Getter
public enum NotificationContent {
RESERVATION_COMPLETED("예약이 완료되었습니다"),
RESERVATION_ONE_HOUR_LEFT("예약 시간 1시간 전입니다."),
RESERVATION_TIME_TO_ENTER("예약시간이 되었습니다"),
WAITING_REGISTER_COMPLETED("웨이팅 등록이 완료되었습니다"),
WAITING_RANK_THIRD("웨이팅 순서가 3번째가 되었습니다"),
WAITING_TIME_TO_ENTER("웨이팅이 끝났습니다. 입장 부탁드립니다."),
WAITING_CANCELLED_AUTOMATICALLY("웨이팅이 자동으로 취소되었습니다.");

private final String message;


}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public enum ErrorCode {
NOT_EXIST_RESERVATION("존재하지 않는 예약입니다"),
EXCEED_PEOPLE_COUNT("예약인원이 해당 시간의 남은 수용가능 인원 수를 초과했습니다."),
ALREADY_COMPLETED_RESERVATION("이미 예약 상태인 예약입니다."),
SLACK_ID_IS_WRONG("요청한 슬랙 Id를 찾을 수 없거나 잘못 되었습니다"),

CAN_NOT_COMPLETE_WAITING("입장 처리가 불가한 대기 상태입니다."),
EXISTING_MEMBER_WAITING("이미 회원이 웨이팅 중인 가게가 존재합니다."),
Expand All @@ -30,9 +31,9 @@ public enum ErrorCode {
SHOP_NOT_RUNNING("가게가 영업시간이 아닙니다."),
INTERNAL_SERVER_ERROR("내부 서버 오류입니다."),


ALREADY_EXIST_OWNER("이미 존재하는 점주입니다"),
BAD_REQUEST_EMAIL_OR_PASSWORD("이메일 혹은 비밀번호를 확인해주세요"),
BAD_REQUEST_INPUT_GENDER_TYPE("성별 타입을 양식대로 입력해주세요");

private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.prgrms.catchtable.notification.controller;

import com.prgrms.catchtable.notification.dto.request.SendMessageRequest;
import com.prgrms.catchtable.notification.service.NotificationService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class NotificationController {

private final NotificationService notificationService;

@GetMapping("/test")
public void test(@RequestBody SendMessageRequest request) {
notificationService.sendMessageToMemberAndSave(request);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public class NotificationOwner extends BaseEntity {
private Owner owner;

@Builder
public NotificationOwner(String message) {
public NotificationOwner(Owner owner, String message) {
this.owner = owner;
this.message = message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.prgrms.catchtable.notification.dto.request;

import com.prgrms.catchtable.common.NotificationContent;

public record SendMessageRequest(NotificationContent content) {

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.prgrms.catchtable.notification.repository;

import com.prgrms.catchtable.member.domain.Member;
import com.prgrms.catchtable.notification.domain.NotificationMember;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface NotificationMemberRepository extends JpaRepository<NotificationMember, Long> {

Optional<NotificationMember> findByMember(Member member);
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.prgrms.catchtable.notification.repository;

import com.prgrms.catchtable.notification.domain.NotificationOwner;
import com.prgrms.catchtable.owner.domain.Owner;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface NotificationOwnerRepository extends JpaRepository<NotificationOwner, Long> {

Optional<NotificationOwner> findByOwner(Owner owner);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package com.prgrms.catchtable.notification.service;

import static com.prgrms.catchtable.common.exception.ErrorCode.SLACK_ID_IS_WRONG;
import static org.springframework.http.HttpMethod.GET;
import static org.springframework.http.HttpMethod.POST;

import com.prgrms.catchtable.common.exception.custom.BadRequestCustomException;
import com.prgrms.catchtable.member.domain.Member;
import com.prgrms.catchtable.member.repository.MemberRepository;
import com.prgrms.catchtable.notification.domain.NotificationMember;
import com.prgrms.catchtable.notification.domain.NotificationOwner;
import com.prgrms.catchtable.notification.dto.request.SendMessageRequest;
import com.prgrms.catchtable.notification.repository.NotificationMemberRepository;
import com.prgrms.catchtable.notification.repository.NotificationOwnerRepository;
import com.prgrms.catchtable.owner.domain.Owner;
import com.prgrms.catchtable.owner.repository.OwnerRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
@RequiredArgsConstructor
@Slf4j
public class NotificationService {

@Value("${slack.token}")
private String slackToken;

private final NotificationMemberRepository notificationMemberRepository;
private final NotificationOwnerRepository notificationOwnerRepository;
private final MemberRepository memberRepository; // 추후 삭제 예정
private final OwnerRepository ownerRepository; // 추후 삭제 예정
private JSONObject jsonObject;

public void sendMessageToMemberAndSave(SendMessageRequest request) {
String url = "https://slack.com/api/chat.postMessage"; // slack 메세지를 보내도록 요청하는 Slack API
// member 예제 데이터
Member member = Member.builder()
.email("[email protected]") // 이 부분 이메일 바꿔서 하면 해당 이메일의 슬랙 개인으로 dm 보냄
.build();

String email = member.getEmail();
String message = request.content().getMessage();
String slackId = getSlackIdByEmail(email); // 이메일을 통해 사용자의 슬랙 고유 ID 추출

requestToSendMessage(slackId, message); // 알림 요청 보내는 함수 호출

NotificationMember notification = NotificationMember.builder()
.member(member)
.message(message)
.build();
memberRepository.save(member); // 추후 삭제 예정
notificationMemberRepository.save(notification); // 해당 사용자의 알림 생성 후 저장

}

public void sendMessageToOwnerAndSave(SendMessageRequest request) {
String url = "https://slack.com/api/chat.postMessage"; // slack 메세지를 보내도록 요청하는 Slack API
//Owner 예제 데이터
Owner owner = Owner.builder()
.email("[email protected]") // 이 부분 이메일 바꿔서 하면 해당 이메일의 슬랙 개인으로 dm 보냄
.build();

String email = owner.getEmail();
String message = request.content().getMessage();
String slackId = getSlackIdByEmail(email);

requestToSendMessage(slackId, message);

NotificationOwner notification = NotificationOwner.builder()
.owner(owner)
.message(message)
.build();
ownerRepository.save(owner); //추후 삭제 예정
notificationOwnerRepository.save(notification);
}

private void requestToSendMessage(String slackId, String message) {
String url = "https://slack.com/api/chat.postMessage";

// 헤더에 캐치테이블 클론 슬랙 토큰 삽입
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + slackToken);
headers.add("Content-type", "application/json; charset=utf-8");

JSONObject jsonObject = new JSONObject();
jsonObject.put("channel", slackId); // 채널 필드에 사용자의 슬랙 고유 ID
jsonObject.put("text", message); // 메세지 필드에 메세지
String body = jsonObject.toString();

HttpEntity<String> requestEntity = new HttpEntity<>(body, headers);
RestTemplate restTemplate = new RestTemplate();

ResponseEntity<String> response = restTemplate.exchange(
url,
POST,
requestEntity,
String.class
);// post로 위에서 만든 Json body 전송 요청

jsonObject = new JSONObject(response.getBody());
String result = jsonObject.get("ok").toString();

if (result.equals("false")) { // 알림 요청 보낸 후 응답의 ok필드 값이 false면 슬랙아이디가 잘못되었다는 것
throw new BadRequestCustomException(SLACK_ID_IS_WRONG);
}

// ok 필드값이 true면 알림 전송 완료 된것임
}

public String getSlackIdByEmail(String email) {
String url = "https://slack.com/api/users.lookupByEmail?email=".concat(email);

HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + slackToken);
headers.add("Content-type", "application/x-www-form-urlencoded");

RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> requestEntity = new HttpEntity<>(headers);
ResponseEntity<String> responseEntity = restTemplate.exchange(
url,
GET,
requestEntity,
String.class
);
jsonObject = new JSONObject(responseEntity.getBody());
JSONObject profile = jsonObject.getJSONObject("user");

return profile.get("id").toString();
}
}
2 changes: 2 additions & 0 deletions src/test/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,6 @@ jwt:
logging.level:
org.hibernate.SQL: debug
org.hibernate.orm.jdbc.bind: trace
slack:
token: xoxb-6433151143155-6426645660710-QosE3VDLJM62hLgSISYqLTUu

0 comments on commit f017312

Please sign in to comment.