Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/becooq81 step4 #10

Open
wants to merge 11 commits into
base: feature/becooq81_step3
Choose a base branch
from
7 changes: 5 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ configurations {
}

repositories {
maven { url "https://repo.osgeo.org/repository/release/"}
mavenCentral()
}

Expand All @@ -30,6 +31,10 @@ dependencies {
implementation group: 'org.springdoc', name: 'springdoc-openapi-ui', version: '1.6.12'
implementation 'net.minidev:json-smart:2.4.9'

implementation 'com.google.maps:google-maps-services:0.2.7'
implementation 'org.apache.httpcomponents:httpclient:4.5.14'
implementation 'org.geotools:gt-main:28.5'

compileOnly 'org.projectlombok:lombok:1.18.30'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok:1.18.30'
Expand All @@ -41,10 +46,8 @@ dependencies {
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'


implementation "com.querydsl:querydsl-jpa:5.0.0"
annotationProcessor "com.querydsl:querydsl-apt:5.0.0"

}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.gdscys.cokepoke.api.geocoding;

import org.geotools.referencing.GeodeticCalculator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.locationtech.jts.geom.Coordinate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

@Service
public class GeocodingAPIService {
@Value("${gcp.geocoding.api-key}")
private String apiKey;

private final String GEOCODING_API_URL = "https://maps.googleapis.com/maps/api/geocode/json";

public double[] getCoordinates(String address) {
try {
HttpClient httpClient = HttpClients.createDefault();
String encodedAddress = URLEncoder.encode(address, StandardCharsets.UTF_8.toString());
HttpGet getRequest = new HttpGet(GEOCODING_API_URL + "?address=" + encodedAddress + "&key=" + apiKey);

HttpResponse response = httpClient.execute(getRequest);

if (response.getStatusLine().getStatusCode() == 200) {
BufferedReader reader = new BufferedReader(new InputStreamReader(
response.getEntity().getContent()));

StringBuilder responseStringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
responseStringBuilder.append(line);
}

ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseStringBuilder.toString());

double latitude = jsonNode.path("results").path(0).path("geometry").path("location").path("lat").asDouble();
double longitude = jsonNode.path("results").path(0).path("geometry").path("location").path("lng").asDouble();

return new double[]{latitude, longitude};
}
} catch (Exception e) {
e.printStackTrace();
}

return null;
}

public double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
GeodeticCalculator calculator = new GeodeticCalculator();
Coordinate coord1 = new Coordinate(lon1, lat1);
Coordinate coord2 = new Coordinate(lon2, lat2);

calculator.setStartingGeographicPoint(coord1.x, coord1.y);
calculator.setDestinationGeographicPoint(coord2.x, coord2.y);

