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 : 알림 서비스 로직 구현(Slack API) #71

Merged
merged 12 commits into from
Jan 8, 2024
Merged
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("dlswns661035@gmail.com") // 이 부분 이메일 바꿔서 하면 해당 이메일의 슬랙 개인으로 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("dlswns661035@gmail.com") // 이 부분 이메일 바꿔서 하면 해당 이메일의 슬랙 개인으로 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public void modifyReservationStatus(
) {
ReservationStatus modifyStatus = request.status(); // 요청으로 들어온 변경하려는 예약상태 추출

if(modifyStatus == COMPLETED){ // 취소, 노쇼 처리가 아닌 경우 예외
if (modifyStatus == COMPLETED) { // 취소, 노쇼 처리가 아닌 경우 예외
throw new BadRequestCustomException(ALREADY_COMPLETED_RESERVATION);
}

Expand Down
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