Skip to content

Commit

Permalink
[merge]: FCM 토큰 로직 수정 및 GPT api 보완 (#169)
Browse files Browse the repository at this point in the history
  • Loading branch information
kanguk01 authored Nov 14, 2024
2 parents 06b07b5 + c3af011 commit a800384
Show file tree
Hide file tree
Showing 14 changed files with 140 additions and 84 deletions.
1 change: 0 additions & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ on:
branches:
- develop
- 'weekly/**'
- 142-refactor-cd-update

jobs:
deploy:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ splanet-db
### env ###
.env
/src/main/resources/env.properties
/src/main/resources/env.properties.base64
splanet-firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public enum ErrorCode {
// redis
REDIS_SCAN_FAILED("Redis 키 스캔 중 오류가 발생했습니다.", HttpStatus.SERVICE_UNAVAILABLE),

// fcm
TOKEN_NOT_FOUND("해당 FCM 토큰을 찾을 수 없습니다.", HttpStatus.NOT_FOUND),

// ect
INVALID_PLAN_FORMAT("", HttpStatus.BAD_REQUEST);


Expand Down
12 changes: 7 additions & 5 deletions src/main/java/com/splanet/splanet/gpt/service/GptService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.splanet.splanet.core.properties.GptProperties;
import com.splanet.splanet.plan.dto.PlanResponseDto;
import com.splanet.splanet.plan.dto.PlanTimeDto;
import com.splanet.splanet.plan.service.PlanService;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.http.ResponseEntity;
Expand All @@ -25,9 +26,9 @@ public class GptService {

private static final double RESPONSE_TEMPERATURE = 0.8;
private static final Map<Integer, String> PROMPT_TEMPLATES = Map.of(
3, "사용자 입력: \"%s\" (deviceId: %s) (groupId: %s) 기존 일정이 있다면 해당 시간과는 겹치지 않게 해 줘 기존일정:%s 현재 시간 이후로 가능한 자주 반복하여 짧고 집중적으로 일정을 완수할 수 있도록 계획을 세워줘. 시험이 포함된 경우, 시험 당일이 아닌 전날까지 준비가 완료되도록 해줘 (%s 기준). 모든 일정은 한국 시간(UTC+9)을 기준으로 설정해줘.",
2, "사용자 입력: \"%s\" (deviceId: %s) (groupId: %s) 기존 일정이 있다면 해당 시간과는 겹치지 않게 해 줘 기존일정:%s 현재 시간 이후로 적당한 간격을 두고 모든 일정을 완수할 수 있도록 계획해줘. 시험이 포함된 경우, 시험 당일이 아닌 전날까지 준비가 완료되도록 해줘 (%s 기준). 모든 일정은 한국 시간(UTC+9)을 기준으로 설정해줘.",
1, "사용자 입력: \"%s\" (deviceId: %s) (groupId: %s) 기존 일정이 있다면 해당 시간과는 겹치지 않게 해 줘 기존일정:%s 현재 시간 이후로 여유 있게 모든 일정을 완수할 수 있도록 계획해줘. 시험이 포함된 경우, 시험 당일이 아닌 전날까지 준비가 완료되도록 해줘 (%s 기준). 모든 일정은 한국 시간(UTC+9)을 기준으로 설정해줘."
3, "사용자 입력: \"%s\" (deviceId: %s) (groupId: %s) 제공된 가이드라인을 따르지 않으면 페널티가 부과될 것입니다. 모든 지침을 주의깊게 읽고 그에 따라 행동하세요. 기존 startDate와 endDate사이에 일정을 생성하지 말아줘. 기존 startDate, endDate:%s 현재 시간 이후로 가능한 자주 반복하여 짧고 집중적으로 일정을 완수할 수 있도록 계획을 세워줘. 현재 시간 : (%s 기준). 모든 일정은 한국 시간(UTC+9)을 기준으로 설정해줘. 또한, 기존 일정을 생각하고 새로운 일정을 적당한 간격을 두고 배치해줘.",
2, "사용자 입력: \"%s\" (deviceId: %s) (groupId: %s) 제공된 가이드라인을 따르지 않으면 페널티가 부과될 것입니다. 모든 지침을 주의깊게 읽고 그에 따라 행동하세요. 기존 startDate와 endDate사이에 일정을 생성하지 말아줘. 기존 startDate, endDate:%s 현재 시간 이후로 적당한 간격을 두고 모든 일정을 완수할 수 있도록 계획해줘. 현재 시간 : (%s 기준). 모든 일정은 한국 시간(UTC+9)을 기준으로 설정해줘. 또한, 기존 일정을 생각하고 새로운 일정을 적당한 간격을 두고 배치해줘.",
1, "사용자 입력: \"%s\" (deviceId: %s) (groupId: %s) 제공된 가이드라인을 따르지 않으면 페널티가 부과될 것입니다. 모든 지침을 주의깊게 읽고 그에 따라 행동하세요. 기존 startDate와 endDate사이에 일정을 생성하지 말아줘. 기존 startDate, endDate:%s 현재 시간 이후로 여유 있게 모든 일정을 완수할 수 있도록 계획해줘. 현재 시간 : (%s 기준). 모든 일정은 한국 시간(UTC+9)을 기준으로 설정해줘. 또한, 기존 일정을 생각하고 새로운 일정을 적당한 간격을 두고 배치해줘."
);

public GptService(OpenAiApi openAiApi, GptProperties gptProperties, PlanService planService, ObjectMapper objectMapper) {
Expand All @@ -39,7 +40,7 @@ public GptService(OpenAiApi openAiApi, GptProperties gptProperties, PlanService

public String generateResponse(String userInput, Long userId, String deviceId, int groupId) {
String currentTime = getCurrentTime();
List<PlanResponseDto> futurePlans = (userId != null) ? planService.getAllFuturePlansByUserId(userId) : List.of();
List<PlanTimeDto> futurePlans = (userId != null) ? planService.getAllFuturePlanTimesByUserId(userId) : List.of();
String planJson = convertPlansToJson(futurePlans);
String promptTemplate = PROMPT_TEMPLATES.get(groupId);
String fullPrompt = String.format(promptTemplate, userInput, deviceId, groupId, planJson, currentTime);
Expand All @@ -54,7 +55,7 @@ private String getCurrentTime() {
return LocalDateTime.now(ZoneId.of("Asia/Seoul")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
}

private String convertPlansToJson(List<PlanResponseDto> futurePlans) {
private String convertPlansToJson(List<PlanTimeDto> futurePlans) {
try {
return objectMapper.writeValueAsString(futurePlans);
} catch (JsonProcessingException e) {
Expand Down Expand Up @@ -85,3 +86,4 @@ private String getGptResponse(OpenAiApi.ChatCompletionRequest chatRequest) {
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,25 @@
public interface FcmTokenApi {

@PostMapping("/register")
@Operation(summary = "FCM 토큰 등록", description = "유저가 FCM 토큰을 등록합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "FCM 토큰이 성공적으로 등록되었습니다."),
@ApiResponse(responseCode = "404", description = "유저를 찾을 수 없습니다.", content = @Content)
})
ResponseEntity<String> registerFcmToken(
@AuthenticationPrincipal Long userId,
@RequestBody FcmTokenRequest fcmTokenRequest
);

@PutMapping("/update")
@Operation(summary = "FCM 토큰 설정 수정", description = "알림 설정 및 알림 오프셋을 수정합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "FCM 토큰 설정이 성공적으로 수정되었습니다."),
@ApiResponse(responseCode = "404", description = "유저를 찾을 수 없습니다.", content = @Content)
})
ResponseEntity<String> updateFcmTokenSettings(
@AuthenticationPrincipal Long userId,
@RequestBody FcmTokenUpdateRequest fcmTokenUpdateRequest
@PutMapping("/update/notification-enabled")
ResponseEntity<String> updateNotificationEnabled(
@RequestParam String token,
@RequestParam Boolean isNotificationEnabled
);

@PutMapping("/update/notification-offset")
ResponseEntity<String> updateNotificationOffset(
@RequestParam String token,
@RequestParam Integer notificationOffset
);

@DeleteMapping("/delete")
@Operation(summary = "FCM 토큰 삭제", description = "유저의 FCM 토큰을 삭제합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "FCM 토큰이 성공적으로 삭제되었습니다."),
@ApiResponse(responseCode = "404", description = "해당 토큰을 찾을 수 없습니다.", content = @Content)
})
ResponseEntity<String> deleteFcmToken(
@AuthenticationPrincipal Long userId,
@RequestParam String token
);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.splanet.splanet.notification.controller;

import com.splanet.splanet.notification.dto.FcmTokenRequest;
import com.splanet.splanet.notification.dto.FcmTokenUpdateRequest;
import com.splanet.splanet.notification.service.FcmTokenService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
Expand All @@ -21,14 +20,20 @@ public ResponseEntity<String> registerFcmToken(Long userId, FcmTokenRequest fcmT
}

@Override
public ResponseEntity<String> updateFcmTokenSettings(Long userId, FcmTokenUpdateRequest fcmTokenUpdateRequest) {
fcmTokenService.updateFcmTokenSettings(userId, fcmTokenUpdateRequest);
return ResponseEntity.ok("FCM token 수정 완료");
public ResponseEntity<String> updateNotificationEnabled(String token, Boolean isNotificationEnabled) {
fcmTokenService.updateNotificationEnabled(token, isNotificationEnabled);
return ResponseEntity.ok("FCM 알림 여부 수정 완료");
}

@Override
public ResponseEntity<String> deleteFcmToken(Long userId, String token) {
fcmTokenService.deleteFcmToken(userId, token);
public ResponseEntity<String> updateNotificationOffset(String token, Integer notificationOffset) {
fcmTokenService.updateNotificationOffset(token, notificationOffset);
return ResponseEntity.ok("FCM 알림 오프셋 수정 완료");
}

@Override
public ResponseEntity<String> deleteFcmToken(String token) {
fcmTokenService.deleteFcmToken(token);
return ResponseEntity.ok("FCM token 삭제 완료");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ public interface FcmTokenRepository extends JpaRepository<FcmToken, Long> {
Optional<FcmToken> findByUserIdAndToken(Long userId, String token);
List<FcmToken> findByUserId(Long userId);
List<FcmToken> findByUserIdIn(Collection<Long> userIds);
Optional<FcmToken> findByToken(String token);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// src/main/java/com/splanet/splanet/notification/service/FcmTokenService.java
package com.splanet.splanet.notification.service;

import com.splanet.splanet.core.exception.BusinessException;
Expand Down Expand Up @@ -34,26 +33,33 @@ public void registerFcmToken(Long userId, String token) {
}

@Transactional
public void updateFcmTokenSettings(Long userId, FcmTokenUpdateRequest request) {
FcmToken fcmToken = fcmTokenRepository.findByUserIdAndToken(userId, request.token())
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
public void updateNotificationEnabled(String token, Boolean isNotificationEnabled) {
FcmToken fcmToken = fcmTokenRepository.findByToken(token)
.orElseThrow(() -> new BusinessException(ErrorCode.TOKEN_NOT_FOUND));

fcmToken = fcmToken.toBuilder()
.isNotificationEnabled(request.isNotificationEnabled() != null
? request.isNotificationEnabled()
: fcmToken.getIsNotificationEnabled())
.notificationOffset(request.notificationOffset() != null
? request.notificationOffset()
: fcmToken.getNotificationOffset())
.isNotificationEnabled(isNotificationEnabled != null ? isNotificationEnabled : fcmToken.getIsNotificationEnabled())
.build();

fcmTokenRepository.save(fcmToken);
}

@Transactional
public void deleteFcmToken(Long userId, String token) {
FcmToken fcmToken = fcmTokenRepository.findByUserIdAndToken(userId, token)
.orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND));
public void updateNotificationOffset(String token, Integer notificationOffset) {
FcmToken fcmToken = fcmTokenRepository.findByToken(token)
.orElseThrow(() -> new BusinessException(ErrorCode.TOKEN_NOT_FOUND));

fcmToken = fcmToken.toBuilder()
.notificationOffset(notificationOffset != null ? notificationOffset : fcmToken.getNotificationOffset())
.build();

fcmTokenRepository.save(fcmToken);
}

@Transactional
public void deleteFcmToken(String token) {
FcmToken fcmToken = fcmTokenRepository.findByToken(token)
.orElseThrow(() -> new BusinessException(ErrorCode.TOKEN_NOT_FOUND));
fcmTokenRepository.delete(fcmToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,16 @@ public void sendNotification(FcmToken fcmToken, Plan plan) {

Notification notification = new Notification(title, body);

Message message = Message.builder().setToken(fcmToken.getToken()).setNotification(notification).putData("title", plan.getTitle()).putData("title", plan.getDescription()).putData("startDate", plan.getStartDate().toString()).build();
String clickActionUrl = "https://www.splanet.co.kr";

Message message = Message.builder().setToken(fcmToken.getToken())
.setNotification(notification)
.putData("click_action", clickActionUrl)
.putData("title", plan.getTitle())
.putData("description", plan.getDescription())
.putData("startDate", plan.getStartDate().toString())
.build();

try {
String response = firebaseMessaging.send(message);
log.info("알림을 정상적으로 전송하였습니다. : {}", response);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ public class PlanResponseDto {
private String description;
private LocalDateTime startDate;
private LocalDateTime endDate;

@JsonIgnore
private Boolean accessibility;
@JsonIgnore
private Boolean isCompleted;

@JsonIgnore
private LocalDateTime createdAt;
@JsonIgnore
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/splanet/splanet/plan/dto/PlanTimeDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.splanet.splanet.plan.dto;

import java.time.LocalDateTime;

public class PlanTimeDto {
private LocalDateTime startDate;
private LocalDateTime endDate;

public PlanTimeDto(LocalDateTime startDate, LocalDateTime endDate) {
this.startDate = startDate;
this.endDate = endDate;
}

public LocalDateTime getStartDate() {
return startDate;
}

public LocalDateTime getEndDate() {
return endDate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ public interface PlanRepository extends JpaRepository<Plan, Long> {
@Query("SELECT p FROM Plan p JOIN FETCH p.user WHERE p.startDate > :now AND p.isCompleted = false")
List<Plan> findUpcomingPlans(@Param("now") LocalDateTime now);

@Query("SELECT p FROM Plan p WHERE p.user.id = :userId AND p.startDate > :currentTime")
List<Plan> findAllFuturePlansByUserId(@Param("userId") Long userId, @Param("currentTime") LocalDateTime currentTime);

}
12 changes: 7 additions & 5 deletions src/main/java/com/splanet/splanet/plan/service/PlanService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.splanet.splanet.plan.dto.PlanRequestDto;
import com.splanet.splanet.plan.dto.PlanResponseDto;
import com.splanet.splanet.plan.dto.PlanTimeDto;
import com.splanet.splanet.plan.entity.Plan;
import com.splanet.splanet.plan.mapper.PlanMapper;
import com.splanet.splanet.plan.repository.PlanRepository;
Expand All @@ -16,6 +17,7 @@
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -118,11 +120,11 @@ private Plan convertToPlan(PlanCardResponseDto previewCard, User user, DateTimeF
}

@Transactional(readOnly = true)
public List<PlanResponseDto> getAllFuturePlansByUserId(Long userId) {
LocalDateTime now = LocalDateTime.now();
List<Plan> futurePlans = planRepository.findAllByUserIdAndStartDateAfter(userId, now);
return futurePlans.stream()
.map(planMapper::toResponseDto)
public List<PlanTimeDto> getAllFuturePlanTimesByUserId(Long userId) {
LocalDateTime currentTime = LocalDateTime.now(ZoneId.of("Asia/Seoul"));
return planRepository.findAllFuturePlansByUserId(userId, currentTime)
.stream()
.map(plan -> new PlanTimeDto(plan.getStartDate(), plan.getEndDate()))
.collect(Collectors.toList());
}

Expand Down
Loading

0 comments on commit a800384

Please sign in to comment.