return calculator.getOrthodromicDistance() / 1000; // Convert to kilometers
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class AuthController {

@PostMapping("/signup")
public ResponseEntity<MemberResponse> signup(@RequestBody @Valid SignupRequest request) {
Member member = memberService.saveMember(request.getEmail(), request.getUsername(), request.getPassword());
Member member = memberService.saveMember(request.getEmail(), request.getUsername(), request.getPassword(), request.getTimezone(), request.getAddress());
return ResponseEntity.status(HttpStatus.CREATED)
.body(MemberResponse.of(member));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
import com.gdscys.cokepoke.friendship.dto.FriendshipRequest;
import com.gdscys.cokepoke.friendship.dto.FriendshipResponse;
import com.gdscys.cokepoke.friendship.service.FriendshipService;
import com.gdscys.cokepoke.member.domain.Member;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

Expand All @@ -29,7 +27,7 @@ public class FriendshipController {
@PostMapping("/create")
public ResponseEntity<Void> createFriendship(@RequestBody @Valid FriendshipRequest request) {
String username = getLoginUsername();
friendshipService.createFriendship(username, request.getToUsername());
friendshipService.createFriendship(username, request.getToUsername(), request.getMyAddress());
return ResponseEntity.status(HttpStatus.CREATED).build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@ public class FriendshipRequest {
@NotBlank
private String toUsername;

@NotBlank
private String myAddress;

protected FriendshipRequest() {}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.gdscys.cokepoke.friendship.service;

import com.gdscys.cokepoke.api.geocoding.GeocodingAPIService;
import com.gdscys.cokepoke.friendship.domain.Friendship;
import com.gdscys.cokepoke.friendship.repository.FriendshipRepository;
import com.gdscys.cokepoke.member.domain.Member;
Expand All @@ -19,14 +20,22 @@
public class FriendshipService implements IFriendshipService {
private final FriendshipRepository friendshipRepository;
private final MemberService memberService;
private final GeocodingAPIService geocodingAPIService;

private static final int PAGE_SIZE = 15;

@Override
@Transactional
public void createFriendship(String username, String recipientUsername) {
public void createFriendship(String username, String recipientUsername, String requestAddress) {
Member member = memberService.getMemberByUsername(username);
member.updateAddress(requestAddress);
Member to = memberService.getMemberByUsername(recipientUsername);

double[] requestingLocation = geocodingAPIService.getCoordinates(requestAddress);
double[] receivingLocation = geocodingAPIService.getCoordinates(to.getAddress());
double distanceInKm = geocodingAPIService.calculateDistance(requestingLocation[0], requestingLocation[1], receivingLocation[0], receivingLocation[1]);
if (distanceInKm > 1.0) throw new IllegalArgumentException("You are too far away from " + to.getUsername());

if (member.equals(to)) throw new IllegalArgumentException("You cannot be friends with yourself");
if (friendshipRepository.findByFromAndTo(member, to).isPresent()) {
throw new IllegalArgumentException("You already sent a friend request");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package com.gdscys.cokepoke.friendship.service;

import com.gdscys.cokepoke.friendship.domain.Friendship;
import com.gdscys.cokepoke.member.domain.Member;

import java.util.List;

public interface IFriendshipService {

void createFriendship(String username, String recipientUsername);
void createFriendship(String username, String recipientUsername, String requestAddress);
Friendship getFriendshipById(Long friendshipId);

Friendship getFriendshipByMembers(String username, String username2);
Expand Down
14 changes: 11 additions & 3 deletions src/main/java/com/gdscys/cokepoke/member/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public class Member implements UserDetails {
@Column(name = "timezone", nullable = false)
private ZoneId timezone;

@Column(name = "address", nullable = false)
private String address;

@OneToOne(mappedBy = "member", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private RefreshToken refreshToken;

Expand All @@ -66,20 +69,22 @@ public class Member implements UserDetails {

protected Member() {}

public Member(String email, String username, String passwordHash, Set<String> roles) {
public Member(String email, String username, String passwordHash) {
this.email = email;
this.username = username;
this.passwordHash = passwordHash;
this.roles = roles;
this.roles = new HashSet<>();
this.timezone = ZoneId.of("Asia/Seoul");
this.address = "1 Gwanghwamun Square, Jongno-gu, Seoul, South Korea";
}

public Member(String email, String username, String passwordHash, Set<String> roles, ZoneId timezone) {
public Member(String email, String username, String passwordHash, Set<String> roles, ZoneId timezone, String address) {
this.email = email;
this.username = username;
this.passwordHash = passwordHash;
this.roles = roles;
this.timezone = timezone;
this.address = address;
}

public void addRequested(Friendship friendship) {
Expand All @@ -94,6 +99,9 @@ public void updatePassword(String passwordHash) {
this.passwordHash = passwordHash;
}

public void updateAddress(String address) {
this.address = address;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return this.roles.stream()
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/com/gdscys/cokepoke/member/dto/SignupRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,21 @@ public class SignupRequest {
@Length(min = 8, max = 20)
private String confirmPassword;

@NotBlank
private String timezone;

@NotBlank
private String address;

protected SignupRequest() {}

@Builder
public SignupRequest(String email, String username, String password, String confirmPassword) {
public SignupRequest(String email, String username, String password, String confirmPassword, String timezone, String address) {
this.email = email;
this.username = username;
this.password = password;
this.confirmPassword = confirmPassword;
this.timezone = timezone;
this.address = address;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import com.gdscys.cokepoke.member.domain.Member;
import com.gdscys.cokepoke.member.dto.UpdateMemberRequest;

import java.time.ZoneId;
import java.util.List;

public interface IMemberService {
Member saveMember(String email, String username, String password);
Member saveMember(String email, String username, String password, String timezone, String address);
Member getMemberByUsername(String username);
void updateMember(Member member, UpdateMemberRequest request);
void deleteMember(Member member);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.ZoneId;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
Expand All @@ -37,8 +38,8 @@ public class MemberService implements IMemberService {
private final static int PAGE_SIZE = 15;

@Override
public Member saveMember(String email, String username, String password) {
Member member = new Member(email, username, passwordEncoder.encode(password), Set.of("USER"));
public Member saveMember(String email, String username, String password, String timezone, String address) {
Member member = new Member(email, username, passwordEncoder.encode(password), Set.of("USER"), ZoneId.of(timezone), address);
return memberRepository.save(member);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import java.util.stream.Collectors;

import static com.gdscys.cokepoke.auth.SecurityUtil.getLoginUsername;
import static java.util.Arrays.stream;

@Controller
@RequiredArgsConstructor
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ spring:
path: /h2-console
jwt:
secret: fpvuvttzKZKGmQ3UoD0QpU7oSDAmCKb8X6l78RULm1Rm01R5Itcg5C8Q9me5sffF
gcp:
geocoding:
api-key: AIzaSyCZ9Kh15ocpwy2GTliq-zKgu64h8_8qPzM

logging:
level:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,21 @@ public class FriendshipControllerTest {
@Autowired
private ObjectMapper objectMapper;

private final String address = "1 Gwanghwamun Square, Jongno-gu, Seoul, South Korea";

@BeforeEach
public void setup() {
memberRepository.save(new Member("[email protected]", "test1", "test1", new HashSet<>()));
memberRepository.save(new Member("[email protected]", "test2", "test2", new HashSet<>()));
memberRepository.save(new Member("[email protected]", "test3", "test3", new HashSet<>()));
memberRepository.save(new Member("[email protected]", "test1", "test1"));
memberRepository.save(new Member("[email protected]", "test2", "test2"));
memberRepository.save(new Member("[email protected]", "test3", "test3"));
friendshipRepository.save(new Friendship(memberRepository.findByUsername("test1").get(), memberRepository.findByUsername("test3").get()));
}

@Test
@DisplayName("친구관계 만들기")
@WithMockUser(username = "test1", password = "test1")
public void create_friendship() throws Exception {
String content = objectMapper.writeValueAsString(new FriendshipRequest("test2"));
String content = objectMapper.writeValueAsString(new FriendshipRequest("test2", address));
mockMvc.perform(post("/friendship/create")
.content(content)
.contentType(MediaType.APPLICATION_JSON))
Expand All @@ -70,7 +72,7 @@ public void create_friendship() throws Exception {
@DisplayName("나 자신과는 친구 관계 못함")
@WithMockUser(username = "test1", password = "test1")
public void create_self_friendship() throws Exception {
String content = objectMapper.writeValueAsString(new FriendshipRequest("test1"));
String content = objectMapper.writeValueAsString(new FriendshipRequest("test1", address));
mockMvc.perform(post("/friendship/create")
.content(content)
.contentType(MediaType.APPLICATION_JSON))
Expand All @@ -82,7 +84,20 @@ public void create_self_friendship() throws Exception {
@DisplayName("이미 친구 신청했으면 다시 하지는 못함")
@WithMockUser(username = "test1", password = "test1")
public void reattempt_friendship() throws Exception {
String content = objectMapper.writeValueAsString(new FriendshipRequest("test3"));
String content = objectMapper.writeValueAsString(new FriendshipRequest("test3", address));
mockMvc.perform(post("/friendship/create")
.content(content)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is4xxClientError())
.andDo(print());
}

@Test
@DisplayName("멀면 친구 못함")
@WithMockUser(username = "test1", password = "test1")
public void far_friendship() throws Exception {

String content = objectMapper.writeValueAsString(new FriendshipRequest("test2", "10 Downing Street, London SW1A 2AA, United Kingdom"));
mockMvc.perform(post("/friendship/create")
.content(content)
.contentType(MediaType.APPLICATION_JSON))
Expand All @@ -99,4 +114,5 @@ public void reattempt_friendship() throws Exception {




}
Loading