Skip to content

Commit

Permalink
[BE] refactor: 발자국 리팩터링 - rebase 관련 이슈 해결 버전 (#105)
Browse files Browse the repository at this point in the history
  • Loading branch information
takoyakimchi authored Jul 18, 2024
1 parent db24205 commit 86c4057
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
Expand All @@ -32,10 +31,7 @@ public ResponseEntity<Void> save(@RequestBody SaveFootprintRequest request) {
}

@GetMapping("/near")
public List<FindNearFootprintResponse> findNear(
@RequestParam double lat,
@RequestParam double lng
) {
return footprintQueryService.findNear(new FindNearFootprintRequest(lat, lng));
public List<FindNearFootprintResponse> findNear(FindNearFootprintRequest request) {
return footprintQueryService.findNear(request);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
@NoArgsConstructor
public class Footprint {

private static final int RADIUS_AS_METER = 1000;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Expand All @@ -40,6 +42,6 @@ public Footprint(Member member, Location location) {
}

public boolean isNear(Location location) {
return this.location.isNear(location);
return this.location.isWithin(location, RADIUS_AS_METER);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package com.woowacourse.friendogly.footprint.domain;

import static java.lang.Math.abs;
import static java.lang.Math.acos;
import static java.lang.Math.cos;
import static java.lang.Math.sin;
import static java.lang.Math.toDegrees;
import static java.lang.Math.toRadians;

import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
Expand All @@ -12,32 +19,19 @@
@AllArgsConstructor
public class Location {

private static final int BOUNDARY_RADIUS_METER = 1000;

private double latitude;
private double longitude;

public boolean isNear(Location other) {
return distance(this.latitude, this.longitude, other.latitude, other.longitude) <= BOUNDARY_RADIUS_METER;
public boolean isWithin(Location other, int radius) {
return calculateDistanceInMeters(other) <= radius;
}

private double distance(double lat1, double lon1, double lat2, double lon2) {
double theta = lon1 - lon2;
double dist = Math.sin(degToRad(lat1)) * Math.sin(degToRad(lat2)) +
Math.cos(degToRad(lat1)) * Math.cos(degToRad(lat2)) * Math.cos(degToRad(theta));

dist = Math.acos(dist);
dist = radToDeg(dist);
dist = dist * 60 * 1.1515 * 1609.344;

return Math.abs(dist);
}

private double degToRad(double deg) {
return (deg * Math.PI / 180.0);
}
private double calculateDistanceInMeters(Location other) {
double theta = this.longitude - other.longitude;
double dist = sin(toRadians(this.latitude)) * sin(toRadians(other.latitude)) +
cos(toRadians(this.latitude)) * cos(toRadians(other.latitude)) * cos(toRadians(theta));

private double radToDeg(double rad) {
return (rad * 180 / Math.PI);
dist = toDegrees(acos(dist));
return abs(dist * 60 * 1.1515 * 1609.344);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.woowacourse.friendogly.footprint.service;

import com.woowacourse.friendogly.exception.FriendoglyException;
import com.woowacourse.friendogly.footprint.domain.Footprint;
import com.woowacourse.friendogly.footprint.domain.Location;
import com.woowacourse.friendogly.footprint.dto.request.SaveFootprintRequest;
Expand All @@ -20,7 +21,7 @@ public class FootprintCommandService {

public Long save(SaveFootprintRequest request) {
Member member = memberRepository.findById(request.memberId())
.orElseThrow(() -> new IllegalArgumentException("멤버 없음"));
.orElseThrow(() -> new FriendoglyException("존재하지 않는 사용자 ID입니다."));

Footprint footprint = footprintRepository.save(
Footprint.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
@RequiredArgsConstructor
public class FootprintQueryService {

private static final int HOURS_AGO = 24;
private static final int FOOTPRINT_DURATION_HOURS = 24;

private final FootprintRepository footprintRepository;

public List<FindNearFootprintResponse> findNear(FindNearFootprintRequest request) {
LocalDateTime since = LocalDateTime.now().minusHours(HOURS_AGO);
List<Footprint> recentFootprints = footprintRepository.findByCreatedAtAfter(since);
LocalDateTime startTime = LocalDateTime.now().minusHours(FOOTPRINT_DURATION_HOURS);
List<Footprint> recentFootprints = footprintRepository.findByCreatedAtAfter(startTime);

Location currentLocation = new Location(request.latitude(), request.longitude());

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package com.woowacourse.friendogly.docs;

import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document;
import static com.epages.restdocs.apispec.ResourceDocumentation.resource;
import static org.mockito.BDDMockito.given;
import static org.springframework.http.HttpHeaders.LOCATION;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.epages.restdocs.apispec.ResourceSnippetParameters;
import com.epages.restdocs.apispec.Schema;
import com.woowacourse.friendogly.footprint.controller.FootprintController;
import com.woowacourse.friendogly.footprint.dto.request.FindNearFootprintRequest;
import com.woowacourse.friendogly.footprint.dto.request.SaveFootprintRequest;
import com.woowacourse.friendogly.footprint.dto.response.FindNearFootprintResponse;
import com.woowacourse.friendogly.footprint.service.FootprintCommandService;
import com.woowacourse.friendogly.footprint.service.FootprintQueryService;
import java.time.LocalDateTime;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;

@WebMvcTest(FootprintController.class)
public class FootprintApiDocsTest extends RestDocsTest {

@MockBean
private FootprintQueryService footprintQueryService;

@MockBean
private FootprintCommandService footprintCommandService;

@DisplayName("발자국 저장")
@Test
void save() throws Exception {
SaveFootprintRequest request = new SaveFootprintRequest(1L, 37.5173316, 127.1011661);

given(footprintCommandService.save(request))
.willReturn(1L);

mockMvc
.perform(post("/footprints")
.content(objectMapper.writeValueAsString(request))
.contentType(APPLICATION_JSON))
.andDo(print())
.andDo(document("footprints/save",
getDocumentRequest(),
getDocumentResponse(),
resource(ResourceSnippetParameters.builder()
.tag("발자국 저장 API")
.summary("발자국 저장 API")
.requestFields(
fieldWithPath("memberId").description("사용자 ID"),
fieldWithPath("latitude").description("현재 위치의 위도"),
fieldWithPath("longitude").description("현재 위치의 경도")
)
.responseHeaders(
headerWithName(LOCATION).description("Location")
)
.requestSchema(Schema.schema("FindNearFootprintRequest"))
.build()
)
))
.andExpect(status().isCreated())
.andExpect(header().string(LOCATION, "/footprints/1"));
}

@DisplayName("주변 발자국 조회")
@Test
void findNear() throws Exception {
FindNearFootprintRequest request = new FindNearFootprintRequest(37.5173316, 127.1011661);
List<FindNearFootprintResponse> response = List.of(
new FindNearFootprintResponse(1L, 37.5136533, 127.0983182, LocalDateTime.now().minusMinutes(10)),
new FindNearFootprintResponse(3L, 37.5131474, 127.1042528, LocalDateTime.now().minusMinutes(20)),
new FindNearFootprintResponse(6L, 37.5171728, 127.1047797, LocalDateTime.now().minusMinutes(30)),
new FindNearFootprintResponse(11L, 37.516183, 127.1068874, LocalDateTime.now().minusMinutes(40))
);

given(footprintQueryService.findNear(request))
.willReturn(response);

mockMvc
.perform(get("/footprints/near")
.param("latitude", "37.5173316")
.param("longitude", "127.1011661"))
.andDo(print())
.andDo(document("footprints/near",
getDocumentRequest(),
getDocumentResponse(),
resource(ResourceSnippetParameters.builder()
.tag("주변 발자국 조회 API")
.summary("주변 발자국 조회 API")
.queryParameters(
parameterWithName("latitude").description("현재 위치의 위도"),
parameterWithName("longitude").description("현재 위치의 경도")
)
.responseFields(
fieldWithPath("[].memberId").description("발자국을 찍은 사용자 ID"),
fieldWithPath("[].latitude").description("발자국 위치의 위도"),
fieldWithPath("[].longitude").description("발자국 위치의 경도"),
fieldWithPath("[].createdAt").description("발자국 생성 시간")
)
.build()
)
))
.andExpect(status().isOk());
}

@Override
protected Object controller() {
return new FootprintController(footprintCommandService, footprintQueryService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ class LocationTest {
void isNear_True() {
Location location1 = new Location(0.0, 0.0);
Location location2 = new Location(0.0, 0.008993216);
assertThat(location1.isNear(location2)).isTrue();
assertThat(location1.isWithin(location2, 1000)).isTrue();
}

@DisplayName("약 1001m 차이 나는 두 위치는 주변에 있지 않다.")
@Test
void isNear_False() {
Location location1 = new Location(0.0, 0.0);
Location location2 = new Location(0.0, 0.009001209);
assertThat(location1.isNear(location2)).isFalse();
assertThat(location1.isWithin(location2, 1000)).isFalse();
}
}

0 comments on commit 86c4057

Please sign in to comment.