Skip to content

Commit e92748f

Browse files
authored
Merge pull request #207 from prgrms-web-devcourse-final-project/develop
메인 머지(10/12)
2 parents c9f1a92 + 7bd8ae3 commit e92748f

29 files changed

+1561
-144
lines changed

.github/workflows/deploy.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,14 @@ jobs:
8989
- name: .env 파일 생성
9090
env:
9191
DOT_ENV: ${{ secrets.DOT_ENV }}
92+
AI_API_KEY: ${{ secrets.AI_API_KEY }}
93+
AI_BASE_URL: ${{ secrets.AI_BASE_URL }}
9294
run: |
9395
mkdir -p "${{ env.BACKEND_DIR }}"
9496
printf "%s" "${DOT_ENV}" > "${{ env.BACKEND_DIR }}/.env"
97+
echo "" >> "${{ env.BACKEND_DIR }}/.env"
98+
echo "AI_API_KEY=${AI_API_KEY}" >> "${{ env.BACKEND_DIR }}/.env"
99+
echo "AI_BASE_URL=${AI_BASE_URL}" >> "${{ env.BACKEND_DIR }}/.env"
95100
96101
- name: Docker Buildx 설치
97102
uses: docker/setup-buildx-action@v3
@@ -216,4 +221,4 @@ jobs:
216221
| xargs -r docker rmi
217222
} || true
218223
219-
echo "✅ 배포 완료!"
224+
echo "✅ 배포 완료!"

back/.env.default

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ DB_USERNAME=NEED_TO_SET
33
DB_PASSWORD=NEED_TO_SET
44
MAIL_USERNAME=NEED_TO_SET
55
MAIL_PASSWORD=NEED_TO_SET
6+
AI_API_KEY=NEED_TO_SET
7+
AI_BASE_URL=NEED_TO_SET

back/build.gradle.kts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ plugins {
33
id("org.springframework.boot") version "3.5.5"
44
id("io.spring.dependency-management") version "1.1.7"
55
}
6+
val springAiVersion by extra("1.0.3")
67

78
group = "com"
89
version = "0.0.1-SNAPSHOT"
@@ -72,6 +73,14 @@ dependencies {
7273

7374
// Sentry
7475
implementation("io.sentry:sentry-spring-boot-starter-jakarta:8.19.1")
76+
77+
// AI
78+
implementation("org.springframework.ai:spring-ai-starter-model-openai")
79+
}
80+
dependencyManagement {
81+
imports {
82+
mavenBom("org.springframework.ai:spring-ai-bom:$springAiVersion")
83+
}
7584
}
7685

