diff --git a/src/main/java/org/khtml/hexagonal/domain/ai/application/GptService.java b/src/main/java/org/khtml/hexagonal/domain/ai/application/GptService.java
new file mode 100644
index 0000000..f572571
--- /dev/null
+++ b/src/main/java/org/khtml/hexagonal/domain/ai/application/GptService.java
@@ -0,0 +1,308 @@
+package org.khtml.hexagonal.domain.ai.application;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import org.khtml.hexagonal.domain.building.dto.BuildingUpdate;
+import org.khtml.hexagonal.domain.building.dto.ImageRequest;
+import org.khtml.hexagonal.domain.building.dto.MaterialInfo;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.*;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Transactional(readOnly = true)
+@RequiredArgsConstructor
+@Service
+public class GptService {
+
+ @Value("${openai.api.url}")
+ private String openAiApiUrl;
+
+ @Value("${openai.api.key}")
+ private String openAiApiKey;
+
+ public BuildingUpdate analyzeBuilding(List urls) throws JsonProcessingException {
+ BuildingUpdate buildingUpdate;
+ ImageRequest imageRequest = new ImageRequest();
+ List contentList = new ArrayList<>();
+
+ imageRequest.setRole("user");
+
+ for (String url : urls) {
+ ImageRequest.Content content = new ImageRequest.Content();
+ ImageRequest.ImageUrl imageUrl = new ImageRequest.ImageUrl();
+
+ imageUrl.setUrl(url);
+ content.setType("image_url");
+ content.setImage_url(imageUrl);
+ contentList.add(content);
+
+ }
+
+ imageRequest.setContent(contentList);
+ return analyzeHouseImages(imageRequest);
+ }
+
+ public BuildingUpdate analyzeHouseImages(ImageRequest imageRequest) throws JsonProcessingException {
+
+ // 시스템 메시지
+ String systemMessage = """
+
+ 너는 오래된 건물의 상태를 평가하는 챗봇 AI다. 제공된 사진을 분석하여 건물의 상태를 진단하고, 결과를 한글로 작성된 JSON 형식으로 출력해야 한다.
+ 이 JSON은 아래 예시와 같이 각 필드가 특정 영어 변수에 매핑되도록 구성되어야 한다.
+
+ 건물 구조 분석 (structureReason):
+
+ 구조: 건물의 구조가 전통적인지 현대적인지 판단한다.
+ 이유: 판단의 이유를 설명한다.
+ 건축 요소 평가 (roof, walls, windowsAndDoors):
+
+ 지붕 형태 (roof):
+ 재료: 지붕에 사용된 재료를 확인한다.
+ 상태: 지붕의 상태를 평가한다.
+ 외벽 재질 (walls):
+ 재료: 외벽에 사용된 재료를 확인한다.
+ 상태: 외벽의 상태를 평가한다.
+ 창문 및 문 형태 (windowsAndDoors):
+ 재료: 창문과 문에 사용된 재료를 확인한다.
+ 상태: 창문과 문의 상태를 평가한다.
+ 건물 상태 평가 (overallCondition):
+
+ 평가: 건물의 전반적인 상태를 구체적으로 평가한다.
+ 이유: 해당 평가의 이유를 제시한다.
+ 상세 점수화 (detailedScores):
+
+ 균열 여부 (cracks): 균열의 존재 여부와 심각성을 100점 만점으로 점수화한다.
+ 누수 여부 (leaks): 누수의 존재 여부와 심각성을 100점 만점으로 점수화한다.
+ 부식 여부 (corrosion): 부식의 정도를 100점 만점으로 점수화한다.
+ 노후화 정도 (aging): 건물의 노후화를 100점 만점으로 점수화한다.
+ 총점수 (totalScore): 위의 점수를 합산하여 반올림한 총점을 계산한다.
+ 보수 필요성 판단 (repairNeeds):
+
+ 조명: 사진 속 조명이 형광등이라면 LED로 교체가 필요한지, 이미 LED라면 교체가 불필요한지 판단한다.
+ 창호 보강, 도배, 장판 교체: 창호 보강, 도배, 장판 교체의 필요 여부를 판단한다.
+
+ 판단하기 어려운 부분, 예를 들어 실내 사진인데 지붕 재료, 지붕 상태 등을 판단해야 하는 경우에는 해당 부분을 ""으로 처리한다.
+ repairlist의 경우 반드시 ,로 구분한다. repairlist가 없는 경우에는 ""으로 처리한다.
+
+
+ 예시 JSON은 다음과 같다.
+
+ {
+ "structureReason": "구조 평가 이유",
+ "roofMaterial": "지붕 재료",
+ "roofCondition": "지붕 상태",
+ "wallMaterial": "외벽 재료",
+ "wallCondition": "외벽 상태",
+ "windowDoorMaterial": "창문 및 문 재료",
+ "windowDoorCondition": "창문 및 문 상태",
+ "overallCondition": "건물 상태 평가",
+ "conditionReason": "건물 상태 평가 이유",
+ "crackScore": 20,
+ "leakScore": 0,
+ "corrosionScore": 0,
+ "agingScore": 0,
+ "totalScore": 20,
+ "repairList": "LED 교체, 창호 보강, 도배, 장판 교체"
+ }
+
+
+
+
+ """;
+
+ // OpenAI API에 보낼 요청 데이터 작성
+ Map requestBody = new HashMap<>();
+ List> messages = new ArrayList<>();
+
+ // System 메시지 추가
+ Map systemContent = new HashMap<>();
+ systemContent.put("type", "text");
+ systemContent.put("text", systemMessage);
+ messages.add(Map.of("role", "system", "content", List.of(systemContent)));
+
+ // User 메시지 추가 (여러 이미지 URL 포함)
+ List> imageUrls = new ArrayList<>();
+ imageRequest.getContent().forEach(content -> {
+ String url = content.getImageUrlOrNull();
+ if (url != null) {
+ Map imageUrlContent = new HashMap<>();
+ imageUrlContent.put("type", "image_url");
+ imageUrlContent.put("image_url", Map.of("url", url));
+ imageUrls.add(imageUrlContent);
+ }
+ });
+ messages.add(Map.of("role", "user", "content", imageUrls));
+
+ // Assistant 메시지 추가 (빈 값으로 초기화)
+ Map assistantContent = new HashMap<>();
+ assistantContent.put("type", "text");
+ assistantContent.put("text", "");
+ messages.add(Map.of("role", "assistant", "content", List.of(assistantContent)));
+
+ // 요청 본문에 messages 리스트와 옵션 추가
+ requestBody.put("messages", messages);
+ requestBody.put("temperature", 0.7);
+ requestBody.put("top_p", 0.95);
+ requestBody.put("max_tokens", 2000);
+
+ // API 요청 헤더 설정
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.set("api-key", openAiApiKey);
+
+ // HTTP 요청 생성
+ HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers);
+
+ // REST API 호출
+ RestTemplate restTemplate = new RestTemplate();
+ ResponseEntity response = restTemplate.exchange(
+ openAiApiUrl,
+ HttpMethod.POST,
+ requestEntity,
+ Map.class
+ );
+
+ // API 결과 처리
+ Map responseBody = response.getBody();
+
+ // responseBody에서 "content" 부분을 추출
+ String content = (String) ((Map) ((Map) ((List) responseBody.get("choices")).get(0)).get("message")).get("content");
+
+ // "```json"과 "```"을 제거하여 실제 JSON 데이터만 추출
+ String jsonContent = content.replaceAll("```json\\n|```", "").trim();
+
+ // JSON 데이터를 Map으로 파싱
+ ObjectMapper objectMapper = new ObjectMapper();
+ Map jsonData = objectMapper.readValue(jsonContent, new TypeReference>() {});
+
+ // BuildingUpdate DTO로 데이터 매핑
+ BuildingUpdate buildingUpdate = new BuildingUpdate();
+ buildingUpdate.setStructureReason((String) jsonData.get("structureReason"));
+ buildingUpdate.setRoofMaterial((String) jsonData.get("roofMaterial"));
+ buildingUpdate.setRoofCondition((String) jsonData.get("roofCondition"));
+ buildingUpdate.setWallMaterial((String) jsonData.get("wallMaterial"));
+ buildingUpdate.setWallCondition((String) jsonData.get("wallCondition"));
+ buildingUpdate.setWindowDoorMaterial((String) jsonData.get("windowDoorMaterial"));
+ buildingUpdate.setWindowDoorCondition((String) jsonData.get("windowDoorCondition"));
+ buildingUpdate.setOverallCondition((String) jsonData.get("overallCondition"));
+ buildingUpdate.setConditionReason((String) jsonData.get("conditionReason"));
+ buildingUpdate.setCrackScore((Integer) jsonData.get("crackScore"));
+ buildingUpdate.setLeakScore((Integer) jsonData.get("leakScore"));
+ buildingUpdate.setCorrosionScore((Integer) jsonData.get("corrosionScore"));
+ buildingUpdate.setAgingScore((Integer) jsonData.get("agingScore"));
+ buildingUpdate.setTotalScore((Integer) jsonData.get("totalScore"));
+ buildingUpdate.setRepairList((String) jsonData.get("repairList"));
+
+ // 매핑된 DTO 출력 (디버깅 용도)
+ System.out.println(buildingUpdate);
+
+ //buildingService.updateBuilding(buildingId, buildingUpdate);
+
+ // 리턴값 반환
+ return buildingUpdate;
+ }
+
+ public ResponseEntity> analyzeMaterialImages(ImageRequest imageRequest) throws JsonProcessingException {
+
+ // 시스템 메시지
+ String systemMessage = """
+ 너는 사진을 받으면 해당 사진에 나오는 부자재의 종류들과 그 사용법들을 반환하는 챗봇 AI다.
+
+ 제공된 사진을 분석하여 부자재의 종류와 사용법을 양식에 맞게 작성된 JSON 형식으로 출력해야 한다.
+
+ 반드시 양식에 맞게 작성해야 하며, 다른 방식으로의 응답은 절대 허용하지 않는다.
+
+ mateial과 usage는 여러개가 올 수 있지만 리스트 형식으로는 '절대' 출력하면 안된다.
+
+ mateiral과 usage가 여러 가지 이상으로 판단될 시, 반드시 ,로만 구분하여 한 개의 string으로 출력해야 한다.
+
+ 예시 JSON은 다음과 같다.
+
+ {
+ "material" : "나무 판자, 쇠 파이프"
+ "usage" : "벽 만들기, 천장 고치기"
+ }
+ """;
+
+ // OpenAI API에 보낼 요청 데이터 작성
+ Map requestBody = new HashMap<>();
+ List> messages = new ArrayList<>();
+
+ // System 메시지 추가
+ Map systemContent = new HashMap<>();
+ systemContent.put("type", "text");
+ systemContent.put("text", systemMessage);
+ messages.add(Map.of("role", "system", "content", List.of(systemContent)));
+
+ // User 메시지 추가 (여러 이미지 URL 포함)
+ List> imageUrls = new ArrayList<>();
+ imageRequest.getContent().forEach(content -> {
+ String url = content.getImageUrlOrNull();
+ if (url != null) {
+ Map imageUrlContent = new HashMap<>();
+ imageUrlContent.put("type", "image_url");
+ imageUrlContent.put("image_url", Map.of("url", url));
+ imageUrls.add(imageUrlContent);
+ }
+ });
+ messages.add(Map.of("role", "user", "content", imageUrls));
+
+ // Assistant 메시지 추가 (빈 값으로 초기화)
+ Map assistantContent = new HashMap<>();
+ assistantContent.put("type", "text");
+ assistantContent.put("text", "");
+ messages.add(Map.of("role", "assistant", "content", List.of(assistantContent)));
+
+ // 요청 본문에 messages 리스트와 옵션 추가
+ requestBody.put("messages", messages);
+ requestBody.put("temperature", 0.7);
+ requestBody.put("top_p", 0.95);
+ requestBody.put("max_tokens", 2000);
+
+ // API 요청 헤더 설정
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ headers.set("api-key", openAiApiKey);
+
+ // HTTP 요청 생성
+ HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers);
+
+ // REST API 호출
+ RestTemplate restTemplate = new RestTemplate();
+ ResponseEntity response = restTemplate.exchange(
+ openAiApiUrl,
+ HttpMethod.POST,
+ requestEntity,
+ Map.class
+ );
+
+ // API 결과 처리
+ Map responseBody = response.getBody();
+
+ // responseBody에서 "content" 부분을 추출
+ String content = (String) ((Map) ((Map) ((List) responseBody.get("choices")).get(0)).get("message")).get("content");
+
+ // "```json"과 "```"을 제거하여 실제 JSON 데이터만 추출
+ String jsonContent = content.replaceAll("```json\\n|```", "").trim();
+
+ // JSON 데이터를 Map으로 파싱
+ ObjectMapper objectMapper = new ObjectMapper();
+ Map jsonData = objectMapper.readValue(jsonContent, new TypeReference>() {});
+ // 필요한 데이터 사용
+ MaterialInfo materialInfo = new MaterialInfo((String) jsonData.get("material"), (String) jsonData.get("usage"));
+ System.out.println(materialInfo);
+
+ // 리턴값 반환
+ return ResponseEntity.ok(responseBody);
+ }
+
+}
diff --git a/src/main/java/org/khtml/hexagonal/domain/ai/presentation/GptController.java b/src/main/java/org/khtml/hexagonal/domain/ai/presentation/GptController.java
index c32d62d..f7392f4 100644
--- a/src/main/java/org/khtml/hexagonal/domain/ai/presentation/GptController.java
+++ b/src/main/java/org/khtml/hexagonal/domain/ai/presentation/GptController.java
@@ -1,293 +1,10 @@
package org.khtml.hexagonal.domain.ai.presentation;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.khtml.hexagonal.domain.ai.dto.BuildingUpdate;
-import org.khtml.hexagonal.domain.ai.dto.ImageRequest;
-import org.khtml.hexagonal.domain.ai.dto.MaterialInfo;
-import org.khtml.hexagonal.domain.building.application.BuildingService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
-import org.springframework.web.client.RestTemplate;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
@RestController
@RequestMapping("/api")
public class GptController {
- @Autowired
- private BuildingService buildingService;
-
- @Value("${openai.api.url}")
- private String openAiApiUrl;
-
- @Value("${openai.api.key}")
- private String openAiApiKey;
-
- @PostMapping("/analyze-images/house")
- public ResponseEntity> analyzeHouseImages(@RequestBody ImageRequest imageRequest) throws JsonProcessingException {
-
- // 시스템 메시지
- String systemMessage = """
-
- 너는 오래된 건물의 상태를 평가하는 챗봇 AI다. 제공된 사진을 분석하여 건물의 상태를 진단하고, 결과를 한글로 작성된 JSON 형식으로 출력해야 한다.
- 이 JSON은 아래 예시와 같이 각 필드가 특정 영어 변수에 매핑되도록 구성되어야 한다.
-
- 건물 구조 분석 (structureReason):
-
- 구조: 건물의 구조가 전통적인지 현대적인지 판단한다.
- 이유: 판단의 이유를 설명한다.
- 건축 요소 평가 (roof, walls, windowsAndDoors):
-
- 지붕 형태 (roof):
- 재료: 지붕에 사용된 재료를 확인한다.
- 상태: 지붕의 상태를 평가한다.
- 외벽 재질 (walls):
- 재료: 외벽에 사용된 재료를 확인한다.
- 상태: 외벽의 상태를 평가한다.
- 창문 및 문 형태 (windowsAndDoors):
- 재료: 창문과 문에 사용된 재료를 확인한다.
- 상태: 창문과 문의 상태를 평가한다.
- 건물 상태 평가 (overallCondition):
-
- 평가: 건물의 전반적인 상태를 구체적으로 평가한다.
- 이유: 해당 평가의 이유를 제시한다.
- 상세 점수화 (detailedScores):
-
- 균열 여부 (cracks): 균열의 존재 여부와 심각성을 100점 만점으로 점수화한다.
- 누수 여부 (leaks): 누수의 존재 여부와 심각성을 100점 만점으로 점수화한다.
- 부식 여부 (corrosion): 부식의 정도를 100점 만점으로 점수화한다.
- 노후화 정도 (aging): 건물의 노후화를 100점 만점으로 점수화한다.
- 총점수 (totalScore): 위의 점수를 합산하여 반올림한 총점을 계산한다.
- 보수 필요성 판단 (repairNeeds):
-
- 조명: 사진 속 조명이 형광등이라면 LED로 교체가 필요한지, 이미 LED라면 교체가 불필요한지 판단한다.
- 창호 보강, 도배, 장판 교체: 창호 보강, 도배, 장판 교체의 필요 여부를 판단한다.
-
- 판단하기 어려운 부분, 예를 들어 실내 사진인데 지붕 재료, 지붕 상태 등을 판단해야 하는 경우에는 해당 부분을 ""으로 처리한다.
- repairlist의 경우 반드시 ,로 구분한다. repairlist가 없는 경우에는 ""으로 처리한다.
-
-
- 예시 JSON은 다음과 같다.
-
- {
- "structureReason": "구조 평가 이유",
- "roofMaterial": "지붕 재료",
- "roofCondition": "지붕 상태",
- "wallMaterial": "외벽 재료",
- "wallCondition": "외벽 상태",
- "windowDoorMaterial": "창문 및 문 재료",
- "windowDoorCondition": "창문 및 문 상태",
- "overallCondition": "건물 상태 평가",
- "conditionReason": "건물 상태 평가 이유",
- "crackScore": 20,
- "leakScore": 0,
- "corrosionScore": 0,
- "agingScore": 0,
- "totalScore": 20,
- "repairList": "LED 교체, 창호 보강, 도배, 장판 교체"
- }
-
-
-
-
- """;
-
- // OpenAI API에 보낼 요청 데이터 작성
- Map requestBody = new HashMap<>();
- List> messages = new ArrayList<>();
-
- // System 메시지 추가
- Map systemContent = new HashMap<>();
- systemContent.put("type", "text");
- systemContent.put("text", systemMessage);
- messages.add(Map.of("role", "system", "content", List.of(systemContent)));
-
- // User 메시지 추가 (여러 이미지 URL 포함)
- List> imageUrls = new ArrayList<>();
- imageRequest.getContent().forEach(content -> {
- String url = content.getImageUrlOrNull();
- if (url != null) {
- Map imageUrlContent = new HashMap<>();
- imageUrlContent.put("type", "image_url");
- imageUrlContent.put("image_url", Map.of("url", url));
- imageUrls.add(imageUrlContent);
- }
- });
- messages.add(Map.of("role", "user", "content", imageUrls));
-
- // Assistant 메시지 추가 (빈 값으로 초기화)
- Map assistantContent = new HashMap<>();
- assistantContent.put("type", "text");
- assistantContent.put("text", "");
- messages.add(Map.of("role", "assistant", "content", List.of(assistantContent)));
-
- // 요청 본문에 messages 리스트와 옵션 추가
- requestBody.put("messages", messages);
- requestBody.put("temperature", 0.7);
- requestBody.put("top_p", 0.95);
- requestBody.put("max_tokens", 2000);
-
- // API 요청 헤더 설정
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_JSON);
- headers.set("api-key", openAiApiKey);
-
- // HTTP 요청 생성
- HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers);
-
- // REST API 호출
- RestTemplate restTemplate = new RestTemplate();
- ResponseEntity response = restTemplate.exchange(
- openAiApiUrl,
- HttpMethod.POST,
- requestEntity,
- Map.class
- );
-
- // API 결과 처리
- Map responseBody = response.getBody();
-
- // responseBody에서 "content" 부분을 추출
- String content = (String) ((Map) ((Map) ((List) responseBody.get("choices")).get(0)).get("message")).get("content");
-
- // "```json"과 "```"을 제거하여 실제 JSON 데이터만 추출
- String jsonContent = content.replaceAll("```json\\n|```", "").trim();
-
- // JSON 데이터를 Map으로 파싱
- ObjectMapper objectMapper = new ObjectMapper();
- Map jsonData = objectMapper.readValue(jsonContent, new TypeReference>() {});
-
- // BuildingUpdate DTO로 데이터 매핑
- BuildingUpdate buildingUpdate = new BuildingUpdate();
- buildingUpdate.setStructureReason((String) jsonData.get("structureReason"));
- buildingUpdate.setRoofMaterial((String) jsonData.get("roofMaterial"));
- buildingUpdate.setRoofCondition((String) jsonData.get("roofCondition"));
- buildingUpdate.setWallMaterial((String) jsonData.get("wallMaterial"));
- buildingUpdate.setWallCondition((String) jsonData.get("wallCondition"));
- buildingUpdate.setWindowDoorMaterial((String) jsonData.get("windowDoorMaterial"));
- buildingUpdate.setWindowDoorCondition((String) jsonData.get("windowDoorCondition"));
- buildingUpdate.setOverallCondition((String) jsonData.get("overallCondition"));
- buildingUpdate.setConditionReason((String) jsonData.get("conditionReason"));
- buildingUpdate.setCrackScore((Integer) jsonData.get("crackScore"));
- buildingUpdate.setLeakScore((Integer) jsonData.get("leakScore"));
- buildingUpdate.setCorrosionScore((Integer) jsonData.get("corrosionScore"));
- buildingUpdate.setAgingScore((Integer) jsonData.get("agingScore"));
- buildingUpdate.setTotalScore((Integer) jsonData.get("totalScore"));
- buildingUpdate.setRepairList((String) jsonData.get("repairList"));
-
- // 매핑된 DTO 출력 (디버깅 용도)
- System.out.println(buildingUpdate);
-
- //buildingService.updateBuilding(buildingId, buildingUpdate);
-
- // 리턴값 반환
- return ResponseEntity.ok(responseBody);
- }
-
- @PostMapping("/analyze-images/material")
- public ResponseEntity> analyzeMaterialImages(@RequestBody ImageRequest imageRequest) throws JsonProcessingException {
-
- // 시스템 메시지
- String systemMessage = """
- 너는 사진을 받으면 해당 사진에 나오는 부자재의 종류들과 그 사용법들을 반환하는 챗봇 AI다.
-
- 제공된 사진을 분석하여 부자재의 종류와 사용법을 양식에 맞게 작성된 JSON 형식으로 출력해야 한다.
-
- 반드시 양식에 맞게 작성해야 하며, 다른 방식으로의 응답은 절대 허용하지 않는다.
-
- mateial과 usage는 여러개가 올 수 있지만 리스트 형식으로는 '절대' 출력하면 안된다.
-
- mateiral과 usage가 여러 가지 이상으로 판단될 시, 반드시 ,로만 구분하여 한 개의 string으로 출력해야 한다.
-
- 예시 JSON은 다음과 같다.
-
- {
- "material" : "나무 판자, 쇠 파이프"
- "usage" : "벽 만들기, 천장 고치기"
- }
- """;
-
- // OpenAI API에 보낼 요청 데이터 작성
- Map requestBody = new HashMap<>();
- List> messages = new ArrayList<>();
-
- // System 메시지 추가
- Map systemContent = new HashMap<>();
- systemContent.put("type", "text");
- systemContent.put("text", systemMessage);
- messages.add(Map.of("role", "system", "content", List.of(systemContent)));
-
- // User 메시지 추가 (여러 이미지 URL 포함)
- List> imageUrls = new ArrayList<>();
- imageRequest.getContent().forEach(content -> {
- String url = content.getImageUrlOrNull();
- if (url != null) {
- Map imageUrlContent = new HashMap<>();
- imageUrlContent.put("type", "image_url");
- imageUrlContent.put("image_url", Map.of("url", url));
- imageUrls.add(imageUrlContent);
- }
- });
- messages.add(Map.of("role", "user", "content", imageUrls));
-
- // Assistant 메시지 추가 (빈 값으로 초기화)
- Map assistantContent = new HashMap<>();
- assistantContent.put("type", "text");
- assistantContent.put("text", "");
- messages.add(Map.of("role", "assistant", "content", List.of(assistantContent)));
-
- // 요청 본문에 messages 리스트와 옵션 추가
- requestBody.put("messages", messages);
- requestBody.put("temperature", 0.7);
- requestBody.put("top_p", 0.95);
- requestBody.put("max_tokens", 2000);
-
- // API 요청 헤더 설정
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_JSON);
- headers.set("api-key", openAiApiKey);
-
- // HTTP 요청 생성
- HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers);
-
- // REST API 호출
- RestTemplate restTemplate = new RestTemplate();
- ResponseEntity response = restTemplate.exchange(
- openAiApiUrl,
- HttpMethod.POST,
- requestEntity,
- Map.class
- );
-
- // API 결과 처리
- Map responseBody = response.getBody();
-
- // responseBody에서 "content" 부분을 추출
- String content = (String) ((Map) ((Map) ((List) responseBody.get("choices")).get(0)).get("message")).get("content");
-
- // "```json"과 "```"을 제거하여 실제 JSON 데이터만 추출
- String jsonContent = content.replaceAll("```json\\n|```", "").trim();
-
- // JSON 데이터를 Map으로 파싱
- ObjectMapper objectMapper = new ObjectMapper();
- Map jsonData = objectMapper.readValue(jsonContent, new TypeReference>() {});
- // 필요한 데이터 사용
- MaterialInfo materialInfo = new MaterialInfo((String) jsonData.get("material"), (String) jsonData.get("usage"));
- System.out.println(materialInfo);
- // 리턴값 반환
- return ResponseEntity.ok(responseBody);
- }
}
diff --git a/src/main/java/org/khtml/hexagonal/domain/auth/AuthService.java b/src/main/java/org/khtml/hexagonal/domain/auth/AuthService.java
index f269ec4..86a8f71 100644
--- a/src/main/java/org/khtml/hexagonal/domain/auth/AuthService.java
+++ b/src/main/java/org/khtml/hexagonal/domain/auth/AuthService.java
@@ -17,17 +17,22 @@ public class AuthService {
@Transactional
public TokenResult login(LoginRequest loginRequest) {
- User newUser = User.builder()
- .providerId(loginRequest.providerId())
- .locationConsent(loginRequest.locationConsent())
- .numberOfPersons(loginRequest.numberOfPersons())
- .phoneNumber(loginRequest.phoneNumber())
- .username(loginRequest.username())
- .userType(UserType.valueOf(loginRequest.userType()))
- .build();
-
- User savedUser = userRepository.save(newUser);
- String accessToken = jwtGenerator.generateAccessToken(savedUser.getId());
+ User user;
+ if (!userRepository.existsByProviderId(loginRequest.providerId())) {
+ user = User.builder()
+ .providerId(loginRequest.providerId())
+ .locationConsent(loginRequest.locationConsent())
+ .numberOfPersons(loginRequest.numberOfPersons())
+ .phoneNumber(loginRequest.phoneNumber())
+ .username(loginRequest.username())
+ .userType(UserType.valueOf(loginRequest.userType()))
+ .build();
+ } else {
+ user = userRepository.findByProviderId(loginRequest.providerId())
+ .orElseThrow(() -> new IllegalArgumentException("User not found"));
+ }
+ userRepository.save(user);
+ String accessToken = jwtGenerator.generateAccessToken(user.getId());
String refreshToken = jwtGenerator.generateRefreshToken();
return new TokenResult(accessToken, refreshToken);
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/ImageType.java b/src/main/java/org/khtml/hexagonal/domain/building/ImageType.java
new file mode 100644
index 0000000..e0a2f60
--- /dev/null
+++ b/src/main/java/org/khtml/hexagonal/domain/building/ImageType.java
@@ -0,0 +1,5 @@
+package org.khtml.hexagonal.domain.building;
+
+public enum ImageType {
+ BUILDING, MATERIAL
+}
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/BlobManager.java b/src/main/java/org/khtml/hexagonal/domain/building/application/BlobManager.java
similarity index 97%
rename from src/main/java/org/khtml/hexagonal/domain/building/BlobManager.java
rename to src/main/java/org/khtml/hexagonal/domain/building/application/BlobManager.java
index b33ff91..8e8325e 100644
--- a/src/main/java/org/khtml/hexagonal/domain/building/BlobManager.java
+++ b/src/main/java/org/khtml/hexagonal/domain/building/application/BlobManager.java
@@ -1,4 +1,4 @@
-package org.khtml.hexagonal.domain.building;
+package org.khtml.hexagonal.domain.building.application;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.BlobContainerClient;
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/application/BuildingService.java b/src/main/java/org/khtml/hexagonal/domain/building/application/BuildingService.java
index 5f6df5a..aecc111 100644
--- a/src/main/java/org/khtml/hexagonal/domain/building/application/BuildingService.java
+++ b/src/main/java/org/khtml/hexagonal/domain/building/application/BuildingService.java
@@ -1,15 +1,23 @@
package org.khtml.hexagonal.domain.building.application;
import lombok.RequiredArgsConstructor;
-import org.khtml.hexagonal.domain.ai.dto.BuildingUpdate;
-import org.khtml.hexagonal.domain.building.*;
+import org.khtml.hexagonal.domain.building.ImageType;
+import org.khtml.hexagonal.domain.building.dto.BuildingUpdate;
+import org.khtml.hexagonal.domain.building.entity.Building;
+import org.khtml.hexagonal.domain.building.entity.BuildingImage;
+import org.khtml.hexagonal.domain.building.entity.Image;
+import org.khtml.hexagonal.domain.building.repository.BuildingImageRepository;
+import org.khtml.hexagonal.domain.building.repository.BuildingRepository;
+import org.khtml.hexagonal.domain.building.repository.ImageRepository;
import org.khtml.hexagonal.domain.user.User;
import org.khtml.hexagonal.domain.user.UserRepository;
+import org.khtml.hexagonal.global.support.error.ErrorType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -62,8 +70,9 @@ public Building updateBuilding(String buildingId, BuildingUpdate buildingUpdate)
}
@Transactional
- public void registerBuilding(String buildingId, User requestUser, List multipartFiles) throws IOException {
- for(MultipartFile file : multipartFiles) {
+ public List registerBuilding(String buildingId, User requestUser, List multipartFiles) throws IOException {
+ List ret = new ArrayList<>();
+ for (MultipartFile file : multipartFiles) {
String url = blobManager.storeFile(file.getOriginalFilename(), file.getInputStream(), file.getSize());
User user = userRepository.findById(requestUser.getId()).orElseThrow(() -> new IllegalArgumentException("User not found"));
@@ -74,6 +83,7 @@ public void registerBuilding(String buildingId, User requestUser, List new IllegalArgumentException("Building not found"));
+
+ building.updateAnalyzedData(buildingUpdate);
+ }
+
+ @Transactional
+ public void updateBuildingDescription(String buildingId, Long userId, String description) {
+ Building building = buildingRepository.findBuildingByGisBuildingId(buildingId)
+ .orElseThrow(() -> new IllegalArgumentException("Building not found"));
+
+ if(!Objects.equals(building.getUser().getId(), userId)) {
+ throw new IllegalArgumentException(ErrorType.DEFAULT_ERROR.getMessage());
}
+
+ building.setDescription(description);
}
}
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/controller/BuildingController.java b/src/main/java/org/khtml/hexagonal/domain/building/controller/BuildingController.java
new file mode 100644
index 0000000..3d374d1
--- /dev/null
+++ b/src/main/java/org/khtml/hexagonal/domain/building/controller/BuildingController.java
@@ -0,0 +1,60 @@
+package org.khtml.hexagonal.domain.building.controller;
+
+import lombok.RequiredArgsConstructor;
+import org.khtml.hexagonal.domain.ai.application.GptService;
+import org.khtml.hexagonal.domain.building.dto.*;
+import org.khtml.hexagonal.domain.auth.JwtValidator;
+import org.khtml.hexagonal.domain.building.application.BuildingService;
+import org.khtml.hexagonal.domain.user.User;
+import org.khtml.hexagonal.global.support.response.ApiResponse;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.List;
+
+@RequestMapping("/api/v1/buildings")
+@RequiredArgsConstructor
+@RestController
+public class BuildingController {
+
+ private final BuildingService buildingService;
+ private final JwtValidator jwtValidator;
+ private final GptService gptService;
+
+ @GetMapping("/{building-id}")
+ public ApiResponse getBuildingDetail(
+ @PathVariable(name = "building-id") String buildingId
+ ) {
+ return ApiResponse.success(BuildingDetailResponse.toResponse(buildingService.getBuilding(buildingId)));
+ }
+
+ @PostMapping("/{building-id}/register")
+ public ApiResponse> registerBuilding(
+ @RequestHeader("Authorization") String token,
+ @RequestParam("images") List multipartFiles,
+ @PathVariable(name = "building-id") String buildingId
+ ) throws IOException {
+ User requestUser = jwtValidator.getUserFromToken(token);
+ List urls = buildingService.registerBuilding(buildingId, requestUser, multipartFiles);
+
+ BuildingUpdate buildingUpdate = gptService.analyzeBuilding(urls);
+ buildingService.updateAnalyzedBuilding(buildingId, buildingUpdate);
+
+ return ApiResponse.success(buildingUpdate);
+ }
+
+ @PostMapping("/{building-id}/register/description")
+ public ApiResponse> registerBuilding(
+ @RequestHeader("Authorization") String token,
+ @PathVariable(name = "building-id") String buildingId,
+ @RequestBody BuildingDescriptionRequest buildingDescriptionRequest
+ ) throws IOException {
+ User requestUser = jwtValidator.getUserFromToken(token);
+ buildingService.updateBuildingDescription(buildingId, requestUser.getId(), buildingDescriptionRequest.description());
+
+ return ApiResponse.success();
+ }
+
+
+}
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/dto/BuildingDescriptionRequest.java b/src/main/java/org/khtml/hexagonal/domain/building/dto/BuildingDescriptionRequest.java
new file mode 100644
index 0000000..83e6487
--- /dev/null
+++ b/src/main/java/org/khtml/hexagonal/domain/building/dto/BuildingDescriptionRequest.java
@@ -0,0 +1,6 @@
+package org.khtml.hexagonal.domain.building.dto;
+
+public record BuildingDescriptionRequest(
+ String description
+) {
+}
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/BuildingDetailResponse.java b/src/main/java/org/khtml/hexagonal/domain/building/dto/BuildingDetailResponse.java
similarity index 92%
rename from src/main/java/org/khtml/hexagonal/domain/building/BuildingDetailResponse.java
rename to src/main/java/org/khtml/hexagonal/domain/building/dto/BuildingDetailResponse.java
index 1bd418b..02d8d82 100644
--- a/src/main/java/org/khtml/hexagonal/domain/building/BuildingDetailResponse.java
+++ b/src/main/java/org/khtml/hexagonal/domain/building/dto/BuildingDetailResponse.java
@@ -1,4 +1,6 @@
-package org.khtml.hexagonal.domain.building;
+package org.khtml.hexagonal.domain.building.dto;
+
+import org.khtml.hexagonal.domain.building.entity.Building;
public record BuildingDetailResponse(
String buildingId,
diff --git a/src/main/java/org/khtml/hexagonal/domain/ai/dto/BuildingUpdate.java b/src/main/java/org/khtml/hexagonal/domain/building/dto/BuildingUpdate.java
similarity index 98%
rename from src/main/java/org/khtml/hexagonal/domain/ai/dto/BuildingUpdate.java
rename to src/main/java/org/khtml/hexagonal/domain/building/dto/BuildingUpdate.java
index 9323374..8b1b6bb 100644
--- a/src/main/java/org/khtml/hexagonal/domain/ai/dto/BuildingUpdate.java
+++ b/src/main/java/org/khtml/hexagonal/domain/building/dto/BuildingUpdate.java
@@ -1,4 +1,4 @@
-package org.khtml.hexagonal.domain.ai.dto;
+package org.khtml.hexagonal.domain.building.dto;
public class BuildingUpdate {
diff --git a/src/main/java/org/khtml/hexagonal/domain/ai/dto/ImageRequest.java b/src/main/java/org/khtml/hexagonal/domain/building/dto/ImageRequest.java
similarity index 91%
rename from src/main/java/org/khtml/hexagonal/domain/ai/dto/ImageRequest.java
rename to src/main/java/org/khtml/hexagonal/domain/building/dto/ImageRequest.java
index 320f12b..12fcd7b 100644
--- a/src/main/java/org/khtml/hexagonal/domain/ai/dto/ImageRequest.java
+++ b/src/main/java/org/khtml/hexagonal/domain/building/dto/ImageRequest.java
@@ -1,4 +1,4 @@
-package org.khtml.hexagonal.domain.ai.dto;
+package org.khtml.hexagonal.domain.building.dto;
import lombok.Getter;
import lombok.Setter;
diff --git a/src/main/java/org/khtml/hexagonal/domain/ai/dto/MaterialInfo.java b/src/main/java/org/khtml/hexagonal/domain/building/dto/MaterialInfo.java
similarity index 93%
rename from src/main/java/org/khtml/hexagonal/domain/ai/dto/MaterialInfo.java
rename to src/main/java/org/khtml/hexagonal/domain/building/dto/MaterialInfo.java
index 5593a00..9db8dc7 100644
--- a/src/main/java/org/khtml/hexagonal/domain/ai/dto/MaterialInfo.java
+++ b/src/main/java/org/khtml/hexagonal/domain/building/dto/MaterialInfo.java
@@ -1,4 +1,4 @@
-package org.khtml.hexagonal.domain.ai.dto;
+package org.khtml.hexagonal.domain.building.dto;
public class MaterialInfo {
private String material;
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/Building.java b/src/main/java/org/khtml/hexagonal/domain/building/entity/Building.java
similarity index 65%
rename from src/main/java/org/khtml/hexagonal/domain/building/Building.java
rename to src/main/java/org/khtml/hexagonal/domain/building/entity/Building.java
index 67f99a9..85aac91 100644
--- a/src/main/java/org/khtml/hexagonal/domain/building/Building.java
+++ b/src/main/java/org/khtml/hexagonal/domain/building/entity/Building.java
@@ -1,13 +1,13 @@
-package org.khtml.hexagonal.domain.building;
+package org.khtml.hexagonal.domain.building.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
+import org.khtml.hexagonal.domain.building.BuildingStatus;
+import org.khtml.hexagonal.domain.building.dto.BuildingUpdate;
import org.khtml.hexagonal.domain.user.User;
-import java.util.List;
-
@Table(name = "building")
@Getter
@Setter
@@ -18,6 +18,7 @@ public class Building {
@Id
private String gisBuildingId;
+ @Enumerated(EnumType.STRING)
@Column(name = "building_status")
private BuildingStatus buildingStatus = BuildingStatus.NOT_REGISTERED;
@@ -105,6 +106,9 @@ public class Building {
@Column(name = "description")
private String description;
+ @Column(name = "is_analyzed")
+ private Boolean isAnalyzed = false;
+
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@@ -113,4 +117,24 @@ public void updateUser(User user) {
this.user = user;
}
+ public void updateAnalyzedData(BuildingUpdate buildingUpdate) {
+ this.structureReason = buildingUpdate.getStructureReason();
+ this.roofMaterial = buildingUpdate.getRoofMaterial();
+ this.roofCondition = buildingUpdate.getRoofCondition();
+ this.wallMaterial = buildingUpdate.getWallMaterial();
+ this.wallCondition = buildingUpdate.getWallCondition();
+ this.windowDoorMaterial = buildingUpdate.getWindowDoorMaterial();
+ this.windowDoorCondition = buildingUpdate.getWindowDoorCondition();
+ this.overallCondition = buildingUpdate.getOverallCondition();
+ this.conditionReason = buildingUpdate.getConditionReason();
+ this.crackScore = buildingUpdate.getCrackScore();
+ this.leakScore = buildingUpdate.getLeakScore();
+ this.corrosionScore = buildingUpdate.getCorrosionScore();
+ this.agingScore = buildingUpdate.getAgingScore();
+ this.totalScore = buildingUpdate.getTotalScore();
+ this.repairList = buildingUpdate.getRepairList();
+ this.isAnalyzed = true;
+ this.buildingStatus = BuildingStatus.REGISTERED;
+ }
+
}
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/BuildingImage.java b/src/main/java/org/khtml/hexagonal/domain/building/entity/BuildingImage.java
similarity index 93%
rename from src/main/java/org/khtml/hexagonal/domain/building/BuildingImage.java
rename to src/main/java/org/khtml/hexagonal/domain/building/entity/BuildingImage.java
index dffab5b..943c997 100644
--- a/src/main/java/org/khtml/hexagonal/domain/building/BuildingImage.java
+++ b/src/main/java/org/khtml/hexagonal/domain/building/entity/BuildingImage.java
@@ -1,4 +1,4 @@
-package org.khtml.hexagonal.domain.building;
+package org.khtml.hexagonal.domain.building.entity;
import jakarta.persistence.*;
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/Image.java b/src/main/java/org/khtml/hexagonal/domain/building/entity/Image.java
similarity index 67%
rename from src/main/java/org/khtml/hexagonal/domain/building/Image.java
rename to src/main/java/org/khtml/hexagonal/domain/building/entity/Image.java
index 80209c0..b8ad672 100644
--- a/src/main/java/org/khtml/hexagonal/domain/building/Image.java
+++ b/src/main/java/org/khtml/hexagonal/domain/building/entity/Image.java
@@ -1,10 +1,11 @@
-package org.khtml.hexagonal.domain.building;
+package org.khtml.hexagonal.domain.building.entity;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
+import org.khtml.hexagonal.domain.building.ImageType;
import org.khtml.hexagonal.domain.common.BaseEntity;
import org.khtml.hexagonal.domain.user.User;
@@ -16,13 +17,17 @@ public class Image extends BaseEntity {
private String url;
+ @Enumerated(EnumType.STRING)
+ private ImageType imageType;
+
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@Builder
- public Image(String url, User user) {
+ public Image(String url, ImageType imageType, User user) {
this.url = url;
+ this.imageType = imageType;
this.user = user;
}
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/BuildingImageRepository.java b/src/main/java/org/khtml/hexagonal/domain/building/repository/BuildingImageRepository.java
similarity index 55%
rename from src/main/java/org/khtml/hexagonal/domain/building/BuildingImageRepository.java
rename to src/main/java/org/khtml/hexagonal/domain/building/repository/BuildingImageRepository.java
index 26afcee..7bda770 100644
--- a/src/main/java/org/khtml/hexagonal/domain/building/BuildingImageRepository.java
+++ b/src/main/java/org/khtml/hexagonal/domain/building/repository/BuildingImageRepository.java
@@ -1,5 +1,6 @@
-package org.khtml.hexagonal.domain.building;
+package org.khtml.hexagonal.domain.building.repository;
+import org.khtml.hexagonal.domain.building.entity.BuildingImage;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BuildingImageRepository extends JpaRepository {
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/BuildingRepository.java b/src/main/java/org/khtml/hexagonal/domain/building/repository/BuildingRepository.java
similarity index 75%
rename from src/main/java/org/khtml/hexagonal/domain/building/BuildingRepository.java
rename to src/main/java/org/khtml/hexagonal/domain/building/repository/BuildingRepository.java
index 404c280..c37b0b1 100644
--- a/src/main/java/org/khtml/hexagonal/domain/building/BuildingRepository.java
+++ b/src/main/java/org/khtml/hexagonal/domain/building/repository/BuildingRepository.java
@@ -1,5 +1,6 @@
-package org.khtml.hexagonal.domain.building;
+package org.khtml.hexagonal.domain.building.repository;
+import org.khtml.hexagonal.domain.building.entity.Building;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/ImageRepository.java b/src/main/java/org/khtml/hexagonal/domain/building/repository/ImageRepository.java
similarity index 54%
rename from src/main/java/org/khtml/hexagonal/domain/building/ImageRepository.java
rename to src/main/java/org/khtml/hexagonal/domain/building/repository/ImageRepository.java
index 437efa3..0fec944 100644
--- a/src/main/java/org/khtml/hexagonal/domain/building/ImageRepository.java
+++ b/src/main/java/org/khtml/hexagonal/domain/building/repository/ImageRepository.java
@@ -1,5 +1,6 @@
-package org.khtml.hexagonal.domain.building;
+package org.khtml.hexagonal.domain.building.repository;
+import org.khtml.hexagonal.domain.building.entity.Image;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ImageRepository extends JpaRepository {
diff --git a/src/main/java/org/khtml/hexagonal/domain/material/Material.java b/src/main/java/org/khtml/hexagonal/domain/material/Material.java
new file mode 100644
index 0000000..44dcfa1
--- /dev/null
+++ b/src/main/java/org/khtml/hexagonal/domain/material/Material.java
@@ -0,0 +1,30 @@
+package org.khtml.hexagonal.domain.material;
+
+import jakarta.persistence.*;
+import lombok.AccessLevel;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.khtml.hexagonal.domain.building.entity.Building;
+import org.khtml.hexagonal.domain.common.BaseEntity;
+
+@Table(name = "material")
+@Getter
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+@Entity
+public class Material extends BaseEntity {
+
+ @Column(name = "name")
+ private String name;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "building_id")
+ private Building building;
+
+ @Builder
+ public Material(String name, Building building) {
+ this.name = name;
+ this.building = building;
+ }
+
+}
diff --git a/src/main/java/org/khtml/hexagonal/domain/building/BuildingController.java b/src/main/java/org/khtml/hexagonal/domain/material/MaterialController.java
similarity index 58%
rename from src/main/java/org/khtml/hexagonal/domain/building/BuildingController.java
rename to src/main/java/org/khtml/hexagonal/domain/material/MaterialController.java
index 5c81de3..efc53b0 100644
--- a/src/main/java/org/khtml/hexagonal/domain/building/BuildingController.java
+++ b/src/main/java/org/khtml/hexagonal/domain/material/MaterialController.java
@@ -1,8 +1,7 @@
-package org.khtml.hexagonal.domain.building;
+package org.khtml.hexagonal.domain.material;
import lombok.RequiredArgsConstructor;
import org.khtml.hexagonal.domain.auth.JwtValidator;
-import org.khtml.hexagonal.domain.building.application.BuildingService;
import org.khtml.hexagonal.domain.user.User;
import org.khtml.hexagonal.global.support.response.ApiResponse;
import org.springframework.web.bind.annotation.*;
@@ -11,21 +10,15 @@
import java.io.IOException;
import java.util.List;
-@RequestMapping("/api/v1/buildings")
+
+@RequestMapping("/api/v1/materials")
@RequiredArgsConstructor
@RestController
-public class BuildingController {
+public class MaterialController {
- private final BuildingService buildingService;
+ private final MaterialService materialService;
private final JwtValidator jwtValidator;
- @GetMapping("/{building-id}")
- public ApiResponse getBuildingDetail(
- @PathVariable(name = "building-id") String buildingId
- ) {
- return ApiResponse.success(BuildingDetailResponse.toResponse(buildingService.getBuilding(buildingId)));
- }
-
@PostMapping("/{building-id}/register")
public ApiResponse> registerBuilding(
@RequestHeader("Authorization") String token,
@@ -33,7 +26,7 @@ public ApiResponse> registerBuilding(
@PathVariable(name = "building-id") String buildingId
) throws IOException {
User requestUser = jwtValidator.getUserFromToken(token);
- buildingService.registerBuilding(buildingId, requestUser, multipartFiles);
+ materialService.registerMaterials(buildingId, requestUser, multipartFiles);
return ApiResponse.success();
}
diff --git a/src/main/java/org/khtml/hexagonal/domain/material/MaterialRepository.java b/src/main/java/org/khtml/hexagonal/domain/material/MaterialRepository.java
new file mode 100644
index 0000000..8a009ad
--- /dev/null
+++ b/src/main/java/org/khtml/hexagonal/domain/material/MaterialRepository.java
@@ -0,0 +1,6 @@
+package org.khtml.hexagonal.domain.material;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface MaterialRepository extends JpaRepository {
+}
diff --git a/src/main/java/org/khtml/hexagonal/domain/material/MaterialService.java b/src/main/java/org/khtml/hexagonal/domain/material/MaterialService.java
new file mode 100644
index 0000000..1497d1e
--- /dev/null
+++ b/src/main/java/org/khtml/hexagonal/domain/material/MaterialService.java
@@ -0,0 +1,51 @@
+package org.khtml.hexagonal.domain.material;
+
+import lombok.RequiredArgsConstructor;
+import org.khtml.hexagonal.domain.building.*;
+import org.khtml.hexagonal.domain.building.application.BlobManager;
+import org.khtml.hexagonal.domain.building.entity.Building;
+import org.khtml.hexagonal.domain.building.entity.BuildingImage;
+import org.khtml.hexagonal.domain.building.entity.Image;
+import org.khtml.hexagonal.domain.building.repository.BuildingImageRepository;
+import org.khtml.hexagonal.domain.building.repository.BuildingRepository;
+import org.khtml.hexagonal.domain.building.repository.ImageRepository;
+import org.khtml.hexagonal.domain.user.User;
+import org.khtml.hexagonal.domain.user.UserRepository;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.List;
+
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+@Service
+public class MaterialService {
+
+ private final BuildingRepository buildingRepository;
+ private final BuildingImageRepository buildingImageRepository;
+ private final ImageRepository imageRepository;
+ private final UserRepository userRepository;
+ private final MaterialRepository materialRepository;
+ private final BlobManager blobManager;
+
+ @Transactional
+ public void registerMaterials(String buildingId, User requestUser, List multipartFiles) throws IOException {
+ for(MultipartFile file : multipartFiles) {
+ String url = blobManager.storeFile(file.getOriginalFilename(), file.getInputStream(), file.getSize());
+ Building building = buildingRepository.findBuildingByGisBuildingId(buildingId)
+ .orElseThrow(() -> new IllegalArgumentException("Building not found"));
+ User user = userRepository.findById(requestUser.getId())
+ .orElseThrow(() -> new IllegalArgumentException("User not found"));
+
+ Image image = Image.builder().url(url).imageType(ImageType.MATERIAL).user(user).build();
+ BuildingImage buildingImage = BuildingImage.builder().image(image).building(building).build();
+
+ // **Material GPT 로직 수행 후 저장 로직 필요 **//
+ imageRepository.save(image);
+ buildingImageRepository.save(buildingImage);
+ }
+ }
+
+}
diff --git a/src/main/java/org/khtml/hexagonal/domain/user/UserRepository.java b/src/main/java/org/khtml/hexagonal/domain/user/UserRepository.java
index 2fb15f0..966eb09 100644
--- a/src/main/java/org/khtml/hexagonal/domain/user/UserRepository.java
+++ b/src/main/java/org/khtml/hexagonal/domain/user/UserRepository.java
@@ -2,5 +2,10 @@
import org.springframework.data.jpa.repository.JpaRepository;
+import java.util.Optional;
+
public interface UserRepository extends JpaRepository {
+
+ Boolean existsByProviderId(String providerId);
+ Optional findByProviderId(String providerId);
}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 7a357ed..6bd0ef8 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -82,3 +82,10 @@ storage:
cacheServerConfiguration: true
elideSetAutoCommits: true
maintainTimeStats: false
+
+spring:
+ servlet:
+ multipart:
+ max-file-size: 50MB
+ max-request-size: 50MB
+