Add FarmImage, FarmImageController, refactor ImageController

This commit is contained in:
Tim
2025-06-12 09:52:05 +02:00
parent 022f08a773
commit 6d7f28d6db
16 changed files with 359 additions and 30 deletions

View File

@@ -30,6 +30,10 @@ public class ControllerPathConfig {
public static final String IMAGE_BASE = "/image";
public static final String IMAGE_ALL = IMAGE_BASE + "/all";
//FarmImageController
public static final String FARM_IMAGE_BASE = "/farm";
public static final String FARM_IMAGE_DEFAULT = FARM_IMAGE_BASE + "/default";
//OrderController
public static final String ORDER_BASE = "/order";
public static final String ORDER_GET_ALL = ORDER_BASE + "/all";

View File

@@ -12,4 +12,5 @@ public class ParameterConfig {
public static final String PARAM_RATING = "rating";
public static final String PARAM_IMAGE = "image";
public static final String PARAM_STATUS = "status";
public static final String PARAM_STANDARD = "standard";
}

View File

@@ -0,0 +1,99 @@
package de.htwsaar.webshop.controller;
import de.htwsaar.webshop.model.FarmImageModel;
import de.htwsaar.webshop.repository.entities.FarmImage;
import de.htwsaar.webshop.service.ArticleService;
import de.htwsaar.webshop.service.FarmImageService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.UUID;
import static de.htwsaar.webshop.config.ControllerPathConfig.FARM_IMAGE_BASE;
import static de.htwsaar.webshop.config.ControllerPathConfig.FARM_IMAGE_DEFAULT;
import static de.htwsaar.webshop.config.ParameterConfig.*;
import static de.htwsaar.webshop.util.LoggerUtil.logRequest;
@RestController
@Slf4j
public class FarmImageController {
private final FarmImageService farmImageService;
private final ArticleService articleService;
@Autowired
public FarmImageController(FarmImageService imageService, ArticleService articleService) {
this.farmImageService = imageService;
this.articleService = articleService;
}
@RequestMapping(path = FARM_IMAGE_DEFAULT, method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<String> getDefault(HttpServletRequest request) {
logRequest(request);
FarmImage image = farmImageService.getStandardImage();
if (image == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(image.getBase64());
}
@RequestMapping(path = FARM_IMAGE_BASE, method = RequestMethod.GET, produces = "text/plain")
public ResponseEntity<String> getFirst(HttpServletRequest request,
@RequestParam(value = PARAM_UUID) UUID uuid) {
logRequest(request);
FarmImage image = farmImageService.getImageByUUID(uuid);
if (image == null) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return ResponseEntity.ok(image.getBase64());
}
@RequestMapping(path = FARM_IMAGE_BASE, method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<Boolean> add(HttpServletRequest request,
@RequestParam(value = PARAM_UUID) UUID articleUuid,
@RequestParam(value = PARAM_IMAGE) MultipartFile file,
@RequestParam(value = PARAM_STANDARD) boolean standard) {
logRequest(request);
if (articleUuid == null || articleService.findByUUID(articleUuid) == null
|| file == null || file.isEmpty()) {
log.warn("[{}] failed Validation, sending bad request", request.getRequestURI());
return ResponseEntity.badRequest().body(false);
}
FarmImage a = farmImageService.save(articleUuid, file, standard);
return ResponseEntity.ok(a != null);
}
@RequestMapping(path = FARM_IMAGE_BASE, method = RequestMethod.PUT, produces = "application/json")
public ResponseEntity<Boolean> update(HttpServletRequest request,
@RequestParam(value = PARAM_ID) Long imageId,
@RequestBody FarmImageModel image) {
logRequest(request);
if (imageId == null || farmImageService.getImageById(imageId) == null) {
return ResponseEntity.badRequest().body(false);
}
image.setId(farmImageService.getImageById(imageId).getId());
return ResponseEntity.ok(farmImageService.save(image) != null);
}
@RequestMapping(path = FARM_IMAGE_BASE, method = RequestMethod.DELETE, produces = "application/json")
public ResponseEntity<Boolean> delete(HttpServletRequest request,
@RequestParam(value = PARAM_ID) Long imageId) {
logRequest(request);
if (imageId == null) {
log.warn("[{}] got invalid imageId", request.getRequestURI());
return ResponseEntity.badRequest().body(false);
}
if (farmImageService.getImageById(imageId) != null) {
log.warn("[{}] got invalid imageId", request.getRequestURI());
return ResponseEntity.badRequest().body(false);
}
farmImageService.deleteById(imageId);
return ResponseEntity.ok().build();
}
}

View File

@@ -1,5 +1,6 @@
package de.htwsaar.webshop.controller;
import de.htwsaar.webshop.model.ImageModel;
import de.htwsaar.webshop.repository.entities.Image;
import de.htwsaar.webshop.service.ArticleService;
import de.htwsaar.webshop.service.ImageService;
@@ -96,7 +97,7 @@ public class ImageController {
@RequestMapping(path = IMAGE_BASE, method = RequestMethod.PUT, produces = "application/json")
public ResponseEntity<Boolean> update(HttpServletRequest request,
@RequestParam(value = PARAM_ID) Long imageId,
@RequestBody Image image) {
@RequestBody ImageModel image) {
logRequest(request);
if (imageId == null || imageService.getImageById(imageId) == null) {
return ResponseEntity.badRequest().body(false);

View File

@@ -0,0 +1,27 @@
package de.htwsaar.webshop.model;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import java.util.UUID;
@Getter
@Setter
@AllArgsConstructor
public class FarmImageModel {
@NotNull
private Long id;
@NotNull
private UUID articleUuid;
@NotNull
@NotEmpty
private String base64;
@NotNull
Boolean standard;
}

View File

@@ -0,0 +1,24 @@
package de.htwsaar.webshop.model;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import java.util.UUID;
@Getter
@Setter
@AllArgsConstructor
public class ImageModel {
@NotNull
private Long id;
@NotNull
private UUID articleUuid;
@NotNull
@NotEmpty
private String base64;
}

View File

@@ -0,0 +1,17 @@
package de.htwsaar.webshop.repository;
import de.htwsaar.webshop.repository.entities.FarmImage;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@Repository
public interface FarmImageRepository extends JpaRepository<FarmImage, Long> {
FarmImage findFarmImageById(Long id);
FarmImage findFarmImageByArticle_Uuid(UUID articleUuid);
FarmImage findFarmImageByStandard(boolean standard);
}

View File

@@ -1,8 +1,6 @@
package de.htwsaar.webshop.repository;
import de.htwsaar.webshop.repository.entities.Image;
import jakarta.validation.constraints.NotNull;
import lombok.NonNull;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@@ -11,9 +9,6 @@ import java.util.UUID;
@Repository
public interface ImageRepository extends JpaRepository<Image, Long> {
List<Image> findAllByArticleId(@NonNull Long articleId);
Image findImageByArticleId(@NotNull Long articleId);
Image findImageById(Long id);

View File

@@ -0,0 +1,35 @@
package de.htwsaar.webshop.repository.entities;
import de.htwsaar.webshop.model.FarmImageModel;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.type.NumericBooleanConverter;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Entity
@Table(name = "FarmImages")
public class FarmImage {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false, unique = true)
private Long id;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "article_id", referencedColumnName = "id", nullable = false)
private Article article;
@Column(name = "base64", nullable = false)
private String base64;
@Convert(converter = NumericBooleanConverter.class)
@Column(name = "standard", nullable = false)
private boolean standard;
public FarmImageModel toModel() {
return new FarmImageModel(id, article.getUuid(), base64, standard);
}
}

View File

@@ -1,5 +1,6 @@
package de.htwsaar.webshop.repository.entities;
import de.htwsaar.webshop.model.ImageModel;
import jakarta.persistence.*;
import lombok.*;
@@ -22,4 +23,8 @@ public class Image {
@Column(name = "base64", nullable = false)
private String base64;
public ImageModel toModel() {
return new ImageModel(id, article.getUuid(), base64);
}
}

View File

@@ -0,0 +1,23 @@
package de.htwsaar.webshop.service;
import de.htwsaar.webshop.model.FarmImageModel;
import de.htwsaar.webshop.repository.entities.FarmImage;
import org.springframework.web.multipart.MultipartFile;
import java.util.UUID;
public interface FarmImageService {
FarmImage getStandardImage();
FarmImage getImageByUUID(UUID uuid);
FarmImage getImageById(Long imageId);
FarmImage save(FarmImage image);
FarmImage save(FarmImageModel model);
FarmImage save(UUID uuid, MultipartFile file, boolean standard);
void deleteById(Long imageId);
}

View File

@@ -1,5 +1,6 @@
package de.htwsaar.webshop.service;
import de.htwsaar.webshop.model.ImageModel;
import de.htwsaar.webshop.repository.entities.Image;
import org.springframework.web.multipart.MultipartFile;
@@ -15,7 +16,7 @@ public interface ImageService {
Image save(Image image);
Image save(UUID uuid, String uri);
Image save(ImageModel model);
Image save(UUID uuid, MultipartFile file);

View File

@@ -0,0 +1,91 @@
package de.htwsaar.webshop.service.impl;
import de.htwsaar.webshop.model.FarmImageModel;
import de.htwsaar.webshop.repository.FarmImageRepository;
import de.htwsaar.webshop.repository.entities.Article;
import de.htwsaar.webshop.repository.entities.FarmImage;
import de.htwsaar.webshop.service.ArticleService;
import de.htwsaar.webshop.service.FarmImageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.util.Base64;
import java.util.UUID;
@Service
@Slf4j
@Transactional
public class FarmImageServiceImpl implements FarmImageService {
private final FarmImageRepository farmImageRepository;
private final ArticleService articleService;
@Autowired
public FarmImageServiceImpl(FarmImageRepository imageRepository, ArticleService articleService) {
this.farmImageRepository = imageRepository;
this.articleService = articleService;
}
@Override
public FarmImage getStandardImage() {
return farmImageRepository.findFarmImageByStandard(true);
}
@Override
public FarmImage getImageByUUID(UUID uuid) {
return farmImageRepository.findFarmImageByArticle_Uuid(uuid);
}
@Override
public FarmImage getImageById(Long imageId) {
return farmImageRepository.findFarmImageById(imageId);
}
@Override
@Transactional
public FarmImage save(FarmImage image) {
return farmImageRepository.save(image);
}
@Override
public FarmImage save(FarmImageModel model) {
return save(new FarmImage(model.getId(), articleService.findByUUID(model.getArticleUuid()), model.getBase64(), model.getStandard()));
}
@Override
public FarmImage save(UUID uuid, MultipartFile file, boolean standard) {
if (uuid == null) {
log.warn("Got no UUID, aborting");
return null;
}
Article article = articleService.findByUUID(uuid);
if (article == null) {
log.warn("Could not find article with id {}", uuid);
return null;
}
if (file == null) {
log.warn("Got no file for {}", uuid);
return null;
}
try {
String based64 = Base64.getEncoder().encodeToString(file.getBytes());
if (based64 == null || based64.isEmpty()) {
log.warn("Could not save image with id {} and file size {}", uuid, file.getSize());
return null;
}
return farmImageRepository.save(new FarmImage(null, article, based64, standard));
} catch (Exception e) {
log.warn("Error saving image {} for article {}", file, uuid, e);
}
return null;
}
@Override
@Transactional
public void deleteById(Long imageId) {
farmImageRepository.deleteById(imageId);
}
}

View File

@@ -1,5 +1,6 @@
package de.htwsaar.webshop.service.impl;
import de.htwsaar.webshop.model.ImageModel;
import de.htwsaar.webshop.repository.ImageRepository;
import de.htwsaar.webshop.repository.entities.Article;
import de.htwsaar.webshop.repository.entities.Image;
@@ -50,15 +51,8 @@ public class ImageServiceImpl implements ImageService {
}
@Override
@Transactional
public Image save(UUID uuid, String uri) {
Article article = articleService.findByUUID(uuid);
if (article == null) {
return null;
}
Image image = new Image(null, article, uri);
System.out.println(image);
return imageRepository.save(image);
public Image save(ImageModel model) {
return save(new Image(model.getId(), articleService.findByUUID(model.getArticleUuid()), model.getBase64()));
}
@Override

View File

@@ -1,18 +1,18 @@
-- articles
CREATE TABLE IF NOT EXISTS Articles
(
id INTEGER PRIMARY KEY NOT NULL,
uuid TEXT UNIQUE NOT NULL, -- UUID
stock INTEGER NOT NULL DEFAULT 0,
stockExpected INTEGER NOT NULL DEFAULT 100,
name TEXT NOT NULL,
description TEXT NULL, --in html
price100 INTEGER NOT NULL, -- in cents
discount100 INTEGER NULL, -- in percent
category TEXT NULL,
id INTEGER PRIMARY KEY NOT NULL,
uuid TEXT UNIQUE NOT NULL, -- UUID
stock INTEGER NOT NULL DEFAULT 0,
stockExpected INTEGER NOT NULL DEFAULT 100,
name TEXT NOT NULL,
description TEXT NULL, --in html
price100 INTEGER NOT NULL, -- in cents
discount100 INTEGER NULL, -- in percent
category TEXT NULL,
CONSTRAINT c_stock CHECK ( stock >= 0 ),
CONSTRAINT c_stockExpected CHECK ( stockExpected >= 0 ), --inactive in current version
CONSTRAINT c_stockExpected CHECK ( stockExpected >= 0 ), --inactive in current version
CONSTRAINT c_discount100 CHECK ( discount100 IS NULL OR 0 <= discount100 AND discount100 <= 100 ) --inactive in current version
);
@@ -25,6 +25,18 @@ CREATE TABLE IF NOT EXISTS Images
FOREIGN KEY (article_id) REFERENCES Articles (id)
);
CREATE TABLE IF NOT EXISTS FarmImages
(
id INTEGER PRIMARY KEY NOT NULL,
article_id INTEGER NOT NULL,
base64 TEXT NOT NULL,
standard INTEGER NOT NULL, --there should always only be one standard image
FOREIGN KEY (article_id) REFERENCES Articles (id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS Reviews
(
id INTEGER PRIMARY KEY NOT NULL,
@@ -89,10 +101,10 @@ CREATE TABLE IF NOT EXISTS OrderItems
CREATE TABLE IF NOT EXISTS Sessions
(
id INTEGER NOT NULL PRIMARY KEY,
token TEXT UNIQUE NOT NULL,
timeout INTEGER NOT NULL,
account_id INTEGER NOT NULL,
id INTEGER NOT NULL PRIMARY KEY,
token TEXT UNIQUE NOT NULL,
timeout INTEGER NOT NULL,
account_id INTEGER NOT NULL,
FOREIGN KEY (account_id) REFERENCES Accounts (id)
ON DELETE CASCADE
ON UPDATE CASCADE