Skip to content

Commit fcde020

Browse files
authored
Merge pull request #13 from 9oormthon-univ/develop
[Feat] ChatGPT로 이미지 분석 및 상세 조회 및 가이드 반환 develop -> main
2 parents 8cef4eb + 14c6d09 commit fcde020

38 files changed

+950
-215
lines changed

build.gradle

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ plugins {
44
id 'io.spring.dependency-management' version '1.1.7'
55
}
66

7+
ext {
8+
springAiVersion = "1.0.1"
9+
}
10+
711
group = 'com'
812
version = '0.0.1-SNAPSHOT'
913
description = 'trash-heroes-be'
@@ -51,6 +55,15 @@ dependencies {
5155

5256
// s3
5357
implementation "io.awspring.cloud:spring-cloud-aws-starter-s3:3.4.0"
58+
59+
// openai
60+
implementation 'org.springframework.ai:spring-ai-starter-model-openai'
61+
}
62+
63+
dependencyManagement {
64+
imports {
65+
mavenBom "org.springframework.ai:spring-ai-bom:$springAiVersion"
66+
}
5467
}
5568

5669
tasks.named('test') {

src/main/java/com/trashheroesbe/feature/district/api/DistrictController.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import com.trashheroesbe.feature.district.application.DistrictService;
66
import com.trashheroesbe.feature.district.dto.response.DistrictListResponse;
77
import com.trashheroesbe.global.response.ApiResponse;
8-
import java.util.ArrayList;
98
import java.util.List;
109
import lombok.RequiredArgsConstructor;
1110
import org.springframework.web.bind.annotation.GetMapping;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.trashheroesbe.feature.gpt.dto.request;
2+
3+
public record TrashAnalysisRequestDt(
4+
String imageUrl
5+
) {
6+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.trashheroesbe.feature.gpt.dto.response;
2+
3+
4+
import com.trashheroesbe.feature.trash.domain.entity.TrashType;
5+
6+
public record TrashAnalysisResponseDto(
7+
TrashType type,
8+
String item,
9+
String description
10+
) {
11+
public static TrashAnalysisResponseDto of(TrashType type, String item, String description) {
12+
return new TrashAnalysisResponseDto(type, item, description);
13+
}
14+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.trashheroesbe.feature.question.api;
2+
3+
import static com.trashheroesbe.global.response.type.SuccessCode.OK;
4+
5+
import com.trashheroesbe.feature.question.application.QuestionService;
6+
import com.trashheroesbe.feature.trash.dto.response.TrashItemResponse;
7+
import com.trashheroesbe.feature.trash.dto.response.TrashTypeResponse;
8+
import com.trashheroesbe.global.response.ApiResponse;
9+
import java.util.List;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.web.bind.annotation.GetMapping;
12+
import org.springframework.web.bind.annotation.PathVariable;
13+
import org.springframework.web.bind.annotation.RequestMapping;
14+
import org.springframework.web.bind.annotation.RestController;
15+
16+
@RestController
17+
@RequiredArgsConstructor
18+
@RequestMapping("/api/v1/questions")
19+
public class QuestionController implements QuestionControllerApi {
20+
21+
private final QuestionService questionService;
22+
23+
@Override
24+
@GetMapping("/trash-types")
25+
public ApiResponse<List<TrashTypeResponse>> getTrashTypes() {
26+
List<TrashTypeResponse> response = questionService.getTrashTypes();
27+
return ApiResponse.success(OK, response);
28+
}
29+
30+
@Override
31+
@GetMapping("/trash-types/{trashTypeId}")
32+
public ApiResponse<List<TrashItemResponse>> getTrashItems(
33+
@PathVariable Long trashTypeId
34+
) {
35+
List<TrashItemResponse> response = questionService.getTrashItems(trashTypeId);
36+
return ApiResponse.success(OK, response);
37+
}
38+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.trashheroesbe.feature.question.api;
2+
3+
import com.trashheroesbe.feature.trash.dto.response.TrashItemResponse;
4+
import com.trashheroesbe.feature.trash.dto.response.TrashTypeResponse;
5+
import com.trashheroesbe.global.response.ApiResponse;
6+
import io.swagger.v3.oas.annotations.Operation;
7+
import io.swagger.v3.oas.annotations.tags.Tag;
8+
import java.util.List;
9+
10+
@Tag(name = "Question", description = "질문 관련 API(챗봇)")
11+
public interface QuestionControllerApi {
12+
13+
@Operation(summary = "상위 쓰레기 카테고리 조회(쓰레기 타입 조회)", description = "쓰레기 카테고리를 조회합니다.(쓰레기 타입)")
14+
ApiResponse<List<TrashTypeResponse>> getTrashTypes();
15+
16+
@Operation(summary = "쓰레기 카테고리별 품목 조회(쓰레기 품목 조회)", description = "쓰레기 품목을 조회 합니다.")
17+
ApiResponse<List<TrashItemResponse>> getTrashItems(Long trashTypeId);
18+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.trashheroesbe.feature.question.application;
2+
3+
import static com.trashheroesbe.global.response.type.ErrorCode.NOT_EXISTS_TRASH_ITEM;
4+
import static com.trashheroesbe.global.response.type.ErrorCode.NOT_EXISTS_TRASH_TYPE;
5+
6+
import com.trashheroesbe.feature.trash.domain.entity.TrashItem;
7+
import com.trashheroesbe.feature.trash.domain.entity.TrashType;
8+
import com.trashheroesbe.feature.trash.domain.service.TrashItemFinder;
9+
import com.trashheroesbe.feature.trash.domain.service.TrashTypeFinder;
10+
import com.trashheroesbe.feature.trash.dto.response.TrashItemResponse;
11+
import com.trashheroesbe.feature.trash.dto.response.TrashTypeResponse;
12+
import com.trashheroesbe.global.exception.BusinessException;
13+
import java.util.List;
14+
import java.util.stream.Collectors;
15+
import lombok.RequiredArgsConstructor;
16+
import org.springframework.stereotype.Service;
17+
import org.springframework.transaction.annotation.Transactional;
18+
19+
@Service
20+
@Transactional(readOnly = true)
21+
@RequiredArgsConstructor
22+
public class QuestionService {
23+
24+
private final TrashTypeFinder trashTypeFinder;
25+
private final TrashItemFinder trashItemFinder;
26+
27+
public List<TrashTypeResponse> getTrashTypes() {
28+
List<TrashType> trashTypes = trashTypeFinder.getAllTrashTypes();
29+
if (trashTypes.isEmpty()) {
30+
throw new BusinessException(NOT_EXISTS_TRASH_TYPE);
31+
}
32+
return trashTypes.stream()
33+
.map(TrashTypeResponse::from)
34+
.collect(Collectors.toList());
35+
}
36+
37+
public List<TrashItemResponse> getTrashItems(Long trashTypeId) {
38+
List<TrashItem> trashItems = trashItemFinder.findTrashItemsByTrashTypeId(trashTypeId);
39+
if (trashItems.isEmpty()) {
40+
throw new BusinessException(NOT_EXISTS_TRASH_ITEM);
41+
}
42+
return trashItems.stream()
43+
.map(TrashItemResponse::from)
44+
.collect(Collectors.toList());
45+
}
46+
}

src/main/java/com/trashheroesbe/feature/trash/api/TrashController.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import com.trashheroesbe.feature.trash.application.TrashService;
44
import com.trashheroesbe.feature.trash.dto.request.CreateTrashRequest;
5-
import com.trashheroesbe.feature.trash.dto.response.TrashResult;
65
import com.trashheroesbe.feature.trash.application.TrashCreateUseCase;
6+
import com.trashheroesbe.feature.trash.dto.response.TrashResultResponse;
77
import com.trashheroesbe.global.auth.security.CustomerDetails;
88
import com.trashheroesbe.global.response.ApiResponse;
99
import com.trashheroesbe.global.response.type.SuccessCode;
@@ -25,34 +25,45 @@ public class TrashController implements TrashControllerApi {
2525

2626
@Override
2727
@PostMapping
28-
public ApiResponse<TrashResult> createTrash(
28+
public ApiResponse<TrashResultResponse> createTrash(
2929
@ModelAttribute CreateTrashRequest request,
3030
@AuthenticationPrincipal CustomerDetails customerDetails
3131
) {
3232
log.info("쓰레기 생성 요청: userId={}, fileName={}",
3333
customerDetails.getUser().getId(), request.imageFile().getOriginalFilename());
3434

35-
TrashResult result = trashCreateUseCase.createTrash(request, customerDetails.getUser());
35+
TrashResultResponse result = trashCreateUseCase.createTrash(request, customerDetails.getUser());
3636
return ApiResponse.success(SuccessCode.OK, result);
3737
}
3838

3939
@Override
4040
@GetMapping("/{trashId}")
41-
public ApiResponse<TrashResult> getTrash(@PathVariable Long trashId) {
41+
public ApiResponse<TrashResultResponse> getTrash(@PathVariable Long trashId) {
4242
// TODO: 조회 로직 구현 필요
4343
return null;
4444
}
4545

4646
@Override
4747
@GetMapping("/my")
48-
public ApiResponse<List<TrashResult>> getMyTrash(
48+
public ApiResponse<List<TrashResultResponse>> getMyTrash(
4949
@AuthenticationPrincipal CustomerDetails customerDetails
5050
) {
5151
log.info("내 쓰레기 목록 조회 요청: userId={}", customerDetails.getUser().getId());
5252

53-
List<TrashResult> myTrashList = trashService.getTrashByUser(customerDetails.getUser());
53+
List<TrashResultResponse> myTrashList = trashService.getTrashByUser(customerDetails.getUser());
5454

5555
return ApiResponse.success(SuccessCode.OK, myTrashList);
5656
}
5757

58+
@Override
59+
@DeleteMapping("/{trashId}")
60+
public ApiResponse<Void> deleteTrash(
61+
@PathVariable Long trashId,
62+
@AuthenticationPrincipal CustomerDetails customerDetails
63+
) {
64+
log.info("쓰레기 삭제 요청: userId={}, trashId={}", customerDetails.getUser().getId(), trashId);
65+
trashService.deleteTrash(trashId, customerDetails.getUser());
66+
return ApiResponse.success(SuccessCode.OK);
67+
}
68+
5869
}

src/main/java/com/trashheroesbe/feature/trash/api/TrashControllerApi.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22

33

44
import com.trashheroesbe.feature.trash.dto.request.CreateTrashRequest;
5-
import com.trashheroesbe.feature.trash.dto.response.TrashResult;
5+
import com.trashheroesbe.feature.trash.dto.response.TrashResultResponse;
66
import com.trashheroesbe.global.auth.security.CustomerDetails;
77
import com.trashheroesbe.global.response.ApiResponse;
88
import io.swagger.v3.oas.annotations.Operation;
99
import io.swagger.v3.oas.annotations.media.Content;
1010
import io.swagger.v3.oas.annotations.media.Schema;
11+
import io.swagger.v3.oas.annotations.parameters.RequestBody;
1112
import io.swagger.v3.oas.annotations.tags.Tag;
1213
import org.springframework.security.core.annotation.AuthenticationPrincipal;
1314
import org.springframework.web.bind.annotation.PathVariable;
@@ -20,18 +21,21 @@ public interface TrashControllerApi {
2021
@Operation(
2122
summary = "쓰레기 생성",
2223
description = "현재 인증된 사용자의 이미지 파일로 쓰레기를 생성합니다. 이미지는 S3에 업로드되고 URL이 저장됩니다.",
23-
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
24+
requestBody = @RequestBody(
2425
content = @Content(
2526
mediaType = "multipart/form-data",
2627
schema = @Schema(implementation = CreateTrashRequest.class)
2728
)
2829
)
2930
)
30-
ApiResponse<TrashResult> createTrash(CreateTrashRequest request, @AuthenticationPrincipal CustomerDetails customerDetails);
31+
ApiResponse<TrashResultResponse> createTrash(CreateTrashRequest request, @AuthenticationPrincipal CustomerDetails customerDetails);
3132

3233
@Operation(summary = "쓰레기 조회", description = "쓰레기 ID로 정보를 조회합니다.")
33-
ApiResponse<TrashResult> getTrash(@PathVariable Long trashId);
34+
ApiResponse<TrashResultResponse> getTrash(@PathVariable Long trashId);
3435

3536
@Operation(summary = "내 쓰레기 목록", description = "현재 인증된 사용자의 모든 쓰레기를 조회합니다.")
36-
ApiResponse<List<TrashResult>> getMyTrash(@AuthenticationPrincipal CustomerDetails customerDetails);
37+
ApiResponse<List<TrashResultResponse>> getMyTrash(@AuthenticationPrincipal CustomerDetails customerDetails);
38+
39+
@Operation(summary = "쓰레기 삭제", description = "쓰레기와 해당 이미지(S3)를 함께 삭제합니다.")
40+
ApiResponse<Void> deleteTrash(@PathVariable Long trashId, @AuthenticationPrincipal CustomerDetails customerDetails);
3741
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package com.trashheroesbe.feature.trash.api.admin;
2+
3+
4+
import com.trashheroesbe.feature.trash.application.admin.TrashTypeAdminService;
5+
import com.trashheroesbe.global.response.ApiResponse;
6+
import com.trashheroesbe.global.response.type.SuccessCode;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.web.bind.annotation.PostMapping;
9+
import org.springframework.web.bind.annotation.RequestMapping;
10+
import org.springframework.web.bind.annotation.RestController;
11+
12+
@RestController
13+
@RequiredArgsConstructor
14+
@RequestMapping("/api/v1/admin/trash-types")
15+
public class TrashTypeAdminController implements TrashTypeAdminControllerApi {
16+
17+
private final TrashTypeAdminService trashTypeAdminService;
18+
19+
@Override
20+
@PostMapping("/initialize")
21+
public ApiResponse<Void> initializeTrashType() {
22+
trashTypeAdminService.initializeTrashType();
23+
return ApiResponse.success(SuccessCode.OK);
24+
}
25+
}

0 commit comments

Comments
 (0)