чето работает чето не работает .

This commit is contained in:
Kirill 2024-05-31 15:16:30 +04:00
parent 1fb86d928b
commit 088f85a88a
30 changed files with 1419 additions and 14 deletions

View File

@ -21,4 +21,5 @@
"cSpell.words": [
"classappend"
],
"java.debug.settings.onBuildFailureProceed": true,
}

View File

@ -16,6 +16,12 @@ import com.example.demo.subscriptions.model.SubscriptionEntity;
import com.example.demo.subscriptions.service.SubscriptionService;
import com.example.demo.types.model.TypeEntity;
import com.example.demo.types.service.TypeService;
import com.example.demo.genres.model.GenreEntity;
import com.example.demo.genres.service.GenreService;
import com.example.demo.films.model.FilmEntity;
import com.example.demo.films.service.FilmService;
import com.example.demo.userfilms.model.UserFilmEntity;
import com.example.demo.userfilms.service.UserFilmService;
import com.example.demo.users.model.UserEntity;
import com.example.demo.users.service.UserService;
@ -24,16 +30,25 @@ public class DemoApplication implements CommandLineRunner {
private final Logger log = LoggerFactory.getLogger(DemoApplication.class);
private final TypeService typeService;
private final GenreService genreService;
private final FilmService filmService;
private final UserFilmService userFilmService;
private final SubscriptionService subscriptionService;
private final UserService userService;
private final OrderService orderService;
public DemoApplication(
TypeService typeService,
GenreService genreService,
FilmService filmService,
UserFilmService userFilmService,
SubscriptionService subscriptionService,
UserService userService,
OrderService orderService) {
this.typeService = typeService;
this.genreService = genreService;
this.filmService = filmService;
this.userFilmService = userFilmService;
this.subscriptionService = subscriptionService;
this.userService = userService;
this.orderService = orderService;
@ -51,6 +66,16 @@ public class DemoApplication implements CommandLineRunner {
final var type2 = typeService.create(new TypeEntity("Телефон"));
final var type3 = typeService.create(new TypeEntity("Игровая приставка"));
log.info("Create default genre values");
final var genre1 = genreService.create(new GenreEntity("Comedy"));
final var genre2 = genreService.create(new GenreEntity("Triller"));
final var genre3 = genreService.create(new GenreEntity("Drama"));
log.info("Create default film values");
final var film1 = filmService.create(new FilmEntity("film1", genre1, 200d, 20));
final var film2 = filmService.create(new FilmEntity("film2", genre2, 200d, 20));
final var film3 = filmService.create(new FilmEntity("film3", genre3, 200d, 20));
log.info("Create default subscription values");
subscriptionService.create(new SubscriptionEntity("Подписка 1"));
subscriptionService.create(new SubscriptionEntity("Подписка 2"));
@ -73,6 +98,10 @@ public class DemoApplication implements CommandLineRunner {
new OrderEntity(type3, 75000.00, 6),
new OrderEntity(type3, 67800.00, 3));
orders.forEach(order -> orderService.create(user1.getId(), order));
userFilmService.create(new UserFilmEntity(user1, film1, false));
userFilmService.create(new UserFilmEntity(user1, film2, false));
userFilmService.create(new UserFilmEntity(user1, film3, false));
}
}
}

View File

@ -0,0 +1,78 @@
package com.example.demo.films.api;
import java.util.List;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.demo.core.configuration.Constants;
import com.example.demo.films.model.FilmEntity;
import com.example.demo.films.service.FilmService;
import com.example.demo.genres.service.GenreService;
import jakarta.validation.Valid;
@Controller
@RequestMapping(FilmController.URL)
public class FilmController {
public static final String URL = Constants.ADMIN_PREFIX + "/film";
private static final String FILM_VIEW = "film";
private static final String FILM_EDIT_VIEW = "film-edit";
private static final String FILM_ATTRIBUTE = "film";
private final FilmService filmService;
private final GenreService genreService;
private final ModelMapper modelMapper;
public FilmController(FilmService filmService, GenreService genreService, ModelMapper modelMapper) {
this.filmService = filmService;
this.genreService = genreService;
this.modelMapper = modelMapper;
}
private FilmDto toDto(FilmEntity entity) {
return modelMapper.map(entity, FilmDto.class);
}
private FilmEntity toEntity(FilmDto dto) {
final FilmEntity entity = modelMapper.map(dto, FilmEntity.class);
entity.setGenre(genreService.get(dto.getGenreId()));
return entity;
}
@GetMapping
public List<FilmDto> getAll() {
return filmService.getAll().stream()
.map(this::toDto)
.toList();
}
@GetMapping("/{id}")
public FilmDto get(@PathVariable(name = "id") Long id) {
return toDto(filmService.get(id));
}
@PostMapping
public FilmDto create(@RequestBody @Valid FilmDto dto) {
return toDto(filmService.create(toEntity(dto)));
}
@PutMapping("/{id}")
public FilmDto update(
@PathVariable(name = "id") Long id,
@RequestBody @Valid FilmDto dto) {
return toDto(filmService.update(id, toEntity(dto)));
}
@DeleteMapping("/{id}")
public FilmDto delete(@PathVariable(name = "id") Long id) {
return toDto(filmService.delete(id));
}
}

