Merge pull request #16 from SlimusMinus/fix_images

added many images in post
This commit is contained in:
SlimusMinus
2026-07-01 00:31:47 +03:00
committed by GitHub
2 changed files with 81 additions and 40 deletions

View File

@@ -1,13 +1,16 @@
package com.krylov.refound.controller; package com.krylov.refound.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.krylov.refound.dto.PostRequest; import com.krylov.refound.dto.PostRequest;
import com.krylov.refound.dto.PostResponse; import com.krylov.refound.dto.PostResponse;
import com.krylov.refound.dto.StatusUpdateRequest; import com.krylov.refound.dto.StatusUpdateRequest;
import com.krylov.refound.enums.PostType; import com.krylov.refound.enums.PostType;
import com.krylov.refound.service.PostService; import com.krylov.refound.service.PostService;
import java.util.Arrays;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
@@ -31,11 +34,13 @@ import org.springframework.web.multipart.MultipartFile;
public class PostController { public class PostController {
private final PostService service; private final PostService service;
private final ObjectMapper objectMapper;
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public PostRequest create(@ModelAttribute PostRequest request, @RequestPart(value = "image", required = false) MultipartFile image) { public PostRequest create(@ModelAttribute PostRequest request,
@RequestParam(value = "images", required = false) List<MultipartFile> images) {
log.info(request.toString()); log.info(request.toString());
return service.create(request, image); return service.create(request, images);
} }
@GetMapping @GetMapping
@@ -44,7 +49,7 @@ public class PostController {
} }
@GetMapping("/{id}") @GetMapping("/{id}")
public List<PostResponse> getById(@PathVariable Long id) { public List<PostResponse> getById(@PathVariable Long id) {
return service.getById(id); return service.getById(id);
} }
@@ -59,8 +64,8 @@ public class PostController {
@RequestParam(required = false) String description, @RequestParam(required = false) String description,
@RequestParam(required = false) String category, @RequestParam(required = false) String category,
@RequestParam(required = false) String phone, @RequestParam(required = false) String phone,
@RequestParam(value = "image", required = false) MultipartFile image @RequestParam(value = "images", required = false) List<MultipartFile> images,
) { @RequestParam(value = "existingImages", required = false) String existingImagesJson) {
PostRequest request = new PostRequest(); PostRequest request = new PostRequest();
request.setType(PostType.from(type)); request.setType(PostType.from(type));
request.setTitle(title); request.setTitle(title);
@@ -70,8 +75,15 @@ public class PostController {
request.setLatitude(latitude); request.setLatitude(latitude);
request.setLongitude(longitude); request.setLongitude(longitude);
request.setAddress(address); request.setAddress(address);
List<String> existingUrls;
try {
existingUrls = existingImagesJson != null ? Arrays.asList(
objectMapper.readValue(existingImagesJson, String[].class)) : List.of();
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
return service.update(id, request, image); return service.update(id, request, images, existingUrls);
} }
@DeleteMapping("/{id}") @DeleteMapping("/{id}")
@@ -80,7 +92,8 @@ public class PostController {
} }
@PatchMapping("/{id}/status") @PatchMapping("/{id}/status")
public PostResponse updateStatus(@PathVariable Long id, @RequestBody StatusUpdateRequest request) { public PostResponse updateStatus(@PathVariable Long id,
@RequestBody StatusUpdateRequest request) {
return service.updateStatus(id, request.getStatus()); return service.updateStatus(id, request.getStatus());
} }
} }

View File

