-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: 모임 삭제 기능 구현 (#102) * refactor: 기획 변경에 따른 회원 카드 등록 API 리팩토링 (#105) * refactor: sql 변경 (#104) * refactor: User 클래스 필드명 변경 (#104) * refactor: API 변경에 따른 코드 리팩토링 (#104) * refactor: 불필요한 getId() 제거 ( 피드백 반영 ) (#104) * feat: 유효성 검증 관련 에러 일괄 처리을 위한 메소드 추가 (#104) * test: API 변경에 따른 통합 테스트 수정 및 단위 테스트 구현 (#104) * refactor: UserMeGetResponse 필드 변경 (#104) * fix: CI 에러 수정 (#104) * refactor: 피드백 반영 (#104) * fix: CI 에러 수정 (#104) * refactor: JWT 에러 응답 리팩토링 및 인가 인증 예최 처리 코드 수정 (#103) * refactor: 기타 코드 리팩토링( 피드백 반영 ) (#101) * chore: JWT 관련 의존성 변경 (#101) * refactor: JWT 및 인증 관련 로직 리팩토링 (#101) * test: 테스트 코드 및 설정 관련 변경(#101) * refactor: ObjectMapper Autowired 로 주입 (#101) * refactor: AuthService 반환 타입 Optional<User> -> User 변경 (#101) * fix: V6_create_users_interests.sql 추가 (#109) (#110) * refactor: 회원 카드 등록시 JWT 정보도 함께 반환하도록 수정 (#114) * chore : application.properties 에 jwt 관련 설정 값 추가 (#113) * refactor : userService 회원 카드 등록 로직 수정 (#113) * feat : UserRegisterResponse 필드 추가(#113) * test: API 변경에 따른 테스트 관련 코드 수정 (#113) * fix: 소셜 로그인 관련 500 에러 수정 및 OAuth 로직 일부 개선 (#112) * fix: OAuthLoginController @RestController 어노테이션 추가(나는 바보..) 및 favicon 관련 임시 컨트롤러 생성 (#111) * refactor : 설정 yml 리팩토링 (#111) * refactor : SecurityConfig 리팩토링 (#111) * refactor : 기타 OAuth 관련 로직 리팩토링(#111) * refactor : Cors 허용 주소 임시 전부 허용 (#111) * fix : SonarCloud 오류 수정 (#111) * fix : SonarCloud 오류 수정 (#111) * feat: 위치 기반 API 구현 및 테스트 (#108) * refactor: 기타 코드 리팩토링( 피드백 반영 ) (#101) * chore: JWT 관련 의존성 변경 (#101) * refactor: JWT 및 인증 관련 로직 리팩토링 (#101) * test: 테스트 코드 및 설정 관련 변경(#101) * refactor: ObjectMapper Autowired 로 주입 (#101) * refactor: AuthService 반환 타입 Optional<User> -> User 변경 (#101) * refactor: application.properties redis.port 변경 (#91) * refactor: 기존 Redis 설정 리팩토링 및 추가 구현 (#91) * feat: 유저 위치 기반 관련 DTO 및 VO 구현 (#91) * feat: 유저 위치 기반 API 구현 (#91) * test: 유저 위치 기반 통합 테스트 관련 클래스 구현 및 테스트 (#91) * test: redis port 변경에 따른 테스트 수정 (#91) * fix: CI 에러 수정 (#91) * fix: sonarCloud 에러 수정 (#91) * refactor: 리뷰 반영 (#108) * build: Gatling 세팅 (#115) * feat: 부하테스트 툴 Gatling을 세팅한다 * feat: 예시 코드를 작성한다 * refactor: Sample 코드의 이름을 변경한다 * feat: 내 정보 조회에 Oauth 정보 추가 (#119) * feat: 내 정보 조회에 Oauth 정보 추가 * test: Oauth 정보 추가에 따른 테스트 변경 * fix: 모임 목록 조회 조건에 활동 지역 조건이 안걸리는 버그 수정 (#121) * feat: 회원 탈퇴 사유 저장 로직 추가 및 cors 관련 재설정 (#123) * refactor: SecurityConfig Cors 관련 전체 허용으로 변경 및 회원 카드 등록 API permitAll 로 변경 (#117) * refactor: 기존 엔티티 관련 리팩토링 (#117) * feat: WithdrawReason 생성 sql 추가 (#117) * feat: WithdrawReason 관련 도메인 구현 (#117) * feat: 회원 탈퇴 시 탈퇴 사유 기능 추가 (#117) * test: 회원 탈퇴 단위 테스트 (#117) * refactor: 회원 탈퇴 API POST 로 수정 (#117) * refactor: 회원탈퇴 관련 통합 테스트 수정 및 요청 DTO 수정 (#117) * refactor: meeting 엔티티명 다시 복구 - 모든 테이블에 `s` 붙이는 방향 논의 필요 (#117) * feat: withdraw_reason 테이블 이름 변경 (#117) * fix: CI 에러 해결 (#117) * fix: 모임 규격 변경 (#127) * refactor: 코드 포매팅 적용 (#130) * refactor: 유저조회, 자신조회, 유저들 조회 api응답에 친구 수 필드 추가 (#135) * refactor: 유저조회, 자신조회, 유저들 조회 api에 친구 수 필드를 추가한다 * refactor: 자기자신 조회 api응답의 중복 oAuthType 필드를 삭제한다 * fix: JWT 재발급 오류 해결 (#138) * fix: 토큰 재발급 에러 수정 (#132) * fix: 토큰 재발급 에러 수정 (#132) * test: 토큰 재발급 에러 수정에 따른 테스트 수정 (#132) * test: SecurityConfig permitAll url 중복 제거 (#132) * refactor: 틈틈 위치 기반 로직 수정 및 테스트 (#139) * refactor: redis 관련 클래스 리팩토링 (#124) * feat: 요청에 따른 100 m 이내 6명 조회 로직 구현 (#124) * refactor: 위치 기반 DTO, VO 관련 리팩토링 (#124) * refactor: 위치 기반 DTO, VO 관련 리팩토링 (#124) * test: RedisRepository 리팩토링 및 추가 구현 (#124) * test: 위치 기반 관련 테스트 수정 (#124) * fix: sonarCloud 에러 수정 (#124) --------- Co-authored-by: ddingmin <[email protected]> Co-authored-by: xb205 <[email protected]> Co-authored-by: devxb <[email protected]>
- Loading branch information
1 parent
738a370
commit b65d9a7
Showing
14 changed files
with
161 additions
and
167 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 47 additions & 5 deletions
52
src/main/java/net/teumteum/core/security/service/RedisService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,72 @@ | ||
package net.teumteum.core.security.service; | ||
|
||
import static java.util.Objects.requireNonNull; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import jakarta.annotation.PostConstruct; | ||
import java.io.IOException; | ||
import java.time.Duration; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
import lombok.RequiredArgsConstructor; | ||
import net.teumteum.teum_teum.domain.UserLocation; | ||
import org.springframework.data.redis.core.RedisTemplate; | ||
import org.springframework.data.redis.core.ValueOperations; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class RedisService { | ||
|
||
private final RedisTemplate<String, Object> redisTemplate; | ||
|
||
private static final String HASH_KEY = "userLocation"; | ||
private final RedisTemplate<String, String> redisTemplate; | ||
private final ObjectMapper objectMapper; | ||
private ValueOperations<String, String> valueOperations; | ||
|
||
@PostConstruct | ||
void init() { | ||
valueOperations = redisTemplate.opsForValue(); | ||
} | ||
|
||
public String getData(String key) { | ||
return (String) redisTemplate.opsForValue().get(key); | ||
return valueOperations.get(key); | ||
} | ||
|
||
public void setData(String key, String value) { | ||
redisTemplate.opsForValue().set(key, value); | ||
valueOperations.set(key, value); | ||
} | ||
|
||
public void setDataWithExpiration(String key, String value, Long duration) { | ||
Duration expireDuration = Duration.ofSeconds(duration); | ||
redisTemplate.opsForValue().set(key, value, expireDuration); | ||
valueOperations.set(key, value, expireDuration); | ||
} | ||
|
||
public void deleteData(String key) { | ||
redisTemplate.delete(key); | ||
valueOperations.getOperations().delete(key); | ||
} | ||
|
||
public void setUserLocation(UserLocation userLocation, Long duration) { | ||
String key = HASH_KEY + userLocation.id(); | ||
String value; | ||
try { | ||
value = objectMapper.writeValueAsString(userLocation); | ||
} catch (JsonProcessingException e) { | ||
throw new IllegalArgumentException(e); | ||
} | ||
valueOperations.set(key, value, duration); | ||
} | ||
|
||
public Set<UserLocation> getAllUserLocations() { | ||
Set<String> keys = redisTemplate.keys(HASH_KEY + ":*"); | ||
return requireNonNull(keys).stream().map(key -> { | ||
String value = valueOperations.get(key); | ||
try { | ||
return objectMapper.readValue(value, UserLocation.class); | ||
} catch (IOException e) { | ||
throw new IllegalArgumentException(e); | ||
} | ||
}).collect(Collectors.toSet()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 3 additions & 2 deletions
5
...t/teumteum/teum_teum/domain/UserData.java → ...umteum/teum_teum/domain/UserLocation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
110 changes: 33 additions & 77 deletions
110
src/main/java/net/teumteum/teum_teum/service/TeumTeumService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,103 +1,59 @@ | ||
package net.teumteum.teum_teum.service; | ||
|
||
import static java.lang.System.currentTimeMillis; | ||
import static java.time.Duration.ofMinutes; | ||
import static java.lang.Math.atan2; | ||
import static java.lang.Math.sin; | ||
import static java.lang.Math.sqrt; | ||
import static java.lang.Math.toRadians; | ||
import static java.util.Comparator.comparingDouble; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import java.time.Duration; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Objects; | ||
import java.util.Set; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import net.teumteum.teum_teum.domain.UserData; | ||
import net.teumteum.core.security.service.RedisService; | ||
import net.teumteum.teum_teum.domain.UserLocation; | ||
import net.teumteum.teum_teum.domain.request.UserLocationRequest; | ||
import net.teumteum.teum_teum.domain.response.UserAroundLocationsResponse; | ||
import net.teumteum.teum_teum.domain.response.UserAroundLocationsResponse.UserAroundLocationResponse; | ||
import org.springframework.data.geo.Circle; | ||
import org.springframework.data.geo.Distance; | ||
import org.springframework.data.geo.GeoResult; | ||
import org.springframework.data.geo.GeoResults; | ||
import org.springframework.data.geo.Point; | ||
import org.springframework.data.redis.connection.RedisGeoCommands.GeoLocation; | ||
import org.springframework.data.redis.core.GeoOperations; | ||
import org.springframework.data.redis.core.RedisTemplate; | ||
import org.springframework.data.redis.domain.geo.Metrics; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Slf4j | ||
@Service | ||
@RequiredArgsConstructor | ||
public class TeumTeumService { | ||
|
||
private static final String KEY = "userLocation"; | ||
private static final int SEARCH_LIMIT = 6; | ||
private static final Duration LOCATION_EXPIRATION = ofMinutes(1); | ||
|
||
private final ObjectMapper objectMapper; | ||
private final RedisService redisService; | ||
|
||
private final RedisTemplate<String, Object> redisTemplate; | ||
|
||
public UserAroundLocationsResponse processingUserAroundLocations(UserLocationRequest request) { | ||
GeoOperations<String, Object> geoValueOperations = redisTemplate.opsForGeo(); | ||
|
||
String userDataJson = null; | ||
try { | ||
userDataJson = objectMapper.writeValueAsString( | ||
request.toUserData()) + ":" + currentTimeMillis(); | ||
} catch (JsonProcessingException e) { | ||
log.error("JsonProcessingException Occurred!"); | ||
} | ||
|
||
geoValueOperations.add(KEY, new Point(request.longitude(), request.latitude()), userDataJson); | ||
|
||
return getUserAroundLocations(geoValueOperations, request.longitude(), request.latitude()); | ||
public UserAroundLocationsResponse saveAndGetUserAroundLocations(UserLocationRequest request) { | ||
redisService.setUserLocation(request.toUserLocation(), 60L); | ||
return getUserAroundLocations(request); | ||
} | ||
|
||
private UserAroundLocationsResponse getUserAroundLocations(GeoOperations<String, Object> geoValueOperations, | ||
Double longitude, Double latitude) { | ||
private UserAroundLocationsResponse getUserAroundLocations(UserLocationRequest request) { | ||
Set<UserLocation> allUserLocations = redisService.getAllUserLocations(); | ||
|
||
GeoResults<GeoLocation<Object>> geoResults | ||
= geoValueOperations.radius(KEY, | ||
new Circle(new Point(longitude, latitude), new Distance(100, Metrics.METERS))); | ||
List<UserLocation> aroundUserLocations = allUserLocations.stream() | ||
.filter(userLocation -> !userLocation.id().equals(request.id())) | ||
.filter(userLocation -> calculateDistance(request.latitude(), request.longitude(), | ||
userLocation.latitude(), userLocation.longitude()) <= 100) | ||
.sorted(comparingDouble(userLocation | ||
-> calculateDistance(request.latitude(), request.longitude(), | ||
userLocation.latitude(), userLocation.longitude())) | ||
).limit(SEARCH_LIMIT) | ||
.toList(); | ||
|
||
return getUserAroundLocationsResponse(geoResults); | ||
return UserAroundLocationsResponse.of(aroundUserLocations); | ||
} | ||
|
||
private UserAroundLocationsResponse getUserAroundLocationsResponse(GeoResults<GeoLocation<Object>> geoResults) { | ||
|
||
List<UserAroundLocationResponse> userAroundLocationResponses = new ArrayList<>(); | ||
|
||
long currentTime = currentTimeMillis(); | ||
int count = 0; | ||
|
||
for (GeoResult<GeoLocation<Object>> geoResult : Objects.requireNonNull(geoResults)) { | ||
String userSavedTime = String.valueOf(geoResult.getContent().getName()).split(":")[5]; | ||
long timestamp = Long.parseLong(userSavedTime); | ||
|
||
if (currentTime - timestamp < LOCATION_EXPIRATION.toMillis()) { | ||
String savedUserLocation = String.valueOf(geoResult.getContent().getName()); | ||
String userDataJson = savedUserLocation.substring(savedUserLocation.lastIndexOf(":") + 1); | ||
|
||
UserData userData = null; | ||
try { | ||
userData = objectMapper.readValue(userDataJson, UserData.class); | ||
} catch (JsonProcessingException e) { | ||
log.error("JsonProcessingException Occurred!"); | ||
} | ||
|
||
UserAroundLocationResponse userAroundLocationResponse | ||
= UserAroundLocationResponse.of(Objects.requireNonNull(userData)); | ||
|
||
userAroundLocationResponses.add(userAroundLocationResponse); | ||
count++; | ||
|
||
if (count >= SEARCH_LIMIT) { | ||
break; | ||
} | ||
} | ||
} | ||
return new UserAroundLocationsResponse(userAroundLocationResponses); | ||
private double calculateDistance(double latitude1, double longitude1, double latitude2, double longitude2) { | ||
final int earthRadius = 6371; | ||
double latDistance = toRadians(latitude2 - latitude1); | ||
double lonDistance = toRadians(longitude2 - longitude1); | ||
double a = sin(latDistance / 2) * sin(latDistance / 2) | ||
+ Math.cos(toRadians(latitude1)) * Math.cos(toRadians(latitude2)) | ||
* sin(lonDistance / 2) * sin(lonDistance / 2); | ||
double c = 2 * atan2(sqrt(a), sqrt(1 - a)); | ||
return earthRadius * c * 1000; | ||
} | ||
} |
Oops, something went wrong.