Skip to content

Commit c04869b

Browse files
authored
Merge pull request #50 from JobDri-Developer/feat/#38-mock-apply-llm
Feat/#38 mock apply llm
2 parents 043a742 + 7186574 commit c04869b

17 files changed

Lines changed: 747 additions & 72 deletions

src/main/java/com/jobdri/jobdri_api/domain/jobposting/controller/JobPostingController.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
import com.jobdri.jobdri_api.domain.jobposting.dto.request.JobPostingUpdateRequest;
77
import com.jobdri.jobdri_api.domain.jobposting.dto.response.JobPostingGenerateResponse;
88
import com.jobdri.jobdri_api.domain.jobposting.dto.response.JobPostingMockGenerateResponse;
9+
import com.jobdri.jobdri_api.domain.jobposting.dto.response.JobPostingMockQuestionResponse;
910
import com.jobdri.jobdri_api.domain.jobposting.dto.response.JobPostingResponse;
1011
import com.jobdri.jobdri_api.domain.jobposting.service.JobPostingAiService;
12+
import com.jobdri.jobdri_api.domain.jobposting.service.MockQuestionCacheService;
13+
import com.jobdri.jobdri_api.domain.jobposting.service.MockJobPostingGenerationService;
1114
import com.jobdri.jobdri_api.domain.jobposting.service.JobPostingService;
1215
import com.jobdri.jobdri_api.global.apiPayload.ApiResponse;
1316
import io.swagger.v3.oas.annotations.Operation;
@@ -32,6 +35,8 @@
3235
public class JobPostingController {
3336

3437
private final JobPostingAiService jobPostingAiService;
38+
private final MockJobPostingGenerationService mockJobPostingGenerationService;
39+
private final MockQuestionCacheService mockQuestionCacheService;
3540
private final JobPostingService jobPostingService;
3641

3742
@Operation(summary = "채용 공고 초안 생성", description = "회사 정보와 직무 정보를 바탕으로 AI가 공고 본문 초안을 생성합니다.")
@@ -55,7 +60,21 @@ public ApiResponse<JobPostingMockGenerateResponse> generateMockJobPosting(
5560
) {
5661
return ApiResponse.onSuccess(
5762
"모의 공고 생성에 성공했습니다.",
58-
jobPostingAiService.generateMockJobPosting(request)
63+
mockJobPostingGenerationService.generate(request)
64+
);
65+
}
66+
67+
@Operation(
68+
summary = "모의 공고 추천 질문 조회",
69+
description = "선택한 회사/직무 기준으로 모의 공고 추천 질문을 조회합니다. 질문은 직무 기준 캐시를 재사용합니다."
70+
)
71+
@PostMapping("/mock/questions")
72+
public ApiResponse<JobPostingMockQuestionResponse> getMockRecommendedQuestions(
73+
@Valid @RequestBody JobPostingMockGenerateRequest request
74+
) {
75+
return ApiResponse.onSuccess(
76+
"모의 공고 추천 질문 조회에 성공했습니다.",
77+
new JobPostingMockQuestionResponse(mockQuestionCacheService.getRecommendedQuestions(request))
5978
);
6079
}
6180

src/main/java/com/jobdri/jobdri_api/domain/jobposting/dto/request/JobPostingMockGenerateRequest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import jakarta.validation.constraints.NotNull;
44

55
public record JobPostingMockGenerateRequest(
6+
@NotNull(message = "회사 ID는 필수입니다.")
7+
Long companyId,
8+
69
@NotNull(message = "중분류 ID는 필수입니다.")
710
Long middleClassificationId,
811

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package com.jobdri.jobdri_api.domain.jobposting.dto.response;
22

3+
import java.util.List;
4+
35
public record JobPostingMockGenerateResponse(
46
String companyName,
57
String jobTitle,
68
String task,
79
String requirement,
810
String preferred,
9-
String summary
11+
String summary,
12+
List<String> recommendedQuestions
1013
) {
1114
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.jobdri.jobdri_api.domain.jobposting.dto.response;
2+
3+
import java.util.List;
4+
5+
public record JobPostingMockQuestionResponse(
6+
List<String> recommendedQuestions
7+
) {
8+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package com.jobdri.jobdri_api.domain.jobposting.entity;
2+
3+
import com.jobdri.jobdri_api.domain.classification.entity.DetailClassification;
4+
import jakarta.persistence.CollectionTable;
5+
import jakarta.persistence.Column;
6+
import jakarta.persistence.ElementCollection;
7+
import jakarta.persistence.Entity;
8+
import jakarta.persistence.FetchType;
9+
import jakarta.persistence.GeneratedValue;
10+
import jakarta.persistence.GenerationType;
11+
import jakarta.persistence.Id;
12+
import jakarta.persistence.JoinColumn;
13+
import jakarta.persistence.ManyToOne;
14+
import jakarta.persistence.OrderColumn;
15+
import jakarta.persistence.Table;
16+
import jakarta.persistence.UniqueConstraint;
17+
import lombok.AccessLevel;
18+
import lombok.AllArgsConstructor;
19+
import lombok.Builder;
20+
import lombok.Getter;
21+
import lombok.NoArgsConstructor;
22+
23+
import java.util.ArrayList;
24+
import java.util.List;
25+
26+
@Entity
27+
@Getter
28+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
29+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
30+
@Builder(access = AccessLevel.PRIVATE)
31+
@Table(
32+
name = "mock_question_caches",
33+
uniqueConstraints = @UniqueConstraint(
34+
name = "uk_mock_question_cache_detail_version",
35+
columnNames = {"detail_classification_id", "prompt_version"}
36+
)
37+
)
38+
public class MockQuestionCache {
39+
40+
@Id
41+
@GeneratedValue(strategy = GenerationType.IDENTITY)
42+
private Long id;
43+
44+
@ManyToOne(fetch = FetchType.LAZY, optional = false)
45+
@JoinColumn(name = "detail_classification_id", nullable = false)
46+
private DetailClassification detailClassification;
47+
48+
@Column(name = "prompt_version", nullable = false, length = 50)
49+
private String promptVersion;
50+
51+
@Builder.Default
52+
@ElementCollection
53+
@CollectionTable(
54+
name = "mock_question_cache_items",
55+
joinColumns = @JoinColumn(name = "mock_question_cache_id")
56+
)
57+
@OrderColumn(name = "question_order")
58+
@Column(name = "question_content", nullable = false, columnDefinition = "TEXT")
59+
private List<String> questions = new ArrayList<>();
60+
61+
public static MockQuestionCache create(
62+
DetailClassification detailClassification,
63+
String promptVersion,
64+
List<String> questions
65+
) {
66+
return MockQuestionCache.builder()
67+
.detailClassification(detailClassification)
68+
.promptVersion(promptVersion)
69+
.questions(new ArrayList<>(questions))
70+
.build();
71+
}
72+
}
Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,37 @@
11
package com.jobdri.jobdri_api.domain.jobposting.repository;
22

33
import com.jobdri.jobdri_api.domain.jobposting.entity.JobPosting;
4+
import org.springframework.data.jpa.repository.Query;
45
import org.springframework.data.jpa.repository.JpaRepository;
6+
import org.springframework.data.repository.query.Param;
57

68
import java.util.List;
79

810
public interface JobPostingRepository extends JpaRepository<JobPosting, Long> {
911
List<JobPosting> findAllByCompanyId(Long companyId);
10-
List<JobPosting> findAllByDetailClassificationId(Long detailClassificationId);
12+
List<JobPosting> findTop5ByDetailClassificationIdOrderByIdDesc(Long detailClassificationId);
13+
List<JobPosting> findTop5ByCompanyIdOrderByIdDesc(Long companyId);
14+
15+
@Query(value = """
16+
SELECT jp.*
17+
FROM job_postings jp
18+
WHERE jp.detail_classification_id = :detailClassificationId
19+
OR (:companyId IS NOT NULL AND jp.company_id = :companyId)
20+
ORDER BY
21+
CASE
22+
WHEN :companyId IS NOT NULL
23+
AND jp.company_id = :companyId
24+
AND jp.detail_classification_id = :detailClassificationId THEN 3
25+
WHEN jp.detail_classification_id = :detailClassificationId THEN 2
26+
WHEN :companyId IS NOT NULL
27+
AND jp.company_id = :companyId THEN 1
28+
ELSE 0
29+
END DESC,
30+
jp.id DESC
31+
LIMIT 5
32+
""", nativeQuery = true)
33+
List<JobPosting> findTop5ReferencePostings(
34+
@Param("companyId") Long companyId,
35+
@Param("detailClassificationId") Long detailClassificationId
36+
);
1137
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.jobdri.jobdri_api.domain.jobposting.repository;
2+
3+
import com.jobdri.jobdri_api.domain.jobposting.entity.MockQuestionCache;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
import java.util.Optional;
7+
8+
public interface MockQuestionCacheRepository extends JpaRepository<MockQuestionCache, Long> {
9+
Optional<MockQuestionCache> findByDetailClassification_IdAndPromptVersion(Long detailClassificationId, String promptVersion);
10+
}

0 commit comments

Comments
 (0)