@@ -44,7 +44,7 @@ public class PostService {
private final UserService userService; private final UserService userService;
private final FavoriteRepository favoriteRepository; private final FavoriteRepository favoriteRepository;
public PostRequest create(PostRequest request, MultipartFile image) { public PostRequest create(PostRequest request, List<MultipartFile> images) {
Post post = mapper.toEntity(request); Post post = mapper.toEntity(request);
post.setCreatedAt(LocalDateTime.now()); post.setCreatedAt(LocalDateTime.now());
@@ -52,21 +52,32 @@ public class PostService {
post.setUser(userService.getCurrentUser()); // присваиваем текущего пользователя post.setUser(userService.getCurrentUser()); // присваиваем текущего пользователя
repository.save(post); // сначала сохраняем post, чтобы получить id repository.save(post); // сначала сохраняем post, чтобы получить id
if (image != null && !image.isEmpty()) { if (images != null && !images.isEmpty()) {
String filename = UUID.randomUUID() + "_" + image.getOriginalFilename();
Path uploadDir = Paths.get("uploads"); Path uploadDir = Paths.get("uploads");
try { try {
Files.createDirectories(uploadDir); Files.createDirectories(uploadDir);
Files.copy(image.getInputStream(), uploadDir.resolve(filename));
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Не удалось сохранить файл: " + filename, e); throw new RuntimeException("Не удалось создать директорию загрузки", e);
} }
Image img = new Image(); for (MultipartFile image : images) {
img.setUrl("/uploads/" + filename); if (image == null || image.isEmpty()) {
img.setPost(post); continue;
imageRepository.save(img); }
String filename = UUID.randomUUID() + "_" + image.getOriginalFilename();
try {
Files.copy(image.getInputStream(), uploadDir.resolve(filename));
} catch (IOException e) {
throw new RuntimeException("Не удалось сохранить файл: " + filename, e);
}
Image img = new Image();
img.setUrl("/uploads/" + filename);
img.setPost(post);
imageRepository.save(img);
}
} }
return request; return request;
@@ -146,8 +157,7 @@ public class PostService {
.toList(); .toList();
} }
public PostResponse update(Long id, PostRequest request, MultipartFile image) { public PostResponse update(Long id, PostRequest request, List<MultipartFile> images, List<String> existingUrls) {
Post post = repository.findById(id) Post post = repository.findById(id)
.orElseThrow(() -> new ApiException( .orElseThrow(() -> new ApiException(
ErrorCode.NOT_FOUND, ErrorCode.NOT_FOUND,
@@ -162,41 +172,59 @@ public class PostService {
} }
post.setPhone(request.getPhone()); post.setPhone(request.getPhone());
post.setType(request.getType()); post.setType(request.getType());
if (request.getLatitude() != null && request.getLongitude() != null){
if (request.getLatitude() != null && request.getLongitude() != null) {
post.setLatitude(request.getLatitude()); post.setLatitude(request.getLatitude());
post.setLongitude(request.getLongitude()); post.setLongitude(request.getLongitude());
String address = nominatimService.getAddress(request.getLatitude().toString(), request.getLongitude().toString()); String address = nominatimService.getAddress(
request.getLatitude().toString(), request.getLongitude().toString());
post.setCity(address); post.setCity(address);
} else { } else {
post.setCity(request.getAddress()); post.setCity(request.getAddress());
} }
if (image != null && !image.isEmpty()) { List<String> keepUrls = existingUrls != null ? existingUrls : List.of();
// удаляем старые картинки с диска
post.getImages().forEach(img -> {
try {
Files.deleteIfExists(Paths.get("." + img.getUrl()));
} catch (IOException e) {
log.warn("Не удалось удалить файл: {}", img.getUrl());
}
});
// очищаем список (orphanRemoval = true удалит из БД)
post.getImages().clear();
// сохраняем новую картинку // удаляем те картинки, которых нет среди "оставленных" с фронта
String filename = UUID.randomUUID() + "_" + image.getOriginalFilename(); List<Image> toRemove = post.getImages().stream()
.filter(img -> !keepUrls.contains(img.getUrl()))
.toList();
toRemove.forEach(img -> {
try {
Files.deleteIfExists(Paths.get("." + img.getUrl()));
} catch (IOException e) {
log.warn("Не удалось удалить файл: {}", img.getUrl());
}
});
post.getImages().removeAll(toRemove); // orphanRemoval = true удалит из БД
// добавляем новые загруженные файлы
if (images != null && !images.isEmpty()) {
Path uploadDir = Paths.get("uploads"); Path uploadDir = Paths.get("uploads");
try { try {
Files.createDirectories(uploadDir); Files.createDirectories(uploadDir);
Files.copy(image.getInputStream(), uploadDir.resolve(filename));
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Не удалось сохранить файл: " + filename, e); throw new RuntimeException("Не удалось создать директорию загрузки", e);
} }
Image img = new Image(); for (MultipartFile image : images) {
img.setUrl("/uploads/" + filename); if (image == null || image.isEmpty()) {
img.setPost(post); continue;
post.getImages().add(img); }
String filename = UUID.randomUUID() + "_" + image.getOriginalFilename();
try {
Files.copy(image.getInputStream(), uploadDir.resolve(filename));
} catch (IOException e) {
throw new RuntimeException("Не удалось сохранить файл: " + filename, e);
}
Image img = new Image();
img.setUrl("/uploads/" + filename);
img.setPost(post);
post.getImages().add(img);
}
} }
Post updated = repository.save(post); Post updated = repository.save(post);