Skip to content

Commit 2d81e08

Browse files
committed
feat: add festival data
1 parent bccc31f commit 2d81e08

File tree

6 files changed

+228
-3
lines changed

6 files changed

+228
-3
lines changed

src/main/java/com/catcher/batch/core/database/CatcherItemRepository.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ public interface CatcherItemRepository {
1414
Optional<CatcherItem> findByItemHashValue(String hashKey);
1515

1616
List<CatcherItem> findByCategory(Category category);
17+
18+
void deleteAll(List<CatcherItem> catcherItem);
1719
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.catcher.batch.core.dto;
2+
3+
import java.time.ZoneId;
4+
import java.time.ZonedDateTime;
5+
6+
public interface ApiResponse {
7+
ZoneId zoneId = ZoneId.of("Asia/Seoul");
8+
9+
String getCategory();
10+
String getHashValue();
11+
String getTitle();
12+
String getDescription();
13+
String getThumbnailUrl();
14+
String getResourceUrl();
15+
ZonedDateTime getStartAt();
16+
ZonedDateTime getEndAt();
17+
String getAddress();
18+
String getProvince();
19+
String getCity();
20+
}

src/main/java/com/catcher/batch/core/dto/FestivalApiResponse.java

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import com.fasterxml.jackson.annotation.JsonProperty;
66
import lombok.Getter;
77

8+
import java.time.ZoneId;
9+
import java.time.ZonedDateTime;
810
import java.util.Date;
911
import java.util.List;
1012

@@ -21,19 +23,79 @@ public class FestivalApiResponse {
2123
@JsonProperty("pageNo")
2224
private Integer pageNo;
2325

24-
@Getter
2526
@JsonIgnoreProperties(ignoreUnknown = true)
26-
static class FestivalItem {
27+
static class FestivalItem implements ApiResponse {
2728
@JsonProperty("fstvlNm")
28-
private String name;
29+
private String fetivalName;
2930

3031
@JsonProperty("rdnmadr")
3132
private String address;
3233

34+
@JsonProperty("fstvlCo")
35+
private String description;
36+
37+
@JsonProperty("homepageUrl")
38+
private String resourceUrl;
39+
3340
@JsonProperty("fstvlStartDate")
3441
private Date startDate;
3542

3643
@JsonProperty("fstvlEndDate")
3744
private Date endDate;
45+
46+
@Override
47+
public String getCategory() {
48+
return "festival";
49+
}
50+
51+
@Override
52+
public String getHashValue() {
53+
return fetivalName;
54+
}
55+
56+
@Override
57+
public String getTitle() {
58+
return fetivalName;
59+
}
60+
61+
@Override
62+
public String getDescription() {
63+
return description;
64+
}
65+
66+
@Override
67+
public String getThumbnailUrl() {
68+
return null;
69+
}
70+
71+
@Override
72+
public String getResourceUrl() {
73+
return resourceUrl;
74+
}
75+
76+
@Override
77+
public ZonedDateTime getStartAt() {
78+
return startDate.toInstant().atZone(zoneId);
79+
}
80+
81+
@Override
82+
public ZonedDateTime getEndAt() {
83+
return endDate.toInstant().atZone(zoneId);
84+
}
85+
86+
@Override
87+
public String getAddress() {
88+
return address;
89+
}
90+
91+
@Override
92+
public String getProvince() {
93+
return null;
94+
}
95+
96+
@Override
97+
public String getCity() {
98+
return null;
99+
}
38100
}
39101
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.catcher.batch.core.service;
2+
3+
import com.catcher.batch.core.database.CatcherItemRepository;
4+
import com.catcher.batch.core.database.CategoryRepository;
5+
import com.catcher.batch.core.database.LocationRepository;
6+
import com.catcher.batch.core.domain.entity.CatcherItem;
7+
import com.catcher.batch.core.domain.entity.Category;
8+
import com.catcher.batch.core.domain.entity.Location;
9+
import com.catcher.batch.core.dto.ApiResponse;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.transaction.annotation.Transactional;
12+
13+
import java.time.ZonedDateTime;
14+
import java.util.ArrayList;
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.function.Function;
18+
import java.util.stream.Collectors;
19+
20+
@RequiredArgsConstructor
21+
public abstract class BatchService {
22+
private final CatcherItemRepository catcherItemRepository;
23+
private final CategoryRepository categoryRepository;
24+
private final LocationRepository locationRepository;
25+
26+
@Transactional
27+
public void batch(List<? extends ApiResponse> apiResponses) {
28+
Category category = categoryRepository.findByName(apiResponses.get(0).getCategory())
29+
.orElseGet(() -> categoryRepository.save(Category.create(apiResponses.get(0).getCategory())));
30+
31+
Map<String, CatcherItem> itemMap = catcherItemRepository.findByCategory(category).stream()
32+
.collect(Collectors.toMap(CatcherItem::getItemHashValue, Function.identity()));
33+
34+
List<CatcherItem> deleteItems = new ArrayList<>();
35+
36+
List<CatcherItem> catcherItems = apiResponses.stream()
37+
.filter(apiResponse -> {
38+
String hashKey = hashString(apiResponse);
39+
if (itemMap.containsKey(hashKey) && isContentChanged(itemMap.get(hashKey), apiResponse)) {
40+
if (isExpired(apiResponse)) {
41+
deleteItems.add(itemMap.get(hashKey));
42+
itemMap.remove(hashKey);
43+
return false;
44+
}
45+
}
46+
return true;
47+
})
48+
.map(apiResponse -> apiResponseToCatcherItem(apiResponse, category, getLocation(apiResponse)))
49+
.toList();
50+
51+
if (!catcherItems.isEmpty()) {
52+
catcherItemRepository.saveAll(catcherItems);
53+
}
54+
55+
if (!deleteItems.isEmpty()) {
56+
catcherItemRepository.deleteAll(deleteItems);
57+
}
58+
}
59+
60+
protected abstract String hashString(ApiResponse apiResponse);
61+
62+
protected abstract boolean isContentChanged(CatcherItem catcherItem, ApiResponse apiResponse);
63+
64+
protected abstract Location getLocation(ApiResponse apiResponse);
65+
66+
protected Location getLocation(String province, String city) {
67+
String withoutDo = province.replace("도", "");
68+
69+
return locationRepository.findByDescription(withoutDo, city)
70+
.orElseThrow();
71+
}
72+
73+
protected Location getLocation(String address) {
74+
String[] parts = address.split("\\s+");
75+
76+
String province = parts[0];
77+
String city = parts[1];
78+
79+
return locationRepository.findByDescription(province, city)
80+
.orElseThrow();
81+
}
82+
83+
protected CatcherItem apiResponseToCatcherItem(ApiResponse apiResponse, Category category, Location location) {
84+
return CatcherItem.builder()
85+
.category(category)
86+
.thumbnailUrl(apiResponse.getThumbnailUrl())
87+
.location(location)
88+
.itemHashValue(hashString(apiResponse))
89+
.description(apiResponse.getDescription())
90+
.resourceUrl(apiResponse.getResourceUrl())
91+
.endAt(apiResponse.getEndAt())
92+
.startAt(apiResponse.getStartAt())
93+
.build();
94+
}
95+
96+
private boolean isExpired(ApiResponse apiResponse) {
97+
return apiResponse.getStartAt() != null && ZonedDateTime.now().isAfter(apiResponse.getStartAt());
98+
}
99+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.catcher.batch.core.service;
2+
3+
import com.catcher.batch.common.utils.HashCodeGenerator;
4+
import com.catcher.batch.core.database.CatcherItemRepository;
5+
import com.catcher.batch.core.database.CategoryRepository;
6+
import com.catcher.batch.core.database.LocationRepository;
7+
import com.catcher.batch.core.domain.entity.CatcherItem;
8+
import com.catcher.batch.core.domain.entity.Location;
9+
import com.catcher.batch.core.dto.ApiResponse;
10+
import org.springframework.stereotype.Service;
11+
12+
import java.util.Objects;
13+
14+
@Service
15+
public class FestivalService extends BatchService {
16+
17+
public FestivalService(CatcherItemRepository catcherItemRepository, CategoryRepository categoryRepository, LocationRepository locationRepository) {
18+
super(catcherItemRepository, categoryRepository, locationRepository);
19+
}
20+
21+
@Override
22+
protected boolean isContentChanged(CatcherItem catcherItem, ApiResponse apiResponse) {
23+
int responseHash = Objects.hash(catcherItem.getStartAt(), catcherItem.getEndAt(), catcherItem.getTitle());
24+
int catcherHash = Objects.hash(apiResponse.getStartAt(), apiResponse.getEndAt(), apiResponse.getTitle());
25+
return responseHash != catcherHash;
26+
}
27+
28+
@Override
29+
protected String hashString(ApiResponse apiResponse) {
30+
return HashCodeGenerator.hashString(apiResponse.getCategory(), apiResponse.getTitle());
31+
}
32+
33+
@Override
34+
protected Location getLocation(ApiResponse apiResponse) {
35+
return getLocation(apiResponse.getAddress());
36+
}
37+
}

src/main/java/com/catcher/batch/datasource/CatcherItemRepositoryImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,9 @@ public Optional<CatcherItem> findByItemHashValue(String hashKey) {
3333
public List<CatcherItem> findByCategory(Category category) {
3434
return catcherItemJpaRepository.findByCategory(category);
3535
}
36+
37+
@Override
38+
public void deleteAll(List<CatcherItem> catcherItem) {
39+
catcherItemJpaRepository.deleteAll(catcherItem);
40+
}
3641
}

0 commit comments

Comments
 (0)