Merge pull request #24 from SlimusMinus/fix-geocoding_serice
fixed GeocodingService add alter city
This commit is contained in:
5
src/main/java/com/krylov/refound/dto/AddressInfo.java
Normal file
5
src/main/java/com/krylov/refound/dto/AddressInfo.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package com.krylov.refound.dto;
|
||||
|
||||
public record AddressInfo(String city, String district) {
|
||||
|
||||
}
|
||||
9
src/main/java/com/krylov/refound/dto/DownloadedFile.java
Normal file
9
src/main/java/com/krylov/refound/dto/DownloadedFile.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.krylov.refound.dto;
|
||||
|
||||
public record DownloadedFile(
|
||||
String originalFilename,
|
||||
String contentType,
|
||||
byte[] content
|
||||
) {
|
||||
|
||||
}
|
||||
@@ -35,14 +35,13 @@ public class ChatService {
|
||||
throw new IllegalArgumentException("Нельзя создать чат с самим собой");
|
||||
}
|
||||
|
||||
Long u1 = Math.min(currentUserId, otherUserId);
|
||||
Long u2 = Math.max(currentUserId, otherUserId);
|
||||
Long userId1 = Math.min(currentUserId, otherUserId);
|
||||
Long userId2 = Math.max(currentUserId, otherUserId);
|
||||
|
||||
Chat chat = chatRepository.findByUsers(u1, u2)
|
||||
.orElseGet(() -> {
|
||||
Chat chat = chatRepository.findByUsers(userId1, userId2).orElseGet(() -> {
|
||||
Chat newChat = new Chat();
|
||||
newChat.setUserOneId(u1);
|
||||
newChat.setUserTwoId(u2);
|
||||
newChat.setUserOneId(userId1);
|
||||
newChat.setUserTwoId(userId2);
|
||||
newChat.setPostId(postId);
|
||||
return chatRepository.save(newChat);
|
||||
});
|
||||
@@ -64,13 +63,12 @@ public class ChatService {
|
||||
Chat chat = getChatOrThrow(chatId);
|
||||
checkParticipant(chat, userId);
|
||||
|
||||
return messageRepository.findByChatIdOrderByCreatedAtDesc(chatId, pageable)
|
||||
.map(this::toMessageDto);
|
||||
return messageRepository.findByChatIdOrderByCreatedAtDesc(chatId, pageable).map(this::toMessageDto);
|
||||
}
|
||||
|
||||
// Отправка сообщения (вызывается из WebSocket контроллера)
|
||||
@Transactional
|
||||
public MessageDto sendMessage(Long senderId, Long chatId, String content) {
|
||||
public void sendMessage(Long senderId, Long chatId, String content) {
|
||||
Chat chat = getChatOrThrow(chatId);
|
||||
checkParticipant(chat, senderId);
|
||||
|
||||
@@ -89,15 +87,13 @@ public class ChatService {
|
||||
? chat.getUserTwoId()
|
||||
: chat.getUserOneId();
|
||||
|
||||
long unreadCount = messageRepository
|
||||
.countByChatIdAndIsReadFalseAndSenderIdNot(chatId, recipientId);
|
||||
long unreadCount = messageRepository.countByChatIdAndIsReadFalseAndSenderIdNot(chatId, recipientId);
|
||||
|
||||
messagingTemplate.convertAndSend(
|
||||
"/topic/user/" + recipientId + "/unread",
|
||||
Map.of("chatId", chatId, "unreadCount", unreadCount)
|
||||
);
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -114,11 +110,9 @@ public class ChatService {
|
||||
messageRepository.markAsReadByChatIdAndRecipient(chatId, userId);
|
||||
}
|
||||
|
||||
// ---------- вспомогательные методы ----------
|
||||
|
||||
private Chat getChatOrThrow(Long chatId) {
|
||||
return chatRepository.findById(chatId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Чат не найден"));
|
||||
return chatRepository.findById(chatId).orElseThrow(() -> new IllegalArgumentException("Чат не найден"));
|
||||
}
|
||||
|
||||
private void checkParticipant(Chat chat, Long userId) {
|
||||
@@ -128,8 +122,7 @@ public class ChatService {
|
||||
}
|
||||
|
||||
private ChatDto toDto(Chat chat, Long currentUserId) {
|
||||
Long otherUserId = chat.getUserOneId().equals(currentUserId)
|
||||
? chat.getUserTwoId() : chat.getUserOneId();
|
||||
Long otherUserId = chat.getUserOneId().equals(currentUserId) ? chat.getUserTwoId() : chat.getUserOneId();
|
||||
|
||||
ChatDto dto = new ChatDto();
|
||||
dto.setId(chat.getId());
|
||||
|
||||
@@ -38,8 +38,7 @@ public class FavoriteService {
|
||||
|
||||
User user = userService.getCurrentUser();
|
||||
|
||||
Post post = postRepository.findById(postId)
|
||||
.orElseThrow(() -> new ApiException(
|
||||
Post post = postRepository.findById(postId).orElseThrow(() -> new ApiException(
|
||||
ErrorCode.NOT_FOUND,
|
||||
"Post not found",
|
||||
HttpStatus.NOT_FOUND
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.krylov.refound.service;
|
||||
|
||||
import com.krylov.refound.config.MinioProperties;
|
||||
import com.krylov.refound.dto.DownloadedFile;
|
||||
import com.krylov.refound.enums.ErrorCode;
|
||||
import com.krylov.refound.exception.ApiException;
|
||||
import java.io.IOException;
|
||||
@@ -30,28 +31,19 @@ import software.amazon.awssdk.services.s3.model.HeadObjectRequest;
|
||||
import software.amazon.awssdk.services.s3.model.HeadObjectResponse;
|
||||
import software.amazon.awssdk.services.s3.model.NoSuchKeyException;
|
||||
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
|
||||
import software.amazon.awssdk.services.s3.model.S3Exception;
|
||||
import software.amazon.awssdk.core.exception.SdkClientException;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class FileStorageService {
|
||||
|
||||
private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
|
||||
private static final Set<String> ALLOWED_CONTENT_TYPES = Set.of(
|
||||
"image/jpeg", "image/png", "image/webp"
|
||||
);
|
||||
|
||||
private final S3Client s3Client;
|
||||
private final MinioProperties properties;
|
||||
|
||||
/**
|
||||
* DTO для передачи данных скачанного файла.
|
||||
*/
|
||||
private record DownloadedFile(
|
||||
String originalFilename,
|
||||
String contentType,
|
||||
byte[] content
|
||||
) {}
|
||||
private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB
|
||||
private static final Set<String> ALLOWED_CONTENT_TYPES = Set.of("image/jpeg", "image/png", "image/webp");
|
||||
private static final String ORIGINAL_FILENAME_METADATA_KEY = "original-filename";
|
||||
|
||||
/**
|
||||
* Загружает файл в MinIO в корень бакета.
|
||||
@@ -67,18 +59,17 @@ public class FileStorageService {
|
||||
public String uploadFile(MultipartFile file, String folder) {
|
||||
validateFile(file);
|
||||
|
||||
// validateFile гарантирует, что originalFilename не null и не blank
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
String extension = extractExtension(originalFilename);
|
||||
String prefix = (folder != null && !folder.isBlank())
|
||||
? folder.replaceAll("/+$", "") + "/"
|
||||
: "";
|
||||
String prefix = (folder != null && !folder.isBlank()) ? folder.replaceAll("/+$", "") + "/" : "";
|
||||
String objectName = prefix + UUID.randomUUID() + extension;
|
||||
|
||||
log.info("Загрузка файла '{}'. Новое имя: {}", originalFilename, objectName);
|
||||
|
||||
try {
|
||||
Map<String, String> metadata = new HashMap<>();
|
||||
metadata.put("original-filename", URLEncoder.encode(originalFilename, StandardCharsets.UTF_8));
|
||||
metadata.put(ORIGINAL_FILENAME_METADATA_KEY, URLEncoder.encode(originalFilename, StandardCharsets.UTF_8));
|
||||
|
||||
PutObjectRequest request = PutObjectRequest.builder()
|
||||
.bucket(properties.bucket())
|
||||
@@ -94,8 +85,8 @@ public class FileStorageService {
|
||||
} catch (IOException e) {
|
||||
log.error("Ошибка при чтении файла для загрузки: {}", e.getMessage(), e);
|
||||
throw new ApiException(ErrorCode.INTERNAL_ERROR, "Ошибка при обработке файла.", HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
} catch (Exception e) {
|
||||
log.error("Не удалось загрузить файл: {}", e.getMessage(), e);
|
||||
} catch (S3Exception | SdkClientException e) {
|
||||
log.error("Не удалось загрузить файл в S3/MinIO: {}", e.getMessage(), e);
|
||||
throw new ApiException(ErrorCode.INTERNAL_ERROR, "Ошибка при загрузке файла.", HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
@@ -133,14 +124,12 @@ public class FileStorageService {
|
||||
|
||||
s3Client.deleteObject(request);
|
||||
log.info("Файл '{}' успешно удален.", objectName);
|
||||
} catch (Exception e) {
|
||||
} catch (S3Exception | SdkClientException e) {
|
||||
log.error("Ошибка при удалении файла '{}': {}", objectName, e.getMessage(), e);
|
||||
throw new ApiException(ErrorCode.INTERNAL_ERROR, "Ошибка при удалении файла.", HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- вспомогательные методы ----------
|
||||
|
||||
private DownloadedFile fetchFile(String objectName) {
|
||||
try {
|
||||
HeadObjectRequest headRequest = HeadObjectRequest.builder()
|
||||
@@ -149,8 +138,12 @@ public class FileStorageService {
|
||||
.build();
|
||||
HeadObjectResponse headResponse = s3Client.headObject(headRequest);
|
||||
|
||||
String originalFilename = URLDecoder.decode(
|
||||
headResponse.metadata().get("original-filename"), StandardCharsets.UTF_8);
|
||||
// Метадата могла быть утеряна/отсутствовать (например, объект залит не через uploadFile) —
|
||||
// в этом случае используем сам ключ объекта как имя файла, а не падаем с NPE.
|
||||
String rawFilename = headResponse.metadata().get(ORIGINAL_FILENAME_METADATA_KEY);
|
||||
String originalFilename = (rawFilename != null && !rawFilename.isBlank())
|
||||
? URLDecoder.decode(rawFilename, StandardCharsets.UTF_8)
|
||||
: objectName;
|
||||
|
||||
String contentType = headResponse.contentType() != null
|
||||
? headResponse.contentType()
|
||||
@@ -168,9 +161,7 @@ public class FileStorageService {
|
||||
} catch (NoSuchKeyException e) {
|
||||
log.warn("Файл не найден: {}", objectName);
|
||||
throw new ApiException(ErrorCode.NOT_FOUND, "Файл не найден.", HttpStatus.NOT_FOUND);
|
||||
} catch (ApiException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
} catch (S3Exception | SdkClientException e) {
|
||||
log.error("Ошибка при скачивании файла '{}': {}", objectName, e.getMessage(), e);
|
||||
throw new ApiException(ErrorCode.INTERNAL_ERROR, "Ошибка при скачивании файла.", HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
@@ -182,6 +173,14 @@ public class FileStorageService {
|
||||
throw new ApiException(ErrorCode.VALIDATION_ERROR, "Файл не может быть пустым.", HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// getOriginalFilename() по контракту MultipartFile может вернуть null —
|
||||
// без этой проверки URLEncoder.encode(null, ...) в uploadFile упадёт с NPE.
|
||||
String originalFilename = file.getOriginalFilename();
|
||||
if (originalFilename == null || originalFilename.isBlank()) {
|
||||
log.warn("Файл без оригинального имени.");
|
||||
throw new ApiException(ErrorCode.VALIDATION_ERROR, "Имя файла отсутствует.", HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (file.getSize() > MAX_FILE_SIZE) {
|
||||
log.warn("Файл превышает допустимый размер: {} байт.", file.getSize());
|
||||
throw new ApiException(ErrorCode.VALIDATION_ERROR, "Файл слишком большой (максимум 10 МБ).", HttpStatus.BAD_REQUEST);
|
||||
|
||||
119
src/main/java/com/krylov/refound/service/GeocodingService.java
Normal file
119
src/main/java/com/krylov/refound/service/GeocodingService.java
Normal file
@@ -0,0 +1,119 @@
|
||||
package com.krylov.refound.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.krylov.refound.dto.AddressInfo;
|
||||
import com.krylov.refound.enums.ErrorCode;
|
||||
import com.krylov.refound.exception.ApiException;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class GeocodingService {
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
// Поля address-объекта Nominatim, которые в сумме считаем "городом" (в порядке приоритета)
|
||||
private static final List<String> CITY_FIELDS = List.of("city", "town", "village", "municipality");
|
||||
|
||||
// HttpClient потокобезопасен и предназначен для переиспользования — не создаём его на каждый запрос
|
||||
private final HttpClient httpClient = HttpClient.newBuilder()
|
||||
.connectTimeout(Duration.ofSeconds(5))
|
||||
.build();
|
||||
|
||||
/**
|
||||
* Определяет город и остальную часть адреса по координатам через Nominatim reverse geocoding.
|
||||
*/
|
||||
public AddressInfo getAddress(String lat, String lon) {
|
||||
String url = "https://nominatim.openstreetmap.org/reverse"
|
||||
+ "?lat=" + encode(lat)
|
||||
+ "&lon=" + encode(lon)
|
||||
+ "&format=jsonv2"
|
||||
+ "&addressdetails=1";
|
||||
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.timeout(Duration.ofSeconds(5))
|
||||
.header("User-Agent", "MyApp/1.0")
|
||||
.GET()
|
||||
.build();
|
||||
|
||||
JsonNode root;
|
||||
try {
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
if (response.statusCode() != 200) {
|
||||
log.warn("Nominatim вернул статус {} для lat={}, lon={}", response.statusCode(), lat, lon);
|
||||
throw new ApiException(ErrorCode.INTERNAL_ERROR, "Сервис геокодирования недоступен.", HttpStatus.SERVICE_UNAVAILABLE);
|
||||
}
|
||||
root = mapper.readTree(response.body());
|
||||
} catch (IOException e) {
|
||||
log.error("Ошибка при обращении к Nominatim: {}", e.getMessage(), e);
|
||||
throw new ApiException(ErrorCode.INTERNAL_ERROR, "Сервис геокодирования недоступен.", HttpStatus.SERVICE_UNAVAILABLE);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new ApiException(ErrorCode.INTERNAL_ERROR, "Запрос к сервису геокодирования прерван.", HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
return toAddressInfo(root);
|
||||
}
|
||||
|
||||
private AddressInfo toAddressInfo(JsonNode root) {
|
||||
JsonNode address = root.path("address");
|
||||
String displayName = root.path("display_name").asText("");
|
||||
|
||||
String city = CITY_FIELDS.stream()
|
||||
.map(field -> address.path(field).asText(null))
|
||||
.filter(value -> value != null && !value.isBlank())
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
String district = buildDistrict(displayName, city);
|
||||
|
||||
return new AddressInfo(city, district);
|
||||
}
|
||||
|
||||
/**
|
||||
* display_name — это склеенные через запятую компоненты адреса от самого точного к самому общему.
|
||||
* Убираем из этого списка компонент, совпавший с городом, и склеиваем остальное обратно.
|
||||
*/
|
||||
private String buildDistrict(String displayName, String city) {
|
||||
if (displayName.isBlank()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String[] parts = displayName.split(",");
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean cityRemoved = false;
|
||||
|
||||
for (String part : parts) {
|
||||
String trimmed = part.trim();
|
||||
if (!cityRemoved && trimmed.equalsIgnoreCase(city)) {
|
||||
cityRemoved = true;
|
||||
continue;
|
||||
}
|
||||
if (!result.isEmpty()) {
|
||||
result.append(", ");
|
||||
}
|
||||
result.append(trimmed);
|
||||
}
|
||||
|
||||
return result.isEmpty() ? null : result.toString();
|
||||
}
|
||||
|
||||
private String encode(String value) {
|
||||
return URLEncoder.encode(value, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.krylov.refound.service;
|
||||
|
||||
import com.krylov.refound.dto.DialogResponse;
|
||||
import com.krylov.refound.entity.Message;
|
||||
import com.krylov.refound.entity.User;
|
||||
import com.krylov.refound.enums.ErrorCode;
|
||||
import com.krylov.refound.exception.ApiException;
|
||||
import com.krylov.refound.repository.MessageRepository;
|
||||
import com.krylov.refound.repository.UserRepository;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MessageService {
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package com.krylov.refound.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ModerationService {
|
||||
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package com.krylov.refound.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class NominatimService {
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public String getAddress(String lat, String lon) {
|
||||
String url = "https://nominatim.openstreetmap.org/reverse?lat=" + lat + "&lon=" + lon + "&format=json";
|
||||
|
||||
HttpClient client = HttpClient.newHttpClient();
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(url))
|
||||
.header("User-Agent", "MyApp/1.0")
|
||||
.build();
|
||||
JsonNode root;
|
||||
try{
|
||||
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
root = mapper.readTree(response.body());
|
||||
} catch (InterruptedException | IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return root.path("display_name").asText();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.krylov.refound.service;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.krylov.refound.ai.facade.ContentModerationFacade;
|
||||
import com.krylov.refound.dto.AddressInfo;
|
||||
import com.krylov.refound.dto.PostRequest;
|
||||
import com.krylov.refound.dto.PostResponse;
|
||||
import com.krylov.refound.entity.Image;
|
||||
@@ -18,22 +19,16 @@ import com.krylov.refound.repository.FavoriteRepository;
|
||||
import com.krylov.refound.repository.ImageRepository;
|
||||
import com.krylov.refound.repository.PostRepository;
|
||||
import com.krylov.refound.util.PostSpecification;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
@@ -50,7 +45,7 @@ public class PostService {
|
||||
private final PostRepository repository;
|
||||
private final PostMapper mapper;
|
||||
private final ImageRepository imageRepository;
|
||||
private final NominatimService nominatimService;
|
||||
private final GeocodingService geocodingService;
|
||||
private final UserService userService;
|
||||
private final FavoriteRepository favoriteRepository;
|
||||
private final FileStorageService fileStorageService;
|
||||
@@ -66,6 +61,10 @@ public class PostService {
|
||||
post.setCreatedAt(LocalDateTime.now());
|
||||
post.setStatus(PostStatus.ACTIVE);
|
||||
post.setUser(userService.getCurrentUser());
|
||||
AddressInfo address = geocodingService.getAddress(
|
||||
request.getLatitude().toString(), request.getLongitude().toString());
|
||||
post.setCity(address.city());
|
||||
post.setDistrict(address.district());
|
||||
repository.save(post);
|
||||
|
||||
if (images != null && !images.isEmpty()) {
|
||||
@@ -209,9 +208,11 @@ public class PostService {
|
||||
if (request.getLatitude() != null && request.getLongitude() != null) {
|
||||
post.setLatitude(request.getLatitude());
|
||||
post.setLongitude(request.getLongitude());
|
||||
String address = nominatimService.getAddress(
|
||||
AddressInfo address = geocodingService.getAddress(
|
||||
request.getLatitude().toString(), request.getLongitude().toString());
|
||||
post.setCity(address);
|
||||
|
||||
post.setCity(address.city());
|
||||
post.setDistrict(address.district());
|
||||
} else {
|
||||
post.setCity(request.getAddress());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
databaseChangeLog:
|
||||
- changeSet:
|
||||
id: 006-alter-posts-district-length
|
||||
author: you
|
||||
changes:
|
||||
- modifyDataType:
|
||||
tableName: posts
|
||||
columnName: district
|
||||
newDataType: VARCHAR(500)
|
||||
@@ -16,4 +16,5 @@ databaseChangeLog:
|
||||
- include: { file: db/changelog/add/005-add-user-avatarUrl.yaml }
|
||||
- include: { file: db/changelog/create/008-create-reviews.yaml }
|
||||
- include: { file: db/changelog/add/013-add-rules_accepted-posts.yaml}
|
||||
- include: { file: db/changelog/create/009-create-statistic.yaml }
|
||||
- include: { file: db/changelog/create/009-create-statistic.yaml }
|
||||
- include: { file: db/changelog/alter/011-alter-posts-district-length.yaml}
|
||||
|
||||
Reference in New Issue
Block a user