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
2 changes: 1 addition & 1 deletion .github/workflows/pr-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- name: JDK 17 설치
uses: actions/setup-java@v4
with:
distribution: 'oracle'
distribution: 'temurin'
java-version: '17'
cache: 'gradle'

Expand Down
8 changes: 4 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ dependencies {
// Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.testcontainers:testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation "org.testcontainers:mariadb:1.19.7"
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.testcontainers:testcontainers:2.0.3'
testImplementation 'org.testcontainers:junit-jupiter:1.21.4'
testImplementation 'org.testcontainers:mariadb:1.21.4'

// Hibernate-spatial
implementation 'org.hibernate:hibernate-spatial:6.6.3.Final'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class EmailService {
private final JavaMailSender javaMailSender;
private final SpringTemplateEngine templateEngine;

private static final SecureRandom SECURE_RANDOM = new SecureRandom();
static final String PREFIX = "auth:email:";

@Async
Expand All @@ -46,8 +47,7 @@ public void sendEmail(String email) {

private String createCode() {

SecureRandom secureRandom = new SecureRandom();
int authenticationCode = secureRandom.nextInt((int) Math.pow(10, 6));
int authenticationCode = SECURE_RANDOM.nextInt((int) Math.pow(10, 6));
log.info("authentication code: {}", authenticationCode);
return String.format("%06d", authenticationCode);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class Crossroad extends BaseTimeEntity {
@Column(nullable = false)
private String name;

@Column(nullable = false)
@Column(nullable = false, columnDefinition = "POINT")
private Point coordinate;

@Column(nullable = false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
package org.programmers.signalbuddyfinal.domain.crossroad.repository;

import static org.programmers.signalbuddyfinal.domain.crossroad.entity.QCrossroad.crossroad;
import static org.programmers.signalbuddyfinal.global.util.QueryDslUtils.mbrContains;

import com.querydsl.core.types.Projections;
import com.querydsl.core.types.QBean;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.locationtech.jts.geom.Point;
import org.programmers.signalbuddyfinal.domain.crossroad.dto.CrossroadResponse;
import static org.programmers.signalbuddyfinal.domain.crossroad.entity.QCrossroad.crossroad;
import org.programmers.signalbuddyfinal.global.util.PointUtils;
import org.programmers.signalbuddyfinal.global.util.QueryDslUtils;
import org.springframework.stereotype.Repository;


Expand All @@ -26,12 +31,17 @@ public class CustomCrossroadRepositoryImpl implements CustomCrossroadRepository

@Override
public List<CrossroadResponse> findNearestCrossroads(double lat, double lng, int radius) {
return jqf.select(crossroadDto).from(crossroad).where(
Expressions.numberTemplate(Double.class, "ST_DISTANCE_SPHERE(POINT({0}, {1}), {2})",
lng, lat, crossroad.coordinate).loe(radius) // 반경 내 필터링
).orderBy(
Expressions.numberTemplate(Double.class, "ST_DISTANCE_SPHERE(POINT({0}, {1}), {2})",
lng, lat, crossroad.coordinate).asc()).fetch();
Point point = PointUtils.toPoint(lat, lng);
NumberExpression<Double> distanceSphere = QueryDslUtils.distanceSphere(
crossroad.coordinate, point
);

return jqf.select(crossroadDto).from(crossroad)
.where(
mbrContains(point, radius, crossroad.coordinate).isTrue(),
distanceSphere.loe(radius)
)
.orderBy(distanceSphere.asc()).fetch();
}

@Override
Expand All @@ -41,9 +51,11 @@ public List<Long> findByCoordinateInWithRadius(List<Point> points, int radius) {
}

private BooleanExpression filterByRadius(List<Point> points, int radius) {
return points.stream().map(
point -> Expressions.numberTemplate(Double.class, "ST_Distance_Sphere({0}, {1})",
crossroad.coordinate, point).loe(radius)) // 반경 내 교차로 필터링
.reduce(BooleanExpression::or).orElse(null);
return points.stream()
.map(point -> QueryDslUtils.mbrContains(point, radius, crossroad.coordinate).isTrue()
.and(QueryDslUtils.distanceSphere(crossroad.coordinate, point).loe(radius))
)
.reduce(BooleanExpression::or)
.orElse(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@

import static com.querydsl.core.types.dsl.Expressions.numberTemplate;

import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Order;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.core.types.dsl.DateTimePath;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.core.types.dsl.NumberExpression;
import com.querydsl.core.types.dsl.PathBuilder;
import com.querydsl.core.types.dsl.StringPath;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.locationtech.jts.geom.Point;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class QueryDslUtils {
Expand Down Expand Up @@ -126,4 +128,72 @@ public static BooleanExpression fulltextSearch(String keyword, StringPath target
target, formattedSearchWord
).gt(0);
}

/**
* 두 좌표 간의 거리 계산
*
* @param coordinate DB의 좌표 데이터 <br> ex) QCrossroad.crossroad.coordinate
* @param target 비교할 좌표
* @return 두 좌표 간의 거리(m)
*/
public static NumberExpression<Double> distanceSphere(
Expression<Point> coordinate,
Point target
) {
return Expressions.numberTemplate(
Double.class,
"ST_Distance_Sphere({0}, {1})",
coordinate, target
);
}

/**
* 중심 좌표와 반경(m)을 받아 MBR(최소 경계 사각형) 내의 데이터 필터링
*
* @param center 중심점
* @param radius 반경(m)
* @param coordinate 필터링할 DB의 좌표 데이터 <br> ex) QCrossroad.crossroad.coordinate)
* @return 반경 내 포함 여부, 1(True) or 0(False) 반환
*/
public static BooleanExpression mbrContains(
Point center,
int radius,
Expression<Point> coordinate
) {
return Expressions.booleanTemplate(
"MBRContains(GeomFromText({0}), {1})",
createMBRPolygonText(center.getY(), center.getX(), radius),
coordinate
);
}

/**
* 중심 좌표와 반경(m)을 받아 MBR(최소 경계 사각형) 좌표에 대한 문자열 생성
*
* @param lat 위도
* @param lng 경도
* @param radius 반경(m)
* @return Polygon 타입의 MBR 좌표 텍스트
*/
private static String createMBRPolygonText(double lat, double lng, int radius) {
double radiusKm = Math.abs(radius) / 1000.0; // m -> km (단위 변환)

// radius 거리 당 위도와 경도의 차이
double latDiff = radiusKm / 111.32;
double lngDiff = radiusKm / (111.32 * Math.cos(Math.toRadians(lat)));

// MBR 좌표
double minLat = lat - latDiff;
double maxLat = lat + latDiff;
double minLng = lng - lngDiff;
double maxLng = lng + lngDiff;

return String.format("Polygon((%f %f, %f %f, %f %f, %f %f, %f %f))",
minLng, minLat, // 좌하단
maxLng, minLat, // 우하단
maxLng, maxLat, // 우상단
minLng, maxLat, // 좌상단
minLng, minLat // 닫기 (시작점과 동일)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.apache.commons.lang3.ArrayUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.programmers.signalbuddyfinal.domain.admin.service.AdminFeedbackService;
Expand All @@ -49,7 +50,6 @@
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.ResultActions;
import org.testcontainers.shaded.org.apache.commons.lang3.ArrayUtils;

@WebMvcTest(AdminFeedbackController.class)
class AdminFeedbackControllerTest extends ControllerTest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import static com.epages.restdocs.apispec.MockMvcRestDocumentationWrapper.document;
import static com.epages.restdocs.apispec.ResourceDocumentation.resource;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.programmers.signalbuddyfinal.global.support.RestDocsFormatGenerators.pageResponseFormat;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
Expand All @@ -15,6 +17,7 @@
import com.epages.restdocs.apispec.ResourceSnippetParameters;
import java.time.LocalDateTime;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.programmers.signalbuddyfinal.domain.admin.dto.AdminMemberResponse;
Expand All @@ -30,12 +33,6 @@
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.ResultActions;
import org.testcontainers.shaded.org.apache.commons.lang3.ArrayUtils;

import static org.mockito.BDDMockito.*;

import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;

@WebMvcTest(AdminMemberController.class)
class AdminMemberControllerTest extends ControllerTest {
Expand Down Expand Up @@ -100,7 +97,7 @@ void getAllMember() throws Exception {

@DisplayName("회원 필터링 조회")
@Test
public void FilteredMember() throws Exception {
void FilteredMember() throws Exception {
final MemberFilterRequest filter = MemberFilterRequest.builder()
.role(MemberRole.USER)
.status(MemberStatus.ACTIVITY)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.epages.restdocs.apispec.SimpleType;
import java.time.LocalDateTime;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.programmers.signalbuddyfinal.domain.admin.dto.AdminPostItResponse;
Expand All @@ -47,10 +48,9 @@
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.ResultActions;
import org.testcontainers.shaded.org.apache.commons.lang3.ArrayUtils;

@WebMvcTest(AdminPostItController.class)
public class AdminPostItControllerTest extends ControllerTest {
class AdminPostItControllerTest extends ControllerTest {

private final String tag = "Admin API";

Expand Down Expand Up @@ -233,8 +233,8 @@ void FilteredPostIt() throws Exception {
.param("startDate", filter.getStartDate().toString())
.param("endDate", filter.getEndDate().toString())
.param("search", filter.getSearch())
.param("danger", filter.getSearch().toString())
.param("deleted", filter.getSearch().toString())
.param("danger", filter.getSearch())
.param("deleted", filter.getSearch())
.header("Accept", "application/json"))
.andExpect(status().isOk())
.andDo(print())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.epages.restdocs.apispec.ResourceSnippetParameters;
import org.apache.commons.lang3.ArrayUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.programmers.signalbuddyfinal.domain.air_quality.dto.AirQualityResponse;
import org.programmers.signalbuddyfinal.domain.air_quality.service.AirQualityService;
import org.programmers.signalbuddyfinal.global.support.ControllerTest;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.testcontainers.shaded.org.apache.commons.lang3.ArrayUtils;
@WebMvcTest(AirQualityController.class)
public class AirQualityControllerTest extends ControllerTest {
class AirQualityControllerTest extends ControllerTest {

private final String tag = "AirQuality API";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.ArrayUtils;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.programmers.signalbuddyfinal.domain.comment.dto.CommentRequest;
Expand All @@ -49,7 +50,6 @@
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.ResultActions;
import org.testcontainers.shaded.org.apache.commons.lang3.ArrayUtils;

@WebMvcTest(CommentController.class)
class CommentControllerTest extends ControllerTest {
Expand Down
Loading