Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import com.example.silverbridgeX_user.global.api_payload.ErrorCode;
import com.example.silverbridgeX_user.global.exception.GeneralException;
import com.example.silverbridgeX_user.user.domain.User;
import com.example.silverbridgeX_user.user.domain.UserRole;
import com.example.silverbridgeX_user.user.repository.UserRepository;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.LinkedHashSet;
Expand All @@ -27,6 +29,7 @@
@Service
@RequiredArgsConstructor
public class RecommendActivityService {
private final UserRepository userRepository;
private final ActivityLogRepository activityLogRepository;
private final ActivityRepository activityRepository;
private final RecommendActivityRepository recommendActivityRepository;
Expand Down Expand Up @@ -155,7 +158,7 @@ public void insertSimilarEdges(List<Map<String, Object>> rows) {
public void generateAllUsersRecommendation() {

// 0) 모든 사용자 조회 (PostgreSQL)
List<User> users = activityRepository.findAllUsers();
List<User> users = userRepository.findAllUsersByRole(UserRole.OLDER);

try (Session session = neo4jDriver.session()) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.example.silverbridgeX_user.activity.repository;

import com.example.silverbridgeX_user.activity.domain.Activity;
import com.example.silverbridgeX_user.user.domain.User;
import java.util.Collection;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -18,7 +17,4 @@ ORDER BY random()
""", nativeQuery = true)
List<Long> pickRandom(int limit, Collection<Long> excluded);

@Query("select u from User u")
List<User> findAllUsers();

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@
import com.example.silverbridgeX_user.activity.dto.ActivityResponseDto.TourSpotItemDto;
import com.example.silverbridgeX_user.activity.repository.ActivityNativeRepository;
import com.example.silverbridgeX_user.activity.repository.ActivityRepository;
import com.example.silverbridgeX_user.global.api_payload.ErrorCode;
import com.example.silverbridgeX_user.global.exception.GeneralException;
import com.example.silverbridgeX_user.global.service.ApiService;
import com.example.silverbridgeX_user.global.service.CoordinateService;
import com.example.silverbridgeX_user.global.util.EmbeddingClient;
import com.example.silverbridgeX_user.user.repository.UserRepository;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.transaction.Transactional;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.time.LocalDate;
import java.util.ArrayList;
Expand All @@ -27,7 +25,6 @@
import org.neo4j.driver.Driver;
import org.neo4j.driver.Session;
import org.neo4j.driver.Values;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Slf4j
Expand All @@ -37,25 +34,25 @@ public class ActivityService {
private final ActivityRepository activityRepository;
private final EmbeddingClient embeddingClient;
private final ActivityNativeRepository activityNativeRepository;
private final ActivityApiService activityApiService;
private final CoordinateService coordinateService;
private final ApiService apiService;
private final UserRepository userRepository;

private final Driver driver;
private final ObjectMapper objectMapper = new ObjectMapper();

String festivalApiUrl = "http://api.data.go.kr/openapi/tn_pubr_public_cltur_fstvl_api";
String tourSpotApiUrl = "http://api.data.go.kr/openapi/tn_pubr_public_trrsrt_api";
String addressToCoordinateApiurl = "https://api.vworld.kr/req/address?service=address&format=json&request=getCoord&refine=false";

public void fetchAndSaveFestivalData() {
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

int page = 1;
while (true) {
try {
String urlStr = activityApiService.buildUrl(festivalApiUrl, page);
String json = activityApiService.getJsonFromUrl(urlStr);
JsonNode items = activityApiService.parseItems(json);
String urlStr = apiService.buildUrl(festivalApiUrl, page);
String json = apiService.getJsonFromUrl(urlStr);
JsonNode items = apiService.parseItems(json);

if (items == null || !items.isArray() || items.isEmpty()) {
log.info("page {} 응답 없음. 중단.", page);
Expand Down Expand Up @@ -111,15 +108,61 @@ public void fetchAndSaveFestivalData() {
}
}

public void fetchAndSaveCoordinate() throws Exception {
List<Activity> activities = activityRepository.findAll();
System.out.println(activities.size());

for (Activity activity : activities) {

try {
if (activity.getLatitude().startsWith("\"")) {
coordinateService.change(activity);
}

if (!activity.getLongitude().isEmpty() || !activity.getLatitude().isEmpty()) {
continue;
}

String urlStr = null;

if (activity.getStreetAddress() != null) {
urlStr = coordinateService.buildCoordinateUrl("ROAD", activity.getStreetAddress());
} else if (activity.getLotNumberAddress() != null) {
urlStr = coordinateService.buildCoordinateUrl("PARCEL", activity.getLotNumberAddress());
}

if (urlStr == null) {
continue;
}

String json = apiService.getJsonFromUrl(urlStr);
log.info(json);
JsonNode point = apiService.parsePoint(json);

if (!point.isMissingNode()) {
String x = String.valueOf(point.get("x"));
String y = String.valueOf(point.get("y"));

activity.updateCoordinate(x, y);
activityRepository.save(activity);
}
} catch (IOException | ParseException e) {
throw new RuntimeException(e);
}

}

}

public void fetchAndSaveTourSpotData() {
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

int page = 1;
while (true) {
try {
String urlStr = activityApiService.buildUrl(tourSpotApiUrl, page);
String json = activityApiService.getJsonFromUrl(urlStr);
JsonNode items = activityApiService.parseItems(json);
String urlStr = apiService.buildUrl(tourSpotApiUrl, page);
String json = apiService.getJsonFromUrl(urlStr);
JsonNode items = apiService.parseItems(json);

if (items == null || !items.isArray() || items.isEmpty()) {
log.info("page {} 응답 없음. 중단.", page);
Expand Down Expand Up @@ -169,97 +212,6 @@ public void fetchAndSaveTourSpotData() {
}
}

public void fetchAndSaveCoordinate() throws Exception {
List<Activity> activities = activityRepository.findAll();
System.out.println(activities.size());

for (Activity activity : activities) {

try {
if (activity.getLatitude().startsWith("\"")) {
change(activity);
}

if (!activity.getLongitude().isEmpty() || !activity.getLatitude().isEmpty()) {
continue;
}

String urlStr = null;

if (activity.getStreetAddress() != null) {
urlStr = buildCoordinateUrl("ROAD", activity.getStreetAddress());
} else if (activity.getLotNumberAddress() != null) {
urlStr = buildCoordinateUrl("PARCEL", activity.getLotNumberAddress());
}

if (urlStr == null) {
continue;
}

String json = activityApiService.getJsonFromUrl(urlStr);
log.info(json);
JsonNode point = activityApiService.parsePoint(json);

if (!point.isMissingNode()) {
String x = String.valueOf(point.get("x"));
String y = String.valueOf(point.get("y"));

activity.updateCoordinate(x, y);
activityRepository.save(activity);
}
} catch (IOException | ParseException e) {
throw new RuntimeException(e);
}

}

}

@Value("${geocoder.api.key}")
private String key;

private String buildCoordinateUrl(String type, String address) throws Exception {
StringBuilder urlBuilder = new StringBuilder(addressToCoordinateApiurl);
urlBuilder.append("&key=" + key);
urlBuilder.append("&type=" + type); // PARCEL : 지번주소, ROAD : 도로명주소
urlBuilder.append("&address=" + URLEncoder.encode(address, StandardCharsets.UTF_8));
return urlBuilder.toString();
}

private void change(Activity activity) {
String latitude = activity.getLatitude();
String longitude = activity.getLongitude();

boolean updated = false;

if (latitude != null && latitude.contains("\"")) {
latitude = latitude.replace("\"", ""); // 모든 " 제거
activity.updateLatitude(latitude);
updated = true;
}

if (longitude != null && longitude.contains("\"")) {
longitude = longitude.replace("\"", "");
activity.updateLongitude(longitude);
updated = true;
}

if (updated) {
activityRepository.save(activity);
System.out.println("좌표 문자열 정제 완료: " + activity.getId());
}
}

// 해당 API에서 제공하지 않는 주소들에 대한 2차 검증 로직
public void test() throws Exception {
Activity activity = activityRepository.findById(Long.valueOf(102))
.orElseThrow(() -> GeneralException.of(ErrorCode.ACTIVITY_NOT_FOUND));
String urlStr = buildCoordinateUrl("PARCEL", activity.getLotNumberAddress());
String json = activityApiService.getJsonFromUrl(urlStr);
System.out.println(json);

}

public void insertActivitiesNeo4j() {
List<Activity> activities = activityRepository.findAll();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,13 @@ public enum ErrorCode implements BaseCode {
// Common
BAD_REQUEST(HttpStatus.BAD_REQUEST, "COMMON_400", "잘못된 요청입니다."),
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON_500", "서버 에러, 서버 개발자에게 문의하세요."),
INVALID_PARAMETER(HttpStatus.INTERNAL_SERVER_ERROR, "COMMON_5002", "잘못된 DAY Parameter 가 들어왔습니다. "),

// User
USER_NOT_FOUND(HttpStatus.NOT_FOUND, "USER_4041", "존재하지 않는 회원입니다."),
USER_NOT_FOUND_BY_EMAIL(HttpStatus.NOT_FOUND, "USER_4042", "존재하지 않는 회원입니다.-EMAIL"),
USER_NOT_FOUND_BY_USERNAME(HttpStatus.NOT_FOUND, "USER_4043", "존재하지 않는 회원입니다.-USERNAME"),
ALREADY_USED_NICKNAME(HttpStatus.FORBIDDEN, "USER_4031", "이미 사용중인 닉네임입니다."),
USER_ADDRESS_NULL(HttpStatus.BAD_REQUEST, "USER_4001", "주소값이 비었거나 NULL입니다."),
INVALID_PHONE_NUMBER_FORMAT(HttpStatus.BAD_REQUEST, "USER_4002", "전화번호 형식은 연속된 숫자 11개여야 합니다."),
SMS_CODE_NOT_VERIFIED(HttpStatus.BAD_REQUEST, "USER_4003", "전화번호 인증 코드가 틀렸습니다"),
USER_NOT_FOUND_BY_USERNAME(HttpStatus.NOT_FOUND, "USER_4043", "존재하지 않는 회원입니다.-USERNAME & KEY"),
USER_NOT_OLDER(HttpStatus.FORBIDDEN, "USER_4031", "해당 유저가 노인이 아니랍니다."),
USER_NOT_PROTECTOR(HttpStatus.FORBIDDEN, "USER_4032", "해당 유저가 보호자가 아닙니다."),

// Jwt
WRONG_REFRESH_TOKEN(HttpStatus.NOT_FOUND, "JWT_4041", "일치하는 리프레시 토큰이 없습니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ public enum SuccessCode implements BaseCode {
CREATED(HttpStatus.CREATED, "COMMON_201", "Created"),

// User
USER_LOGIN_SUCCESS(HttpStatus.CREATED, "USER_2011", "회원가입& 로그인이 완료되었습니다."),
USER_SOCIAL_LOGIN_SUCCESS(HttpStatus.CREATED, "USER_2011", "소셜 회원가입 혹은 로그인이 완료되었습니다."),
USER_KEY_LOGIN_SUCCESS(HttpStatus.CREATED, "USER_2012", "KEY 로그인이 완료되었습니다."),
USER_LOGOUT_SUCCESS(HttpStatus.OK, "USER_2001", "로그아웃 되었습니다."),
USER_REISSUE_SUCCESS(HttpStatus.OK, "USER_2002", "토큰 재발급이 완료되었습니다."),
USER_DELETE_SUCCESS(HttpStatus.OK, "USER_2003", "회원탈퇴가 완료되었습니다."),
USER_NICKNAME_SUCCESS(HttpStatus.OK, "USER_2004", "닉네임 생성/수정이 완료되었습니다."),
USER_INFO_UPDATE_SUCCESS(HttpStatus.OK, "USER_2005", "회원 정보 수정이 완료 되었습니다."),
USER_INFO_VIEW_SUCCESS(HttpStatus.OK, "USER_2006", "회원 정보 조회가 완료 되었습니다."),
USER_PROFILE_IMAGE_UPDATE_SUCCESS(HttpStatus.OK, "USER_2007", "프로필 사진 이미지 업로드가 완료 되었습니다."),
USER_MYPAGE_VIEW_SUCCESS(HttpStatus.OK, "USER_2010", "마이페이지 정보 조회가 완료되었습니다."),
USER_NICKNAME_UPDATE_SUCCESS(HttpStatus.OK, "USER_2004", "닉네임 수정이 완료되었습니다."),
USER_ADDRESS_UPDATE_SUCCESS(HttpStatus.OK, "USER_2005", "주소지 수정이 완료되었습니다."),
USER_MYPAGE_VIEW_SUCCESS(HttpStatus.OK, "USER_2006", "마이페이지 정보 조회가 완료되었습니다."),
USER_PROTECTOR_CONNECT_OLDER_SUCCESS(HttpStatus.OK, "USER_2007", "보호자의 노인 연결이 완료되었습니다."),
USER_PROTECTOR_REGISTER_OLDER_SUCCESS(HttpStatus.OK, "USER_2008", "보호자의 노인 등록이 완료되었습니다."),

// Recommend Activity
RECOMMEND_ACTIVITY_VIEW_LIST_SUCCESS(HttpStatus.OK, "ACTIVITY_2001", "추천하는 활동 리스를 반환 완료했습니다."),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.silverbridgeX_user.global.converter;

import com.example.silverbridgeX_user.global.dto.CoordinateDto.simpleCoordinateDto;

public class CoordinateConverter {

public static simpleCoordinateDto simpleCoordinateDto(String x, String y) {

return simpleCoordinateDto.builder()
.x(x)
.y(y)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.silverbridgeX_user.global.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

public class CoordinateDto {

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class simpleCoordinateDto {
private String x;
private String y;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.example.silverbridgeX_user.activity.service;
package com.example.silverbridgeX_user.global.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -12,7 +12,7 @@
import org.springframework.stereotype.Service;

@Service
public class ActivityApiService {
public class ApiService {

private final ObjectMapper objectMapper = new ObjectMapper();

Expand Down
Loading