feat: implement image management functionality; add ImageController, ImageService, ImageMapper, and related DTOs

This commit is contained in:
Vincent Guillet
2025-11-10 16:29:55 +01:00
parent a849a4dd15
commit f063a245b9
9 changed files with 198 additions and 6 deletions

View File

@@ -0,0 +1,63 @@
package fr.gameovergne.api.controller.app;
import fr.gameovergne.api.dto.app.ImageDTO;
import fr.gameovergne.api.mapper.app.ImageMapper;
import fr.gameovergne.api.model.app.Image;
import fr.gameovergne.api.service.app.ImageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/app/images")
public class ImageController {
private final ImageService imageService;
private final ImageMapper imageMapper;
@Autowired
public ImageController(ImageService imageService, ImageMapper imageMapper) {
this.imageService = imageService;
this.imageMapper = imageMapper;
}
@GetMapping
public List<ImageDTO> getAllImages() {
return imageService.getAllImages()
.stream()
.map(imageMapper::toDto)
.toList();
}
@GetMapping("/{id}")
public ResponseEntity<Image> getImageById(@PathVariable Long id) {
return imageService.getImageById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public void saveImage(@RequestBody ImageDTO imageDTO) {
imageService.saveImage(imageMapper.fromDto(imageDTO));
}
@PutMapping("/{id}")
public ResponseEntity<Image> updateImage(@PathVariable Long id, @RequestBody ImageDTO imageDTO) {
Image image = imageMapper.fromDto(imageDTO);
image.setId(id);
return imageService.getImageById(id)
.map(existingImage -> imageService.updateImage(image)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build()))
.orElse(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public ResponseEntity<Image> deleteImageById(@PathVariable Long id) {
return imageService.deleteImageById(id)
.map(image -> ResponseEntity.ok().body(image))
.orElse(ResponseEntity.notFound().build());
}
}

View File

@@ -0,0 +1,11 @@
package fr.gameovergne.api.controller.app;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/app/products_images")
public class ProductImageController {
}

View File

@@ -0,0 +1,21 @@
package fr.gameovergne.api.dto.app;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ImageDTO {
@JsonProperty("id")
private Long id;
@JsonProperty("name")
private String name;
@JsonProperty("url")
private String url;
}

View File

@@ -0,0 +1,27 @@
package fr.gameovergne.api.mapper.app;
import fr.gameovergne.api.dto.app.ImageDTO;
import fr.gameovergne.api.model.app.Image;
import org.springframework.stereotype.Component;
@Component
public class ImageMapper {
public Image fromDto(ImageDTO imageDTO) {
if (imageDTO == null) return null;
Image image = new Image();
image.setId(imageDTO.getId());
image.setName(imageDTO.getName());
image.setUrl(imageDTO.getUrl());
return image;
}
public ImageDTO toDto(Image image) {
if (image == null) return null;
ImageDTO imageDTO = new ImageDTO();
imageDTO.setId(image.getId());
imageDTO.setName(image.getName());
imageDTO.setUrl(image.getUrl());
return imageDTO;
}
}

View File

@@ -1,6 +1,5 @@
package fr.gameovergne.api.model.app; package fr.gameovergne.api.model.app;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIdentityInfo; import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators; import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import fr.gameovergne.api.model.user.User; import fr.gameovergne.api.model.user.User;
@@ -26,7 +25,7 @@ public class Image {
@Column(length = 120, unique = true, nullable = false) @Column(length = 120, unique = true, nullable = false)
@NotBlank @NotBlank
private String title; private String name;
@Column(length = 255, unique = true, nullable = false) @Column(length = 255, unique = true, nullable = false)
@NotBlank @NotBlank
@@ -34,7 +33,7 @@ public class Image {
@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinTable( @JoinTable(
name = "user_images", name = "users_images",
joinColumns = @JoinColumn(name = "image_id"), joinColumns = @JoinColumn(name = "image_id"),
inverseJoinColumns = @JoinColumn(name = "user_id") inverseJoinColumns = @JoinColumn(name = "user_id")
) )
@@ -42,7 +41,7 @@ public class Image {
@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinTable( @JoinTable(
name = "product_images", name = "products_images",
joinColumns = @JoinColumn(name = "image_id"), joinColumns = @JoinColumn(name = "image_id"),
inverseJoinColumns = @JoinColumn(name = "product_id") inverseJoinColumns = @JoinColumn(name = "product_id")
) )

View File

@@ -63,7 +63,7 @@ public class Product {
@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinTable( @JoinTable(
name = "product_images", name = "products_images",
joinColumns = @JoinColumn(name = "image_id"), joinColumns = @JoinColumn(name = "image_id"),
inverseJoinColumns = @JoinColumn(name = "product_id") inverseJoinColumns = @JoinColumn(name = "product_id")
) )

View File

@@ -51,7 +51,7 @@ public class User implements UserDetails {
@ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}) @ManyToMany(fetch = FetchType.EAGER, cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
@JoinTable( @JoinTable(
name = "user_images", name = "users_images",
joinColumns = @JoinColumn(name = "image_id"), joinColumns = @JoinColumn(name = "image_id"),
inverseJoinColumns = @JoinColumn(name = "user_id") inverseJoinColumns = @JoinColumn(name = "user_id")
) )

View File

@@ -0,0 +1,11 @@
package fr.gameovergne.api.repository.app;
import fr.gameovergne.api.model.app.Image;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface ImageRepository extends JpaRepository<Image, Long> {
Optional<Image> findByName(String name);
Optional<Image> findByUrl(String url);
}

View File

@@ -0,0 +1,60 @@
package fr.gameovergne.api.service.app;
import fr.gameovergne.api.model.app.Image;
import fr.gameovergne.api.repository.app.ImageRepository;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class ImageService {
private final ImageRepository imageRepository;
@Autowired
public ImageService(ImageRepository imageRepository) {
this.imageRepository = imageRepository;
}
@Transactional
public void saveImage(Image image) {
if (image.getId() == null) {
imageRepository.save(image);
}
}
public List<Image> getAllImages() {
return imageRepository.findAll();
}
public Optional<Image> getImageById(Long id) {
return imageRepository.findById(id);
}
public Optional<Image> getImageByName(String name) {
return imageRepository.findByName(name);
}
public Optional<Image> getImageByUrl(String url) {
return imageRepository.findByUrl(url);
}
@Transactional
public Optional<Image> updateImage(Image image) {
return imageRepository.findById(image.getId()).map(existingImage -> {
existingImage.setName(image.getName());
existingImage.setUrl(image.getUrl());
return imageRepository.save(existingImage);
});
}
@Transactional
public Optional<Image> deleteImageById(Long id) {
Optional<Image> image = imageRepository.findById(id);
image.ifPresent(imageRepository::delete);
return image;
}
}