Skip to content

Commit 2355ccb

Browse files
authored
[FEAT]: GPT API 구현 (#11)
1 parent c33545b commit 2355ccb

File tree

17 files changed

+181
-60
lines changed

17 files changed

+181
-60
lines changed

src/main/java/org/khtml/hexagonal/domain/building/BuildingController.java renamed to src/main/java/org/khtml/hexagonal/domain/ai/application/GptService.java

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.khtml.hexagonal.domain.building;
1+
package org.khtml.hexagonal.domain.ai.application;
22

33
import com.fasterxml.jackson.core.JsonProcessingException;
44
import com.fasterxml.jackson.core.type.TypeReference;
@@ -7,52 +7,29 @@
77
import org.khtml.hexagonal.domain.building.dto.BuildingUpdate;
88
import org.khtml.hexagonal.domain.building.dto.ImageRequest;
99
import org.khtml.hexagonal.domain.building.dto.MaterialInfo;
10-
import org.khtml.hexagonal.domain.auth.JwtValidator;
11-
import org.khtml.hexagonal.domain.building.application.BuildingService;
12-
import org.khtml.hexagonal.domain.user.User;
13-
import org.khtml.hexagonal.global.support.response.ApiResponse;
1410
import org.springframework.beans.factory.annotation.Value;
1511
import org.springframework.http.*;
16-
import org.springframework.web.bind.annotation.*;
12+
import org.springframework.stereotype.Service;
13+
import org.springframework.transaction.annotation.Transactional;
1714
import org.springframework.web.client.RestTemplate;
18-
import org.springframework.web.multipart.MultipartFile;
1915

20-
import java.io.IOException;
2116
import java.util.ArrayList;
2217
import java.util.HashMap;
2318
import java.util.List;
2419
import java.util.Map;
2520

26-
@RequestMapping("/api/v1/buildings")
21+
@Transactional(readOnly = true)
2722
@RequiredArgsConstructor
28-
@RestController
29-
public class BuildingController {
30-
31-
private final BuildingService buildingService;
32-
private final JwtValidator jwtValidator;
23+
@Service
24+
public class GptService {
3325

3426
@Value("${openai.api.url}")
3527
private String openAiApiUrl;
3628

3729
@Value("${openai.api.key}")
3830
private String openAiApiKey;
3931

40-
@GetMapping("/{building-id}")
41-
public ApiResponse<BuildingDetailResponse> getBuildingDetail(
42-
@PathVariable(name = "building-id") String buildingId
43-
) {
44-
return ApiResponse.success(BuildingDetailResponse.toResponse(buildingService.getBuilding(buildingId)));
45-
}
46-
47-
@PostMapping("/{building-id}/register")
48-
public ApiResponse<?> registerBuilding(
49-
@RequestHeader("Authorization") String token,
50-
@RequestParam("images") List<MultipartFile> multipartFiles,
51-
@PathVariable(name = "building-id") String buildingId
52-
53-
) throws IOException {
54-
User requestUser = jwtValidator.getUserFromToken(token);
55-
List<String> urls = buildingService.registerBuilding(buildingId, requestUser, multipartFiles);
32+
public BuildingUpdate analyzeBuilding(List<String> urls) throws JsonProcessingException {
5633
BuildingUpdate buildingUpdate;
5734
ImageRequest imageRequest = new ImageRequest();
5835
List<ImageRequest.Content> contentList = new ArrayList<>();
@@ -71,14 +48,9 @@ public ApiResponse<?> registerBuilding(
7148
}
7249

7350
imageRequest.setContent(contentList);
74-
75-
76-
buildingUpdate = analyzeHouseImages(imageRequest);
77-
78-
return ApiResponse.success(buildingUpdate);
51+
return analyzeHouseImages(imageRequest);
7952
}
8053

81-
8254
public BuildingUpdate analyzeHouseImages(ImageRequest imageRequest) throws JsonProcessingException {
8355

8456
// 시스템 메시지

src/main/java/org/khtml/hexagonal/domain/auth/AuthService.java

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,22 @@ public class AuthService {
1717

1818
@Transactional
1919
public TokenResult login(LoginRequest loginRequest) {
20-
User newUser = User.builder()
21-
.providerId(loginRequest.providerId())
22-
.locationConsent(loginRequest.locationConsent())
23-
.numberOfPersons(loginRequest.numberOfPersons())
24-
.phoneNumber(loginRequest.phoneNumber())
25-
.username(loginRequest.username())
26-
.userType(UserType.valueOf(loginRequest.userType()))
27-
.build();
28-
29-
User savedUser = userRepository.save(newUser);
30-
String accessToken = jwtGenerator.generateAccessToken(savedUser.getId());
20+
User user;
21+
if (!userRepository.existsByProviderId(loginRequest.providerId())) {
22+
user = User.builder()
23+
.providerId(loginRequest.providerId())
24+
.locationConsent(loginRequest.locationConsent())
25+
.numberOfPersons(loginRequest.numberOfPersons())
26+
.phoneNumber(loginRequest.phoneNumber())
27+
.username(loginRequest.username())
28+
.userType(UserType.valueOf(loginRequest.userType()))
29+
.build();
30+
} else {
31+
user = userRepository.findByProviderId(loginRequest.providerId())
32+
.orElseThrow(() -> new IllegalArgumentException("User not found"));
33+
}
34+
userRepository.save(user);
35+
String accessToken = jwtGenerator.generateAccessToken(user.getId());
3136
String refreshToken = jwtGenerator.generateRefreshToken();
3237

3338
return new TokenResult(accessToken, refreshToken);

src/main/java/org/khtml/hexagonal/domain/building/BlobManager.java renamed to src/main/java/org/khtml/hexagonal/domain/building/application/BlobManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.khtml.hexagonal.domain.building;
1+
package org.khtml.hexagonal.domain.building.application;
22

33
import com.azure.storage.blob.BlobClient;
44
import com.azure.storage.blob.BlobContainerClient;

src/main/java/org/khtml/hexagonal/domain/building/application/BuildingService.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,25 @@
11
package org.khtml.hexagonal.domain.building.application;
22

33
import lombok.RequiredArgsConstructor;
4+
import org.khtml.hexagonal.domain.building.ImageType;
45
import org.khtml.hexagonal.domain.building.dto.BuildingUpdate;
5-
import org.khtml.hexagonal.domain.building.*;
6+
import org.khtml.hexagonal.domain.building.entity.Building;
7+
import org.khtml.hexagonal.domain.building.entity.BuildingImage;
8+
import org.khtml.hexagonal.domain.building.entity.Image;
9+
import org.khtml.hexagonal.domain.building.repository.BuildingImageRepository;
10+
import org.khtml.hexagonal.domain.building.repository.BuildingRepository;
11+
import org.khtml.hexagonal.domain.building.repository.ImageRepository;
612
import org.khtml.hexagonal.domain.user.User;
713
import org.khtml.hexagonal.domain.user.UserRepository;
14+
import org.khtml.hexagonal.global.support.error.ErrorType;
815
import org.springframework.stereotype.Service;
916
import org.springframework.transaction.annotation.Transactional;
1017
import org.springframework.web.multipart.MultipartFile;
1118

1219
import java.io.IOException;
1320
import java.util.ArrayList;
1421
import java.util.List;
22+
import java.util.Objects;
1523

1624
@RequiredArgsConstructor
1725
@Transactional(readOnly = true)
@@ -64,7 +72,7 @@ public Building updateBuilding(String buildingId, BuildingUpdate buildingUpdate)
6472
@Transactional
6573
public List<String> registerBuilding(String buildingId, User requestUser, List<MultipartFile> multipartFiles) throws IOException {
6674
List<String> ret = new ArrayList<>();
67-
for(MultipartFile file : multipartFiles) {
75+
for (MultipartFile file : multipartFiles) {
6876
String url = blobManager.storeFile(file.getOriginalFilename(), file.getInputStream(), file.getSize());
6977

7078
User user = userRepository.findById(requestUser.getId()).orElseThrow(() -> new IllegalArgumentException("User not found"));
@@ -75,6 +83,7 @@ public List<String> registerBuilding(String buildingId, User requestUser, List<M
7583

7684
Image image = Image.builder()
7785
.url(url)
86+
.imageType(ImageType.BUILDING)
7887
.user(user)
7988
.build();
8089

@@ -92,4 +101,24 @@ public List<String> registerBuilding(String buildingId, User requestUser, List<M
92101
return ret;
93102
}
94103

104+
@Transactional
105+
public void updateAnalyzedBuilding(String buildingId, BuildingUpdate buildingUpdate) {
106+
Building building = buildingRepository.findBuildingByGisBuildingId(buildingId)
107+
.orElseThrow(() -> new IllegalArgumentException("Building not found"));
108+
109+
building.updateAnalyzedData(buildingUpdate);
110+
}
111+
112+
@Transactional
113+
public void updateBuildingDescription(String buildingId, Long userId, String description) {
114+
Building building = buildingRepository.findBuildingByGisBuildingId(buildingId)
115+
.orElseThrow(() -> new IllegalArgumentException("Building not found"));
116+
117+
if(!Objects.equals(building.getUser().getId(), userId)) {
118+
throw new IllegalArgumentException(ErrorType.DEFAULT_ERROR.getMessage());
119+
}
120+
121+
building.setDescription(description);
122+
}
123+
95124
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package org.khtml.hexagonal.domain.building.controller;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.khtml.hexagonal.domain.ai.application.GptService;
5+
import org.khtml.hexagonal.domain.building.dto.*;
6+
import org.khtml.hexagonal.domain.auth.JwtValidator;
7+
import org.khtml.hexagonal.domain.building.application.BuildingService;
8+
import org.khtml.hexagonal.domain.user.User;
9+
import org.khtml.hexagonal.global.support.response.ApiResponse;
10+
import org.springframework.web.bind.annotation.*;
11+
import org.springframework.web.multipart.MultipartFile;
12+
13+
import java.io.IOException;
14+
import java.util.List;
15+
16+
@RequestMapping("/api/v1/buildings")
17+
@RequiredArgsConstructor
18+
@RestController
19+
public class BuildingController {
20+
21+
private final BuildingService buildingService;
22+
private final JwtValidator jwtValidator;
23+
private final GptService gptService;
24+
25+
@GetMapping("/{building-id}")
26+
public ApiResponse<BuildingDetailResponse> getBuildingDetail(
27+
@PathVariable(name = "building-id") String buildingId
28+
) {
29+
return ApiResponse.success(BuildingDetailResponse.toResponse(buildingService.getBuilding(buildingId)));
30+
}
31+
32+
@PostMapping("/{building-id}/register")
33+
public ApiResponse<?> registerBuilding(
34+
@RequestHeader("Authorization") String token,
35+
@RequestParam("images") List<MultipartFile> multipartFiles,
36+
@PathVariable(name = "building-id") String buildingId
37+
) throws IOException {
38+
User requestUser = jwtValidator.getUserFromToken(token);
39+
List<String> urls = buildingService.registerBuilding(buildingId, requestUser, multipartFiles);
40+
41+
BuildingUpdate buildingUpdate = gptService.analyzeBuilding(urls);
42+
buildingService.updateAnalyzedBuilding(buildingId, buildingUpdate);
43+
44+
return ApiResponse.success(buildingUpdate);
45+
}
46+
47+
@PostMapping("/{building-id}/register/description")
48+
public ApiResponse<?> registerBuilding(
49+
@RequestHeader("Authorization") String token,
50+
@PathVariable(name = "building-id") String buildingId,
51+
@RequestBody BuildingDescriptionRequest buildingDescriptionRequest
52+
) throws IOException {
53+
User requestUser = jwtValidator.getUserFromToken(token);
54+
buildingService.updateBuildingDescription(buildingId, requestUser.getId(), buildingDescriptionRequest.description());
55+
56+
return ApiResponse.success();
57+
}
58+
59+
60+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package org.khtml.hexagonal.domain.building.dto;
2+
3+
public record BuildingDescriptionRequest(
4+
String description
5+
) {
6+
}

src/main/java/org/khtml/hexagonal/domain/building/BuildingDetailResponse.java renamed to src/main/java/org/khtml/hexagonal/domain/building/dto/BuildingDetailResponse.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
package org.khtml.hexagonal.domain.building;
1+
package org.khtml.hexagonal.domain.building.dto;
2+
3+
import org.khtml.hexagonal.domain.building.entity.Building;
24

35
public record BuildingDetailResponse(
46
String buildingId,

src/main/java/org/khtml/hexagonal/domain/building/Building.java renamed to src/main/java/org/khtml/hexagonal/domain/building/entity/Building.java

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
package org.khtml.hexagonal.domain.building;
1+
package org.khtml.hexagonal.domain.building.entity;
22

33
import jakarta.persistence.*;
44
import lombok.Getter;
55
import lombok.NoArgsConstructor;
66
import lombok.Setter;
7+
import org.khtml.hexagonal.domain.building.BuildingStatus;
8+
import org.khtml.hexagonal.domain.building.dto.BuildingUpdate;
79
import org.khtml.hexagonal.domain.user.User;
810

9-
import java.util.List;
10-
1111
@Table(name = "building")
1212
@Getter
1313
@Setter
@@ -18,6 +18,7 @@ public class Building {
1818
@Id
1919
private String gisBuildingId;
2020

21+
@Enumerated(EnumType.STRING)
2122
@Column(name = "building_status")
2223
private BuildingStatus buildingStatus = BuildingStatus.NOT_REGISTERED;
2324

@@ -105,6 +106,9 @@ public class Building {
105106
@Column(name = "description")
106107
private String description;
107108

109+
@Column(name = "is_analyzed")
110+
private Boolean isAnalyzed = false;
111+
108112
@OneToOne(fetch = FetchType.LAZY)
109113
@JoinColumn(name = "user_id")
110114
private User user;
@@ -113,4 +117,24 @@ public void updateUser(User user) {
113117
this.user = user;
114118
}
115119

120+
public void updateAnalyzedData(BuildingUpdate buildingUpdate) {
121+
this.structureReason = buildingUpdate.getStructureReason();
122+
this.roofMaterial = buildingUpdate.getRoofMaterial();
123+
this.roofCondition = buildingUpdate.getRoofCondition();
124+
this.wallMaterial = buildingUpdate.getWallMaterial();
125+
this.wallCondition = buildingUpdate.getWallCondition();
126+
this.windowDoorMaterial = buildingUpdate.getWindowDoorMaterial();
127+
this.windowDoorCondition = buildingUpdate.getWindowDoorCondition();
128+
this.overallCondition = buildingUpdate.getOverallCondition();
129+
this.conditionReason = buildingUpdate.getConditionReason();
130+
this.crackScore = buildingUpdate.getCrackScore();
131+
this.leakScore = buildingUpdate.getLeakScore();
132+
this.corrosionScore = buildingUpdate.getCorrosionScore();
133+
this.agingScore = buildingUpdate.getAgingScore();
134+
this.totalScore = buildingUpdate.getTotalScore();
135+
this.repairList = buildingUpdate.getRepairList();
136+
this.isAnalyzed = true;
137+
this.buildingStatus = BuildingStatus.REGISTERED;
138+
}
139+
116140
}

src/main/java/org/khtml/hexagonal/domain/building/BuildingImage.java renamed to src/main/java/org/khtml/hexagonal/domain/building/entity/BuildingImage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package org.khtml.hexagonal.domain.building;
1+
package org.khtml.hexagonal.domain.building.entity;
22

33

44
import jakarta.persistence.*;

src/main/java/org/khtml/hexagonal/domain/building/Image.java renamed to src/main/java/org/khtml/hexagonal/domain/building/entity/Image.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
package org.khtml.hexagonal.domain.building;
1+
package org.khtml.hexagonal.domain.building.entity;
22

33
import jakarta.persistence.*;
44
import lombok.AccessLevel;
55
import lombok.Builder;
66
import lombok.Getter;
77
import lombok.NoArgsConstructor;
8+
import org.khtml.hexagonal.domain.building.ImageType;
89
import org.khtml.hexagonal.domain.common.BaseEntity;
910
import org.khtml.hexagonal.domain.user.User;
1011

0 commit comments

Comments
 (0)