Skip to content

Commit

Permalink
Merge pull request #484 from woowacourse-teams/feat/451-map
Browse files Browse the repository at this point in the history
지도 기능 main merge
  • Loading branch information
green-kong authored Sep 25, 2023
2 parents acb36e2 + 4d014f2 commit 41b7486
Show file tree
Hide file tree
Showing 22 changed files with 729 additions and 48 deletions.
1 change: 1 addition & 0 deletions server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ dependencies {
runtimeOnly 'com.mysql:mysql-connector-j'
testImplementation "org.testcontainers:junit-jupiter:1.19.0"
testImplementation 'org.testcontainers:mysql'
implementation 'org.hibernate:hibernate-spatial:6.2.5.Final'

//JWT
implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile;

import com.project.yozmcafe.controller.dto.cafe.CafeCoordinateRequest;
import com.project.yozmcafe.controller.dto.cafe.CafeRequest;
import com.project.yozmcafe.controller.dto.cafe.CafeResponse;
import com.project.yozmcafe.controller.dto.cafe.CafeUpdateRequest;
Expand Down Expand Up @@ -108,4 +110,11 @@ public ResponseEntity<String> saveMenuBoards(@PathVariable("cafeId") final Long

return ResponseEntity.created(URI.create("/admin/cafes/" + cafeId)).build();
}

@PostMapping("/{cafeId}/coordinate")
public ResponseEntity<String> saveCoordinate(@PathVariable("cafeId") final Long cafeId,
@RequestBody final CafeCoordinateRequest cafeCoordinateRequest) {
cafeAdminService.saveCafeCoordinate(cafeId, cafeCoordinateRequest);
return ResponseEntity.created(URI.create("/admin/cafes/" + cafeId)).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.project.yozmcafe.controller;

import java.util.List;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.project.yozmcafe.controller.dto.cafe.CafeLocationRequest;
import com.project.yozmcafe.controller.dto.cafe.CafeLocationResponse;
import com.project.yozmcafe.service.LocationService;

@RestController
@RequestMapping("/cafes/location")
public class LocationController {

private final LocationService locationService;

public LocationController(final LocationService locationService) {
this.locationService = locationService;
}

@GetMapping
public ResponseEntity<List<CafeLocationResponse>> findCafesFromLocation(
final CafeLocationRequest cafeLocationRequest) {
final List<CafeLocationResponse> cafesFromLocations = locationService.findCafesFromLocations(
cafeLocationRequest);
return ResponseEntity.ok(cafesFromLocations);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.project.yozmcafe.controller.dto.cafe;

import org.locationtech.jts.geom.Point;

import com.project.yozmcafe.domain.cafe.Cafe;
import com.project.yozmcafe.domain.cafe.GeometryGenerator;
import com.project.yozmcafe.domain.cafe.coordinate.CafeCoordinate;

public record CafeCoordinateRequest(double latitude, double longitude) {

public CafeCoordinate toCafeCoordinateWithCafe(final Cafe cafe) {
final Point point = GeometryGenerator.generatePointWithCoordinate(latitude, longitude);
return new CafeCoordinate(point, cafe);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.project.yozmcafe.controller.dto.cafe;

public record CafeLocationRequest(double latitude, double longitude, double latitudeDelta, double longitudeDelta) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.project.yozmcafe.controller.dto.cafe;

import com.project.yozmcafe.domain.cafe.coordinate.dto.CafePinDto;

public record CafeLocationResponse(Long id, String name, String address, double latitude, double longitude) {

public static CafeLocationResponse from(final CafePinDto cafePinDto) {
return new CafeLocationResponse(
cafePinDto.getId(),
cafePinDto.getName(),
cafePinDto.getAddress(),
cafePinDto.getLatitude(),
cafePinDto.getLongitude()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.project.yozmcafe.domain.cafe;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;

import com.project.yozmcafe.controller.dto.cafe.CafeLocationRequest;

public class GeometryGenerator {

private static final GeometryFactory GEOMETRY_FACTORY = new GeometryFactory();
/**
* SRID(Spatial Reference Identifier)는 고유한 공간 좌표 식별자입니다.
* <p>
* 그 중, 4326은 WGS84 경위도 좌표계를 의미합니다. GPS 기술에서도 사용되는 좌표계로 범용적으로 가장 널리 사용되는 좌표계입니다.
*/
private static final int SRID = 4326;
private static final String STRING_POLYGON_FORMAT = "POLYGON((%s))";
private static final String STRING_GEOMETRY_DELIMITER = " ";
private static final String POINT_DELIMITER = ", ";

private GeometryGenerator() {
}

public static Point generatePointWithCoordinate(final double latitude, final double longitude) {
final Point point = GEOMETRY_FACTORY.createPoint(new Coordinate(longitude, latitude));
point.setSRID(SRID);
return point;
}

public static String generateStringPolygon(final CafeLocationRequest cafeLocationRequest) {
final double latitude = cafeLocationRequest.latitude();
final double longitude = cafeLocationRequest.longitude();
final double latitudeDelta = cafeLocationRequest.latitudeDelta();
final double longitudeDelta = cafeLocationRequest.longitudeDelta();

final String minLatitude = String.valueOf(latitude - latitudeDelta);
final String maxLatitude = String.valueOf(latitude + latitudeDelta);
final String minLongitude = String.valueOf(longitude - longitudeDelta);
final String maxLongitude = String.valueOf(longitude + longitudeDelta);

final String firstVertex = String.join(STRING_GEOMETRY_DELIMITER, minLatitude, maxLongitude);
final String secondVertex = String.join(STRING_GEOMETRY_DELIMITER, maxLatitude, maxLongitude);
final String thirdVertex = String.join(STRING_GEOMETRY_DELIMITER, maxLatitude, minLongitude);
final String fourthVertex = String.join(STRING_GEOMETRY_DELIMITER, minLatitude, minLongitude);

final String vertexes = String.join(POINT_DELIMITER, firstVertex, secondVertex, thirdVertex, fourthVertex,
firstVertex);
return String.format(STRING_POLYGON_FORMAT, vertexes);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package com.project.yozmcafe.domain.cafe;

import java.util.List;

import jakarta.persistence.CollectionTable;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.FetchType;

import java.util.List;

@Embeddable
public class Images {

private static final int REPRESENTATIVE_INDEX = 0;

@ElementCollection(fetch = FetchType.LAZY)
@ElementCollection
@CollectionTable(name = "image")
private List<String> urls;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.project.yozmcafe.domain.cafe.coordinate;

import static jakarta.persistence.GenerationType.IDENTITY;

import org.locationtech.jts.geom.Point;

import com.project.yozmcafe.domain.cafe.Cafe;

import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;

@Entity
public class CafeCoordinate {

@Id
@GeneratedValue(strategy = IDENTITY)
private Long id;

private Point coordinate;

@OneToOne(fetch = FetchType.LAZY)
private Cafe cafe;

protected CafeCoordinate() {
}

public CafeCoordinate(final Long id, final Point coordinate, final Cafe cafe) {
this.id = id;
this.coordinate = coordinate;
this.cafe = cafe;
}

public CafeCoordinate(final Point coordinate, final Cafe cafe) {
this(null, coordinate, cafe);
}

public double getLatitude() {
return coordinate.getY();
}

public double getLongitude() {
return coordinate.getX();
}

public Long getId() {
return id;
}

public Point getCoordinate() {
return coordinate;
}

public Cafe getCafe() {
return cafe;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.project.yozmcafe.domain.cafe.coordinate;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.project.yozmcafe.domain.cafe.coordinate.dto.CafePinDto;

public interface CafeCoordinateRepository extends JpaRepository<CafeCoordinate, Long> {

@Query(nativeQuery = true,
value = """
SELECT c.id, c.name, c.address, ST_X(co.coordinate) AS latitude, ST_Y(co.coordinate) AS longitude
FROM cafe_coordinate co
JOIN cafe AS c
ON co.cafe_id = c.id
WHERE ST_CONTAINS(ST_GeomFromText(:area, 4326), co.coordinate);
""")
List<CafePinDto> findCafePinsFromCoordinate(@Param("area") final String area);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.project.yozmcafe.domain.cafe.coordinate.dto;

public interface CafePinDto {
Long getId();

String getName();

String getAddress();

double getLatitude();

double getLongitude();
}
Original file line number Diff line number Diff line change
@@ -1,34 +1,43 @@
package com.project.yozmcafe.service;

import static com.project.yozmcafe.exception.ErrorCode.NOT_EXISTED_CAFE;

import java.util.List;
import java.util.stream.Stream;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.project.yozmcafe.controller.dto.cafe.CafeCoordinateRequest;
import com.project.yozmcafe.controller.dto.cafe.CafeRequest;
import com.project.yozmcafe.controller.dto.cafe.CafeResponse;
import com.project.yozmcafe.controller.dto.cafe.CafeUpdateRequest;
import com.project.yozmcafe.domain.cafe.Cafe;
import com.project.yozmcafe.domain.cafe.CafeRepository;
import com.project.yozmcafe.domain.cafe.Images;
import com.project.yozmcafe.domain.cafe.coordinate.CafeCoordinate;
import com.project.yozmcafe.domain.cafe.coordinate.CafeCoordinateRepository;
import com.project.yozmcafe.exception.BadRequestException;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Stream;

import static com.project.yozmcafe.exception.ErrorCode.NOT_EXISTED_CAFE;

@Service
@Transactional(readOnly = true)
public class CafeAdminService {

private final CafeRepository cafeRepository;
private final CafeCoordinateRepository cafeCoordinateRepository;
@PersistenceContext
private final EntityManager entityManager;

public CafeAdminService(final CafeRepository cafeRepository, final EntityManager entityManager) {
public CafeAdminService(final CafeRepository cafeRepository,
final CafeCoordinateRepository cafeCoordinateRepository,
final EntityManager entityManager) {
this.cafeRepository = cafeRepository;
this.cafeCoordinateRepository = cafeCoordinateRepository;
this.entityManager = entityManager;
}

Expand All @@ -40,13 +49,13 @@ public Long save(final CafeRequest cafeRequest, List<String> imageNames) {

@Transactional
public void update(final long cafeId, final CafeUpdateRequest request, List<String> images) {
final Cafe cafe = getOrThrow(cafeId);
final Cafe cafe = getCafeOrThrow(cafeId);
final Cafe requestedCafe = request.toCafeWithId(cafeId, images);

cafe.update(requestedCafe);
}

private Cafe getOrThrow(final long cafeId) {
private Cafe getCafeOrThrow(final long cafeId) {
return cafeRepository.findById(cafeId)
.orElseThrow(() -> new BadRequestException(NOT_EXISTED_CAFE));
}
Expand All @@ -58,7 +67,7 @@ public List<CafeResponse> findAll() {
}

public CafeResponse findById(final long cafeId) {
final Cafe cafe = getOrThrow(cafeId);
final Cafe cafe = getCafeOrThrow(cafeId);
return CafeResponse.fromUnLoggedInUser(cafe);
}

Expand All @@ -75,7 +84,15 @@ public void delete(final long cafeId) {
}

public List<String> findImagesByCafeId(final Long cafeId) {
final Images images = getOrThrow(cafeId).getImages();
final Images images = getCafeOrThrow(cafeId).getImages();
return images.getUrls();
}

@Transactional
public Long saveCafeCoordinate(final Long cafeId, final CafeCoordinateRequest cafeCoordinateRequest) {
final Cafe cafe = getCafeOrThrow(cafeId);
final CafeCoordinate cafeCoordinate = cafeCoordinateRequest.toCafeCoordinateWithCafe(cafe);
cafeCoordinateRepository.save(cafeCoordinate);
return cafeCoordinate.getId();
}
}
Loading

0 comments on commit 41b7486

Please sign in to comment.