Ну допустим что-то есть

This commit is contained in:
Kirill 2024-06-05 16:00:35 +04:00
parent 0ba91b1a06
commit 23c059922d
27 changed files with 1305 additions and 4 deletions

View File

@ -6,7 +6,7 @@
"request": "launch",
"cwd": "${workspaceFolder}",
"mainClass": "com.example.demo.DemoApplication",
"projectName": "lec7",
"projectName": "Laba5.1",
"args": "--populate",
"envFile": "${workspaceFolder}/.env"
}

View File

@ -11,12 +11,18 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
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.model.GenreEntity;
import com.example.demo.genres.service.GenreService;
import com.example.demo.orders.model.OrderEntity;
import com.example.demo.orders.service.OrderService;
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.userfilms.model.UserfilmEntity;
import com.example.demo.userfilms.service.UserfilmService;
import com.example.demo.users.model.UserEntity;
import com.example.demo.users.model.UserRole;
import com.example.demo.users.service.UserService;
@ -29,16 +35,25 @@ public class DemoApplication implements CommandLineRunner {
private final SubscriptionService subscriptionService;
private final UserService userService;
private final OrderService orderService;
private final GenreService genreService;
private final FilmService filmService;
private final UserfilmService userfilmService;
public DemoApplication(
TypeService typeService,
SubscriptionService subscriptionService,
UserService userService,
OrderService orderService) {
OrderService orderService,
GenreService genreService,
FilmService filmService,
UserfilmService userfilmService) {
this.typeService = typeService;
this.subscriptionService = subscriptionService;
this.userService = userService;
this.orderService = orderService;
this.genreService = genreService;
this.filmService = filmService;
this.userfilmService = userfilmService;
}
public static void main(String[] args) {
@ -79,6 +94,21 @@ 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));
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(genre1, "Ivan Vasilevich menyaet proffessiu"));
final var film2 = filmService.create(new FilmEntity(genre2, "Ironia sudby"));
final var film3 = filmService.create(new FilmEntity(genre3, "S legkim parom"));
log.info("Create default userfilm values");
userfilmService.create(user1.getId(), new UserfilmEntity(film1));
userfilmService.create(user1.getId(), new UserfilmEntity(film2));
userfilmService.create(user1.getId(), new UserfilmEntity(film3));
}
}
}

View File

@ -9,6 +9,8 @@ public class Constants {
public static final String ADMIN_PREFIX = "/admin";
public static final String USER_COOKIE = "userIdCookie";
public static final String LOGIN_URL = "/login";
public static final String LOGOUT_URL = "/logout";

View File

@ -0,0 +1,104 @@
package com.example.demo.films.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.films.model.FilmEntity;
import com.example.demo.films.service.FilmService;
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 ModelMapper modelMapper;
public FilmController(FilmService filmService, ModelMapper modelMapper) {
this.filmService = filmService;
this.modelMapper = modelMapper;
}
private FilmDto toDto(FilmEntity entity) {
return modelMapper.map(entity, FilmDto.class);
}
private FilmEntity toEntity(FilmDto dto) {
return modelMapper.map(dto, FilmEntity.class);
}
@GetMapping
public String getAll(Model model) {
model.addAttribute(
"items",
filmService.getAll().stream()
.map(this::toDto)
.toList());
return FILM_VIEW;
}
@GetMapping("/edit/")
public String create(Model model) {
model.addAttribute(FILM_ATTRIBUTE, new FilmDto());
return FILM_EDIT_VIEW;
}
@PostMapping("/edit/")
public String create(
@ModelAttribute(name = FILM_ATTRIBUTE) @Valid FilmDto film,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
return FILM_EDIT_VIEW;
}
filmService.create(toEntity(film));
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(FILM_ATTRIBUTE, toDto(filmService.get(id)));
return FILM_EDIT_VIEW;
}
@PostMapping("/edit/{id}")
public String update(
@PathVariable(name = "id") Long id,
@ModelAttribute(name = FILM_ATTRIBUTE) @Valid FilmDto film,
BindingResult bindingResult,
Model model) {
if (bindingResult.hasErrors()) {
return FILM_EDIT_VIEW;
}
if (id <= 0) {
throw new IllegalArgumentException();
}
filmService.update(id, toEntity(film));
return Constants.REDIRECT_VIEW + URL;
}
@PostMapping("/delete/{id}")
public String delete(
@PathVariable(name = "id") Long id) {
filmService.delete(id);
return Constants.REDIRECT_VIEW + URL;
}
}