7786
tasks.withType<Test> {

back/src/main/java/com/back/domain/file/video/service/FileManager.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ public class FileManager {
1818
public PresignedUrlResponse getUploadUrl() {
1919
String uuid = UUID.randomUUID().toString();
2020
Integer expires = 5;
21-
URL url = s3Service.generateUploadUrl("videos", uuid, expires);
21+
URL url = s3Service.generateUploadUrl("videos/"+uuid, expires);
2222
LocalDateTime expiresAt = LocalDateTime.now().plusMinutes(expires);
2323
return new PresignedUrlResponse(url, expiresAt);
2424
}
2525

2626
public PresignedUrlResponse getDownloadUrl(String objectKey) {
2727
Integer expires = 60;
28-
URL url = s3Service.generateDownloadUrl("videos", objectKey, expires);
28+
URL url = s3Service.generateDownloadUrl(objectKey, expires);
2929
LocalDateTime expiresAt = LocalDateTime.now().plusMinutes(expires);
3030
return new PresignedUrlResponse(url, expiresAt);
3131
}

back/src/main/java/com/back/domain/file/video/service/S3Service.java

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

33
import com.back.global.exception.ServiceException;
44
import lombok.RequiredArgsConstructor;
5+
import org.springframework.beans.factory.annotation.Value;
56
import org.springframework.stereotype.Service;
67
import software.amazon.awssdk.services.s3.S3Client;
78
import software.amazon.awssdk.services.s3.model.*;
@@ -17,8 +18,12 @@
1718
public class S3Service {
1819
private final S3Presigner presigner;
1920
private final S3Client s3Client;
21+
@Value("${aws.s3.bucket}")
22+
private String bucket;
23+
24+
public URL generateUploadUrl(String objectKey, Integer expireMinutes) {
25+
validateRequest(objectKey);
2026

21-
public URL generateUploadUrl(String bucket, String objectKey, Integer expireMinutes) {
2227
PutObjectRequest request = PutObjectRequest.builder()
2328
.bucket(bucket)
2429
.key(objectKey)
@@ -33,19 +38,15 @@ public URL generateUploadUrl(String bucket, String objectKey, Integer expireMinu
3338
throw new ServiceException("500", "Presigned URL 생성 실패");
3439
}
3540

36-
URL url = presignedRequest.url();
37-
38-
return url;
41+
return presignedRequest.url();
3942
}
4043

41-
public URL generateUploadUrl(String bucket, String objectKey) {
42-
validateRequest(bucket, objectKey);
43-
44-
return generateUploadUrl(bucket, objectKey, 30);
44+
public URL generateUploadUrl(String objectKey) {
45+
return generateUploadUrl(objectKey, 30);
4546
}
4647

47-
public URL generateDownloadUrl(String bucket, String objectKey, Integer expireHours) {
48-
isExist(bucket, objectKey);
48+
public URL generateDownloadUrl(String objectKey, Integer expireMinutes) {
49+
isExist(objectKey);
4950

5051
GetObjectRequest request = GetObjectRequest.builder()
5152
.bucket(bucket)
@@ -54,25 +55,22 @@ public URL generateDownloadUrl(String bucket, String objectKey, Integer expireHo
5455

5556
PresignedGetObjectRequest presignedRequest =
5657
presigner.presignGetObject(builder -> builder
57-
.signatureDuration(Duration.ofMinutes(expireHours))
58+
.signatureDuration(Duration.ofMinutes(expireMinutes))
5859
.getObjectRequest(request));
5960

6061
if (presignedRequest == null) {
6162
throw new ServiceException("500", "Presigned URL 생성 실패");
6263
}
6364

64-
URL url = presignedRequest.url();
65-
66-
return url;
65+
return presignedRequest.url();
6766
}
6867

69-
public URL generateDownloadUrl(String bucket, String objectKey) {
70-
validateRequest(bucket, objectKey);
71-
72-
return generateDownloadUrl(bucket, objectKey, 60);
68+
public URL generateDownloadUrl(String objectKey) {
69+
return generateDownloadUrl(objectKey, 60);
7370
}
7471

75-
public void isExist(String bucket, String objectKey) {
72+
public void isExist(String objectKey) {
73+
validateRequest(objectKey);
7674
try {
7775
HeadObjectRequest headRequest = HeadObjectRequest.builder()
7876
.bucket(bucket)
@@ -87,9 +85,9 @@ public void isExist(String bucket, String objectKey) {
8785
}
8886
}
8987

90-
public void validateRequest(String bucket, String objectKey) {
91-
if (bucket == null || bucket.isEmpty() || objectKey == null || objectKey.isEmpty()) {
92-
throw new ServiceException("400", "버킷 이름과 객체 키는 필수입니다.");
88+
public void validateRequest(String objectKey) {
89+
if (objectKey == null || objectKey.isEmpty()) {
90+
throw new ServiceException("400", "객체 키는 필수입니다.");
9391
}
9492
}
9593
}

back/src/main/java/com/back/domain/post/comment/controller/PostCommentController.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@
2121
import java.util.List;
2222

2323
@RestController
24-
@RequestMapping("/post/comment")
24+
@RequestMapping("/post")
2525
@RequiredArgsConstructor
2626
public class PostCommentController {
2727

2828
private final Rq rq;
2929
private final PostCommentService postCommentService;
3030

3131
@Operation(summary = "댓글 생성", description = "comment는 공백이나 Null이 될 수 없습니다. comment의 글자 수 제한은 없습니다.")
32-
@PostMapping("/post/{post_id}")
32+
@PostMapping("/{post_id}/comment")
3333
public RsData<Void> createComment(@PathVariable Long post_id,
3434
@Valid @RequestBody CommentCreateRequest commentCreateRequest
3535
) {
@@ -47,7 +47,7 @@ public RsData<List<CommentAllResponse>> getAllPostComment(@PathVariable Long pos
4747
}
4848

4949
@Operation(summary = "댓글 삭제", description = "commentId는 공백이나 Null이 될 수 없습니다.")
50-
@DeleteMapping("/post/{post_id}/comment")
50+
@DeleteMapping("/{post_id}/comment")
5151
public RsData<Void> removePostComment(@PathVariable @Positive Long post_id
5252
, @RequestBody @Valid CommentDeleteRequest commentDeleteRequest) {
5353
Member member = rq.getActor();
@@ -58,7 +58,7 @@ public RsData<Void> removePostComment(@PathVariable @Positive Long post_id
5858
}
5959

6060
@Operation(summary = "댓글 수정", description = "commentId, content는 공백이나 Null이 될 수 없습니다. content의 글자 수 제한은 없습니다. ")
61-
@PutMapping("/post/{post_id}/comment/")
61+
@PutMapping("/{post_id}/comment")
6262
public RsData<Void> updatePostComment(@PathVariable Long post_id
6363
, @Valid @RequestBody CommentModifyRequest commentModifyRequest) {
6464
Member member = rq.getActor();
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.back.domain.roadmap.roadmap.dto.response;
2+
3+
public record TextFieldIntegrationResponse(
4+
String learningAdvice,
5+
String recommendedResources,
6+
String learningGoals
7+
) {
8+
}

back/src/main/java/com/back/domain/roadmap/roadmap/repository/MentorRoadmapRepository.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@ public interface MentorRoadmapRepository extends JpaRepository<MentorRoadmap, Lo
4141
boolean existsByMentor(Mentor mentor);
4242

4343
/**
44-
* 직업 ID로 멘토 로드맵 목록 조회 (노드 포함)
44+
* 직업 ID로 멘토 로드맵 목록 조회 (노드 및 Task 포함)
4545
* - 특정 직업에 속한 멘토들의 로드맵 목록 조회
4646
* - 통합된 직업 로드맵 생성용
47+
* - Task까지 fetch하여 트랜잭션 외부에서도 접근 가능하도록 보장
4748
*/
4849
@Query("""
49-
SELECT mr FROM MentorRoadmap mr
50+
SELECT DISTINCT mr FROM MentorRoadmap mr
5051
LEFT JOIN FETCH mr.nodes n
52+
LEFT JOIN FETCH n.task t
5153
WHERE mr.mentor.jobId = :jobId
5254
ORDER BY mr.id, n.stepOrder
5355
""")

back/src/main/java/com/back/domain/roadmap/roadmap/service/JobRoadmapBatchIntegrator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class JobRoadmapBatchIntegrator {
1818
private final JobRoadmapIntegrationProcessor processor;
1919
private static final int MAX_RETRY = 3;
2020

21-
@Scheduled(fixedDelay = 120000) // 2분
21+
@Scheduled(fixedDelay = 3600000) // 1시간 (3600000ms)
2222
public void integrate() {
2323
List<JobRoadmapIntegrationQueue> pendingQueues = queueRepository.findAllOrderByRequestedAt();
2424

back/src/main/java/com/back/domain/roadmap/roadmap/service/JobRoadmapIntegrationProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
@Slf4j
1414
public class JobRoadmapIntegrationProcessor {
1515
private final JobRoadmapIntegrationQueueRepository queueRepository;
16-
private final JobRoadmapIntegrationServiceV2 integrationService;
16+
private final JobRoadmapIntegrationServiceV3 integrationService;
1717

1818
/**
1919
* 단일 큐 항목 처리 (통합 + 큐 삭제를 하나의 트랜잭션으로)

0 commit comments

Comments
 (0)