diff --git a/src/main/java/com/example/backend/common/ErrorCode.java b/src/main/java/com/example/backend/common/ErrorCode.java index bb3964a..0ab4ed7 100644 --- a/src/main/java/com/example/backend/common/ErrorCode.java +++ b/src/main/java/com/example/backend/common/ErrorCode.java @@ -3,11 +3,13 @@ public enum ErrorCode { APPLICATION_SETTING_NOT_FOUND("APPLICATION_SETTING_NOT_FOUND"), + CAR_IDS_REQUIRED("CAR_IDS_REQUIRED"), INPUT_DATA_INVALID("INPUT_DATA_INVALID"), SCHEME_DATA_INVALID("SCHEME_DATA_INVALID"), SCHEME_DATA_REQUIRED("SCHEME_DATA_REQUIRED"), SCHEME_NOT_FOUND("SCHEME_NOT_FOUND"), SCHEME_NAME_REQUIRED("SCHEME_NAME_REQUIRED"), + STATUS_NOT_ALLOWED("STATUS_NOT_ALLOWED"), USER_NOT_FOUND("USER_NOT_FOUND"); private String message; diff --git a/src/main/java/com/example/backend/scheme/CarStatus.java b/src/main/java/com/example/backend/scheme/CarStatus.java index e0b860e..3f5dbeb 100644 --- a/src/main/java/com/example/backend/scheme/CarStatus.java +++ b/src/main/java/com/example/backend/scheme/CarStatus.java @@ -2,6 +2,6 @@ public enum CarStatus { - FIRST_INITIALIZATION, NEW, DISPLAYED; + NEW, SEEN, FIRST_INITIALIZATION; } diff --git a/src/main/java/com/example/backend/scheme/controller/SchemeController.java b/src/main/java/com/example/backend/scheme/controller/SchemeController.java index e35a469..5c51dcf 100644 --- a/src/main/java/com/example/backend/scheme/controller/SchemeController.java +++ b/src/main/java/com/example/backend/scheme/controller/SchemeController.java @@ -5,17 +5,22 @@ import com.example.backend.page.TableDto; import com.example.backend.exception.exceptions.EntityNotFoundException; import com.example.backend.exception.exceptions.IllegalInputException; +import com.example.backend.scheme.dto.SchemeDisplayDto; import com.example.backend.scheme.dto.SchemeDto; +import com.example.backend.scheme.dto.SchemeToCarDto; import com.example.backend.scheme.serializer.SchemeSerializer; import com.example.backend.scheme.service.SchemeService; import com.example.backend.common.UserEmail; import com.example.backend.scheme.model.Scheme; +import com.example.backend.scheme.service.SchemeToCarService; import jakarta.validation.Valid; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.web.bind.annotation.*; +import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; import static com.example.backend.scheme.serializer.SchemeSerializer.Task.BASE; import static com.example.backend.scheme.serializer.SchemeSerializer.Task.CARS; @@ -25,9 +30,11 @@ public class SchemeController { private final SchemeService schemeService; + private final SchemeToCarService schemeToCarService; - public SchemeController(SchemeService schemeService) { + public SchemeController(SchemeService schemeService, SchemeToCarService schemeToCarService) { this.schemeService = schemeService; + this.schemeToCarService = schemeToCarService; } @GetMapping @@ -37,7 +44,11 @@ public TableDto> list(@UserEmail String email, Pageable pageable PageSerializer.fromPageable(pageDto, pageable); PageSerializer.fromPage(pageDto, schemas); - return new TableDto<>(pageDto, schemas.stream().map((scheme -> SchemeSerializer.serialize(scheme, BASE))).toList()); + List schemeDtoList = new ArrayList<>(schemas.stream() + .map((scheme -> SchemeSerializer.serialize(scheme, BASE))).toList()); + schemeService.checkNewOffersCount(schemeDtoList); + + return new TableDto<>(pageDto, schemeDtoList); } @GetMapping("{id}") @@ -65,4 +76,9 @@ public void delete(@UserEmail String email, @PathVariable("id") Long id) throws schemeService.delete(id, email); } + @PutMapping("visibility") + public List changeVisibility(@RequestBody @Valid SchemeDisplayDto schemeDisplayDto) throws IllegalInputException { + return schemeToCarService.changeVisibility(schemeDisplayDto).stream().map(SchemeSerializer::schemeToCar).collect(Collectors.toList()); + } + } diff --git a/src/main/java/com/example/backend/scheme/dto/SchemeDisplayDto.java b/src/main/java/com/example/backend/scheme/dto/SchemeDisplayDto.java new file mode 100644 index 0000000..03acdf8 --- /dev/null +++ b/src/main/java/com/example/backend/scheme/dto/SchemeDisplayDto.java @@ -0,0 +1,32 @@ +package com.example.backend.scheme.dto; + +import com.example.backend.scheme.CarStatus; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.Set; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonIgnoreProperties(ignoreUnknown = true) +public class SchemeDisplayDto { + + private Set ids; + + private CarStatus status; + + public Set getIds() { + return ids; + } + + public void setIds(Set ids) { + this.ids = ids; + } + + public CarStatus getStatus() { + return status; + } + + public void setStatus(CarStatus status) { + this.status = status; + } +} diff --git a/src/main/java/com/example/backend/scheme/dto/SchemeDto.java b/src/main/java/com/example/backend/scheme/dto/SchemeDto.java index 6dad12c..fc1340c 100644 --- a/src/main/java/com/example/backend/scheme/dto/SchemeDto.java +++ b/src/main/java/com/example/backend/scheme/dto/SchemeDto.java @@ -20,6 +20,8 @@ public class SchemeDto { private Set variables; private UserDto user; private Boolean notifications; + private String thumbnail; + private Boolean newOffers; private List cars; public Long getId() { @@ -77,4 +79,20 @@ public Boolean getNotifications() { public void setNotifications(Boolean notifications) { this.notifications = notifications; } + + public String getThumbnail() { + return thumbnail; + } + + public void setThumbnail(String thumbnail) { + this.thumbnail = thumbnail; + } + + public Boolean getNewOffers() { + return newOffers; + } + + public void setNewOffers(Boolean newOffers) { + this.newOffers = newOffers; + } } \ No newline at end of file diff --git a/src/main/java/com/example/backend/scheme/model/Scheme.java b/src/main/java/com/example/backend/scheme/model/Scheme.java index bba418e..c385da0 100644 --- a/src/main/java/com/example/backend/scheme/model/Scheme.java +++ b/src/main/java/com/example/backend/scheme/model/Scheme.java @@ -22,6 +22,9 @@ public class Scheme { @Column(nullable = false) private boolean notifications; + @Column(length = 512) + private String thumbnail; + @ManyToOne @JoinColumn(name = "user_id") private User user; @@ -77,4 +80,11 @@ public void setNotifications(boolean notifications) { this.notifications = notifications; } + public String getThumbnail() { + return thumbnail; + } + + public void setThumbnail(String defaultThumbnail) { + this.thumbnail = defaultThumbnail; + } } diff --git a/src/main/java/com/example/backend/scheme/repository/SchemeRepository.java b/src/main/java/com/example/backend/scheme/repository/SchemeRepository.java index d9c13d4..e590e69 100644 --- a/src/main/java/com/example/backend/scheme/repository/SchemeRepository.java +++ b/src/main/java/com/example/backend/scheme/repository/SchemeRepository.java @@ -1,9 +1,13 @@ package com.example.backend.scheme.repository; import com.example.backend.scheme.model.Scheme; +import jakarta.transaction.Transactional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import java.util.List; import java.util.Optional; @@ -14,4 +18,15 @@ public interface SchemeRepository extends JpaRepository { Page findAllByUserEmail(String email, Pageable pageable); + @Transactional + @Modifying + @Query("UPDATE Scheme s SET s.thumbnail = :thumbnail WHERE s.id = :id") + void updateSchemeById(@Param("id") Long id, @Param("thumbnail") String thumbnail); + + @Query("SELECT s.id AS schemeId, CASE WHEN COUNT(stc.id) > 0 THEN true ELSE false END AS hasNewCars " + + "FROM Scheme s LEFT JOIN SchemeToCar stc ON s.id = stc.scheme.id " + + "AND stc.status IN(com.example.backend.scheme.CarStatus.NEW, com.example.backend.scheme.CarStatus.FIRST_INITIALIZATION) " + + "WHERE s.id IN :schemaIds GROUP BY s.id") + List haveNewCars(@Param("schemaIds") List schemaIds); + } diff --git a/src/main/java/com/example/backend/scheme/repository/SchemeToCarRepository.java b/src/main/java/com/example/backend/scheme/repository/SchemeToCarRepository.java index b683f58..1f3fa80 100644 --- a/src/main/java/com/example/backend/scheme/repository/SchemeToCarRepository.java +++ b/src/main/java/com/example/backend/scheme/repository/SchemeToCarRepository.java @@ -4,8 +4,10 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.query.Param; import java.util.List; +import java.util.Set; public interface SchemeToCarRepository extends JpaRepository { @@ -13,4 +15,6 @@ public interface SchemeToCarRepository extends JpaRepository Page findBySchemeId(Long id, Pageable pageable); + Set findAllByIdIn(@Param("ids") Set ids); + } diff --git a/src/main/java/com/example/backend/scheme/serializer/SchemeSerializer.java b/src/main/java/com/example/backend/scheme/serializer/SchemeSerializer.java index a1faff4..c4f9ae0 100644 --- a/src/main/java/com/example/backend/scheme/serializer/SchemeSerializer.java +++ b/src/main/java/com/example/backend/scheme/serializer/SchemeSerializer.java @@ -5,6 +5,7 @@ import com.example.backend.scheme.dto.SchemeToCarDto; import com.example.backend.scheme.dto.SchemeDto; import com.example.backend.scheme.model.Scheme; +import com.example.backend.scheme.model.SchemeToCar; import com.example.backend.timer.otomoto.request.FilterValueDto; import com.example.backend.timer.otomoto.request.OtomotoDto; import com.example.backend.timer.otomoto.request.VariablesDto; @@ -43,6 +44,7 @@ private static void base(SchemeDto schemeDto, Scheme scheme) { schemeDto.setId(scheme.getId()); schemeDto.setName(scheme.getName()); schemeDto.setNotifications(scheme.isNotifications()); + schemeDto.setThumbnail(scheme.getThumbnail()); try { OtomotoDto otomotoDto = OBJECT_MAPPER.readValue(scheme.getData(), OtomotoDto.class); Set filterList = Optional.ofNullable(otomotoDto).map(OtomotoDto::getVariables) @@ -60,21 +62,21 @@ private static void user(SchemeDto schemeDto, Scheme scheme) { } private static void cars(SchemeDto schemeDto, Scheme scheme) { - List cars = scheme.getSchemeToCars().stream().map((schemeToCar -> { - SchemeToCarDto schemeToCarDto = new SchemeToCarDto(); - schemeToCarDto.setId(schemeToCar.getId()); - schemeToCarDto.setCarId(schemeToCar.getCarId()); - schemeToCarDto.setCreatedAt(schemeToCar.getCreatedAt()); - try { - schemeToCarDto.setData(OBJECT_MAPPER.readValue(schemeToCar.getData(), NodeDto.class)); - } catch (JsonProcessingException e) { - LOGGER.error("Error parsing SchemeToCar {} data!", schemeToCar.getId()); - } - schemeToCarDto.setStatus(schemeToCar.getStatus()); - return schemeToCarDto; - })).toList(); - - schemeDto.setCars(cars); + schemeDto.setCars(scheme.getSchemeToCars().stream().map(SchemeSerializer::schemeToCar).toList()); + } + + public static SchemeToCarDto schemeToCar(SchemeToCar schemeToCar) { + SchemeToCarDto schemeToCarDto = new SchemeToCarDto(); + schemeToCarDto.setId(schemeToCar.getId()); + schemeToCarDto.setCarId(schemeToCar.getCarId()); + schemeToCarDto.setCreatedAt(schemeToCar.getCreatedAt()); + try { + schemeToCarDto.setData(OBJECT_MAPPER.readValue(schemeToCar.getData(), NodeDto.class)); + } catch (JsonProcessingException e) { + LOGGER.error("Error parsing SchemeToCar {} data!", schemeToCar.getId()); + } + schemeToCarDto.setStatus(schemeToCar.getStatus()); + return schemeToCarDto; } diff --git a/src/main/java/com/example/backend/scheme/service/SchemeService.java b/src/main/java/com/example/backend/scheme/service/SchemeService.java index 87e0783..94cc7c4 100644 --- a/src/main/java/com/example/backend/scheme/service/SchemeService.java +++ b/src/main/java/com/example/backend/scheme/service/SchemeService.java @@ -154,6 +154,19 @@ public String getTimerQuery() throws EntityNotFoundException { } } + public void checkNewOffersCount(List schemeDtoList) { + if (schemeDtoList.isEmpty()) return; + + List results = schemeRepository.haveNewCars(schemeDtoList.stream().map(SchemeDto::getId).toList()); + + schemeDtoList.forEach(schemeDto -> { + results.stream() + .filter(result -> result[0] == schemeDto.getId()) + .findFirst() + .ifPresent(result -> schemeDto.setNewOffers((Boolean) result[1])); + }); + } + /** * Prepare dto which will be sent to otomoto REST API * and run loop searching for new cars. @@ -274,6 +287,7 @@ private TimerResponse calculatePageWithOtomotoData(Scheme scheme, OtomotoRespons // Save all new cars into the database boolean firstInitialization = scheme.getSchemeToCars().isEmpty(); saveNewCarsToDatabase(carIdToNodeDtoMap, scheme, firstInitialization); + updateDefaultThumbnail(scheme); if (firstInitialization) return new TimerResponse(true, diff.size()); @@ -337,6 +351,39 @@ private void sendNotification(Scheme scheme, Integer count) { } } + private void updateDefaultThumbnail(Scheme scheme) { + if (!isNullOrEmpty(scheme.getThumbnail())) return; + + List schemeToCars = scheme.getSchemeToCars().stream() + .sorted(Comparator.comparing(SchemeToCar::getCreatedAt)).toList(); + String defaultThumbnail = null; + + for (SchemeToCar schemeToCar : schemeToCars) { + String thumbnail = getThumbnail(schemeToCar); + if (thumbnail != null) { + defaultThumbnail = thumbnail; + break; + } + } + + schemeRepository.updateSchemeById(scheme.getId(), defaultThumbnail); + } + + private String getThumbnail(SchemeToCar schemeToCar) { + try { + NodeDto schemeData = OBJECT_MAPPER.readValue(schemeToCar.getData(), NodeDto.class); + + return Optional.ofNullable(schemeData) + .map(NodeDto::getThumbnail) + .flatMap(thumbnailDto -> Optional.ofNullable(thumbnailDto.getX2()).filter(set -> !set.isEmpty()) + .or(() -> Optional.ofNullable(thumbnailDto.getX1()))) + .orElse(null); + } catch (JsonProcessingException e) { + LOGGER.error("[{}] There was an error while parsing data.", TIMER_NAME, e); + return null; + } + } + private static class TimerResponse { private final boolean shouldFinish; diff --git a/src/main/java/com/example/backend/scheme/service/SchemeToCarService.java b/src/main/java/com/example/backend/scheme/service/SchemeToCarService.java new file mode 100644 index 0000000..53e6f03 --- /dev/null +++ b/src/main/java/com/example/backend/scheme/service/SchemeToCarService.java @@ -0,0 +1,40 @@ +package com.example.backend.scheme.service; + +import com.example.backend.common.ErrorCode; +import com.example.backend.exception.exceptions.IllegalInputException; +import com.example.backend.scheme.CarStatus; +import com.example.backend.scheme.dto.SchemeDisplayDto; +import com.example.backend.scheme.model.SchemeToCar; +import com.example.backend.scheme.repository.SchemeToCarRepository; +import org.springframework.stereotype.Service; + +import java.util.Set; + +@Service +public class SchemeToCarService { + + private final SchemeToCarRepository schemeToCarRepository; + + public SchemeToCarService(SchemeToCarRepository schemeToCarRepository) { + this.schemeToCarRepository = schemeToCarRepository; + } + + public Set changeVisibility(SchemeDisplayDto schemeDisplayDto) throws IllegalInputException { + CarStatus status = schemeDisplayDto.getStatus(); + Set schemeToCarIds = schemeDisplayDto.getIds(); + + if (status == null || CarStatus.FIRST_INITIALIZATION.equals(status)) + throw new IllegalInputException(ErrorCode.STATUS_NOT_ALLOWED); + + if (schemeToCarIds == null || schemeToCarIds.isEmpty()) + throw new IllegalInputException(ErrorCode.CAR_IDS_REQUIRED); + + Set schemeToCars = schemeToCarRepository.findAllByIdIn(schemeToCarIds); + for (SchemeToCar schemeToCar : schemeToCars) + schemeToCar.setStatus(status); + + schemeToCarRepository.saveAll(schemeToCars); + return schemeToCars; + } + +}