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

[BE] 2.1.0 배포 #627

Merged
merged 4 commits into from
Nov 18, 2023
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/be-merge-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:

- name: Docker Image Build
working-directory: backend
run: docker build --platform linux/arm64/v8 -t mapbefine/mapbefine -f Dockerfile-dev .
run: docker build --platform linux/arm64/v8 -t mapbefine/mapbefine -f Dockerfile-prod .

- name: Docker Hub Push
run: docker push mapbefine/mapbefine
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/fe-merge-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
workflow_dispatch:

pull_request:
branches: [develop-FE-2]
branches: [develop-FE]
types: [closed]
paths: frontend/**

Expand Down Expand Up @@ -35,6 +35,7 @@ jobs:
working-directory: frontend
env:
REACT_APP_GOOGLE_ANALYTICS: ${{ secrets.REACT_APP_GOOGLE_ANALYTICS }}
REACT_APP_TMAP_API_KEY: ${{ secrets.REACT_APP_TMAP_API_KEY }}
APP_URL: "https://mapbefine.kro.kr/api"

- name: upload to artifact
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/fe-merge-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
working-directory: frontend
env:
REACT_APP_GOOGLE_ANALYTICS: ${{ secrets.REACT_APP_GOOGLE_ANALYTICS }}
REACT_APP_TMAP_API_KEY: ${{ secrets.REACT_APP_TMAP_API_KEY }}
APP_URL: "https://mapbefine.com/api"

- name: upload to artifact
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/fe-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Frontend CI For Test Validation
on:
# pull request open과 reopen 시 실행한다.
pull_request:
branches: [main, develop-FE-2]
branches: [main, develop-FE]
paths: frontend/**

defaults:
Expand Down
4 changes: 2 additions & 2 deletions backend/Dockerfile-prod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
FROM openjdk:17
COPY build/libs/mapbefine.jar mapbefine.jar
FROM openjdk:17
COPY build/libs/mapbefine.jar mapbefine.jar
ENTRYPOINT ["java", "-jar","-Dspring.profiles.active=prod", "mapbefine.jar"]
3 changes: 3 additions & 0 deletions backend/src/docs/asciidoc/topic.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,6 @@ operation::topic-controller-test/update[snippets='http-request,http-response']
=== 토픽 이미지 수정

operation::topic-controller-test/update-image[snippets='http-request,http-response']

=== 토픽 클러스터링 조회
operation::topic-controller-test/get-clusters-of-pins[snippets='http-request,http-response']
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ public void deletePin(Long pinId) {
.orElseThrow(() -> new PinNotFoundException(PIN_NOT_FOUND, pinId));

pin.decreaseTopicPinCount();
pinRepository.flush();
pinImageRepository.deleteAllByPinId(pinId);
pinRepository.deleteById(pin.getId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public class Coordinate {
private static final double LATITUDE_UPPER_BOUND = 43;
private static final double LONGITUDE_LOWER_BOUND = 124;
private static final double LONGITUDE_UPPER_BOUND = 132;
private static final double EARTH_RADIUS = 6371.0;
private static final int CONVERT_TO_METER = 1000;

/*
* 4326은 데이터베이스에서 사용하는 여러 SRID 값 중, 일반적인 GPS기반의 위/경도 좌표를 저장할 때 쓰이는 값입니다.
Expand All @@ -34,7 +36,6 @@ private Coordinate(Point point) {
this.coordinate = point;
}


public static Coordinate of(double latitude, double longitude) {
validateRange(latitude, longitude);

Expand Down Expand Up @@ -62,4 +63,13 @@ public double getLongitude() {
return coordinate.getX();
}

public double calculateDistance(Coordinate coordinate) {
return Math.acos(
Math.sin(Math.toRadians(coordinate.getLatitude())) * Math.sin(Math.toRadians(this.getLatitude()))
+ (Math.cos(Math.toRadians(coordinate.getLatitude())) * Math.cos(
Math.toRadians(this.getLatitude())) * Math.cos(
Math.toRadians(coordinate.getLongitude() - this.getLongitude())))
) * EARTH_RADIUS * CONVERT_TO_METER;
}

}
22 changes: 4 additions & 18 deletions backend/src/main/java/com/mapbefine/mapbefine/pin/domain/Pin.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,24 +108,6 @@ public void decreaseTopicPinCount() {
topic.decreasePinCount();
}

public void copyToTopic(Member creator, Topic topic) {
Pin copiedPin = Pin.createPinAssociatedWithLocationAndTopicAndMember(
pinInfo.getName(),
pinInfo.getDescription(),
location,
topic,
creator
);

copyPinImages(copiedPin);
}

private void copyPinImages(Pin pin) {
for (PinImage pinImage : pinImages) {
PinImage.createPinImageAssociatedWithPin(pinImage.getImageUrl(), pin);
}
}

public void addPinImage(PinImage pinImage) {
pinImages.add(pinImage);
}
Expand All @@ -138,6 +120,10 @@ public double getLongitude() {
return location.getLongitude();
}

public String getName() {
return pinInfo.getName();
}

public String getDescription() {
return pinInfo.getDescription();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mapbefine.mapbefine.pin.domain;

import com.mapbefine.mapbefine.pin.infrastructure.PinBatchRepositoryCustom;
import java.util.List;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -9,11 +10,14 @@
import org.springframework.stereotype.Repository;

@Repository
public interface PinRepository extends JpaRepository<Pin, Long> {
public interface PinRepository extends JpaRepository<Pin, Long>, PinBatchRepositoryCustom {

@EntityGraph(attributePaths = {"location", "topic", "creator", "pinImages"})
List<Pin> findAll();

@EntityGraph(attributePaths = {"location", "topic", "creator", "pinImages"})
List<Pin> findAllByIdIn(List<Long> pinIds);

@EntityGraph(attributePaths = {"location", "topic", "creator", "pinImages"})
List<Pin> findAllByTopicId(Long topicId);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.mapbefine.mapbefine.pin.infrastructure;

import com.mapbefine.mapbefine.pin.domain.Pin;
import com.mapbefine.mapbefine.topic.domain.Topic;
import java.util.List;

public interface PinBatchRepositoryCustom {

int[] saveAllToTopic(Topic topicForCopy, List<Pin> originalPins);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package com.mapbefine.mapbefine.pin.infrastructure;

import static java.sql.Statement.EXECUTE_FAILED;

import com.mapbefine.mapbefine.pin.domain.Pin;
import com.mapbefine.mapbefine.pin.domain.PinImage;
import com.mapbefine.mapbefine.topic.domain.Topic;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.IntStream;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.BatchPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Slf4j
@Repository
public class PinBatchRepositoryCustomImpl implements PinBatchRepositoryCustom {

private final JdbcTemplate jdbcTemplate;

public PinBatchRepositoryCustomImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

public int[] saveAllToTopic(Topic topicForCopy, List<Pin> originalPins) {
int[] rowCount = bulkInsertPins(topicForCopy, originalPins);
List<PinImageInsertDto> pinImageInsertDtos = createPinImageInsertDtos(originalPins, rowCount);

if (pinImageInsertDtos.isEmpty()) {
return rowCount;
}
return bulkInsertPinImages(pinImageInsertDtos);
}

private int[] bulkInsertPins(Topic topicForCopy, List<Pin> originalPins) {
String bulkInsertSql = "INSERT INTO pin "
+ "(name, description, member_id, topic_id, location_id, "
+ "created_at, updated_at) "
+ "VALUES "
+ "(?, ?, ?, ?, ?, "
+ "?, ?)";
LocalDateTime createdAt = topicForCopy.getLastPinUpdatedAt();
Long topicId = topicForCopy.getId();
Long creatorId = topicForCopy.getCreator().getId();
log.debug("[Query] bulk insert size {} : {}", originalPins.size(), bulkInsertSql);

return jdbcTemplate.batchUpdate(bulkInsertSql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
Pin pin = originalPins.get(i);
ps.setString(1, pin.getName());
ps.setString(2, pin.getDescription());
ps.setLong(3, creatorId);
ps.setLong(4, topicId);
ps.setLong(5, pin.getLocation().getId());
ps.setTimestamp(6, Timestamp.valueOf(createdAt));
ps.setTimestamp(7, Timestamp.valueOf(createdAt));
log.trace("[Parameter Binding] {} : "
+ "name={}, description={}, member_id={}, topic_id={}, location_id={}, "
+ "created_at={}, updated_at={}",
i, pin.getName(), pin.getDescription(), creatorId, topicId, pin.getLocation().getId(),
createdAt, createdAt);
}

@Override
public int getBatchSize() {
return originalPins.size();
}
});
}

private List<PinImageInsertDto> createPinImageInsertDtos(List<Pin> originalPins, int[] rowCount) {
Long firstIdFromBatch = jdbcTemplate.queryForObject("SELECT last_insert_id()", Long.class);
validateId(firstIdFromBatch);

return IntStream.range(0, originalPins.size())
.filter(index -> rowCount[index] != EXECUTE_FAILED)
.mapToObj(index -> {
Pin pin = originalPins.get(index);
return PinImageInsertDto.of(pin.getPinImages(), firstIdFromBatch + index);
}).flatMap(Collection::stream)
.toList();
}

private void validateId(Long firstIdFromBatch) {
if (Objects.isNull(firstIdFromBatch)) {
throw new IllegalStateException("fail to batch update pins");
}
}

private int[] bulkInsertPinImages(List<PinImageInsertDto> pinImages) {
String bulkInsertSql = "INSERT INTO pin_image "
+ "(image_url, pin_id) "
+ "VALUES "
+ "(?, ?)";
log.debug("[Query] bulk insert size {} : {}", pinImages.size(), bulkInsertSql);

return jdbcTemplate.batchUpdate(bulkInsertSql, new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement ps, int i) throws SQLException {
PinImageInsertDto pinImage = pinImages.get(i);
ps.setString(1, pinImage.imageUrl);
ps.setLong(2, pinImage.pinId);
log.trace("[Parameter Binding] {} : imageUrl={}, pinImage={} ",
i, pinImage.imageUrl, pinImage.pinId);
}

@Override
public int getBatchSize() {
return pinImages.size();
}
});
}

private record PinImageInsertDto(
String imageUrl,
Long pinId,
boolean isDeleted
) {

public static PinImageInsertDto of(PinImage pinImage, Long pinId) {
return new PinImageInsertDto(
pinImage.getImageUrl(),
pinId,
pinImage.isDeleted()
);
}

private static List<PinImageInsertDto> of(List<PinImage> pinImages, Long pinId) {
return pinImages.stream()
.map(pinImage -> PinImageInsertDto.of(pinImage, pinId))
.toList();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,11 @@ public Long saveTopic(AuthMember member, TopicCreateRequest request) {
Topic topic = convertToTopic(member, request);
List<Long> pinIds = request.pins();

topicRepository.save(topic);
if (!pinIds.isEmpty()) {
copyPinsToTopic(member, topic, pinIds);
}

topicRepository.save(topic);

return topic.getId();
}

Expand Down Expand Up @@ -105,14 +104,14 @@ private void copyPinsToTopic(
) {
List<Pin> originalPins = findAllPins(pinIds);
validateCopyablePins(member, originalPins);
topic.increasePinCount(pinIds.size());
pinRepository.flush();

Member creator = findCreatorByAuthMember(member);

originalPins.forEach(pin -> pin.copyToTopic(creator, topic));
pinRepository.saveAllToTopic(topic, originalPins);
}

private List<Pin> findAllPins(List<Long> pinIds) {
List<Pin> findPins = pinRepository.findAllById(pinIds);
List<Pin> findPins = pinRepository.findAllByIdIn(pinIds);

if (pinIds.size() != findPins.size()) {
throw new PinBadRequestException(ILLEGAL_PIN_ID);
Expand All @@ -134,15 +133,13 @@ private void validateCopyablePins(AuthMember member, List<Pin> originalPins) {
public Long merge(AuthMember member, TopicMergeRequest request) {
Topic topic = convertToTopic(member, request);
List<Topic> originalTopics = findAllTopics(request.topics());

validateCopyableTopics(member, originalTopics);

Member creator = findCreatorByAuthMember(member);
List<Pin> originalPins = getAllPinsFromTopics(originalTopics);
originalPins.forEach(pin -> pin.copyToTopic(creator, topic));

topicRepository.save(topic);
topic.increasePinCount(originalPins.size());

topicRepository.save(topic);
pinRepository.saveAllToTopic(topic, originalPins);
return topic.getId();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
import com.mapbefine.mapbefine.bookmark.domain.BookmarkRepository;
import com.mapbefine.mapbefine.member.domain.Member;
import com.mapbefine.mapbefine.member.domain.MemberRepository;
import com.mapbefine.mapbefine.pin.domain.Pin;
import com.mapbefine.mapbefine.topic.domain.Clusters;
import com.mapbefine.mapbefine.topic.domain.Topic;
import com.mapbefine.mapbefine.topic.domain.TopicRepository;
import com.mapbefine.mapbefine.topic.dto.response.ClusterResponse;
import com.mapbefine.mapbefine.topic.dto.response.TopicDetailResponse;
import com.mapbefine.mapbefine.topic.dto.response.TopicResponse;
import com.mapbefine.mapbefine.topic.exception.TopicException.TopicForbiddenException;
Expand Down Expand Up @@ -241,4 +244,24 @@ private List<TopicResponse> getUserBestTopicResponse(AuthMember authMember) {
)).toList();
}

public List<ClusterResponse> findClustersPinsByIds(
AuthMember authMember,
List<Long> topicIds,
Double imageDiameter
) {
List<Topic> topics = topicRepository.findByIdIn(topicIds);
topics.forEach(topic -> validateReadableTopic(authMember, topic));

List<Pin> allPins = topics.stream()
.map(Topic::getPins)
.flatMap(List::stream)
.toList();

return Clusters.from(allPins, imageDiameter)
.getClusters()
.stream()
.map(ClusterResponse::from)
.toList();
}

}
Loading
Loading