Skip to content

Commit

Permalink
Production #113
Browse files Browse the repository at this point in the history
Production
  • Loading branch information
DDonghyeo authored Jun 22, 2024
2 parents 84b02db + 0b59a86 commit 8567679
Show file tree
Hide file tree
Showing 38 changed files with 919 additions and 305 deletions.
3 changes: 3 additions & 0 deletions noti-service/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ dependencies {
//JUnit + AssertJ
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'
testImplementation 'org.assertj:assertj-core:3.23.1'

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

//openApi {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.waither.notiservice.api;

import com.waither.notiservice.api.request.LocationDto;
import com.waither.notiservice.global.annotation.AuthUser;
import com.waither.notiservice.global.response.ApiResponse;
import com.waither.notiservice.service.NotificationService;
import com.waither.notiservice.utils.RedisUtils;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
Expand All @@ -17,31 +17,32 @@ public class NotificationController {

private final NotificationService notificationService;

private final RedisUtils redisUtils;

@Operation(summary = "Get notification", description = "알림 목록 조회하기")
@GetMapping("")
public ApiResponse<?> getNotifications(Long userId) {
return ApiResponse.onSuccess(notificationService.getNotifications(userId));
public ApiResponse<?> getNotifications(@AuthUser String email) {
return ApiResponse.onSuccess(notificationService.getNotifications(email));
}

@Operation(summary = "Delete notification", description = "알림 삭제하기")
@DeleteMapping("")
public ApiResponse<?> deleteNotification(@RequestParam("id") String notificationId) {
notificationService.deleteNotification(notificationId);
public ApiResponse<?> deleteNotification(@AuthUser String email, @RequestParam("id") String notificationId) {
notificationService.deleteNotification(email, notificationId);
return ApiResponse.onSuccess(HttpStatus.OK);
}

@Operation(summary = "Send Go Out Alarm", description = "외출 알림 전송하기")
@PostMapping("/goOut")
public void sendGoOutAlarm(Long userId) {
notificationService.sendGoOutAlarm(userId);
public ApiResponse<?> sendGoOutAlarm(@AuthUser String email) {
notificationService.sendGoOutAlarm(email);
return ApiResponse.onSuccess(HttpStatus.OK);
}

@Operation(summary = "Current Location", description = "현재 위치 전송")
@PostMapping("/location")
public void checkCurrentAlarm(@RequestBody @Valid LocationDto locationDto) {
notificationService.checkCurrentAlarm(locationDto);
public ApiResponse<?> updateLocation(@AuthUser String email, @RequestBody @Valid LocationDto locationDto) {
notificationService.updateLocation(email, locationDto);
return ApiResponse.onSuccess(HttpStatus.OK);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.waither.notiservice.api;

import com.waither.notiservice.api.request.TokenDto;
import com.waither.notiservice.global.annotation.AuthUser;
import com.waither.notiservice.global.response.ApiResponse;
import com.waither.notiservice.service.AlarmService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RequestMapping("/noti")
@RestController
public class TokenContoller {

private final AlarmService alarmService;

@Operation(summary = "Firebase Token 업데이트", description = "Request Body에 발급한 FCM토큰 값을 넣어서 주시면 됩니다.")
@PostMapping("/token")
public ApiResponse<?> updateToken(@AuthUser String email, @RequestBody TokenDto tokenDto) {
alarmService.updateToken(email, tokenDto);
return ApiResponse.onSuccess("토큰 업로드가 완료되었습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
package com.waither.notiservice.api.request;

import jakarta.validation.Valid;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@AllArgsConstructor
@NoArgsConstructor
public class LocationDto {
public record LocationDto (
@NotBlank(message = " 위도(lat) 값은 필수입니다.")
@DecimalMax(value = "132.0", inclusive = true, message = "위도(lat)는 대한민국 내에서만 가능합니다.")
@DecimalMin(value = "124.0", inclusive = true, message = "위도(lat)는 대한민국 내에서만 가능합니다.")
double lat,

@NotBlank(message = " 경도(y) 값은 필수입니다.")
@DecimalMax(value = "43.0", inclusive = true, message = "경도(lon)는 대한민국 내에서만 가능합니다.")
@DecimalMin(value = "33.0", inclusive = true, message = "경도(lon)는 대한민국 내에서만 가능합니다.")
double lon
) {

@NotBlank(message = " 위도(x) 값은 필수입니다.")
@DecimalMax(value = "132.0", inclusive = true, message = "위도(x)는 대한민국 내에서만 가능합니다.")
@DecimalMin(value = "124.0", inclusive = true, message = "위도(x)는 대한민국 내에서만 가능합니다.")
public double x;

@NotBlank(message = " 경도(y) 값은 필수입니다.")
@DecimalMax(value = "43.0", inclusive = true, message = "경도(y)는 대한민국 내에서만 가능합니다.")
@DecimalMin(value = "33.0", inclusive = true, message = "경도(y)는 대한민국 내에서만 가능합니다.")
public double y;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.waither.notiservice.api.request;

public record TokenDto(
String token

) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.waither.notiservice.config;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

import java.io.FileInputStream;
import java.io.IOException;

@Configuration
@RequiredArgsConstructor
public class FirebaseConfig {

@Value("${firebase.key.path}")
private final String keyPath;

@PostConstruct
public void initializeApp() throws IOException {
FileInputStream serviceAccount =
new FileInputStream(keyPath);

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

FirebaseApp.initializeApp(options);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.waither.notiservice.config;

import com.waither.notiservice.dto.kafka.TokenDto;
import com.waither.notiservice.dto.kafka.UserMedianDto;
import com.waither.notiservice.dto.kafka.UserSettingsDto;
import com.waither.notiservice.dto.kafka.KafkaDto;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.beans.factory.annotation.Value;
Expand Down Expand Up @@ -81,52 +79,83 @@ public ConsumerFactory<String, String> stringConsumerFactory() {


@Bean("userMedianKafkaListenerContainerFactory")
public ConcurrentKafkaListenerContainerFactory<String, UserMedianDto> userMedianDtoConcurrentKafkaListenerContainerFactory(){
ConcurrentKafkaListenerContainerFactory<String, UserMedianDto> factory = new ConcurrentKafkaListenerContainerFactory<>();
public ConcurrentKafkaListenerContainerFactory<String, KafkaDto.UserMedianDto> userMedianDtoConcurrentKafkaListenerContainerFactory(){
ConcurrentKafkaListenerContainerFactory<String, KafkaDto.UserMedianDto> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(userMedianConsumerFactory());
factory.setConcurrency(3);
factory.setBatchListener(true);
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.BATCH);
return factory;
}

private ConsumerFactory<String, UserMedianDto> userMedianConsumerFactory() {
private ConsumerFactory<String, KafkaDto.UserMedianDto> userMedianConsumerFactory() {
Map<String, Object> props = dtoSettings();
return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(), new JsonDeserializer<>(UserMedianDto.class));
return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(), new JsonDeserializer<>(KafkaDto.UserMedianDto.class));
}



@Bean("firebaseTokenKafkaListenerContainerFactory")
public ConcurrentKafkaListenerContainerFactory<String, TokenDto> firebaseTokenConcurrentKafkaListenerContainerFactory(){
ConcurrentKafkaListenerContainerFactory<String, TokenDto> factory = new ConcurrentKafkaListenerContainerFactory<>();
public ConcurrentKafkaListenerContainerFactory<String, KafkaDto.TokenDto> firebaseTokenConcurrentKafkaListenerContainerFactory(){
ConcurrentKafkaListenerContainerFactory<String, KafkaDto.TokenDto> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(firebaseTokenConsumerFactory());
factory.setConcurrency(3);
factory.setBatchListener(true);
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.BATCH);
return factory;
}

private ConsumerFactory<String, TokenDto> firebaseTokenConsumerFactory() {
private ConsumerFactory<String, KafkaDto.TokenDto> firebaseTokenConsumerFactory() {
Map<String, Object> props = dtoSettings();
return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(), new JsonDeserializer<>(TokenDto.class));
return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(), new JsonDeserializer<>(KafkaDto.TokenDto.class));
}



@Bean("userSettingsKafkaListenerContainerFactory")
public ConcurrentKafkaListenerContainerFactory<String, UserSettingsDto> userSettingsConcurrentKafkaListenerContainerFactory(){
ConcurrentKafkaListenerContainerFactory<String, UserSettingsDto> factory = new ConcurrentKafkaListenerContainerFactory<>();
public ConcurrentKafkaListenerContainerFactory<String, KafkaDto.UserSettingsDto> userSettingsConcurrentKafkaListenerContainerFactory(){
ConcurrentKafkaListenerContainerFactory<String, KafkaDto.UserSettingsDto> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(userSettingsConsumerFactory());
factory.setConcurrency(3);
factory.setBatchListener(true);
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.BATCH);
return factory;
}

private ConsumerFactory<String, UserSettingsDto> userSettingsConsumerFactory() {
private ConsumerFactory<String, KafkaDto.UserSettingsDto> userSettingsConsumerFactory() {
Map<String, Object> props = dtoSettings();
return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(), new JsonDeserializer<>(UserSettingsDto.class));
return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(), new JsonDeserializer<>(KafkaDto.UserSettingsDto.class));
}


@Bean("initialDataKafkaListenerContainerFactory")
public ConcurrentKafkaListenerContainerFactory<String, KafkaDto.InitialDataDto> initialDataConcurrentKafkaListenerContainerFactory(){
ConcurrentKafkaListenerContainerFactory<String, KafkaDto.InitialDataDto> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(initialDataConsumerFactory());
factory.setConcurrency(3);
factory.setBatchListener(true);
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.BATCH);
return factory;
}

private ConsumerFactory<String, KafkaDto.InitialDataDto> initialDataConsumerFactory() {
Map<String, Object> props = dtoSettings();
return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(), new JsonDeserializer<>(KafkaDto.InitialDataDto.class));
}

@Bean("weatherKafkaListenerContainerFactory")
public ConcurrentKafkaListenerContainerFactory<String, KafkaDto.WeatherDto> weatherConcurrentKafkaListenerContainerFactory(){
ConcurrentKafkaListenerContainerFactory<String, KafkaDto.WeatherDto> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(weatherConsumerFactory());
factory.setConcurrency(3);
factory.setBatchListener(true);
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.BATCH);
return factory;
}

private ConsumerFactory<String, KafkaDto.WeatherDto> weatherConsumerFactory() {
Map<String, Object> props = dtoSettings();
return new DefaultKafkaConsumerFactory<>(props, new StringDeserializer(), new JsonDeserializer<>(KafkaDto.WeatherDto.class));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public KafkaAdmin kafkaAdmin() {
* <h2>userMedian 동기화 토픽</h2>
* @Producer : user-service
* @Consumer : noti-service
* @MessageObject : {@link com.waither.notiservice.dto.kafka.UserMedianDto}
* @MessageObject : {@link com.waither.notiservice.dto.kafka.KafkaDto.UserMedianDto}
* @Description : noti-service의 userMedian 테이블의 데이터를 동기화 하기 위해 사용합니다.
* 계절은 자동으로 계산합니다.
* <br>
Expand All @@ -48,7 +48,7 @@ public NewTopic userMedianTopic(){
* <h2>Firebase Token 동기화 토픽</h2>
* @Producer : user-service
* @Consumer : noti-service
* @MessageObject : {@link com.waither.notiservice.dto.kafka.TokenDto}
* @MessageObject : {@link com.waither.notiservice.dto.kafka.KafkaDto.TokenDto}
* @Description : noti-service의 firebase 토큰을 저장을 위해 사용됩니다.
*
*/
Expand All @@ -64,7 +64,7 @@ public NewTopic fireBaseTokenTopic(){
* <h2>User Settings 동기화 토픽</h2>
* @Producer : user-service
* @Consumer : noti-service
* @MessageObject : {@link com.waither.notiservice.dto.kafka.UserSettingsDto}
* @MessageObject : {@link com.waither.notiservice.dto.kafka.KafkaDto.UserSettingsDto}
* @Description : noti-service의 User Data 데이터 동기화를 위해 사용됩니다.
*
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
public class FireBaseToken {

@Id
private Long userId;
private String email;

private String token;
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ public class Notification extends BaseEntity {

private String content;

private Long userId;
private String email;

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.waither.notiservice.domain;

import com.waither.notiservice.domain.type.Season;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.DynamicInsert;

@Builder
Expand All @@ -16,7 +14,7 @@
public class UserData {

@Id
private Long userId;
private String email;

private String nickName;

Expand All @@ -38,6 +36,9 @@ public class UserData {
// 직장 지역 레포트 알림 받기
private boolean regionReport;

//가중치
private Double weight;

public void updateValue(String key, String value) {
switch (key) {
case "nickName" -> nickName = value;
Expand All @@ -47,6 +48,7 @@ public void updateValue(String key, String value) {
case "windAlert" -> windAlert = Boolean.parseBoolean(value);
case "regionReport" -> regionReport = Boolean.parseBoolean(value);
case "windDegree" -> windDegree = Integer.valueOf(value);
case "weight" -> weight = Double.valueOf(value);

}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.waither.notiservice.domain;

import com.waither.notiservice.domain.type.Season;
import com.waither.notiservice.enums.Season;
import com.waither.notiservice.dto.kafka.KafkaDto;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.DynamicInsert;
Expand All @@ -15,7 +16,7 @@
public class UserMedian {

@Id
private Long userId;
private String email;
private Double medianOf1And2;
private Double medianOf2And3;
private Double medianOf3And4;
Expand All @@ -24,12 +25,21 @@ public class UserMedian {
@Enumerated(value = EnumType.STRING)
public Season season;

public void setLevel(int level, double value) {
public void setLevel(int level, Double value) {
switch (level) {
case 1 -> medianOf1And2 = value;
case 2 -> medianOf2And3 = value;
case 3 -> medianOf3And4 = value;
case 4 -> medianOf4And5 = value;
}
}

public void setLevel(KafkaDto.UserMedianDto userMedianDto) {
KafkaDto.SeasonData seasonData = userMedianDto.seasonData();
medianOf1And2 = seasonData.medianOf1And2();
medianOf2And3 = seasonData.medianOf2And3();
medianOf3And4 = seasonData.medianOf3And4();
medianOf4And5 = seasonData.medianOf4And5();

}
}
Loading

0 comments on commit 8567679

Please sign in to comment.