Skip to content

Commit 733b150

Browse files
authored
Merge pull request #4 from CarToi/feat/ai
[Feat/ai] 클로바 스튜디오 AI 도입 + 임베딩 벡터 관련 전처리 큐 로직
2 parents 86c89da + 2683d8c commit 733b150

58 files changed

Lines changed: 1472 additions & 411 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build.gradle

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,13 @@ repositories {
2626

2727
dependencies {
2828
implementation 'org.springframework.boot:spring-boot-starter-web'
29-
implementation 'org.springframework.boot:spring-boot-starter-webflux'
3029

3130
implementation 'org.projectlombok:lombok'
3231
annotationProcessor 'org.projectlombok:lombok'
3332
testCompileOnly 'org.projectlombok:lombok'
3433
testAnnotationProcessor 'org.projectlombok:lombok'
3534

3635
testImplementation 'org.springframework.boot:spring-boot-starter-test'
37-
testImplementation 'io.projectreactor:reactor-test'
3836
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
3937

4038
// H2 DB & JPA
@@ -43,7 +41,7 @@ dependencies {
4341

4442
// selenium
4543
// https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java
46-
implementation("org.seleniumhq.selenium:selenium-java:4.33.0")
44+
// implementation("org.seleniumhq.selenium:selenium-java:4.33.0")
4745

4846
// JSoup
4947
implementation 'org.jsoup:jsoup:1.20.1'
@@ -58,6 +56,10 @@ dependencies {
5856
jmh 'org.openjdk.jmh:jmh-generator-bytecode:1.37'
5957
}
6058

59+
test {
60+
systemProperty "spring.profiles.active", "test"
61+
}
62+
6163
dependencyManagement {
6264
imports {
6365
mavenBom "org.springframework.ai:spring-ai-bom:$springAiVersion"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package org.jun.saemangeum.global.domain;
22

33
public enum Category {
4-
FESTIVAL, EVENT, TOUR
4+
FESTIVAL, EVENT, TOUR, CULTURE
55
}

src/main/java/org/jun/saemangeum/global/domain/Content.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import lombok.Builder;
66
import lombok.Getter;
77
import lombok.NoArgsConstructor;
8-
import org.jun.saemangeum.process.domain.dto.RefinedDataDTO;
8+
import org.jun.saemangeum.process.application.dto.RefinedDataDTO;
99

1010
// H2의 엔티티로 쓰이게 될 클래스
1111
@Entity
@@ -22,23 +22,31 @@ public class Content {
2222
@Column(nullable = false)
2323
private String title;
2424

25+
@Column(nullable = false)
26+
private String position;
27+
2528
@Enumerated(EnumType.STRING)
2629
@Column(nullable = false)
2730
private Category category;
2831

2932
@Column
3033
private String image;
3134

35+
@Column
36+
private String url;
37+
3238
@Lob // MySQL 등에서는 TEXT 등으로
3339
@Column
3440
private String introduction;
3541

3642
public static Content create(RefinedDataDTO dto) {
3743
return Content.builder()
3844
.title(dto.title())
45+
.position(dto.position())
3946
.category(dto.category())
4047
.image(dto.image())
4148
.introduction(dto.introduction())
49+
.url(dto.url())
4250
.build();
4351
}
4452
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.jun.saemangeum.global.domain;
2+
3+
import jakarta.persistence.*;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
@Entity
10+
@Builder
11+
@Table(name = "vectors")
12+
@Getter
13+
@NoArgsConstructor
14+
@AllArgsConstructor
15+
public class Vector {
16+
@Id
17+
@GeneratedValue(strategy = GenerationType.IDENTITY)
18+
private Long id;
19+
20+
@Lob
21+
@Column
22+
private byte[] vector;
23+
24+
@OneToOne
25+
@JoinColumn(name = "content_id")
26+
private Content content;
27+
}

src/main/java/org/jun/saemangeum/global/repository/ContentRepository.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,4 @@
99

1010
@Repository
1111
public interface ContentRepository extends JpaRepository<Content, Long> {
12-
List<Content> findByCategory(Category category);
1312
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.jun.saemangeum.global.repository;
2+
3+
import org.jun.saemangeum.global.domain.Vector;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.stereotype.Repository;
6+
7+
@Repository
8+
public interface VectorRepository extends JpaRepository<Vector, Long> {
9+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.jun.saemangeum.global.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.jun.saemangeum.global.domain.Vector;
5+
import org.jun.saemangeum.global.repository.VectorRepository;
6+
import org.springframework.stereotype.Service;
7+
import org.springframework.transaction.annotation.Transactional;
8+
9+
import java.util.List;
10+
11+
@Service
12+
@RequiredArgsConstructor
13+
public class VectorService {
14+
15+
private final VectorRepository vectorRepository;
16+
17+
@Transactional
18+
public void saveVectors(List<Vector> vectors) {
19+
vectorRepository.saveAll(vectors);
20+
}
21+
22+
@Transactional
23+
public void saveVector(Vector vector) {
24+
vectorRepository.save(vector);
25+
}
26+
27+
@Transactional(readOnly = true)
28+
public List<Vector> getVectors() {
29+
return vectorRepository.findAll();
30+
}
31+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.jun.saemangeum.process.application.collect.api;
2+
3+
import org.jun.saemangeum.process.application.collect.base.OpenApiCollector;
4+
import org.jun.saemangeum.process.application.util.TitleDuplicateChecker;
5+
import org.jun.saemangeum.process.application.dto.RefinedDataDTO;
6+
import org.jun.saemangeum.process.infrastructure.api.OpenApiClient;
7+
import org.jun.saemangeum.process.application.dto.GimjeCultureResponse;
8+
import org.springframework.stereotype.Service;
9+
10+
import java.util.List;
11+
12+
@Service
13+
public class GimjeCultureCollector extends OpenApiCollector {
14+
15+
private static final String LAST_PATH = "/15041593/v1/uddi:d5397703-2829-4811-a0f6-b02cac614b38";
16+
private static final String URL = "https://www.gimje.go.kr/index.gimje?menuCd=DOM_000000106011003000";
17+
18+
public GimjeCultureCollector(
19+
OpenApiClient openApiClient, TitleDuplicateChecker titleDuplicateChecker) {
20+
super(openApiClient, titleDuplicateChecker);
21+
}
22+
23+
@Override
24+
public List<RefinedDataDTO> collectData() {
25+
GimjeCultureResponse response = openApiClient.get(
26+
LAST_PATH,
27+
GimjeCultureResponse.class,
28+
q -> q.queryParam("page", 1).queryParam("perPage", 100)
29+
);
30+
31+
return response.data().stream().map(e -> RefinedDataDTO.to(e, URL)).toList();
32+
}
33+
34+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.jun.saemangeum.process.application.collect.api;
2+
3+
import org.jun.saemangeum.process.application.collect.base.OpenApiCollector;
4+
import org.jun.saemangeum.process.application.util.TitleDuplicateChecker;
5+
import org.jun.saemangeum.process.application.dto.RefinedDataDTO;
6+
import org.jun.saemangeum.process.infrastructure.api.OpenApiClient;
7+
import org.jun.saemangeum.process.application.dto.GunsanCultureResponse;
8+
import org.springframework.stereotype.Service;
9+
10+
import java.util.List;
11+
12+
/**
13+
* 공공데이터 군산 문화시설 API
14+
*/
15+
@Service
16+
public class GunsanCultureCollector extends OpenApiCollector {
17+
18+
private static final String LAST_PATH = "/15041531/v1/uddi:282417f2-01d9-4bff-9d8c-dcc80d78b20e";
19+
private static final String URL = "https://www.ktriptips.com/kor/culture?&s_ac=37&s_sc=2";
20+
21+
public GunsanCultureCollector(
22+
OpenApiClient openApiClient, TitleDuplicateChecker titleDuplicateChecker) {
23+
super(openApiClient, titleDuplicateChecker);
24+
}
25+
26+
@Override
27+
public List<RefinedDataDTO> collectData() {
28+
GunsanCultureResponse response = openApiClient.get(
29+
LAST_PATH,
30+
GunsanCultureResponse.class,
31+
q -> q.queryParam("page", 1).queryParam("perPage", 100)
32+
);
33+
34+
return response.data().stream().map(e -> RefinedDataDTO.to(e, URL)).toList();
35+
}
36+
}
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package org.jun.saemangeum.process.application.collect.api;
22

33
import org.jun.saemangeum.process.application.collect.base.OpenApiCollector;
4-
import org.jun.saemangeum.process.domain.dto.RefinedDataDTO;
5-
import org.jun.saemangeum.process.infrastructure.api.RestTemplateClient;
6-
import org.jun.saemangeum.process.presentation.dto.Event;
7-
import org.jun.saemangeum.process.presentation.dto.EventResponse;
4+
import org.jun.saemangeum.process.application.util.TitleDuplicateChecker;
5+
import org.jun.saemangeum.process.application.dto.RefinedDataDTO;
6+
import org.jun.saemangeum.process.infrastructure.api.OpenApiClient;
7+
import org.jun.saemangeum.process.application.dto.EventResponse;
88
import org.springframework.stereotype.Service;
99

1010
import java.util.List;
@@ -16,19 +16,21 @@
1616
public class SmgEventCollector extends OpenApiCollector {
1717

1818
private static final String LAST_PATH = "/15006173/v1/uddi:f353f7f5-589e-4354-9b3e-ef6923273b55";
19+
private static final String URL = "https://www.saemangeum.go.kr/sda/content.do?key=2010083672101";
1920

20-
public SmgEventCollector(RestTemplateClient restTemplateClient) {
21-
super(restTemplateClient);
21+
public SmgEventCollector(
22+
OpenApiClient openApiClient, TitleDuplicateChecker titleDuplicateChecker) {
23+
super(openApiClient, titleDuplicateChecker);
2224
}
2325

2426
@Override
2527
public List<RefinedDataDTO> collectData() {
26-
EventResponse response = restTemplateClient.get(
28+
EventResponse response = openApiClient.get(
2729
LAST_PATH,
2830
EventResponse.class,
2931
q -> q.queryParam("page", 1).queryParam("perPage", 100)
3032
);
3133

32-
return response.data().stream().map(Event::convertToDTO).toList();
34+
return response.data().stream().map(e -> RefinedDataDTO.to(e, URL)).toList();
3335
}
3436
}

0 commit comments

Comments
 (0)