View File

@ -0,0 +1,42 @@
package com.example.demo.films.api;
import groovyjarjarantlr4.v4.runtime.misc.NotNull;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public class FilmDto {
private Long id;
@NotNull
@Min(1)
private String genreName;
@NotBlank
@Size(min = 5, max = 50)
private String name;
private String imageExtension;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getGenreName() {
return genreName;
}
public void setGenreName(String genreName) {
this.genreName = genreName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,84 @@
package com.example.demo.films.model;
import java.util.Objects;
import com.example.demo.core.model.BaseEntity;
import com.example.demo.genres.model.GenreEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
@Entity
@Table(name = "films")
public class FilmEntity extends BaseEntity {
@ManyToOne
@JoinColumn(name = "genreId", nullable = false)
private GenreEntity genre;
@Column(nullable = false, unique = true, length = 50)
private String name;
private String imageExtension;
@Lob
private byte[] image;
public FilmEntity() {
}
public FilmEntity(GenreEntity genre, String name) {
this.name = name;
this.genre = genre;
}
public GenreEntity getGenre() {
return genre;
}
public void setGenre(GenreEntity genre) {
this.genre = genre;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getImageExtension() {
return imageExtension;
}
public void setImageExtension(String imageExtension) {
this.imageExtension = imageExtension;
}
public byte[] getImage() {
return image;
}
public void setImage(byte[] image) {
this.image = image;
}
@Override
public int hashCode() {
return Objects.hash(id, genre, name);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
final FilmEntity other = (FilmEntity) obj;
return Objects.equals(other.getId(), id)
&& Objects.equals(other.getGenre(), genre)
&& Objects.equals(other.getName(), name);
}
}

View File

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

View File

@ -0,0 +1,78 @@
package com.example.demo.films.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.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(Long id, String name) {
final Optional<FilmEntity> existsFilm = repository.findByNameIgnoreCase(name);
if (existsFilm.isPresent() && !existsFilm.get().getId().equals(id)) {
throw new IllegalArgumentException(
String.format("Film with name %s is already exists", name));
}
}
@Transactional(readOnly = true)
public List<FilmEntity> getAll() {
return StreamSupport.stream(repository.findAll(Sort.by("id")).spliterator(), false).toList();
}
@Transactional(readOnly = true)
public List<FilmEntity> getByIds(Collection<Long> ids) {
final List<FilmEntity> films = StreamSupport.stream(repository.findAllById(ids).spliterator(), false).toList();
if (films.size() < ids.size()) {
throw new IllegalArgumentException("Invalid film");
}
return films;
}
@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(null, entity.getName());
return repository.save(entity);
}
@Transactional
public FilmEntity update(Long id, FilmEntity entity) {
final FilmEntity existsEntity = get(id);
checkName(id, entity.getName());
existsEntity.setName(entity.getName());
existsEntity.setGenre(entity.getGenre());
existsEntity.setImageExtension(entity.getImageExtension());
existsEntity.setImage(entity.getImage());
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,38 @@
package com.example.demo.userfilms.api;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
public class UserfilmDto {
private Long id;
@NotNull
@Min(1)
private String filmName;
@NotNull
@Min(1)
private String genreName;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFilmName() {
return filmName;
}
public void setFilmName(String filmName) {
this.filmName = filmName;
}
public String getGenreName() {
return genreName;
}
public void setGenreName(String genreName) {
this.genreName = genreName;
}
}

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,72 @@
package com.example.demo.userfilms.model;
import java.util.Objects;
import com.example.demo.core.model.BaseEntity;
import com.example.demo.films.model.FilmEntity;
import com.example.demo.genres.model.GenreEntity;
import com.example.demo.users.model.UserEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
@Entity
@Table(name = "userfilms")
public class UserfilmEntity extends BaseEntity {
@ManyToOne
@JoinColumn(name = "filmId", nullable = false)
private FilmEntity film;
@ManyToOne
@JoinColumn(name = "userId", nullable = false)
private UserEntity user;
@ManyToOne
@JoinColumn(name = "genreId", nullable = false)
private GenreEntity genre;
public UserfilmEntity() {
}
public UserfilmEntity(FilmEntity film) {
this.film = film;
this.genre = film.getGenre();
}
public GenreEntity getGenre() {
return film.getGenre();
}
public FilmEntity getFilm() {
return film;
}
public void setFilm(FilmEntity film) {
this.film = film;
}
public UserEntity getUser() {
return user;
}
public void setUser(UserEntity user) {
this.user = user;
}
@Override
public int hashCode() {
return Objects.hash(id, film, user.getId());
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
final UserfilmEntity other = (UserfilmEntity) obj;
return Objects.equals(other.getId(), id)
&& Objects.equals(other.getFilm(), film)
&& Objects.equals(other.getUser().getId(), user.getId());
}
}

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,38 @@
package com.example.demo.userfilms.repository;
import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import com.example.demo.userfilms.model.UserfilmEntity;
import com.example.demo.userfilms.model.UserfilmGrouped;
public interface UserfilmRepository
extends CrudRepository<UserfilmEntity, Long>, PagingAndSortingRepository<UserfilmEntity, Long> {
Optional<UserfilmEntity> findOneByUserIdAndId(long userId, long id);
List<UserfilmEntity> findByUserId(long userId);
Page<UserfilmEntity> findByUserId(long userId, Pageable pageable);
List<UserfilmEntity> findByUserIdAndGenreId(long userId, long genreId);
Page<UserfilmEntity> findByUserIdAndGenreId(long userId, long genreId, Pageable pageable);
@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,105 @@
package com.example.demo.userfilms.service;
import java.util.List;
import java.util.stream.StreamSupport;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
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.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 repository;
private final UserService userService;
public UserfilmService(UserfilmRepository repository, UserService userService) {
this.repository = repository;
this.userService = userService;
}
@Transactional(readOnly = true)
public List<UserfilmEntity> getAll(long userId, long genreId) {
userService.get(userId);
if (genreId <= 0L) {
return repository.findByUserId(userId);
} else {
return repository.findByUserIdAndGenreId(userId, genreId);
}
}
@Transactional(readOnly = true)
public List<UserfilmEntity> getAll(long userId) {
userService.get(userId);
return repository.findByUserId(userId);
}
@Transactional(readOnly = true)
public Page<UserfilmEntity> getAll(long userId, long genreId, int page, int size) {
final Pageable pageable = PageRequest.of(page, size, Sort.by("id"));
userService.get(userId);
if (genreId <= 0L) {
return repository.findByUserId(userId, pageable);
} else {
return repository.findByUserIdAndGenreId(userId, genreId, pageable);
}
}
@Transactional(readOnly = true)
public UserfilmEntity get(long userId, long id) {
userService.get(userId);
return repository.findOneByUserIdAndId(userId, id)
.orElseThrow(() -> new NotFoundException(UserfilmEntity.class, id));
}
@Transactional
public UserfilmEntity create(long userId, UserfilmEntity entity) {
if (entity == null) {
throw new IllegalArgumentException("Entity is null");
}
final UserEntity existsUser = userService.get(userId);
entity.setUser(existsUser);
return repository.save(entity);
}
@Transactional
public List<UserfilmEntity> createAll(long userId, List<UserfilmEntity> entities) {
if (entities == null || entities.isEmpty()) {
throw new IllegalArgumentException("Userfilms list is null or empty");
}
final UserEntity existsUser = userService.get(userId);
entities.forEach(entity -> entity.setUser(existsUser));
return StreamSupport.stream(repository.saveAll(entities).spliterator(), false).toList();
}
@Transactional
public UserfilmEntity update(long userId, long id, UserfilmEntity entity) {
userService.get(userId);
final UserfilmEntity existsEntity = get(userId, id);
existsEntity.setFilm(entity.getFilm());
return repository.save(existsEntity);
}
@Transactional
public UserfilmEntity delete(long userId, long id) {
userService.get(userId);
final UserfilmEntity existsEntity = get(userId, id);
repository.delete(existsEntity);
return existsEntity;
}
@Transactional(readOnly = true)
public List<UserfilmGrouped> getTotal(long userId) {
userService.get(userId);
return repository.getFilmsTotalByGenre(userId);
}
}

View File

@ -0,0 +1,108 @@
package com.example.demo.users.api;
import java.util.stream.Collectors;
import org.modelmapper.ModelMapper;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.CookieValue;
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 org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.example.demo.core.api.PageAttributesMapper;
import com.example.demo.core.configuration.Constants;
import com.example.demo.core.security.UserPrincipal;
import com.example.demo.films.service.FilmService;
import com.example.demo.genres.api.GenreDto;
import com.example.demo.genres.model.GenreEntity;
import com.example.demo.genres.service.GenreService;
import com.example.demo.userfilms.api.UserfilmDto;
import com.example.demo.userfilms.api.UserfilmGroupedDto;
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.model.UserSubscriptionWithStatus;
import com.example.demo.users.service.UserService;
import jakarta.validation.Valid;
@Controller
@RequestMapping("/cabinet")
public class UserCabinetController {
private static final String PROFILE_VIEW = "cabinet";
private static final String PAGE_ATTRIBUTE = "page";
private static final String GENREID_ATTRIBUTE = "genreId";
private static final String PROFILE_ATTRIBUTE = "profile";
private final UserfilmService userfilmService;
private final GenreService genreService;
private final FilmService filmService;
private final UserService userService;
private final ModelMapper modelMapper;
public UserCabinetController(
UserfilmService userfilmService,
GenreService genreService,
FilmService filmService,
UserService userService,
ModelMapper modelMapper) {
this.userfilmService = userfilmService;
this.genreService = genreService;
this.filmService = filmService;
this.userService = userService;
this.modelMapper = modelMapper;
}
private GenreDto toGenreDto(GenreEntity entity) {
return modelMapper.map(entity, GenreDto.class);
}
private UserfilmDto toUserfilmDto(UserfilmEntity entity) {
return modelMapper.map(entity, UserfilmDto.class);
}
private UserfilmGroupedDto toUserfilmGroupedDto(UserfilmGrouped entity) {
return modelMapper.map(entity, UserfilmGroupedDto.class);
}
@GetMapping()
public String getProfile(
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
@RequestParam(name = GENREID_ATTRIBUTE, defaultValue = "0") int genreId,
Model model,
@AuthenticationPrincipal UserPrincipal principal) {
final long userId = principal.getId();
model.addAttribute(PAGE_ATTRIBUTE, page);
model.addAttribute(GENREID_ATTRIBUTE, genreId);
model.addAllAttributes(PageAttributesMapper.toAttributes(
userfilmService.getAll(userId, genreId, page, Constants.DEFUALT_PAGE_SIZE),
this::toUserfilmDto));
model.addAttribute("genres",
genreService.getAll().stream()
.map(this::toGenreDto)
.toList());
return PROFILE_VIEW;
}
@PostMapping("/delete/{id}")
public String deleteUserfilm(
@PathVariable(name = "id") Long id,
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
@RequestParam(name = GENREID_ATTRIBUTE, defaultValue = "0") int genreId,
RedirectAttributes redirectAttributes,
@AuthenticationPrincipal UserPrincipal principal) {
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
redirectAttributes.addAttribute(GENREID_ATTRIBUTE, genreId);
userfilmService.delete(principal.getId(), id);
return Constants.REDIRECT_VIEW + "/cabinet";
}
}

File diff suppressed because one or more lines are too long

View File

@ -38,6 +38,14 @@
th:classappend="${activeLink.startsWith('/admin/type') ? 'active' : ''}">
Типы заказов
</a>
<a class="nav-link" href="/admin/genre"
th:classappend="${activeLink.startsWith('/admin/genre') ? 'active' : ''}">
Жанры фильмов
</a>
<a class="nav-link" href="/admin/film"
th:classappend="${activeLink.startsWith('/admin/film') ? 'active' : ''}">
Фильмы
</a>
<a class="nav-link" href="/admin/subscription"
th:classappend="${activeLink.startsWith('/admin/subscription') ? 'active' : ''}">
Списки рассылки
@ -73,7 +81,7 @@
</div>
<div class="card-body d-flex flex-column">
<div id="messages" class="flex-grow-1 pe-3">
<!-- <div class="message d-flex flex-row justify-content-start mb-4">
<div class="message d-flex flex-row justify-content-start mb-4">
<img class="message-avatar" src="/einstein.svg" alt="avatar">
<div class="p-3 ms-3 message-body message-in">
<p class="small mb-0">
@ -88,7 +96,7 @@
</p>
</div>
<img class="message-avatar" src="/batman.svg" alt="avatar">
</div> -->
</div>
</div>
<div class="card-text pt-2">
<div data-mdb-input-init class="form-outline">

View File

@ -0,0 +1,36 @@
<!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/film/edit/{id}(id=${film.id})}" th:object="${film}" method="post">
<div class="mb-3">
<label for="id" class="form-label">ID</label>
<input type="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
</div>
<div class="mb-2">
<label for="genre" class="form-label">Товары</label>
<select th:field="*{genreName}" id="genre" class="form-select">
<option selected value="">Укажите тип товара</option>
<option th:each="genre : ${genres}" th:value="${genre.id}">[[${genre.name}]]</option>
</select>
<div th:if="${#fields.hasErrors('genreName')}" th:errors="*{genreName}" class="invalid-feedback"></div>
</div>
<div class="mb-3">
<label for="name" class="form-label">Фильм</label>
<input type="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" type="submit">Сохранить</button>
<a class="btn btn-secondary button-fixed-width" href="/admin/film">Отмена</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/film/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="film : ${items}">
<th scope="row" th:text="${film.id}"></th>
<td th:text="${film.name}"></td>
<td>
<form th:action="@{/admin/film/edit/{id}(id=${film.id})}" method="get">
<button type="submit" class="btn btn-link button-link">Редактировать</button>
</form>
</td>
<td>
<form th:action="@{/admin/film/delete/{id}(id=${film.id})}" method="post">
<button type="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

@ -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 type="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
</div>
<div class="mb-3">
<label for="name" class="form-label">Жанр фильма</label>
<input type="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" type="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 type="submit" class="btn btn-link button-link">Редактировать</button>
</form>
</td>
<td>
<form th:action="@{/admin/genre/delete/{id}(id=${genre.id})}" method="post">
<button type="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

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<th:block th:fragment="userfilms (items, totalPages, currentPage)">
<th:block th:switch="${items.size()}">
<h2 th:case="0">Данные отсутствуют</h2>
<th:block th:case="*">
<form th:action="@{/cabinet}" method="get" class="row mt-2">
<div class="col-sm-10">
<input type="hidden" th:name="page" th:value="${page}">
<select th:name="genreId" id="genreId" class="form-select">
<option selected value="">Фильтр по типу товара</option>
<option th:each="genre : ${genres}" th:value="${genre.id}"
th:selected="${genre.id==genreId}">
[[${genre.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>
</tr>
</thead>
<div>
<ul class="list-group flex-row flex-wrap">
<li th:each="userfilm : ${items}" class="list-group-item col-sm-6">
<strong>[[${userfilm.filmName}]]</strong> : [[${userfilm.genreName}]]
<form th:action="@{/cabinet/delete/{id}(id=${userfilm.id})}" method="post">
<input type="hidden" th:name="page" th:value="${page}">
<input type="hidden" th:name="filmId" th:value="${filmId}">
<button type="submit" class="btn btn-link button-link"
onclick="return confirm('Вы уверены?')">Удалить</button>
</form>
</li>
</ul>
</div>
<tbody>
<tr th:each="userfilm : ${items}">
<th scope="row" th:text="${userfilm.id}"></th>
<td th:text="${userfilm.filmName}"></td>
<td>
<form th:action="@{cabinet//delete/{id}(id=${userfilm.id})}" method="post">
<input type="hidden" th:name="page" th:value="${page}">
<input type="hidden" th:name="filmId" th:value="${filmId}">
<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>