View File

@ -0,0 +1,70 @@
package com.example.demo.films.api;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
public class FilmDto {
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long id;
@NotBlank
@Size(min = 5, max = 50)
private String name;
@NotNull
@Min(1)
private Long genreId;
@NotNull
@Min(1)
private Double price;
@NotNull
@Min(1)
private Integer discount;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getGenreId() {
return genreId;
}
public void setGenreId(Long genreId) {
this.genreId = genreId;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public Integer getDiscount() {
return discount;
}
public void setDiscount(Integer discount) {
this.discount = discount;
}
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public Double getSum() {
return price - ((price * discount) * 0.01);
}
}

View File

@ -3,7 +3,7 @@ package com.example.demo.films.model;
import java.util.Objects;
import com.example.demo.core.model.BaseEntity;
import com.example.demo.types.model.TypeEntity;
import com.example.demo.genres.model.GenreEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@ -14,26 +14,40 @@ import jakarta.persistence.Table;
@Entity
@Table(name = "films")
public class FilmEntity extends BaseEntity {
@Column(nullable = false, unique = true, length = 50)
private String name;
@ManyToOne
@JoinColumn(name = "typeId", nullable = false)
private TypeEntity type;
@JoinColumn(name = "genreId", nullable = false)
private GenreEntity genre;
@Column(nullable = false)
private Double price;
@Column(nullable = false)
private Integer discount;
public FilmEntity() {
}
public FilmEntity(TypeEntity type, Double price) {
this.type = type;
public FilmEntity(String name, GenreEntity genre, Double price, Integer discount) {
this.name = name;
this.genre = genre;
this.price = price;
this.discount = discount;
}
public TypeEntity getType() {
return type;
public String getName() {
return name;
}
public void setType(TypeEntity type) {
this.type = type;
public void setName(String name) {
this.name = name;
}
public GenreEntity getGenre() {
return genre;
}
public void setGenre(GenreEntity genre) {
this.genre = genre;
}
public Double getPrice() {
@ -44,9 +58,17 @@ public class FilmEntity extends BaseEntity {
this.price = price;
}
public Integer getDiscount() {
return discount;
}
public void setDiscount(Integer discount) {
this.discount = discount;
}
@Override
public int hashCode() {
return Objects.hash(id, type, price);
return Objects.hash(id, name, genre, price, discount);
}
@Override
@ -55,9 +77,11 @@ public class FilmEntity extends BaseEntity {
return true;
if (obj == null || getClass() != obj.getClass())
return false;
final FilmEntity other = (FilmEntity) obj;
return Objects.equals(other.getId(), id)
&& Objects.equals(other.getType(), type)
&& Objects.equals(other.getPrice(), price);
FilmEntity other = (FilmEntity) obj;
return Objects.equals(id, other.id)
&& Objects.equals(name, other.name)
&& Objects.equals(genre, other.genre)
&& Objects.equals(price, other.price)
&& Objects.equals(discount, other.discount);
}
}

View File

@ -0,0 +1,14 @@
package com.example.demo.films.repository;
import java.util.List;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
import com.example.demo.films.model.FilmEntity;
public interface FilmRepository extends CrudRepository<FilmEntity, Long> {
Optional<FilmEntity> findByNameIgnoreCase(String name);
List<FilmEntity> findByGenreId(long genreId);
}

View File

@ -0,0 +1,72 @@
package com.example.demo.films.service;
import java.util.List;
import java.util.stream.StreamSupport;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.demo.core.error.NotFoundException;
import com.example.demo.films.model.FilmEntity;
import com.example.demo.films.repository.FilmRepository;
@Service
public class FilmService {
private final FilmRepository repository;
public FilmService(FilmRepository repository) {
this.repository = repository;
}
private void checkName(String name) {
if (repository.findByNameIgnoreCase(name).isPresent()) {
throw new IllegalArgumentException(
String.format("Type with name %s is already exists", name));
}
}
// @Transactional(readOnly = true)
// public List<FilmEntity> getAll(long genreId) {
// return repository.findByGenreId(genreId);
// }
@Transactional(readOnly = true)
public List<FilmEntity> getAll() {
return StreamSupport.stream(repository.findAll().spliterator(),
false).toList();
}
@Transactional(readOnly = true)
public FilmEntity get(long id) {
return repository.findById(id)
.orElseThrow(() -> new NotFoundException(FilmEntity.class, id));
}
@Transactional
public FilmEntity create(FilmEntity entity) {
if (entity == null) {
throw new IllegalArgumentException("Entity is null");
}
checkName(entity.getName());
return repository.save(entity);
}
@Transactional
public FilmEntity update(Long id, FilmEntity entity) {
final FilmEntity existsEntity = get(id);
checkName(entity.getName());
existsEntity.setName(entity.getName());
existsEntity.setPrice(entity.getPrice());
existsEntity.setDiscount(entity.getDiscount());
existsEntity.setGenre(entity.getGenre());
return repository.save(existsEntity);
}
@Transactional
public FilmEntity delete(Long id) {
final FilmEntity existsEntity = get(id);
repository.delete(existsEntity);
return existsEntity;
}
}

View File

@ -0,0 +1,104 @@
package com.example.demo.genres.api;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.demo.core.configuration.Constants;
import com.example.demo.genres.model.GenreEntity;
import com.example.demo.genres.service.GenreService;
import jakarta.validation.Valid;
@Controller
@RequestMapping(GenreController.URL)
public class GenreController {
public static final String URL = Constants.ADMIN_PREFIX + "/genre";
private static final String GENRE_VIEW = "genre";
private static final String GENRE_EDIT_VIEW = "genre-edit";
private static final String GENRE_ATTRIBUTE = "genre";
private final GenreService genreService;
private final ModelMapper modelMapper;
public GenreController(GenreService genreService, ModelMapper modelMapper) {
this.genreService = genreService;
this.modelMapper = modelMapper;
}
private GenreDto toDto(GenreEntity entity) {
return modelMapper.map(entity, GenreDto.class);
}
private GenreEntity toEntity(GenreDto dto) {
return modelMapper.map(dto, GenreEntity.class);
}
@GetMapping
public String getAll(Model model) {
model.addAttribute(
"items",
genreService.getAll().stream()
.map(this::toDto)
.toList());
return GENRE_VIEW;
}
@GetMapping("/edit/")
public String create(Model model) {
model.addAttribute(GENRE_ATTRIBUTE, new GenreDto());
return GENRE_EDIT_VIEW;
}
@PostMapping("/edit/")
public String create(
@ModelAttribute(name = GENRE_ATTRIBUTE) @Valid GenreDto genre,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
return GENRE_EDIT_VIEW;
}
genreService.create(toEntity(genre));
return Constants.REDIRECT_VIEW + URL;
}
@GetMapping("/edit/{id}")
public String update(
@PathVariable(name = "id") Long id,
Model model) {
if (id <= 0) {
throw new IllegalArgumentException();
}
model.addAttribute(GENRE_ATTRIBUTE, toDto(genreService.get(id)));
return GENRE_EDIT_VIEW;
}
@PostMapping("/edit/{id}")
public String update(
@PathVariable(name = "id") Long id,
@ModelAttribute(name = GENRE_ATTRIBUTE) @Valid GenreDto genre,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
return GENRE_EDIT_VIEW;
}
if (id <= 0) {
throw new IllegalArgumentException();
}
genreService.update(id, toEntity(genre));
return Constants.REDIRECT_VIEW + URL;
}
@PostMapping("/delete/{id}")
public String delete(
@PathVariable(name = "id") Long id) {
genreService.delete(id);
return Constants.REDIRECT_VIEW + URL;
}
}

View File

@ -0,0 +1,27 @@
package com.example.demo.genres.api;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class GenreDto {
private Long id;
@NotBlank
@Size(min = 5, max = 50)
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,48 @@
package com.example.demo.genres.model;
import java.util.Objects;
import com.example.demo.core.model.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
@Entity
@Table(name = "genres")
public class GenreEntity extends BaseEntity {
@Column(nullable = false, unique = true, length = 50)
private String name;
public GenreEntity() {
}
public GenreEntity(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
final GenreEntity other = (GenreEntity) obj;
return Objects.equals(other.getId(), id)
&& Objects.equals(other.getName(), name);
}
}

View File

@ -0,0 +1,13 @@
package com.example.demo.genres.repository;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import com.example.demo.genres.model.GenreEntity;
public interface GenreRepository
extends CrudRepository<GenreEntity, Long>, PagingAndSortingRepository<GenreEntity, Long> {
Optional<GenreEntity> findByNameIgnoreCase(String name);
}

View File

@ -0,0 +1,76 @@
package com.example.demo.genres.service;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.StreamSupport;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.demo.core.error.NotFoundException;
import com.example.demo.genres.model.GenreEntity;
import com.example.demo.genres.repository.GenreRepository;
@Service
public class GenreService {
private final GenreRepository repository;
public GenreService(GenreRepository repository) {
this.repository = repository;
}
private void checkName(Long id, String name) {
final Optional<GenreEntity> existsGenre = repository.findByNameIgnoreCase(name);
if (existsGenre.isPresent() && !existsGenre.get().getId().equals(id)) {
throw new IllegalArgumentException(
String.format("Genre with name %s is already exists", name));
}
}
@Transactional(readOnly = true)
public List<GenreEntity> getAll() {
return StreamSupport.stream(repository.findAll(Sort.by("id")).spliterator(), false).toList();
}
@Transactional(readOnly = true)
public List<GenreEntity> getByIds(Collection<Long> ids) {
final List<GenreEntity> genres = StreamSupport.stream(repository.findAllById(ids).spliterator(), false)
.toList();
if (genres.size() < ids.size()) {
throw new IllegalArgumentException("Invalid genre");
}
return genres;
}
@Transactional(readOnly = true)
public GenreEntity get(long id) {
return repository.findById(id)
.orElseThrow(() -> new NotFoundException(GenreEntity.class, id));
}
@Transactional
public GenreEntity create(GenreEntity entity) {
if (entity == null) {
throw new IllegalArgumentException("Entity is null");
}
checkName(null, entity.getName());
return repository.save(entity);
}
@Transactional
public GenreEntity update(Long id, GenreEntity entity) {
final GenreEntity existsEntity = get(id);
checkName(id, entity.getName());
existsEntity.setName(entity.getName());
return repository.save(existsEntity);
}
@Transactional
public GenreEntity delete(Long id) {
final GenreEntity existsEntity = get(id);
repository.delete(existsEntity);
return existsEntity;
}
}

View File

@ -0,0 +1,73 @@
package com.example.demo.subscribes.api;
import java.util.List;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.demo.core.configuration.Constants;
import com.example.demo.subscribes.model.SubscribeEntity;
import com.example.demo.subscribes.service.SubscribeService;
import jakarta.validation.Valid;
@Controller
@RequestMapping(SubscribeController.URL)
public class SubscribeController {
public static final String URL = Constants.ADMIN_PREFIX + "/subscribe";
private static final String SUBSCRIBE_VIEW = "subscribe";
private static final String SUBSCRIBE_EDIT_VIEW = "subscribe-edit";
private static final String SUBSCRIBE_ATTRIBUTE = "subscribe";
private final SubscribeService subscribeService;
private final ModelMapper modelMapper;
public SubscribeController(SubscribeService subscribeService, ModelMapper modelMapper) {
this.subscribeService = subscribeService;
this.modelMapper = modelMapper;
}
private SubscribeDto toDto(SubscribeEntity entity) {
return modelMapper.map(entity, SubscribeDto.class);
}
private SubscribeEntity toEntity(SubscribeDto dto) {
return modelMapper.map(dto, SubscribeEntity.class);
}
@GetMapping
public List<SubscribeDto> getAll() {
return subscribeService.getAll().stream()
.map(this::toDto)
.toList();
}
@GetMapping("/{id}")
public SubscribeDto get(@PathVariable(name = "id") Long id) {
return toDto(subscribeService.get(id));
}
@PostMapping
public SubscribeDto create(@RequestBody @Valid SubscribeDto dto) {
return toDto(subscribeService.create(toEntity(dto)));
}
@PutMapping("/{id}")
public SubscribeDto update(
@PathVariable(name = "id") Long id,
@RequestBody @Valid SubscribeDto dto) {
return toDto(subscribeService.update(id, toEntity(dto)));
}
@DeleteMapping("/{id}")
public SubscribeDto delete(@PathVariable(name = "id") Long id) {
return toDto(subscribeService.delete(id));
}
}

View File

@ -0,0 +1,43 @@
package com.example.demo.subscribes.api;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
public class SubscribeDto {
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long id;
@NotBlank
@Size(min = 5, max = 50)
private String name;
@NotNull
@Min(1)
private Double price;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}

View File

@ -0,0 +1,60 @@
package com.example.demo.subscribes.model;
import java.util.Objects;
import com.example.demo.core.model.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
@Entity
@Table(name = "subscribes")
public class SubscribeEntity extends BaseEntity {
@Column(nullable = false, unique = true, length = 50)
private String name;
@Column(nullable = false)
private Double price;
public SubscribeEntity() {
}
public SubscribeEntity(String name, Double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
@Override
public int hashCode() {
return Objects.hash(id, name, price);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
final SubscribeEntity other = (SubscribeEntity) obj;
return Objects.equals(other.getId(), id)
&& Objects.equals(other.getName(), name)
&& Objects.equals(other.getPrice(), price);
}
}

View File

@ -0,0 +1,11 @@
package com.example.demo.subscribes.repository;
import java.util.Optional;
import com.example.demo.subscribes.model.SubscribeEntity;
import org.springframework.data.repository.CrudRepository;
public interface SubscribeRepository extends CrudRepository<SubscribeEntity, Long> {
Optional<SubscribeEntity> findByNameIgnoreCase(String name);
}

View File

@ -0,0 +1,63 @@
package com.example.demo.subscribes.service;
import java.util.List;
import java.util.stream.StreamSupport;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.demo.core.error.NotFoundException;
import com.example.demo.subscribes.model.SubscribeEntity;
import com.example.demo.subscribes.repository.SubscribeRepository;
@Service
public class SubscribeService {
private final SubscribeRepository repository;
public SubscribeService(SubscribeRepository repository) {
this.repository = repository;
}
private void checkName(String name) {
if (repository.findByNameIgnoreCase(name).isPresent()) {
throw new IllegalArgumentException(
String.format("Subscribe with name %s is already exists", name));
}
}
@Transactional(readOnly = true)
public List<SubscribeEntity> getAll() {
return StreamSupport.stream(repository.findAll().spliterator(), false).toList();
}
@Transactional(readOnly = true)
public SubscribeEntity get(long id) {
return repository.findById(id)
.orElseThrow(() -> new NotFoundException(SubscribeEntity.class, id));
}
@Transactional
public SubscribeEntity create(SubscribeEntity entity) {
if (entity == null) {
throw new IllegalArgumentException("Entity is null");
}
checkName(entity.getName());
return repository.save(entity);
}
@Transactional
public SubscribeEntity update(Long id, SubscribeEntity entity) {
final SubscribeEntity existsEntity = get(id);
checkName(entity.getName());
existsEntity.setName(entity.getName());
existsEntity.setPrice(entity.getPrice());
return repository.save(existsEntity);
}
@Transactional
public SubscribeEntity delete(Long id) {
final SubscribeEntity existsEntity = get(id);
repository.delete(existsEntity);
return existsEntity;
}
}

View File

@ -0,0 +1,110 @@
package com.example.demo.userfilms.api;
import java.util.List;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import com.example.demo.core.configuration.Constants;
import com.example.demo.films.service.FilmService;
import com.example.demo.userfilms.model.UserFilmEntity;
import com.example.demo.userfilms.model.UserFilmGrouped;
import com.example.demo.userfilms.service.UserFilmService;
import com.example.demo.users.service.UserService;
import jakarta.validation.Valid;
@Controller
@RequestMapping(UserFilmController.URL)
public class UserFilmController {
public static final String URL = Constants.ADMIN_PREFIX + "/userfilm";
private static final String USERFILM_VIEW = "userfilm";
private static final String USERFILM_EDIT_VIEW = "userfilm-edit";
private static final String USERFILM_ATTRIBUTE = "userfilm";
private final UserFilmService userFilmService;
private final UserService userService;
private final FilmService filmService;
private final ModelMapper modelMapper;
public UserFilmController(UserFilmService userFilmService, UserService userService, FilmService filmService,
ModelMapper modelMapper) {
this.userFilmService = userFilmService;
this.userService = userService;
this.filmService = filmService;
this.modelMapper = modelMapper;
}
private UserFilmDto toDto(UserFilmEntity userFilmEntity) {
return modelMapper.map(userFilmEntity, UserFilmDto.class);
}
private UserFilmGroupedDto toGroupedDto(UserFilmGrouped entity) {
return modelMapper.map(entity, UserFilmGroupedDto.class);
}
private UserFilmEntity toEntity(UserFilmDto dto) {
final UserFilmEntity entity = modelMapper.map(dto, UserFilmEntity.class);
entity.setUser(userService.get(dto.getUserId()));
entity.setFilm(filmService.get(dto.getFilmId()));
return entity;
}
@GetMapping
public List<UserFilmDto> getAll(
@PathVariable(name = "user") Long userId) {
return userFilmService.getAll().stream()
.map(this::toDto)
.toList();
}
@GetMapping("/{film}")
public UserFilmDto get(
@PathVariable(name = "user") Long userId,
@PathVariable(name = "film") Long filmId) {
return toDto(userFilmService.get(userId, filmId));
}
@PostMapping("/{film}")
public UserFilmDto create(
@PathVariable(name = "user") Long userId,
@PathVariable(name = "film") Long filmId) {
return toDto(
userFilmService.create(new UserFilmEntity(userService.get(userId), filmService.get(filmId), false)));
}
@PutMapping("/{id}")
public UserFilmDto update(
@PathVariable(name = "id") Long id,
@RequestBody @Valid UserFilmDto dto) {
return toDto(userFilmService.update(id, toEntity(dto)));
}
@PutMapping("/{id}/view")
public UserFilmDto updateViewed(
@PathVariable(name = "id") Long id,
@PathVariable(name = "view") Boolean view) {
return toDto(userFilmService.viewed(id, view));
}
@DeleteMapping("/{film}")
public UserFilmDto delete(
@PathVariable(name = "user") Long userId,
@PathVariable(name = "film") Long filmId) {
return toDto(userFilmService.delete(userId, filmId));
}
@GetMapping("/total")
public List<UserFilmGroupedDto> getMethodName(@PathVariable(name = "user") Long userId) {
return userFilmService.getTotal(userId).stream()
.map(this::toGroupedDto)
.toList();
}
}

View File

@ -0,0 +1,50 @@
package com.example.demo.userfilms.api;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
public class UserFilmDto {
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
private Long id;
@NotNull
@Min(1)
private Long userId;
@NotNull
@Min(1)
private Long filmId;
private boolean viewed;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Long getFilmId() {
return filmId;
}
public void setFilmId(Long filmId) {
this.filmId = filmId;
}
public Boolean getViewed() {
return viewed;
}
public void setViewed(Boolean viewed) {
this.viewed = viewed;
}
}

View File

@ -0,0 +1,22 @@
package com.example.demo.userfilms.api;
public class UserFilmGroupedDto {
private Long filmCount;
private String genreName;
public Long getfilmCount() {
return filmCount;
}
public void setfilmCount(Long filmCount) {
this.filmCount = filmCount;
}
public String getGenreName() {
return genreName;
}
public void setGenreName(String genreName) {
this.genreName = genreName;
}
}

View File

@ -0,0 +1,87 @@
package com.example.demo.userfilms.model;
import com.example.demo.users.model.UserEntity;
import java.util.Objects;
import com.example.demo.core.model.BaseEntity;
import com.example.demo.films.model.FilmEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
@Entity
@Table(name = "users_films")
public class UserFilmEntity extends BaseEntity {
private Long id;
@ManyToOne
@JoinColumn(name = "userId", nullable = false)
private UserEntity user;
@ManyToOne
@JoinColumn(name = "filmId", nullable = false)
private FilmEntity film;
private boolean viewed;
public UserFilmEntity() {
}
public UserFilmEntity(UserEntity user, FilmEntity film, boolean viewed) {
this.user = user;
this.film = film;
this.viewed = viewed;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public UserEntity getUser() {
return user;
}
public void setUser(UserEntity user) {
this.user = user;
}
public FilmEntity getFilm() {
return film;
}
public void setFilm(FilmEntity film) {
this.film = film;
}
public boolean isViewed() {
return viewed;
}
public void setViewed(boolean viewed) {
this.viewed = viewed;
}
@Override
public int hashCode() {
return Objects.hash(id, user.getId(), film.getId(), viewed);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
UserFilmEntity other = (UserFilmEntity) obj;
return Objects.equals(id, other.id)
&& Objects.equals(user.getId(), other.user.getId())
&& Objects.equals(film.getId(), other.film.getId())
&& viewed == other.viewed;
}
}

View File

@ -0,0 +1,9 @@
package com.example.demo.userfilms.model;
import com.example.demo.genres.model.GenreEntity;
public interface UserFilmGrouped {
Long getCount();
GenreEntity getGenre();
}

View File

@ -0,0 +1,30 @@
package com.example.demo.userfilms.repository;
import com.example.demo.userfilms.model.UserFilmEntity;
import com.example.demo.userfilms.model.UserFilmGrouped;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
import java.util.Optional;
import org.springframework.data.repository.CrudRepository;
public interface UserFilmRepository extends CrudRepository<UserFilmEntity, Long> {
Optional<UserFilmEntity> findOneByUserIdAndFilmId(long userId, long filmId);
List<UserFilmEntity> findByUserId(long userId);
List<UserFilmEntity> findByFilmId(long filmId);
@Query("select "
+ "count(*) as count, "
+ "g as genre "
+ "from FilmEntity f "
+ "left join GenreEntity g "
+ "on f.genre = g "
+ "right join UserFilmEntity uf "
+ "on uf.film = f and uf.user.id = ?1 "
+ "group by g order by g.name")
List<UserFilmGrouped> getFilmsTotalByGenre(long userId);
}

View File

@ -0,0 +1,125 @@
package com.example.demo.userfilms.service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.StreamSupport;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.demo.core.error.NotFoundException;
import com.example.demo.films.model.FilmEntity;
import com.example.demo.films.service.FilmService;
import com.example.demo.userfilms.model.UserFilmEntity;
import com.example.demo.userfilms.model.UserFilmGrouped;
import com.example.demo.userfilms.repository.UserFilmRepository;
import com.example.demo.users.model.UserEntity;
import com.example.demo.users.service.UserService;
@Service
public class UserFilmService {
private final UserFilmRepository userFilmRepository;
private final FilmService filmService;
private final UserService userService;
public UserFilmService(
UserFilmRepository userFilmRepository, FilmService filmService, UserService userService) {
this.userFilmRepository = userFilmRepository;
this.filmService = filmService;
this.userService = userService;
}
private void checkUserFilm(UserEntity user, FilmEntity film) {
if (userFilmRepository.findOneByUserIdAndFilmId(user.getId(), film.getId()).isPresent()) {
throw new IllegalArgumentException(
String.format("UserFilm with login is already exists"));
}
}
@Transactional(readOnly = true)
public List<UserFilmEntity> getAll() {
return StreamSupport.stream(userFilmRepository.findAll().spliterator(), false).toList();
}
@Transactional(readOnly = true)
public UserFilmEntity get(Long id) {
return userFilmRepository.findById(id)
.orElseThrow(() -> new NotFoundException(UserFilmEntity.class, id));
}
@Transactional(readOnly = true)
public UserFilmEntity get(Long userId, Long filmId) {
return userFilmRepository.findOneByUserIdAndFilmId(userId, filmId)
.orElseThrow(() -> new NotFoundException(UserFilmEntity.class, userId));
}
@Transactional
public UserFilmEntity create(UserFilmEntity entity) {
if (entity == null) {
throw new IllegalArgumentException("Entity is null");
}
checkUserFilm(entity.getUser(), entity.getFilm());
return userFilmRepository.save(entity);
}
@Transactional
public UserFilmEntity update(Long id, UserFilmEntity entity) {
final UserFilmEntity existsEntity = get(id);
existsEntity.setUser(entity.getUser());
existsEntity.setFilm(entity.getFilm());
existsEntity.setViewed(entity.isViewed());
userFilmRepository.save(existsEntity);
return existsEntity;
}
@Transactional
public UserFilmEntity delete(long userId, long filmId) {
final UserFilmEntity existsEntity = userFilmRepository.findOneByUserIdAndFilmId(userId, filmId).get();
userFilmRepository.delete(existsEntity);
return existsEntity;
}
@Transactional
public UserFilmEntity delete(long id) {
final UserFilmEntity existsEntity = get(id);
userFilmRepository.delete(existsEntity);
return existsEntity;
}
@Transactional
public List<UserFilmEntity> addAllFilm(UserEntity user) {
List<UserFilmEntity> list = new ArrayList<UserFilmEntity>();
filmService.getAll().forEach(film -> {
final UserFilmEntity userFilm = new UserFilmEntity(user, film, true);
userFilm.setUser(user);
userFilm.setFilm(film);
list.add(userFilm);
});
userFilmRepository.saveAll(list);
return list;
}
@Transactional
public UserFilmEntity update(long id, UserFilmEntity entity) {
final UserFilmEntity existsEntity = get(id);
existsEntity.setUser(entity.getUser());
existsEntity.setFilm(entity.getFilm());
existsEntity.setViewed(entity.isViewed());
userFilmRepository.save(existsEntity);
return existsEntity;
}
@Transactional
public UserFilmEntity viewed(long id, Boolean viewed) {
final UserFilmEntity existsEntity = get(id);
existsEntity.setViewed(viewed);
userFilmRepository.save(existsEntity);
return existsEntity;
}
@Transactional(readOnly = true)
public List<UserFilmGrouped> getTotal(long userId) {
userService.get(userId);
return userFilmRepository.getFilmsTotalByGenre(userId);
}
}

View File

@ -24,6 +24,9 @@ import com.example.demo.orders.service.OrderService;
import com.example.demo.types.api.TypeDto;
import com.example.demo.types.model.TypeEntity;
import com.example.demo.types.service.TypeService;
import com.example.demo.userfilms.api.UserFilmDto;
import com.example.demo.userfilms.model.UserFilmEntity;
import com.example.demo.userfilms.service.UserFilmService;
import com.example.demo.users.model.UserSubscriptionWithStatus;
import com.example.demo.users.service.UserService;
@ -38,16 +41,19 @@ public class UserProfileController {
private static final String PROFILE_ATTRIBUTE = "profile";
private final OrderService orderService;
private final UserFilmService userFilmService;
private final TypeService typeService;
private final UserService userService;
private final ModelMapper modelMapper;
public UserProfileController(
OrderService orderService,
UserFilmService userFilmService,
TypeService typeService,
UserService userService,
ModelMapper modelMapper) {
this.orderService = orderService;
this.userFilmService = userFilmService;
this.typeService = typeService;
this.userService = userService;
this.modelMapper = modelMapper;
@ -57,6 +63,10 @@ public class UserProfileController {
return modelMapper.map(entity, OrderDto.class);
}
private UserFilmDto toUserFilmDto(UserFilmEntity entity) {
return modelMapper.map(entity, UserFilmDto.class);
}
private OrderGroupedDto toGroupedDto(OrderGrouped entity) {
return modelMapper.map(entity, OrderGroupedDto.class);
}
@ -87,6 +97,10 @@ public class UserProfileController {
model.addAllAttributes(PageAttributesMapper.toAttributes(
orderService.getAll(userId, typeId, page, Constants.DEFUALT_PAGE_SIZE),
this::toDto));
model.addAttribute("userfilms",
userFilmService.getAll().stream()
.map(this::toUserFilmDto)
.toList());
model.addAttribute("stats",
orderService.getTotal(userId).stream()
.map(this::toGroupedDto)

View File

@ -38,6 +38,10 @@
th:classappend="${activeLink.startsWith('/admin/subscription') ? 'active' : ''}">
Списки рассылки
</a>
<a class="nav-link" href="/admin/genre"
th:classappend="${activeLink.startsWith('/admin/genre') ? 'active' : ''}">
Жанры фильмов
</a>
<a class="nav-link" href="/h2-console/" target="_blank">Консоль H2</a>
<a class="nav-link" href="123" target="_blank">Ошибка</a>
</ul>

View File

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
<head>
<title>Редакторовать жанр фильма</title>
</head>
<body>
<main layout:fragment="content">
<form action="#" th:action="@{/admin/genre/edit/{id}(id=${genre.id})}" th:object="${genre}" method="post">
<div class="mb-3">
<label for="id" class="form-label">ID</label>
<input genre="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
</div>
<div class="mb-3">
<label for="name" class="form-label">Жанр фильма</label>
<input genre="text" th:field="*{name}" id="name" class="form-control">
<div th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="invalid-feedback"></div>
</div>
<div class="mb-3 d-flex flex-row">
<button class="btn btn-primary me-2 button-fixed-width" genre="submit">Сохранить</button>
<a class="btn btn-secondary button-fixed-width" href="/admin/genre">Отмена</a>
</div>
</form>
</main>
</body>
</html>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="ru" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{default}">
<head>
<title>Жанры фильмов</title>
</head>
<body>
<main layout:fragment="content">
<th:block th:switch="${items.size()}">
<h2 th:case="0">Данные отсутствуют</h2>
<th:block th:case="*">
<h2>Жанры фильмов</h2>
<div>
<a href="/admin/genre/edit/" class="btn btn-primary">Добавить жанр фильма</a>
</div>
<table class="table">
<caption></caption>
<thead>
<tr>
<th scope="col" class="w-10">ID</th>
<th scope="col" class="w-auto">Жанр фильма</th>
<th scope="col" class="w-10"></th>
<th scope="col" class="w-10"></th>
</tr>
</thead>
<tbody>
<tr th:each="genre : ${items}">
<th scope="row" th:text="${genre.id}"></th>
<td th:text="${genre.name}"></td>
<td>
<form th:action="@{/admin/genre/edit/{id}(id=${genre.id})}" method="get">
<button genre="submit" class="btn btn-link button-link">Редактировать</button>
</form>
</td>
<td>
<form th:action="@{/admin/genre/delete/{id}(id=${genre.id})}" method="post">
<button genre="submit" class="btn btn-link button-link"
onclick="return confirm('Вы уверены?')">Удалить</button>
</form>
</td>
</tr>
</tbody>
</table>
</th:block>
</th:block>
</main>
</body>
</html>

View File

@ -11,6 +11,9 @@
<li class="nav-item">
<a class="nav-link active" data-bs-toggle="pill" href="#orders">Заказы</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#userfilms">фильмы</a>
</li>
<li class="nav-item">
<a class="nav-link" data-bs-toggle="pill" href="#stats">Статистика</a>
</li>
@ -26,6 +29,12 @@
totalPages=${totalPages},
currentPage=${currentPage}) }" />
</div>
<div class="tab-pane container fade" id="userfilms">
<th:block th:replace="~{ orders :: orders (
items=${items},
totalPages=${totalPages},
currentPage=${currentPage}) }" />
</div>
<div class="tab-pane container fade" id="stats">
<ul class="list-group mb-2">
<li th:each="stat : ${stats}" class="list-group-item">

View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<th:block th:fragment="orders (items, totalPages, currentPage)">
<th:block th:switch="${items.size()}">
<h2 th:case="0">Данные отсутствуют</h2>
<th:block th:case="*">
<form th:action="@{/}" method="get" class="row mt-2">
<div class="col-sm-10">
<input type="hidden" th:name="page" th:value="${page}">
<select th:name="typeId" id="typeId" class="form-select">
<option selected value="">Фильтр по типу товара</option>
<option th:each="type : ${types}" th:value="${type.id}" th:selected="${type.id==typeId}">
[[${type.name}]]
</option>
</select>
</div>
<button type="submit" class="btn btn-primary col-sm-2">Показать</button>
</form>
<table class="table mt-2">
<caption></caption>
<thead>
<tr>
<th scope="col" class="w-10">ID</th>
<th scope="col" class="w-auto">Тип заказа</th>
<th scope="col" class="w-10">Цена</th>
<th scope="col" class="w-10">Количество</th>
<th scope="col" class="w-10">Сумма</th>
<th scope="col" class="w-10"></th>
</tr>
</thead>
<tbody>
<tr th:each="order : ${items}">
<th scope="row" th:text="${order.id}"></th>
<td th:text="${order.typeName}"></td>
<td th:text="${order.price}"></td>
<td th:text="${order.count}"></td>
<td th:text="${order.sum}"></td>
<td>
<form th:action="@{/delete/{id}(id=${order.id})}" method="post">
<input type="hidden" th:name="page" th:value="${page}">
<input type="hidden" th:name="typeId" th:value="${typeId}">
<button type="submit" class="btn btn-link button-link"
onclick="return confirm('Вы уверены?')">Удалить</button>
</form>
</td>
</tr>
</tbody>
</table>
</th:block>
<th:block th:replace="~{ pagination :: pagination (
url='',
totalPages=${totalPages},
currentPage=${currentPage}) }" />
</th:block>
</th:block>
</body>
</html>