diff --git a/HalfFifty_BE/build.gradle b/HalfFifty_BE/build.gradle index 7391bb0..900ea17 100644 --- a/HalfFifty_BE/build.gradle +++ b/HalfFifty_BE/build.gradle @@ -37,6 +37,8 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.security:spring-security-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'software.amazon.awssdk:lambda:2.20.26' // AWS Lambda SDK + } tasks.named('test') { diff --git a/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/FlaskSignLanguageBean.java b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/FlaskSignLanguageBean.java new file mode 100644 index 0000000..7394ab7 --- /dev/null +++ b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/FlaskSignLanguageBean.java @@ -0,0 +1,50 @@ +package HalfFifty.HalfFifty_BE.translation.bean; + +import HalfFifty.HalfFifty_BE.translation.domain.DTO.RequestSignLanguageDTO; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class FlaskSignLanguageBean { + private final RestTemplate restTemplate; + private final String aiServerUrl = "http://3.39.24.155/predict"; // Flask 서버 URL + + public FlaskSignLanguageBean() { + this.restTemplate = new RestTemplate(); + } + + // Flask AI 서버로 요청 보내기 + public Map exec(RequestSignLanguageDTO requestSignLanguageDTO) { + Map requestBody = new HashMap<>(); + requestBody.put("keypoints", requestSignLanguageDTO.getKeypoints()); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + HttpEntity> request = new HttpEntity<>(requestBody, headers); + + try { + ResponseEntity response = restTemplate.postForEntity(aiServerUrl, request, Map.class); + + if (response.getStatusCode() == HttpStatus.OK) { + return response.getBody(); + } else { + Map errorResponse = new HashMap<>(); + errorResponse.put("success", false); + errorResponse.put("error", "AI 서버에서 오류 응답 발생"); + return errorResponse; + } + } catch (Exception e) { + e.printStackTrace(); + Map errorResponse = new HashMap<>(); + errorResponse.put("success", false); + errorResponse.put("error", e.getMessage()); + return errorResponse; + } + } +} + diff --git a/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/SaveTranslationBean.java b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/SaveTranslationBean.java new file mode 100644 index 0000000..2052184 --- /dev/null +++ b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/SaveTranslationBean.java @@ -0,0 +1,38 @@ +package HalfFifty.HalfFifty_BE.translation.bean; + +import HalfFifty.HalfFifty_BE.translation.bean.small.CreateTranslationDAOBean; +import HalfFifty.HalfFifty_BE.translation.bean.small.CreateTranslationDTOBean; +import HalfFifty.HalfFifty_BE.translation.bean.small.SaveTranslationDAOBean; +import HalfFifty.HalfFifty_BE.translation.domain.DTO.ResponseTranslationGetDTO; +import HalfFifty.HalfFifty_BE.translation.domain.TranslationDAO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +@Component +public class SaveTranslationBean { + CreateTranslationDAOBean createTranslationDAOBean; + CreateTranslationDTOBean createTranslationDTOBean; + SaveTranslationDAOBean saveTranslationDAOBean; + + @Autowired + public SaveTranslationBean(CreateTranslationDAOBean createTranslationDAOBean, CreateTranslationDTOBean createTranslationDTOBean, SaveTranslationDAOBean saveTranslationDAOBean) { + this.createTranslationDAOBean = createTranslationDAOBean; + this.createTranslationDTOBean = createTranslationDTOBean; + this.saveTranslationDAOBean = saveTranslationDAOBean; + } + + // 번역 기록 저장 + public ResponseTranslationGetDTO exec(UUID userId, String translationWord, Double probability) { + // 번역 객체 생성 + TranslationDAO translationDAO = createTranslationDAOBean.exec(userId, translationWord); + if(translationDAO == null) return null; + + // 번역 저장 + saveTranslationDAOBean.exec(translationDAO); + + // 수화 번역 객체를 DTO로 변환해서 반환 + return createTranslationDTOBean.exec(translationDAO, probability); + } +} diff --git a/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/small/CreateTranslationDAOBean.java b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/small/CreateTranslationDAOBean.java new file mode 100644 index 0000000..316b2fa --- /dev/null +++ b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/small/CreateTranslationDAOBean.java @@ -0,0 +1,20 @@ +package HalfFifty.HalfFifty_BE.translation.bean.small; + +import HalfFifty.HalfFifty_BE.translation.domain.TranslationDAO; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Component +public class CreateTranslationDAOBean { + + // 번역 기록 객체 생성 + public TranslationDAO exec(UUID userId, String translationWord) { + return TranslationDAO.builder() + .userId(userId) + .translationWord(translationWord) + .createdAt(LocalDateTime.now()) + .build(); + } +} diff --git a/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/small/CreateTranslationDTOBean.java b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/small/CreateTranslationDTOBean.java new file mode 100644 index 0000000..960da5f --- /dev/null +++ b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/small/CreateTranslationDTOBean.java @@ -0,0 +1,20 @@ +package HalfFifty.HalfFifty_BE.translation.bean.small; + +import HalfFifty.HalfFifty_BE.translation.domain.DTO.ResponseTranslationGetDTO; +import HalfFifty.HalfFifty_BE.translation.domain.TranslationDAO; +import org.springframework.stereotype.Component; + +@Component +public class CreateTranslationDTOBean { + + // 수화 번역 객체를 DTO로 변환해서 반환 + public ResponseTranslationGetDTO exec(TranslationDAO translationDAO, Double probability) { + return ResponseTranslationGetDTO.builder() + .translationId(translationDAO.getTranslationId()) + .userId(translationDAO.getUserId()) + .translationWord(translationDAO.getTranslationWord()) + .createdAt(translationDAO.getCreatedAt()) + .probability(probability) + .build(); + } +} diff --git a/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/small/SaveTranslationDAOBean.java b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/small/SaveTranslationDAOBean.java new file mode 100644 index 0000000..be1b949 --- /dev/null +++ b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/bean/small/SaveTranslationDAOBean.java @@ -0,0 +1,21 @@ +package HalfFifty.HalfFifty_BE.translation.bean.small; + +import HalfFifty.HalfFifty_BE.translation.domain.TranslationDAO; +import HalfFifty.HalfFifty_BE.translation.repository.TranslationRepositoryJPA; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SaveTranslationDAOBean { + TranslationRepositoryJPA translationRepositoryJPA; + + @Autowired + public SaveTranslationDAOBean(TranslationRepositoryJPA translationRepositoryJPA) { + this.translationRepositoryJPA = translationRepositoryJPA; + } + + // 번역 기록 저장 + public void exec(TranslationDAO translationDAO) { + translationRepositoryJPA.save(translationDAO); + } +} diff --git a/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/controller/TranslationController.java b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/controller/TranslationController.java new file mode 100644 index 0000000..415896c --- /dev/null +++ b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/controller/TranslationController.java @@ -0,0 +1,42 @@ +package HalfFifty.HalfFifty_BE.translation.controller; + +import HalfFifty.HalfFifty_BE.translation.domain.DTO.RequestSignLanguageDTO; +import HalfFifty.HalfFifty_BE.translation.domain.DTO.ResponseTranslationGetDTO; +import HalfFifty.HalfFifty_BE.translation.service.TranslationService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/translation") +@CrossOrigin("*") +public class TranslationController { + TranslationService translationService; + + public TranslationController(TranslationService translationService) { + this.translationService = translationService; + } + + // 수화 번역 API + @PostMapping + public ResponseEntity> translateSignLanguage(@RequestBody RequestSignLanguageDTO requestSignLanguageDTO) { + // 번역된 데이터 가져오기 + ResponseTranslationGetDTO responseTranslationGetDTO = translationService.signLanguageTranslation(requestSignLanguageDTO); + + // 번역 성공 여부 확인 + boolean success = responseTranslationGetDTO != null; + + // 응답 데이터 구성 + Map responseMap = new HashMap<>(); + responseMap.put("success", success); + responseMap.put("message", success ? "수화 번역 성공" : "수화 번역 실패"); + responseMap.put("translationId", success ? responseTranslationGetDTO.getTranslationId() : null); + responseMap.put("translatedWord", success ? responseTranslationGetDTO.getTranslationWord() : null); + responseMap.put("probability", success ? responseTranslationGetDTO.getProbability() : null); + + return ResponseEntity.status(HttpStatus.OK).body(responseMap); + } +} diff --git a/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/domain/DTO/RequestSignLanguageDTO.java b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/domain/DTO/RequestSignLanguageDTO.java new file mode 100644 index 0000000..abd06d8 --- /dev/null +++ b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/domain/DTO/RequestSignLanguageDTO.java @@ -0,0 +1,12 @@ +package HalfFifty.HalfFifty_BE.translation.domain.DTO; + +import lombok.Data; + +import java.util.List; +import java.util.UUID; + +@Data +public class RequestSignLanguageDTO { + private List> keypoints; // 10 x 55 형태의 키포인트 데이터 + private UUID userId; // 사용자 식별 ID +} diff --git a/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/domain/DTO/ResponseTranslationGetDTO.java b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/domain/DTO/ResponseTranslationGetDTO.java new file mode 100644 index 0000000..cc47918 --- /dev/null +++ b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/domain/DTO/ResponseTranslationGetDTO.java @@ -0,0 +1,17 @@ +package HalfFifty.HalfFifty_BE.translation.domain.DTO; + +import lombok.Builder; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Data +@Builder +public class ResponseTranslationGetDTO { + UUID translationId; + UUID userId; + String translationWord; + Double probability; + LocalDateTime createdAt; +} diff --git a/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/domain/TranslationDAO.java b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/domain/TranslationDAO.java new file mode 100644 index 0000000..709306c --- /dev/null +++ b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/domain/TranslationDAO.java @@ -0,0 +1,24 @@ +package HalfFifty.HalfFifty_BE.translation.domain; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.*; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder +public class TranslationDAO { + @Id @GeneratedValue(strategy = GenerationType.AUTO) + UUID translationId; + UUID userId; + String translationWord; + LocalDateTime createdAt; +} diff --git a/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/repository/TranslationRepositoryJPA.java b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/repository/TranslationRepositoryJPA.java new file mode 100644 index 0000000..366e759 --- /dev/null +++ b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/repository/TranslationRepositoryJPA.java @@ -0,0 +1,11 @@ +package HalfFifty.HalfFifty_BE.translation.repository; + +import HalfFifty.HalfFifty_BE.translation.domain.TranslationDAO; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.UUID; + +@Repository +public interface TranslationRepositoryJPA extends JpaRepository { +} diff --git a/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/service/TranslationService.java b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/service/TranslationService.java new file mode 100644 index 0000000..5e0ff09 --- /dev/null +++ b/HalfFifty_BE/src/main/java/HalfFifty/HalfFifty_BE/translation/service/TranslationService.java @@ -0,0 +1,36 @@ +package HalfFifty.HalfFifty_BE.translation.service; + +import HalfFifty.HalfFifty_BE.translation.bean.FlaskSignLanguageBean; +import HalfFifty.HalfFifty_BE.translation.bean.SaveTranslationBean; +import HalfFifty.HalfFifty_BE.translation.domain.DTO.RequestSignLanguageDTO; +import HalfFifty.HalfFifty_BE.translation.domain.DTO.ResponseTranslationGetDTO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Map; + + +@Service +public class TranslationService { + SaveTranslationBean saveTranslationBean; + FlaskSignLanguageBean flaskSignLanguageBean; + + @Autowired + public TranslationService(SaveTranslationBean saveTranslationBean, FlaskSignLanguageBean flaskSignLanguageBean) { + this.saveTranslationBean = saveTranslationBean; + this.flaskSignLanguageBean = flaskSignLanguageBean; + } + + public ResponseTranslationGetDTO signLanguageTranslation(RequestSignLanguageDTO requestSignLanguageDTO) { + Map aiResponse = flaskSignLanguageBean.exec(requestSignLanguageDTO); + + if (aiResponse != null && Boolean.TRUE.equals(aiResponse.get("success"))) { + String translatedWord = (String) aiResponse.get("predicted_label"); + Double probability = (Double) aiResponse.get("confidence"); + return saveTranslationBean.exec(requestSignLanguageDTO.getUserId(), translatedWord, probability); + } else { + return null; + } + } + +} diff --git a/HalfFifty_BE/src/main/resources/application.properties b/HalfFifty_BE/src/main/resources/application.properties index eda0eca..22cb0b0 100644 --- a/HalfFifty_BE/src/main/resources/application.properties +++ b/HalfFifty_BE/src/main/resources/application.properties @@ -13,4 +13,7 @@ spring.jpa.properties.hibernate.format_sql=true spring.datasource.driver-class-name=${DB_DRIVER_CLASS_NAME} spring.datasource.url=${DB_URL} spring.datasource.username=${DB_USERNAME} -spring.datasource.password=${DB_PASSWORD} \ No newline at end of file +spring.datasource.password=${DB_PASSWORD} + +# lambda +aws.lambda.functionName = myLambda \ No newline at end of file