Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
6903dc353b | |||
68f1e0b0f8 | |||
f2f313b4f8 | |||
bbbb903b2e | |||
87105799eb |
28
build.gradle
28
build.gradle
@ -1,12 +1,23 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.2.2'
|
||||
id 'org.springframework.boot' version '3.2.5'
|
||||
id 'io.spring.dependency-management' version '1.1.4'
|
||||
}
|
||||
|
||||
group = 'com.example'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
|
||||
defaultTasks 'bootRun'
|
||||
|
||||
jar {
|
||||
enabled = false
|
||||
}
|
||||
|
||||
bootJar {
|
||||
archiveFileName = String.format('%s-%s.jar', rootProject.name, version)
|
||||
}
|
||||
|
||||
assert System.properties['java.specification.version'] == '17' || '21'
|
||||
java {
|
||||
sourceCompatibility = '17'
|
||||
}
|
||||
@ -17,7 +28,20 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||
implementation 'org.modelmapper:modelmapper:3.2.0'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
|
||||
implementation 'com.h2database:h2:2.2.224'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-devtools'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
|
||||
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.3.0'
|
||||
runtimeOnly 'org.webjars.npm:bootstrap:5.3.3'
|
||||
runtimeOnly 'org.webjars.npm:bootstrap-icons:1.11.3'
|
||||
|
||||
implementation 'org.springframework.boot:spring-boot-starter-security'
|
||||
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
|
||||
|
||||
testImplementation 'org.springframework.boot:spring-boot-starter-test'
|
||||
}
|
||||
|
BIN
data.mv.db
Normal file
BIN
data.mv.db
Normal file
Binary file not shown.
10877
data.trace.db
Normal file
10877
data.trace.db
Normal file
File diff suppressed because it is too large
Load Diff
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
20
gradlew.bat
vendored
20
gradlew.bat
vendored
@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
|
@ -1,66 +0,0 @@
|
||||
package com.example.demo;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
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 org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/usertest")
|
||||
public class ApiController {
|
||||
private final Logger log = LoggerFactory.getLogger(ApiController.class);
|
||||
|
||||
List<UserTest> data = new ArrayList<>(List.of(
|
||||
new UserTest("handle1", "email1", "name1", "password1"),
|
||||
new UserTest("handle2", "email2", "name2", "password2")));
|
||||
|
||||
@PostMapping
|
||||
public UserTest create(@RequestBody UserTest user) {
|
||||
data.add(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<UserTest> readAll() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public UserTest read(@PathVariable(name = "id") int id) {
|
||||
return data.get(id);
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public UserTest update(@PathVariable(name = "id") int id, @RequestBody UserTest user) {
|
||||
UserTest us = data.get(id);
|
||||
|
||||
if (us != null) {
|
||||
data.remove(id);
|
||||
}
|
||||
|
||||
data.add(id, user);
|
||||
return data.get(id);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public UserTest delete(@PathVariable(name = "id") int id) {
|
||||
UserTest us = data.get(id);
|
||||
|
||||
if (us != null) {
|
||||
data.remove(id);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +1,138 @@
|
||||
package com.example.demo;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
import com.example.demo.orders.api.OrderIventDto;
|
||||
import com.example.demo.orders.service.OrderService;
|
||||
import com.example.demo.ivents.model.IventEntity;
|
||||
import com.example.demo.ivents.service.IventService;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
import com.example.demo.places.model.PlaceEntity;
|
||||
import com.example.demo.places.service.PlaceService;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.model.UserRole;
|
||||
import com.example.demo.users.service.UserService;
|
||||
|
||||
@SpringBootApplication
|
||||
public class DemoApplication {
|
||||
public class DemoApplication implements CommandLineRunner {
|
||||
private final Logger log = LoggerFactory.getLogger(DemoApplication.class);
|
||||
|
||||
private final TypeService typeService;
|
||||
private final PlaceService placeService;
|
||||
private final IventService iventService;
|
||||
private final OrderService orderService;
|
||||
private final UserService userService;
|
||||
|
||||
public DemoApplication(PlaceService placeService, TypeService typeService, IventService iventService,
|
||||
OrderService orderService, UserService userService) {
|
||||
this.placeService = placeService;
|
||||
this.typeService = typeService;
|
||||
this.iventService = iventService;
|
||||
this.orderService = orderService;
|
||||
this.userService = userService;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DemoApplication.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
log.info("Create default types&dates values");
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
LocalDate _date = LocalDate.parse("2023-10-15", formatter);
|
||||
|
||||
final var type1 = typeService.create(new TypeEntity("Концерт"));
|
||||
final var type2 = typeService.create(new TypeEntity("Кино"));
|
||||
final var type3 = typeService.create(new TypeEntity("Театр"));
|
||||
|
||||
final var place1 = placeService.create(new PlaceEntity("US, California"));
|
||||
final var place2 = placeService.create(new PlaceEntity("Germany, Berlin"));
|
||||
final var place3 = placeService.create(new PlaceEntity("UK, London"));
|
||||
|
||||
log.info("Create default ivents values");
|
||||
final var ivent1 = iventService.create(new IventEntity(
|
||||
"Slipknot",
|
||||
type1,
|
||||
_date,
|
||||
"Slipknot(test) - американская ню-метал группа, основанная в 1995 году. Они известны своим агрессивным звучанием, смешением хардкор-панк и тяжелого метала с индастриалом и элементами альтернативного рока.",
|
||||
"Участники группы Slipknot - это:<....>",
|
||||
2500.33,
|
||||
30,
|
||||
place1,
|
||||
null));
|
||||
|
||||
final var ivent2 = iventService.create(new IventEntity(
|
||||
"Placebo",
|
||||
type1,
|
||||
_date,
|
||||
"Placebo - британская альтернативная рок-группа, основанная в 1994 году. Они известны своим уникальным звучанием, погружающим слушателей в меланхоличную атмосферу и наполняющим музыку эмоциональным содержанием.",
|
||||
"Группа Placebo состояла из следующих участников:<....>",
|
||||
2500.33,
|
||||
30,
|
||||
place2,
|
||||
null));
|
||||
|
||||
iventService.create(new IventEntity(
|
||||
"Radiohead",
|
||||
type1,
|
||||
_date,
|
||||
"Radiohead - британская рок-группа, образованная в 1985 году.",
|
||||
"Участниками Radiohead являются:<....>",
|
||||
2500.33,
|
||||
30,
|
||||
place3,
|
||||
null));
|
||||
|
||||
iventService.create(new IventEntity(
|
||||
"Сияние",
|
||||
type2,
|
||||
_date,
|
||||
"\"Сияние\" - это психологический триллер режиссера Стэнли Кубрика, вышедший в 1980 году.",
|
||||
"Актеры:<....>",
|
||||
2500.33,
|
||||
45,
|
||||
place3,
|
||||
null));
|
||||
|
||||
iventService.create(new IventEntity(
|
||||
"Мастер и Маргарита",
|
||||
type3,
|
||||
_date,
|
||||
"тут должно быть описание",
|
||||
"Автор: М. А. Булгаков",
|
||||
2500.33,
|
||||
30,
|
||||
place2,
|
||||
null));
|
||||
|
||||
log.info("Create default users values");
|
||||
final var admin = new UserEntity("Admin", "Admin@gmail.com", "admin");
|
||||
final var user1 = userService.create(new UserEntity("Milana", "milana@gmail.com", "milana"));
|
||||
final var user2 = userService.create(new UserEntity("TestUser", "user@gmail.com", "test"));
|
||||
|
||||
admin.setRole(UserRole.ADMIN);
|
||||
userService.create(admin);
|
||||
|
||||
log.info("Create default orders values");
|
||||
|
||||
OrderIventDto ivent10 = new OrderIventDto();
|
||||
ivent10.setIventId(ivent1.getId());
|
||||
ivent10.setQuantity(4);
|
||||
|
||||
OrderIventDto ivent20 = new OrderIventDto();
|
||||
ivent20.setIventId(ivent2.getId());
|
||||
ivent20.setQuantity(6);
|
||||
|
||||
orderService.create(user1.getId(), Arrays.asList(ivent10, ivent20));
|
||||
}
|
||||
}
|
||||
|
@ -1,42 +0,0 @@
|
||||
package com.example.demo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
public class UserTest {
|
||||
private String handle;
|
||||
private String email;
|
||||
private String name;
|
||||
private String password;
|
||||
|
||||
public UserTest() {
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public UserTest(
|
||||
@JsonProperty(value = "handle") String handle,
|
||||
@JsonProperty(value = "email") String email,
|
||||
@JsonProperty(value = "name") String name,
|
||||
@JsonProperty(value = "password") String password) {
|
||||
this.handle = handle;
|
||||
this.email = email;
|
||||
this.name = name;
|
||||
this.password = password;
|
||||
}
|
||||
public String getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package com.example.demo;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addCorsMappings(@NonNull CorsRegistry registry) {
|
||||
registry.addMapping("/**")
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE");
|
||||
}
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
package com.example.demo.cartItems.api;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
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.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
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.bind.annotation.SessionAttributes;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.core.security.UserPrincipal;
|
||||
import com.example.demo.core.session.SessionCart;
|
||||
import com.example.demo.orders.api.OrderIventDto;
|
||||
import com.example.demo.orders.service.OrderService;
|
||||
import com.example.demo.ivents.api.IventDto;
|
||||
import com.example.demo.ivents.model.IventEntity;
|
||||
import com.example.demo.ivents.service.IventService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(CartItemController.URL)
|
||||
@SessionAttributes("ivents")
|
||||
public class CartItemController {
|
||||
public static final String URL = "/cart";
|
||||
private static final String ORDER_VIEW = "cart";
|
||||
private static final String CART_ITEM_ATTRIBUTE = "cartItem";
|
||||
private static final String CART_ATTRIBUTE = "cart";
|
||||
|
||||
private final OrderService orderService;
|
||||
private final IventService iventService;
|
||||
private final ModelMapper modelMapper;
|
||||
private final SessionCart cart;
|
||||
|
||||
public CartItemController(IventService iventService, SessionCart cart, OrderService orderService,
|
||||
ModelMapper modelMapper) {
|
||||
this.iventService = iventService;
|
||||
this.orderService = orderService;
|
||||
this.modelMapper = modelMapper;
|
||||
this.cart = cart;
|
||||
}
|
||||
|
||||
private IventDto toIventDto(IventEntity entity) {
|
||||
return modelMapper.map(entity, IventDto.class);
|
||||
}
|
||||
|
||||
private List<OrderIventDto> toOrderIventDtos(Collection<CartItemDto> cartItems) {
|
||||
return cartItems.stream().map(item -> {
|
||||
final OrderIventDto dto = new OrderIventDto();
|
||||
dto.setQuantity(item.getQuantity());
|
||||
dto.setIventId(item.getIventId());
|
||||
return dto;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getCart(Model model, @AuthenticationPrincipal UserPrincipal principal) {
|
||||
model.addAttribute("ivents",
|
||||
iventService.getAll(0L, 0L, null, null).stream()
|
||||
.map(this::toIventDto)
|
||||
.toList());
|
||||
model.addAttribute(CART_ITEM_ATTRIBUTE, new CartItemDto());
|
||||
model.addAttribute(CART_ATTRIBUTE, cart.values());
|
||||
return ORDER_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public String addItemToCart(
|
||||
@ModelAttribute(name = CART_ITEM_ATTRIBUTE) @Valid CartItemDto cartItem,
|
||||
BindingResult bindingResult,
|
||||
@AuthenticationPrincipal UserPrincipal principal,
|
||||
SessionStatus status,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return ORDER_VIEW;
|
||||
}
|
||||
|
||||
status.setComplete();
|
||||
|
||||
IventEntity ivent = iventService.get(cartItem.getIventId());
|
||||
cartItem.setIventName(ivent.getName());
|
||||
cartItem.setIventPrice(ivent.getPrice());
|
||||
|
||||
cart.computeIfPresent(cartItem.hashCode(), (key, value) -> {
|
||||
value.setQuantity(value.getQuantity() + cartItem.getQuantity());
|
||||
return value;
|
||||
});
|
||||
cart.putIfAbsent(cartItem.hashCode(), cartItem);
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
public String createOrder(
|
||||
Model model,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
orderService.create(principal.getId(), toOrderIventDtos(cart.values()));
|
||||
cart.clear();
|
||||
return Constants.REDIRECT_VIEW + "/";
|
||||
}
|
||||
|
||||
@PostMapping("/clear")
|
||||
public String clearCart(@AuthenticationPrincipal UserPrincipal principal) {
|
||||
cart.clear();
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/toggle-quantity")
|
||||
public String increaseCartCount(
|
||||
@RequestParam(name = "iventId", defaultValue = "0") Long iventId,
|
||||
@RequestParam(name = "isIncrease", defaultValue = "false") boolean isIncrease,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
if (iventId <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
cart.computeIfPresent(Objects.hash(iventId), (key, value) -> {
|
||||
if (isIncrease) value.setQuantity(value.getQuantity() + 1);
|
||||
else if (!isIncrease && value.getQuantity() - 1 > 0) value.setQuantity(value.getQuantity() - 1);
|
||||
return value;
|
||||
});
|
||||
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package com.example.demo.cartItems.api;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class CartItemDto {
|
||||
private Long id;
|
||||
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Long iventId;
|
||||
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Integer quantity;
|
||||
|
||||
private String iventName;
|
||||
private Double iventPrice;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getIventId() {
|
||||
return iventId;
|
||||
}
|
||||
|
||||
public void setIventId(Long iventId) {
|
||||
this.iventId = iventId;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(Integer quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public String getIventName() {
|
||||
return iventName;
|
||||
}
|
||||
|
||||
public void setIventName(String name) {
|
||||
this.iventName = name;
|
||||
}
|
||||
|
||||
public Double getIventPrice() {
|
||||
return iventPrice;
|
||||
}
|
||||
|
||||
public void setIventPrice(Double price) {
|
||||
this.iventPrice = price;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(iventId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
CartItemDto other = (CartItemDto) obj;
|
||||
return Objects.equals(iventId, other.iventId) && Objects.equals(quantity, other.quantity);
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package com.example.demo.catalog.api;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.support.SessionStatus;
|
||||
|
||||
import com.example.demo.cartItems.api.CartItemDto;
|
||||
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.core.session.SessionCart;
|
||||
import com.example.demo.ivents.api.ExpandedIventDto;
|
||||
import com.example.demo.ivents.api.IventDto;
|
||||
import com.example.demo.ivents.model.IventEntity;
|
||||
import com.example.demo.ivents.service.IventService;
|
||||
|
||||
@Controller
|
||||
public class CatalogController {
|
||||
public static final String PRODUCTS_VIEW = "catalog";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
|
||||
private final IventService iventService;
|
||||
private final SessionCart cart;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public CatalogController(IventService iventService,
|
||||
ModelMapper modelMapper, SessionCart cart) {
|
||||
this.iventService = iventService;
|
||||
this.cart = cart;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
protected ExpandedIventDto toExpandedDto(IventEntity entity, Long userId) {
|
||||
ExpandedIventDto dto = modelMapper.map(entity, ExpandedIventDto.class);
|
||||
dto.setTypeName(entity.getType().getName());
|
||||
final var t = cart.containsKey(Objects.hash(dto.getId()));
|
||||
dto.setIsCartIvent(cart.containsKey(Objects.hash(dto.getId())));
|
||||
return dto;
|
||||
}
|
||||
|
||||
protected IventDto toDto(IventEntity entity) {
|
||||
ExpandedIventDto dto = modelMapper.map(entity, ExpandedIventDto.class);
|
||||
dto.setTypeName(entity.getType().getName());
|
||||
final var isCartIvent = cart.containsKey(Objects.hash(dto.getId()));
|
||||
dto.setIsCartIvent(isCartIvent);
|
||||
return dto;
|
||||
}
|
||||
|
||||
@GetMapping("/catalog")
|
||||
public String getAll(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
final Map<String, Object> attributes = PageAttributesMapper.toAttributes(
|
||||
iventService.getAll(0L, 0L, null, null, page, Constants.DEFAULT_PAGE_SIZE),
|
||||
item -> {
|
||||
if(principal != null) {
|
||||
return toExpandedDto(item, principal.getId());
|
||||
} else {
|
||||
// Handle the case when the user is not authenticated
|
||||
return toDto(item);
|
||||
}
|
||||
}
|
||||
);
|
||||
model.addAllAttributes(attributes);
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
|
||||
return PRODUCTS_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/buy")
|
||||
public String addIventToCart(
|
||||
Model model,
|
||||
@RequestParam(name = "id") Long iventId,
|
||||
@RequestParam(name = "isCartIvent") boolean isCartIvent,
|
||||
SessionStatus status,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
if (iventId <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
status.setComplete();
|
||||
if (!isCartIvent)
|
||||
buyIvent(iventId);
|
||||
else
|
||||
increaseCartItemQuantity(iventId);
|
||||
|
||||
return Constants.REDIRECT_VIEW + "/catalog";
|
||||
}
|
||||
|
||||
private void buyIvent(Long iventId) {
|
||||
IventEntity ivent = iventService.get(iventId);
|
||||
CartItemDto cartItem = new CartItemDto();
|
||||
cartItem.setIventId(iventId);
|
||||
cartItem.setQuantity(1);
|
||||
cartItem.setIventName(ivent.getName());
|
||||
cartItem.setIventPrice(ivent.getPrice());
|
||||
|
||||
cart.putIfAbsent(cartItem.hashCode(), cartItem);
|
||||
}
|
||||
|
||||
private void increaseCartItemQuantity(Long iventId) {
|
||||
cart.computeIfPresent(Objects.hash(iventId), (key, value) -> {
|
||||
value.setQuantity(value.getQuantity() + 1);
|
||||
return value;
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package com.example.demo.core.api;
|
||||
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
|
||||
import com.example.demo.core.session.SessionCart;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@ControllerAdvice
|
||||
public class GlobalController {
|
||||
private final SessionCart cart;
|
||||
|
||||
public GlobalController(SessionCart cart) {
|
||||
this.cart = cart;
|
||||
}
|
||||
|
||||
@ModelAttribute("servletPath")
|
||||
String getRequestServletPath(HttpServletRequest request) {
|
||||
return request.getServletPath();
|
||||
}
|
||||
|
||||
@ModelAttribute("totalCart")
|
||||
double getTotalCart() {
|
||||
return cart.getSum();
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.example.demo.core.api;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
public class PageAttributesMapper {
|
||||
private PageAttributesMapper() {
|
||||
}
|
||||
|
||||
public static <E, D> Map<String, Object> toAttributes(Page<E> page, Function<E, D> mapper) {
|
||||
return Map.of(
|
||||
"items", page.getContent().stream().map(mapper::apply).toList(),
|
||||
"currentPage", page.getNumber(),
|
||||
"totalPages", page.getTotalPages());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package com.example.demo.core.configuration;
|
||||
|
||||
public class Constants {
|
||||
public static final String API_URL = "/api/1.0";
|
||||
public static final String SEQUENCE_NAME = "hibernate_sequence";
|
||||
public static final int DEFAULT_PAGE_SIZE = 5;
|
||||
|
||||
public static final String REDIRECT_VIEW = "redirect:";
|
||||
public static final String ADMIN_PREFIX = "/admin";
|
||||
public static final String LOGIN_URL = "/login";
|
||||
public static final String LOGOUT_URL = "/logout";
|
||||
public static final String DEFAULT_PASSWORD = "123456";
|
||||
|
||||
private Constants() {
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.example.demo.core.configuration;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.modelmapper.PropertyMap;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
|
||||
@Configuration
|
||||
public class MapperConfiguration {
|
||||
@Bean
|
||||
ModelMapper modelMapper() {
|
||||
final ModelMapper mapper = new ModelMapper();
|
||||
mapper.addMappings(new PropertyMap<Object, BaseEntity>() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
skip(destination.getId());
|
||||
}
|
||||
});
|
||||
return mapper;
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.example.demo.core.configuration;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
@Configuration
|
||||
public class WebConfiguration implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addViewControllers(ViewControllerRegistry registry) {
|
||||
registry.addViewController("/login").setViewName("login");
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.example.demo.core.error;
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
|
||||
@ControllerAdvice
|
||||
public class AdviceController {
|
||||
private final Logger log = LoggerFactory.getLogger(AdviceController.class);
|
||||
|
||||
private static Throwable getRootCause(Throwable throwable) {
|
||||
Throwable rootCause = throwable;
|
||||
while (rootCause.getCause() != null && rootCause.getCause() != rootCause) {
|
||||
rootCause = rootCause.getCause();
|
||||
}
|
||||
return rootCause;
|
||||
}
|
||||
|
||||
private static Map<String, Object> getAttributes(HttpServletRequest request, Throwable throwable) {
|
||||
final Throwable rootCause = getRootCause(throwable);
|
||||
final StackTraceElement firstError = rootCause.getStackTrace()[0];
|
||||
return Map.of(
|
||||
"message", rootCause.getMessage(),
|
||||
"url", request.getRequestURL(),
|
||||
"exception", rootCause.getClass().getName(),
|
||||
"file", firstError.getFileName(),
|
||||
"method", firstError.getMethodName(),
|
||||
"line", firstError.getLineNumber());
|
||||
}
|
||||
|
||||
@ExceptionHandler(value = Exception.class)
|
||||
public ModelAndView defaultErrorHandler(HttpServletRequest request, Throwable throwable) throws Throwable {
|
||||
if (AnnotationUtils.findAnnotation(throwable.getClass(),
|
||||
ResponseStatus.class) != null) {
|
||||
throw throwable;
|
||||
}
|
||||
|
||||
log.error("{}", throwable.getMessage());
|
||||
throwable.printStackTrace();
|
||||
final ModelAndView model = new ModelAndView();
|
||||
model.addAllObjects(getAttributes(request, throwable));
|
||||
model.setViewName("error");
|
||||
return model;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.example.demo.core.error;
|
||||
|
||||
public class NotFoundException extends RuntimeException {
|
||||
public <T> NotFoundException(Class<T> clazz, Long id) {
|
||||
super(String.format("%s with id [%s] is not found or not exists", clazz.getSimpleName(), id));
|
||||
}
|
||||
}
|
28
src/main/java/com/example/demo/core/model/BaseEntity.java
Normal file
28
src/main/java/com/example/demo/core/model/BaseEntity.java
Normal file
@ -0,0 +1,28 @@
|
||||
package com.example.demo.core.model;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.SequenceGenerator;
|
||||
|
||||
@MappedSuperclass
|
||||
public abstract class BaseEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = Constants.SEQUENCE_NAME)
|
||||
@SequenceGenerator(name = Constants.SEQUENCE_NAME, sequenceName = Constants.SEQUENCE_NAME, allocationSize = 1)
|
||||
protected Long id;
|
||||
|
||||
protected BaseEntity() {
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package com.example.demo.core.security;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.users.api.UserSignupController;
|
||||
import com.example.demo.users.model.UserRole;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class SecurityConfiguration {
|
||||
@Bean
|
||||
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
httpSecurity.headers(headers -> headers.frameOptions(FrameOptionsConfig::sameOrigin));
|
||||
httpSecurity.csrf(AbstractHttpConfigurer::disable);
|
||||
httpSecurity.cors(Customizer.withDefaults());
|
||||
|
||||
httpSecurity.authorizeHttpRequests(requests -> requests
|
||||
.requestMatchers("/css/**", "/webjars/**", "/*.svg", "/*.jpg").permitAll()
|
||||
.requestMatchers("/public/**").permitAll()
|
||||
.requestMatchers(Constants.ADMIN_PREFIX + "/**").hasRole(UserRole.ADMIN.name())
|
||||
.requestMatchers("/h2-console/**").hasRole(UserRole.ADMIN.name())
|
||||
.requestMatchers(UserSignupController.URL).anonymous()
|
||||
.requestMatchers(Constants.LOGIN_URL).anonymous()
|
||||
.requestMatchers("/catalog").permitAll()
|
||||
.anyRequest().authenticated()
|
||||
);
|
||||
|
||||
httpSecurity.formLogin(formLogin -> formLogin
|
||||
.loginPage(Constants.LOGIN_URL));
|
||||
|
||||
httpSecurity.rememberMe(rememberMe -> rememberMe.key("uniqueAndSecret"));
|
||||
|
||||
httpSecurity.logout(logout -> logout
|
||||
.deleteCookies("JSESSIONID"));
|
||||
|
||||
return httpSecurity.build();
|
||||
}
|
||||
@Bean
|
||||
DaoAuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
|
||||
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
||||
authProvider.setUserDetailsService(userDetailsService);
|
||||
authProvider.setPasswordEncoder(passwordEncoder());
|
||||
return authProvider;
|
||||
}
|
||||
|
||||
@Bean
|
||||
PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,65 @@
|
||||
package com.example.demo.core.security;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
|
||||
public class UserPrincipal implements UserDetails {
|
||||
private final long id;
|
||||
private final String handle;
|
||||
private final String password;
|
||||
private final Set<? extends GrantedAuthority> roles;
|
||||
private final boolean active;
|
||||
|
||||
public UserPrincipal(UserEntity user) {
|
||||
this.id = user.getId();
|
||||
this.handle = user.getHandle();
|
||||
this.password = user.getPassword();
|
||||
this.roles = Set.of(user.getRole());
|
||||
this.active = true;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUsername() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return active;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonExpired() {
|
||||
return isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAccountNonLocked() {
|
||||
return isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCredentialsNonExpired() {
|
||||
return isEnabled();
|
||||
}
|
||||
}
|
||||
|
14
src/main/java/com/example/demo/core/session/SessionCart.java
Normal file
14
src/main/java/com/example/demo/core/session/SessionCart.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.example.demo.core.session;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.example.demo.cartItems.api.CartItemDto;
|
||||
|
||||
public class SessionCart extends HashMap<Integer, CartItemDto> {
|
||||
public double getSum() {
|
||||
return this.values().stream()
|
||||
.map(item -> item.getQuantity() * item.getIventPrice())
|
||||
.mapToDouble(Double::doubleValue)
|
||||
.sum();
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package com.example.demo.core.session;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.context.annotation.ScopedProxyMode;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
@Configuration
|
||||
public class SessionHelper {
|
||||
@Bean
|
||||
@Scope(value = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
|
||||
SessionCart todos() {
|
||||
return new SessionCart();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.example.demo.ivents.api;
|
||||
|
||||
public class ExpandedIventDto extends IventDto {
|
||||
private String typeName;
|
||||
private String placeName;
|
||||
private boolean isCartIvent;
|
||||
|
||||
public String getTypeName() {
|
||||
return typeName;
|
||||
}
|
||||
|
||||
public void setTypeName(String name) {
|
||||
this.typeName = name;
|
||||
}
|
||||
|
||||
public String getPlaceName() {
|
||||
return placeName;
|
||||
}
|
||||
|
||||
public void setPlaceName(String name) {
|
||||
this.placeName = name;
|
||||
}
|
||||
|
||||
public boolean getIsCartIvent() {
|
||||
return isCartIvent;
|
||||
}
|
||||
|
||||
public void setIsCartIvent(boolean flag) {
|
||||
this.isCartIvent = flag;
|
||||
}
|
||||
}
|
161
src/main/java/com/example/demo/ivents/api/IventController.java
Normal file
161
src/main/java/com/example/demo/ivents/api/IventController.java
Normal file
@ -0,0 +1,161 @@
|
||||
package com.example.demo.ivents.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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.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 com.example.demo.core.api.PageAttributesMapper;
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.core.security.UserPrincipal;
|
||||
import com.example.demo.ivents.model.IventEntity;
|
||||
import com.example.demo.ivents.service.IventService;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.api.TypeDto;
|
||||
import com.example.demo.places.service.PlaceService;
|
||||
import com.example.demo.places.model.PlaceEntity;
|
||||
import com.example.demo.places.api.PlaceDto;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(IventController.URL)
|
||||
public class IventController {
|
||||
public static final String URL = Constants.ADMIN_PREFIX + "/ivent";
|
||||
private static final String IVENT_VIEW = "ivent";
|
||||
private static final String IVENT_EDIT_VIEW = "ivent-edit";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
private static final String IVENT_ATTRIBUTE = "ivent";
|
||||
|
||||
private final IventService iventService;
|
||||
private final TypeService typeService;
|
||||
private final PlaceService placeService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public IventController(IventService iventService, TypeService typeService,
|
||||
PlaceService placeService, ModelMapper modelMapper) {
|
||||
this.iventService = iventService;
|
||||
this.typeService = typeService;
|
||||
this.placeService = placeService;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
private IventDto toDto(IventEntity entity) {
|
||||
return modelMapper.map(entity, IventDto.class);
|
||||
}
|
||||
|
||||
private ExpandedIventDto toExpandedDto(IventEntity entity, Long userId) {
|
||||
ExpandedIventDto dto = modelMapper.map(entity, ExpandedIventDto.class);
|
||||
dto.setTypeName(typeService.get(dto.getTypeId()).getName());
|
||||
dto.setPlaceName(placeService.get(dto.getPlaceId()).getName());
|
||||
return dto;
|
||||
}
|
||||
|
||||
private TypeDto toTypeDto(TypeEntity entity) {
|
||||
return modelMapper.map(entity, TypeDto.class);
|
||||
}
|
||||
|
||||
private PlaceDto toPlaceDto(PlaceEntity entity) {
|
||||
return modelMapper.map(entity, PlaceDto.class);
|
||||
}
|
||||
|
||||
private IventEntity toEntity(IventDto dto) {
|
||||
final IventEntity entity = modelMapper.map(dto, IventEntity.class);
|
||||
entity.setType(typeService.get(dto.getTypeId()));
|
||||
entity.setPlace(placeService.get(dto.getPlaceId()));
|
||||
return entity;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getAll(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
final Map<String, Object> attributes = PageAttributesMapper.toAttributes(
|
||||
iventService.getAll(0L, 0L, null, null, page, Constants.DEFAULT_PAGE_SIZE),
|
||||
item -> toExpandedDto(item, principal.getId()));
|
||||
model.addAllAttributes(attributes);
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
|
||||
return IVENT_VIEW;
|
||||
}
|
||||
|
||||
@GetMapping("/edit/")
|
||||
public String create(Model model) {
|
||||
model.addAttribute(IVENT_ATTRIBUTE, new IventDto());
|
||||
model.addAttribute("types",
|
||||
typeService.getAll().stream()
|
||||
.map(this::toTypeDto)
|
||||
.toList());
|
||||
model.addAttribute("places",
|
||||
placeService.getAll().stream()
|
||||
.map(this::toPlaceDto)
|
||||
.toList());
|
||||
return IVENT_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/")
|
||||
public String create(
|
||||
@ModelAttribute(name = IVENT_ATTRIBUTE) @Valid IventDto ivent,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return IVENT_EDIT_VIEW;
|
||||
}
|
||||
|
||||
iventService.create(toEntity(ivent));
|
||||
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("types",
|
||||
typeService.getAll().stream()
|
||||
.map(this::toTypeDto)
|
||||
.toList());
|
||||
model.addAttribute("places",
|
||||
placeService.getAll().stream()
|
||||
.map(this::toPlaceDto)
|
||||
.toList());
|
||||
|
||||
model.addAttribute(IVENT_ATTRIBUTE, toDto(iventService.get(id)));
|
||||
return IVENT_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@ModelAttribute(name = IVENT_ATTRIBUTE) @Valid IventDto ivent,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return IVENT_EDIT_VIEW;
|
||||
}
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
iventService.update(id, toEntity(ivent));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String delete(
|
||||
@PathVariable(name = "id") Long id) {
|
||||
iventService.delete(id);
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
}
|
111
src/main/java/com/example/demo/ivents/api/IventDto.java
Normal file
111
src/main/java/com/example/demo/ivents/api/IventDto.java
Normal file
@ -0,0 +1,111 @@
|
||||
package com.example.demo.ivents.api;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.time.LocalDate;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class IventDto {
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
private Long id;
|
||||
@NotBlank
|
||||
private String itName;
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Long typeId;
|
||||
@NotNull
|
||||
private LocalDate date;
|
||||
@NotBlank
|
||||
private String description;
|
||||
@NotBlank
|
||||
private String other;
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Double price;
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Integer count;
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Long placeId;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getTypeId() {
|
||||
return typeId;
|
||||
}
|
||||
|
||||
public void setTypeId(Long typeId) {
|
||||
this.typeId = typeId;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return itName;
|
||||
}
|
||||
|
||||
public void setName(String itName) {
|
||||
this.itName = itName;
|
||||
}
|
||||
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(LocalDate date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getOther() {
|
||||
return other;
|
||||
}
|
||||
|
||||
public void setOther(String other) {
|
||||
this.other = other;
|
||||
}
|
||||
|
||||
public Double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(Double price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public Integer getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(Integer count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public Long getPlaceId() {
|
||||
return placeId;
|
||||
}
|
||||
|
||||
public void setPlaceId(Long placeId) {
|
||||
this.placeId = placeId;
|
||||
}
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Double getSum() {
|
||||
return price * count;
|
||||
}
|
||||
}
|
||||
|
156
src/main/java/com/example/demo/ivents/model/IventEntity.java
Normal file
156
src/main/java/com/example/demo/ivents/model/IventEntity.java
Normal file
@ -0,0 +1,156 @@
|
||||
package com.example.demo.ivents.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.places.model.PlaceEntity;
|
||||
|
||||
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 = "ivents")
|
||||
public class IventEntity extends BaseEntity {
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "typeId", nullable = false)
|
||||
private TypeEntity type;
|
||||
@Column(nullable = false)
|
||||
@DateTimeFormat(pattern = "yyyy-MM-dd")
|
||||
private LocalDate date;
|
||||
@Column(nullable = false)
|
||||
private String description;
|
||||
@Column(nullable = false)
|
||||
private String other;
|
||||
@Column(nullable = false)
|
||||
private Double price;
|
||||
@Column(nullable = false)
|
||||
private Integer count;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "placeId", nullable = false)
|
||||
private PlaceEntity place;
|
||||
private String image;
|
||||
|
||||
public IventEntity() {
|
||||
}
|
||||
|
||||
public IventEntity(String name, TypeEntity type, LocalDate date,
|
||||
String description, String other, Double price, Integer count, PlaceEntity place, String image) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.date = date;
|
||||
this.description = description;
|
||||
this.other = other;
|
||||
this.price = price;
|
||||
this.count = count;
|
||||
this.place = place;
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public TypeEntity getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(TypeEntity type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(LocalDate date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public String getDesc() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDesc(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getOther() {
|
||||
return other;
|
||||
}
|
||||
|
||||
public void setOther(String other) {
|
||||
this.other = other;
|
||||
}
|
||||
|
||||
public Double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(Double price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public Integer getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public void setCount(Integer count) {
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public PlaceEntity getPlace() {
|
||||
return place;
|
||||
}
|
||||
|
||||
public void setPlace(PlaceEntity place) {
|
||||
this.place = place;
|
||||
}
|
||||
|
||||
public String getImage() {
|
||||
return image;
|
||||
}
|
||||
|
||||
public void setImage(String image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, type, date, description, other, price, count, place, image);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
final IventEntity otheren = (IventEntity) obj;
|
||||
return Objects.equals(otheren.getId(), id)
|
||||
&& Objects.equals(otheren.getName(), name)
|
||||
&& Objects.equals(otheren.getType(), type)
|
||||
&& Objects.equals(otheren.getDate(), date)
|
||||
&& Objects.equals(otheren.getDesc(), description)
|
||||
&& Objects.equals(otheren.getOther(), other)
|
||||
&& Objects.equals(otheren.getPrice(), price)
|
||||
&& Objects.equals(otheren.getCount(), count)
|
||||
&& Objects.equals(otheren.getPlace(), place)
|
||||
&& Objects.equals(otheren.getImage(), image);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,24 @@
|
||||
package com.example.demo.ivents.repository;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
import com.example.demo.ivents.model.IventEntity;
|
||||
|
||||
public interface IventRepository extends CrudRepository<IventEntity, Long> , PagingAndSortingRepository<IventEntity, Long> {
|
||||
List<IventEntity> findByTypeId(Long typeId);
|
||||
List<IventEntity> findByPlaceId(Long placeId);
|
||||
List<IventEntity> findByTypeIdAndPlaceId(Long typeId, Long placeId);
|
||||
List<IventEntity> findAllByDateBetween(LocalDate startDate, LocalDate endDate);
|
||||
|
||||
Page<IventEntity> findByTypeId(Long typeId, Pageable pageable);
|
||||
Page<IventEntity> findByPlaceId(Long placeId, Pageable pageable);
|
||||
Page<IventEntity> findByTypeIdAndPlaceId(Long typeId, Long placeId, Pageable pageable);
|
||||
Page<IventEntity> findAllByDateBetween(LocalDate startDate, LocalDate endDate, Pageable pageable);
|
||||
}
|
||||
|
137
src/main/java/com/example/demo/ivents/service/IventService.java
Normal file
137
src/main/java/com/example/demo/ivents/service/IventService.java
Normal file
@ -0,0 +1,137 @@
|
||||
package com.example.demo.ivents.service;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
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.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.ivents.model.IventEntity;
|
||||
import com.example.demo.ivents.repository.IventRepository;
|
||||
import com.example.demo.types.repository.TypeRepository;
|
||||
import com.example.demo.places.repository.PlaceRepository;
|
||||
|
||||
|
||||
@Service
|
||||
public class IventService {
|
||||
private final TypeRepository typeRepository;
|
||||
private final PlaceRepository placeRepository;
|
||||
private final IventRepository repository;
|
||||
|
||||
public IventService(TypeRepository typeRepository, PlaceRepository placeRepository, IventRepository repository) {
|
||||
this.repository = repository;
|
||||
this.typeRepository = typeRepository;
|
||||
this.placeRepository = placeRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<IventEntity> getAll(Long typeId, Long placeId, LocalDate startDate, LocalDate endDate) {
|
||||
List<IventEntity> result;
|
||||
|
||||
if (startDate != null || endDate != null) {
|
||||
if (startDate != null && endDate != null) {
|
||||
result = StreamSupport.stream(repository.findAllByDateBetween(startDate, endDate).spliterator(), false).toList();
|
||||
} else if (startDate != null) {
|
||||
result = StreamSupport.stream(repository.findAllByDateBetween(startDate, LocalDate.now()).spliterator(), false).toList();
|
||||
} else {
|
||||
result = StreamSupport.stream(repository.findAllByDateBetween(LocalDate.MIN, endDate).spliterator(), false).toList();
|
||||
}
|
||||
} else {
|
||||
result = StreamSupport.stream(repository.findAll().spliterator(), false).toList();
|
||||
}
|
||||
|
||||
if (typeId != null && typeId != 0L) {
|
||||
if (placeId != null && placeId != 0L) {
|
||||
result.retainAll(repository.findByTypeIdAndPlaceId(typeId, placeId));
|
||||
} else {
|
||||
result.retainAll(repository.findByTypeId(typeId));
|
||||
}
|
||||
}
|
||||
|
||||
if (placeId != null && placeId != 0L) {
|
||||
if (typeId == null || typeId == 0L) {
|
||||
result.retainAll(repository.findByPlaceId(placeId));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Page<IventEntity> getAll(Long typeId, Long placeId, LocalDate startDate, LocalDate endDate, int page, int size) {
|
||||
final PageRequest pageable = PageRequest.of(page, size);
|
||||
Page<IventEntity> resultPage;
|
||||
|
||||
if (startDate != null || endDate != null) {
|
||||
if (startDate != null && endDate != null) {
|
||||
resultPage = repository.findAllByDateBetween(startDate, endDate, pageable);
|
||||
} else if (startDate != null) {
|
||||
resultPage = repository.findAllByDateBetween(startDate, LocalDate.now(), pageable);
|
||||
} else {
|
||||
resultPage = repository.findAllByDateBetween(LocalDate.MIN, endDate, pageable);
|
||||
}
|
||||
} else {
|
||||
resultPage = repository.findAll(pageable);
|
||||
}
|
||||
|
||||
if (typeId != null && typeId != 0L) {
|
||||
if (placeId != null && placeId != 0L) {
|
||||
resultPage = repository.findByTypeIdAndPlaceId(typeId, placeId, pageable);
|
||||
} else {
|
||||
resultPage = repository.findByTypeId(typeId, pageable);
|
||||
}
|
||||
}
|
||||
|
||||
if (placeId != null && placeId != 0L) {
|
||||
if (typeId == null || typeId == 0L) {
|
||||
resultPage = repository.findByPlaceId(placeId, pageable);
|
||||
}
|
||||
}
|
||||
|
||||
return resultPage;
|
||||
}
|
||||
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public IventEntity get(Long id) {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(IventEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public IventEntity create(IventEntity entity) {
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("Entity is null");
|
||||
}
|
||||
return repository.save(entity);
|
||||
}
|
||||
@Transactional
|
||||
public IventEntity update(Long id, IventEntity entity) {
|
||||
final IventEntity existsEntity = get(id);
|
||||
existsEntity.setName(entity.getName());
|
||||
existsEntity.setType(entity.getType());
|
||||
existsEntity.setDate(entity.getDate());
|
||||
existsEntity.setDesc(entity.getDesc());
|
||||
existsEntity.setOther(entity.getOther());
|
||||
existsEntity.setPrice(entity.getPrice());
|
||||
existsEntity.setCount(entity.getCount());
|
||||
existsEntity.setPlace(entity.getPlace());
|
||||
existsEntity.setImage(entity.getImage());
|
||||
return repository.save(existsEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public IventEntity delete(Long id) {
|
||||
final IventEntity existsEntity = get(id);
|
||||
repository.delete(existsEntity);
|
||||
return existsEntity;
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package com.example.demo.orders.api;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class ExpandedOrderIventDto extends OrderIventDto {
|
||||
private String iventName;
|
||||
private LocalDate iventDate;
|
||||
private Double iventPrice;
|
||||
|
||||
public String getIventName() {
|
||||
return iventName;
|
||||
}
|
||||
|
||||
public void setIventName(String name) {
|
||||
this.iventName = name;
|
||||
}
|
||||
|
||||
public LocalDate getIventDate() {
|
||||
return iventDate;
|
||||
}
|
||||
|
||||
public void setIventName(LocalDate date) {
|
||||
this.iventDate= date;
|
||||
}
|
||||
|
||||
public Double getIventPrice() {
|
||||
return iventPrice;
|
||||
}
|
||||
|
||||
public void setIventPrice(Double price) {
|
||||
this.iventPrice = price;
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
package com.example.demo.orders.api;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
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.orders.model.OrderEntity;
|
||||
import com.example.demo.orders.model.OrderIventEntity;
|
||||
import com.example.demo.orders.service.OrderIventService;
|
||||
import com.example.demo.orders.service.OrderService;
|
||||
import com.example.demo.ivents.model.IventEntity;
|
||||
import com.example.demo.ivents.service.IventService;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(OrderController.URL)
|
||||
public class OrderController {
|
||||
public static final String URL = "/orders";
|
||||
private static final String ORDER_VIEW = "orders";
|
||||
private static final String ORDER_DETAILS_VIEW = "order-details";
|
||||
|
||||
private static final String ORDER_ATTRIBUTE = "order";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
|
||||
private final OrderService orderService;
|
||||
private final OrderIventService orderIventService;
|
||||
private final ModelMapper modelMapper;
|
||||
private final IventService iventService;
|
||||
|
||||
public OrderController(
|
||||
OrderService orderService,
|
||||
OrderIventService orderIventService,
|
||||
IventService iventService,
|
||||
ModelMapper modelMapper) {
|
||||
this.orderService = orderService;
|
||||
this.iventService = iventService;
|
||||
this.orderIventService = orderIventService;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
private OrderDto toDto(OrderEntity entity) {
|
||||
return modelMapper.map(entity, OrderDto.class);
|
||||
}
|
||||
|
||||
private ExpandedOrderIventDto toOrderIventDto(OrderIventEntity entity) {
|
||||
ExpandedOrderIventDto dto = modelMapper.map(entity, ExpandedOrderIventDto.class);
|
||||
IventEntity ivent = iventService.get(dto.getIventId());
|
||||
dto.setIventName(ivent.getName());
|
||||
dto.setIventPrice(ivent.getPrice());
|
||||
return dto;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getProfile(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
final long userId = principal.getId();
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
model.addAllAttributes(PageAttributesMapper.toAttributes(
|
||||
orderService.getAll(userId, page, Constants.DEFAULT_PAGE_SIZE),
|
||||
this::toDto));
|
||||
return ORDER_VIEW;
|
||||
}
|
||||
|
||||
@GetMapping("/details/{id}")
|
||||
public String getOrderDetails(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
final long userId = principal.getId();
|
||||
model.addAttribute(ORDER_ATTRIBUTE, orderService.get(userId, id));
|
||||
model.addAllAttributes(PageAttributesMapper.toAttributes(
|
||||
orderIventService.getAll(id, page, Constants.DEFAULT_PAGE_SIZE),
|
||||
this::toOrderIventDto));
|
||||
return ORDER_DETAILS_VIEW;
|
||||
}
|
||||
}
|
46
src/main/java/com/example/demo/orders/api/OrderDto.java
Normal file
46
src/main/java/com/example/demo/orders/api/OrderDto.java
Normal file
@ -0,0 +1,46 @@
|
||||
package com.example.demo.orders.api;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.cglib.core.Local;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class OrderDto {
|
||||
private Long id;
|
||||
|
||||
private LocalDate date;
|
||||
|
||||
@NotNull
|
||||
@Size(min = 1)
|
||||
private List<OrderIventDto> orderIvents;
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<OrderIventDto> getOrderIvents() {
|
||||
return orderIvents;
|
||||
}
|
||||
|
||||
public void setOrderIvents(List<OrderIventDto> orderIvents) {
|
||||
this.orderIvents = orderIvents;
|
||||
}
|
||||
|
||||
public LocalDate getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(LocalDate date) {
|
||||
this.date = date;
|
||||
}
|
||||
}
|
43
src/main/java/com/example/demo/orders/api/OrderIventDto.java
Normal file
43
src/main/java/com/example/demo/orders/api/OrderIventDto.java
Normal file
@ -0,0 +1,43 @@
|
||||
package com.example.demo.orders.api;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
public class OrderIventDto {
|
||||
private Long id;
|
||||
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Long iventId;
|
||||
|
||||
@NotNull
|
||||
@Min(1)
|
||||
private Integer quantity;
|
||||
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getIventId() {
|
||||
return iventId;
|
||||
}
|
||||
|
||||
public void setIventId(Long iventId) {
|
||||
this.iventId = iventId;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(Integer quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.example.demo.orders.model;
|
||||
|
||||
public class ExpandedOrderEntity extends OrderEntity {
|
||||
private Double total;
|
||||
|
||||
public ExpandedOrderEntity(OrderEntity order, Double total) {
|
||||
// Вызов конструктора суперкласса для установки общих полей заказа
|
||||
super(order.getUser(), order.getDate());
|
||||
this.setId(order.getId()); // Устанавливаем идентификатор заказа
|
||||
this.total = total; // Устанавливаем общую сумму
|
||||
}
|
||||
|
||||
public Double getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(Double total) {
|
||||
this.total = total;
|
||||
}
|
||||
}
|
95
src/main/java/com/example/demo/orders/model/OrderEntity.java
Normal file
95
src/main/java/com/example/demo/orders/model/OrderEntity.java
Normal file
@ -0,0 +1,95 @@
|
||||
package com.example.demo.orders.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OrderBy;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "orders")
|
||||
public class OrderEntity extends BaseEntity {
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "userId", nullable = false)
|
||||
private UserEntity user;
|
||||
|
||||
@Column(nullable = false)
|
||||
private Date date = new Date();
|
||||
|
||||
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
|
||||
@OrderBy("id ASC")
|
||||
private List<OrderIventEntity> items = new ArrayList<>();
|
||||
|
||||
public OrderEntity() {
|
||||
}
|
||||
|
||||
public OrderEntity(UserEntity user, Date date) {
|
||||
this.user = user;
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public UserEntity getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public void setUser(UserEntity user) {
|
||||
this.user = user;
|
||||
if (!user.getOrders().contains(this)) {
|
||||
user.getOrders().add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public void setDate(Date date) {
|
||||
this.date = date;
|
||||
}
|
||||
|
||||
public List<OrderIventEntity> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<OrderIventEntity> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public void addItem(OrderIventEntity orderIvent) {
|
||||
if (orderIvent.getOrder() != this) {
|
||||
orderIvent.setOrder(this);
|
||||
}
|
||||
|
||||
items.add(orderIvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, user, items, date);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
final OrderEntity other = (OrderEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getItems(), items)
|
||||
&& Objects.equals(other.getDate(), date)
|
||||
&& Objects.equals(other.getUser(), user);
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package com.example.demo.orders.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
import com.example.demo.ivents.model.IventEntity;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "orderIvents")
|
||||
public class OrderIventEntity extends BaseEntity {
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "iventId", nullable = false)
|
||||
private IventEntity ivent;
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "orderId", nullable = false)
|
||||
private OrderEntity order;
|
||||
@Column(nullable = false)
|
||||
private Integer quantity;
|
||||
|
||||
public OrderIventEntity() {
|
||||
}
|
||||
|
||||
public OrderIventEntity(IventEntity ivent, Integer quantity) {
|
||||
this.ivent = ivent;
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
public OrderEntity getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
public void setOrder(OrderEntity order) {
|
||||
this.order = order;
|
||||
if (!order.getItems().contains(this)) {
|
||||
order.getItems().add(this);
|
||||
}
|
||||
}
|
||||
|
||||
public IventEntity getIvent() {
|
||||
return ivent;
|
||||
}
|
||||
|
||||
public void setIvent(IventEntity ivent) {
|
||||
this.ivent = ivent;
|
||||
}
|
||||
|
||||
public Integer getQuantity() {
|
||||
return quantity;
|
||||
}
|
||||
|
||||
public void setQuantity(Integer quantity) {
|
||||
this.quantity = quantity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, order, ivent, quantity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
final OrderIventEntity other = (OrderIventEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getOrder(), order)
|
||||
&& Objects.equals(other.getIvent(), ivent)
|
||||
&& Objects.equals(other.getQuantity(), quantity);
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.example.demo.orders.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
import com.example.demo.orders.model.OrderIventEntity;
|
||||
|
||||
public interface OrderIventRepository extends CrudRepository<OrderIventEntity, Long>,
|
||||
PagingAndSortingRepository<OrderIventEntity, Long> {
|
||||
List<OrderIventEntity> findByOrderId(long orderId);
|
||||
|
||||
Page<OrderIventEntity> findByOrderId(long orderId, Pageable pageable);
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package com.example.demo.orders.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 org.springframework.data.repository.query.Param;
|
||||
|
||||
import com.example.demo.orders.model.ExpandedOrderEntity;
|
||||
import com.example.demo.orders.model.OrderEntity;
|
||||
|
||||
public interface OrderRepository extends CrudRepository<OrderEntity, Long>,
|
||||
PagingAndSortingRepository<OrderEntity, Long> {
|
||||
List<OrderEntity> findByUserId(long userId);
|
||||
|
||||
Page<OrderEntity> findByUserId(long userId, Pageable pageable);
|
||||
|
||||
Optional<OrderEntity> findByUserIdAndId(long userId, long id);
|
||||
|
||||
@Query("SELECT NEW com.example.demo.orders.model.ExpandedOrderEntity(o, SUM(oi.quantity * p.price)) " +
|
||||
"FROM OrderEntity o " +
|
||||
"JOIN o.items oi " +
|
||||
"JOIN oi.ivent p " +
|
||||
"WHERE o.id = :orderId " +
|
||||
"GROUP BY o")
|
||||
ExpandedOrderEntity findOrderWithTotal(@Param("orderId") Long orderId);
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package com.example.demo.orders.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
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.orders.model.OrderIventEntity;
|
||||
import com.example.demo.orders.repository.OrderIventRepository;
|
||||
|
||||
@Service
|
||||
public class OrderIventService {
|
||||
private final OrderIventRepository repository;
|
||||
|
||||
public OrderIventService(OrderIventRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<OrderIventEntity> getAll(Long orderId) {
|
||||
return repository.findByOrderId(orderId);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<OrderIventEntity> getAll(long orderId, int page, int size) {
|
||||
final Pageable pageable = PageRequest.of(page, size, Sort.by("id"));
|
||||
return repository.findByOrderId(orderId, pageable);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public OrderIventEntity get(Long id) {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(OrderIventEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public List<OrderIventEntity> createMany(List<OrderIventEntity> orderIvents) {
|
||||
repository.saveAll(orderIvents);
|
||||
return orderIvents;
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package com.example.demo.orders.service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
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.orders.api.OrderIventDto;
|
||||
import com.example.demo.orders.model.ExpandedOrderEntity;
|
||||
import com.example.demo.orders.model.OrderEntity;
|
||||
import com.example.demo.orders.model.OrderIventEntity;
|
||||
import com.example.demo.orders.repository.OrderRepository;
|
||||
import com.example.demo.ivents.service.IventService;
|
||||
import com.example.demo.users.service.UserService;
|
||||
|
||||
@Service
|
||||
public class OrderService {
|
||||
private final UserService userService;
|
||||
private final OrderRepository repository;
|
||||
private final OrderIventService orderIventService;
|
||||
private final IventService iventService;
|
||||
|
||||
public OrderService(OrderRepository repository,
|
||||
OrderIventService orderIventService,
|
||||
UserService userService, IventService iventService) {
|
||||
this.repository = repository;
|
||||
this.orderIventService = orderIventService;
|
||||
this.userService = userService;
|
||||
this.iventService = iventService;
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<OrderEntity> getAll(Long userId) {
|
||||
userService.get(userId);
|
||||
return repository.findByUserId(userId);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<OrderEntity> getAll(long userId, int page, int size) {
|
||||
final Pageable pageable = PageRequest.of(page, size, Sort.by("id"));
|
||||
userService.get(userId);
|
||||
return repository.findByUserId(userId, pageable);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public ExpandedOrderEntity get(Long userId, Long id) {
|
||||
userService.get(userId);
|
||||
return repository.findOrderWithTotal(id);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public OrderEntity create(Long userId, List<OrderIventDto> list) {
|
||||
OrderEntity entity = new OrderEntity(userService.get(userId), new Date());
|
||||
|
||||
List<OrderIventEntity> orderIvents = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
OrderIventEntity oie = new OrderIventEntity(iventService.get(list.get(i)
|
||||
.getIventId()),
|
||||
list.get(i).getQuantity());
|
||||
oie.setOrder(entity);
|
||||
orderIvents.add(oie);
|
||||
}
|
||||
|
||||
entity.setItems(orderIvents);
|
||||
|
||||
OrderEntity order = repository.save(entity);
|
||||
orderIventService.createMany(order.getItems());
|
||||
|
||||
return order;
|
||||
}
|
||||
}
|
115
src/main/java/com/example/demo/places/api/PlaceController.java
Normal file
115
src/main/java/com/example/demo/places/api/PlaceController.java
Normal file
@ -0,0 +1,115 @@
|
||||
package com.example.demo.places.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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.DeleteMapping;
|
||||
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.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import com.example.demo.core.api.PageAttributesMapper;
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.places.model.PlaceEntity;
|
||||
import com.example.demo.places.service.PlaceService;
|
||||
import com.example.demo.types.api.TypeDto;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(PlaceController.URL)
|
||||
public class PlaceController {
|
||||
public static final String URL = Constants.ADMIN_PREFIX + "/place";
|
||||
public static final String PLACE_VIEW = "place";
|
||||
public static final String PLACE_EDIT_VIEW = "place-edit";
|
||||
public static final String PLACE_ATTRIBUTE = "place";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
|
||||
private final PlaceService placeService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public PlaceController(PlaceService placeService, ModelMapper modelMapper) {
|
||||
this.placeService = placeService;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
private PlaceDto toDto(PlaceEntity entity) {
|
||||
return modelMapper.map(entity, PlaceDto.class);
|
||||
}
|
||||
|
||||
private PlaceEntity toEntity(PlaceDto dto) {
|
||||
return modelMapper.map(dto, PlaceEntity.class);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getAll(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
final Map<String, Object> attributes = PageAttributesMapper.toAttributes(
|
||||
placeService.getAll(page, Constants.DEFAULT_PAGE_SIZE), this::toDto);
|
||||
model.addAllAttributes(attributes);
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return PLACE_VIEW;
|
||||
}
|
||||
|
||||
@GetMapping("/edit/")
|
||||
public String create(Model model) {
|
||||
model.addAttribute(PLACE_ATTRIBUTE, new PlaceDto());
|
||||
return PLACE_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/")
|
||||
public String create(
|
||||
@ModelAttribute(name = PLACE_ATTRIBUTE) @Valid PlaceDto place,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return PLACE_EDIT_VIEW;
|
||||
}
|
||||
|
||||
placeService.create(toEntity(place));
|
||||
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(PLACE_ATTRIBUTE, toDto(placeService.get(id)));
|
||||
return PLACE_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@ModelAttribute(name = PLACE_ATTRIBUTE) @Valid PlaceDto place,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return PLACE_EDIT_VIEW;
|
||||
}
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
placeService.update(id, toEntity(place));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String delete(
|
||||
@PathVariable(name = "id") Long id) {
|
||||
placeService.delete(id);
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
}
|
||||
|
26
src/main/java/com/example/demo/places/api/PlaceDto.java
Normal file
26
src/main/java/com/example/demo/places/api/PlaceDto.java
Normal file
@ -0,0 +1,26 @@
|
||||
package com.example.demo.places.api;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class PlaceDto {
|
||||
private Long id;
|
||||
@NotBlank
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
49
src/main/java/com/example/demo/places/model/PlaceEntity.java
Normal file
49
src/main/java/com/example/demo/places/model/PlaceEntity.java
Normal file
@ -0,0 +1,49 @@
|
||||
package com.example.demo.places.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 = "places")
|
||||
public class PlaceEntity extends BaseEntity {
|
||||
@Column(nullable = false, unique = true)
|
||||
private String name;
|
||||
|
||||
public PlaceEntity() {
|
||||
}
|
||||
|
||||
public PlaceEntity(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 PlaceEntity other = (PlaceEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getName(), name);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
package com.example.demo.places.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
import com.example.demo.places.model.PlaceEntity;
|
||||
|
||||
public interface PlaceRepository extends CrudRepository<PlaceEntity, Long>, PagingAndSortingRepository<PlaceEntity, Long> {
|
||||
Optional<PlaceEntity> findByNameIgnoreCase(String name);
|
||||
}
|
||||
|
@ -0,0 +1,71 @@
|
||||
package com.example.demo.places.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.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.places.model.PlaceEntity;
|
||||
import com.example.demo.places.repository.PlaceRepository;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
|
||||
@Service
|
||||
public class PlaceService {
|
||||
private final PlaceRepository repository;
|
||||
|
||||
public PlaceService(PlaceRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
private void checkName(String name) {
|
||||
if (repository.findByNameIgnoreCase(name).isPresent()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Place with name %s is already exists", name));
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<PlaceEntity> getAll() {
|
||||
return StreamSupport.stream(repository.findAll().spliterator(), false).toList();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<PlaceEntity> getAll(int page, int size) {
|
||||
return repository.findAll(PageRequest.of(page, size, Sort.by("id")));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public PlaceEntity get(Long id) {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(PlaceEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public PlaceEntity create(PlaceEntity entity) {
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("Entity is null");
|
||||
}
|
||||
checkName(entity.getName());
|
||||
return repository.save(entity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public PlaceEntity update(Long id, PlaceEntity entity) {
|
||||
final PlaceEntity existsEntity = get(id);
|
||||
existsEntity.setName(entity.getName());
|
||||
return repository.save(existsEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public PlaceEntity delete(Long id) {
|
||||
final PlaceEntity existsEntity = get(id);
|
||||
repository.delete(existsEntity);
|
||||
return existsEntity;
|
||||
}
|
||||
}
|
||||
|
114
src/main/java/com/example/demo/types/api/TypeController.java
Normal file
114
src/main/java/com/example/demo/types/api/TypeController.java
Normal file
@ -0,0 +1,114 @@
|
||||
package com.example.demo.types.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
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.DeleteMapping;
|
||||
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.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
|
||||
import com.example.demo.core.api.PageAttributesMapper;
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(TypeController.URL)
|
||||
public class TypeController {
|
||||
public static final String URL = Constants.ADMIN_PREFIX + "/type";
|
||||
public static final String CATEGORY_VIEW = "type";
|
||||
public static final String CATEGORY_EDIT_VIEW = "type-edit";
|
||||
public static final String CATEGORY_ATTRIBUTE = "type";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
|
||||
private final TypeService typeService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public TypeController(TypeService typeService, ModelMapper modelMapper) {
|
||||
this.typeService = typeService;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
private TypeDto toDto(TypeEntity entity) {
|
||||
return modelMapper.map(entity, TypeDto.class);
|
||||
}
|
||||
|
||||
private TypeEntity toEntity(TypeDto dto) {
|
||||
return modelMapper.map(dto, TypeEntity.class);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getAll(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
final Map<String, Object> attributes = PageAttributesMapper.toAttributes(
|
||||
typeService.getAll(page, Constants.DEFAULT_PAGE_SIZE), this::toDto);
|
||||
model.addAllAttributes(attributes);
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return CATEGORY_VIEW;
|
||||
}
|
||||
|
||||
@GetMapping("/edit/")
|
||||
public String create(Model model) {
|
||||
model.addAttribute(CATEGORY_ATTRIBUTE, new TypeDto());
|
||||
return CATEGORY_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/")
|
||||
public String create(
|
||||
@ModelAttribute(name = CATEGORY_ATTRIBUTE) @Valid TypeDto type,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return CATEGORY_EDIT_VIEW;
|
||||
}
|
||||
|
||||
typeService.create(toEntity(type));
|
||||
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(CATEGORY_ATTRIBUTE, toDto(typeService.get(id)));
|
||||
return CATEGORY_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@ModelAttribute(name = CATEGORY_ATTRIBUTE) @Valid TypeDto type,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return CATEGORY_EDIT_VIEW;
|
||||
}
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
typeService.update(id, toEntity(type));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String delete(
|
||||
@PathVariable(name = "id") Long id) {
|
||||
typeService.delete(id);
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
}
|
||||
|
26
src/main/java/com/example/demo/types/api/TypeDto.java
Normal file
26
src/main/java/com/example/demo/types/api/TypeDto.java
Normal file
@ -0,0 +1,26 @@
|
||||
package com.example.demo.types.api;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public class TypeDto {
|
||||
private Long id;
|
||||
@NotBlank
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
49
src/main/java/com/example/demo/types/model/TypeEntity.java
Normal file
49
src/main/java/com/example/demo/types/model/TypeEntity.java
Normal file
@ -0,0 +1,49 @@
|
||||
package com.example.demo.types.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 = "types")
|
||||
public class TypeEntity extends BaseEntity {
|
||||
@Column(nullable = false, unique = true)
|
||||
private String name;
|
||||
|
||||
public TypeEntity() {
|
||||
}
|
||||
|
||||
public TypeEntity(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 TypeEntity other = (TypeEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getName(), name);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
package com.example.demo.types.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
|
||||
public interface TypeRepository extends CrudRepository<TypeEntity, Long>, PagingAndSortingRepository<TypeEntity, Long> {
|
||||
Optional<TypeEntity> findByNameIgnoreCase(String name);
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
package com.example.demo.types.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.Sort;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.repository.TypeRepository;
|
||||
|
||||
@Service
|
||||
public class TypeService {
|
||||
private final TypeRepository repository;
|
||||
|
||||
public TypeService(TypeRepository 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<TypeEntity> getAll() {
|
||||
return StreamSupport.stream(repository.findAll().spliterator(), false).toList();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<TypeEntity> getAll(int page, int size) {
|
||||
return repository.findAll(PageRequest.of(page, size, Sort.by("id")));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public TypeEntity get(Long id) {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(TypeEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public TypeEntity create(TypeEntity entity) {
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("Entity is null");
|
||||
}
|
||||
checkName(entity.getName());
|
||||
return repository.save(entity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public TypeEntity update(Long id, TypeEntity entity) {
|
||||
final TypeEntity existsEntity = get(id);
|
||||
existsEntity.setName(entity.getName());
|
||||
return repository.save(existsEntity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public TypeEntity delete(Long id) {
|
||||
final TypeEntity existsEntity = get(id);
|
||||
repository.delete(existsEntity);
|
||||
return existsEntity;
|
||||
}
|
||||
}
|
||||
|
132
src/main/java/com/example/demo/users/api/UserController.java
Normal file
132
src/main/java/com/example/demo/users/api/UserController.java
Normal file
@ -0,0 +1,132 @@
|
||||
package com.example.demo.users.api;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.modelmapper.ModelMapper;
|
||||
import org.springframework.data.domain.jaxb.SpringDataJaxb.PageDto;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
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.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
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.users.model.UserEntity;
|
||||
import com.example.demo.users.service.UserService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(UserController.URL)
|
||||
public class UserController {
|
||||
public static final String URL = Constants.ADMIN_PREFIX + "/user";
|
||||
private static final String USER_VIEW = "user";
|
||||
private static final String USER_EDIT_VIEW = "user-edit";
|
||||
private static final String USER_CREATE_VIEW = "user-create";
|
||||
private static final String PAGE_ATTRIBUTE = "page";
|
||||
private static final String USER_ATTRIBUTE = "user";
|
||||
|
||||
private final UserService userService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public UserController(UserService userService, ModelMapper modelMapper) {
|
||||
this.userService = userService;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
private UserDto toDto(UserEntity entity) {
|
||||
return modelMapper.map(entity, UserDto.class);
|
||||
}
|
||||
|
||||
private UserEntity toEntity(UserDto dto) {
|
||||
return modelMapper.map(dto, UserEntity.class);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getAll(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
final Map<String, Object> attributes = PageAttributesMapper.toAttributes(
|
||||
userService.getAll(page, Constants.DEFAULT_PAGE_SIZE), this::toDto);
|
||||
model.addAllAttributes(attributes);
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USER_VIEW;
|
||||
}
|
||||
|
||||
@GetMapping("/create/")
|
||||
public String create(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
model.addAttribute(USER_ATTRIBUTE, new UserDto());
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USER_CREATE_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/create/")
|
||||
public String create(
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
@ModelAttribute(name = USER_ATTRIBUTE) @Valid UserDto user,
|
||||
BindingResult bindingResult,
|
||||
Model model,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USER_CREATE_VIEW;
|
||||
}
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
userService.create(toEntity(user));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@GetMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
Model model) {
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
model.addAttribute(USER_ATTRIBUTE, toDto(userService.get(id)));
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USER_EDIT_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping("/edit/{id}")
|
||||
public String update(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
@ModelAttribute(name = USER_ATTRIBUTE) @Valid UserDto user,
|
||||
BindingResult bindingResult,
|
||||
Model model,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
model.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
return USER_EDIT_VIEW;
|
||||
}
|
||||
if (id <= 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
userService.update(id, toEntity(user));
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public String delete(
|
||||
@PathVariable(name = "id") Long id,
|
||||
@RequestParam(name = PAGE_ATTRIBUTE, defaultValue = "0") int page,
|
||||
RedirectAttributes redirectAttributes) {
|
||||
redirectAttributes.addAttribute(PAGE_ATTRIBUTE, page);
|
||||
userService.delete(id);
|
||||
return Constants.REDIRECT_VIEW + URL;
|
||||
}
|
||||
}
|
53
src/main/java/com/example/demo/users/api/UserDto.java
Normal file
53
src/main/java/com/example/demo/users/api/UserDto.java
Normal file
@ -0,0 +1,53 @@
|
||||
package com.example.demo.users.api;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class UserDto {
|
||||
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
|
||||
private Long id;
|
||||
@NotBlank
|
||||
private String handle;
|
||||
@NotBlank
|
||||
private String email;
|
||||
@NotBlank
|
||||
private String password;
|
||||
@NotBlank
|
||||
@Size(min = 1, max = 20)
|
||||
private String accessLevel;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
public void setHandle(String handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,65 @@
|
||||
package com.example.demo.users.api;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
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.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.service.UserService;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
@Controller
|
||||
@RequestMapping(UserSignupController.URL)
|
||||
public class UserSignupController {
|
||||
public static final String URL = "/signup";
|
||||
|
||||
private static final String SIGNUP_VIEW = "signup";
|
||||
private static final String USER_ATTRIBUTE = "user";
|
||||
|
||||
private final UserService userService;
|
||||
private final ModelMapper modelMapper;
|
||||
|
||||
public UserSignupController(
|
||||
UserService userService,
|
||||
ModelMapper modelMapper) {
|
||||
this.userService = userService;
|
||||
this.modelMapper = modelMapper;
|
||||
}
|
||||
|
||||
private UserEntity toEntity(UserSignupDto dto) {
|
||||
return modelMapper.map(dto, UserEntity.class);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String getSignup(Model model) {
|
||||
model.addAttribute(USER_ATTRIBUTE, new UserSignupDto());
|
||||
return SIGNUP_VIEW;
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public String signup(
|
||||
@ModelAttribute(name = USER_ATTRIBUTE) @Valid UserSignupDto user,
|
||||
BindingResult bindingResult,
|
||||
Model model) {
|
||||
if (bindingResult.hasErrors()) {
|
||||
return SIGNUP_VIEW;
|
||||
}
|
||||
if (!Objects.equals(user.getPassword(), user.getPasswordConfirm())) {
|
||||
bindingResult.rejectValue("password", "signup:passwords", "Пароли не совпадают.");
|
||||
model.addAttribute(USER_ATTRIBUTE, user);
|
||||
return SIGNUP_VIEW;
|
||||
}
|
||||
userService.create(toEntity(user));
|
||||
return Constants.REDIRECT_VIEW + Constants.LOGIN_URL + "?signup";
|
||||
}
|
||||
|
||||
}
|
39
src/main/java/com/example/demo/users/api/UserSignupDto.java
Normal file
39
src/main/java/com/example/demo/users/api/UserSignupDto.java
Normal file
@ -0,0 +1,39 @@
|
||||
package com.example.demo.users.api;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class UserSignupDto {
|
||||
@NotBlank
|
||||
private String handle;
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 20)
|
||||
private String password;
|
||||
@NotBlank
|
||||
@Size(min = 3, max = 20)
|
||||
private String passwordConfirm;
|
||||
|
||||
public String getLogin() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
public void setLogin(String handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getPasswordConfirm() {
|
||||
return passwordConfirm;
|
||||
}
|
||||
|
||||
public void setPasswordConfirm(String passwordConfirm) {
|
||||
this.passwordConfirm = passwordConfirm;
|
||||
}
|
||||
}
|
105
src/main/java/com/example/demo/users/model/UserEntity.java
Normal file
105
src/main/java/com/example/demo/users/model/UserEntity.java
Normal file
@ -0,0 +1,105 @@
|
||||
package com.example.demo.users.model;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.OrderBy;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import com.example.demo.core.model.BaseEntity;
|
||||
import com.example.demo.orders.model.OrderEntity;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class UserEntity extends BaseEntity {
|
||||
@Column(nullable = false, unique = true)
|
||||
private String handle;
|
||||
@Column(nullable = false, unique = true)
|
||||
private String email;
|
||||
@Column(nullable = false)
|
||||
private String password;
|
||||
|
||||
private UserRole role;
|
||||
|
||||
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
|
||||
@OrderBy("id ASC")
|
||||
private Set<OrderEntity> orders = new HashSet<>();
|
||||
|
||||
public UserEntity() {
|
||||
}
|
||||
|
||||
public UserEntity(String handle, String email, String password) {
|
||||
this.handle = handle;
|
||||
this.email = email;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getHandle() {
|
||||
return handle;
|
||||
}
|
||||
|
||||
public void setHandle(String handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public Set<OrderEntity> getOrders() {
|
||||
return orders;
|
||||
}
|
||||
|
||||
public UserRole getRole() {
|
||||
return role;
|
||||
}
|
||||
|
||||
public void setRole(UserRole role) {
|
||||
this.role = role;
|
||||
}
|
||||
|
||||
public void addOrder(OrderEntity order) {
|
||||
if (order.getUser() != this) {
|
||||
order.setUser(this);
|
||||
}
|
||||
orders.add(order);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, handle, password, orders, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null || getClass() != obj.getClass())
|
||||
return false;
|
||||
final UserEntity other = (UserEntity) obj;
|
||||
return Objects.equals(other.getId(), id)
|
||||
&& Objects.equals(other.getHandle(), handle)
|
||||
&& Objects.equals(other.getPassword(), password)
|
||||
&& Objects.equals(other.getRole(), role)
|
||||
&& Objects.equals(other.getOrders(), orders);
|
||||
}
|
||||
|
||||
}
|
||||
|
15
src/main/java/com/example/demo/users/model/UserRole.java
Normal file
15
src/main/java/com/example/demo/users/model/UserRole.java
Normal file
@ -0,0 +1,15 @@
|
||||
package com.example.demo.users.model;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
public enum UserRole implements GrantedAuthority {
|
||||
USER,
|
||||
ADMIN;
|
||||
|
||||
private static final String PREFIX = "ROLE_";
|
||||
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return PREFIX + this.name();
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.example.demo.users.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
|
||||
public interface UserRepository
|
||||
extends CrudRepository<UserEntity, Long>, PagingAndSortingRepository<UserEntity, Long> {
|
||||
Optional<UserEntity> findByHandleIgnoreCase(String handle);
|
||||
}
|
||||
|
103
src/main/java/com/example/demo/users/service/UserService.java
Normal file
103
src/main/java/com/example/demo/users/service/UserService.java
Normal file
@ -0,0 +1,103 @@
|
||||
package com.example.demo.users.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.example.demo.core.configuration.Constants;
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.core.security.UserPrincipal;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.model.UserRole;
|
||||
import com.example.demo.users.repository.UserRepository;
|
||||
|
||||
@Service
|
||||
public class UserService implements UserDetailsService {
|
||||
private final UserRepository repository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public UserService(UserRepository repository, PasswordEncoder passwordEncoder) {
|
||||
this.repository = repository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
private void checkHandle(String handle) {
|
||||
if (repository.findByHandleIgnoreCase(handle).isPresent()) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("User with login %s is already exists", handle));
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<UserEntity> getAll() {
|
||||
return StreamSupport.stream(repository.findAll().spliterator(), false).toList();
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<UserEntity> getAll(int page, int size) {
|
||||
return repository.findAll(PageRequest.of(page, size));
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public UserEntity get(long id) {
|
||||
return repository.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException(UserEntity.class, id));
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public UserEntity create(UserEntity entity) {
|
||||
if (entity == null) {
|
||||
throw new IllegalArgumentException("Entity is null");
|
||||
}
|
||||
checkHandle(entity.getHandle());
|
||||
final String password = Optional.ofNullable(entity.getPassword()).orElse("");
|
||||
entity.setPassword(
|
||||
passwordEncoder.encode(
|
||||
StringUtils.hasText(password.strip()) ? password : Constants.DEFAULT_PASSWORD));
|
||||
entity.setRole(Optional.ofNullable(entity.getRole()).orElse(UserRole.USER));
|
||||
repository.save(entity);
|
||||
return repository.save(entity);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public UserEntity update(long id, UserEntity entity) {
|
||||
final UserEntity existsEntity = get(id);
|
||||
existsEntity.setHandle(entity.getHandle());
|
||||
existsEntity.setEmail(entity.getEmail());
|
||||
existsEntity.setPassword(entity.getPassword());
|
||||
repository.save(existsEntity);
|
||||
return existsEntity;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public UserEntity delete(long id) {
|
||||
final UserEntity existsEntity = get(id);
|
||||
repository.delete(existsEntity);
|
||||
return existsEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
final UserEntity existsUser = getByHandle(username);
|
||||
return new UserPrincipal(existsUser);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public UserEntity getByHandle(String login) {
|
||||
return repository.findByHandleIgnoreCase(
|
||||
login)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Invalid handle"));
|
||||
}
|
||||
}
|
||||
|
@ -1 +1,20 @@
|
||||
# Server
|
||||
spring.main.banner-mode=off
|
||||
server.port=8081
|
||||
|
||||
# Logger settings
|
||||
# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
|
||||
logging.level.com.example.demo=DEBUG
|
||||
|
||||
# JPA Settings
|
||||
spring.datasource.url=jdbc:h2:file:./data
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=password
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.jpa.hibernate.ddl-auto=create
|
||||
spring.jpa.open-in-view=false
|
||||
# spring.jpa.show-sql=true
|
||||
# spring.jpa.properties.hibernate.format_sql=true
|
||||
|
||||
# H2 console
|
||||
spring.h2.console.enabled=true
|
BIN
src/main/resources/public/Placebo.jpg
Normal file
BIN
src/main/resources/public/Placebo.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
BIN
src/main/resources/public/Radiohead.jpg
Normal file
BIN
src/main/resources/public/Radiohead.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 71 KiB |
BIN
src/main/resources/public/Slipknot.jpg
Normal file
BIN
src/main/resources/public/Slipknot.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 69 KiB |
235
src/main/resources/public/css/style.css
Normal file
235
src/main/resources/public/css/style.css
Normal file
@ -0,0 +1,235 @@
|
||||
html,
|
||||
header nav {
|
||||
font-family: 'Correction Tape', sans-serif;
|
||||
background-color:#2F2727;
|
||||
color: #faebd7;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
header nav {
|
||||
height: 54px;
|
||||
}
|
||||
}
|
||||
|
||||
header nav a:hover {
|
||||
font-family: 'Correction Tape', sans-serif;
|
||||
text-decoration: overline rgb(255, 119, 0);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
body{
|
||||
background-color: #3E3333;
|
||||
font-family: 'Correction Tape', sans-serif;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
#container {
|
||||
gap: 20px;
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - 50px); /* Высота контейнера будет рассчитана как 100% высоты видимой области минус 20px */
|
||||
}
|
||||
|
||||
|
||||
.img-card {
|
||||
width: 330px; /* Замените значение на желаемый размер для ширины */
|
||||
height: 330px; /* Замените значение на желаемый размер для высоты */
|
||||
object-fit: cover; /* Этот стиль поможет вписать изображение в указанные размеры */
|
||||
}
|
||||
|
||||
.textForm {
|
||||
color: #faebd7;
|
||||
}
|
||||
|
||||
/*orders*/
|
||||
.ordersbody {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 5cm;
|
||||
}
|
||||
|
||||
.ordersmain {
|
||||
width: 66.6%; /* 2/3 от ширины страницы */
|
||||
margin-right: 1cm; /* отступ между блоками */
|
||||
}
|
||||
|
||||
.ordersform {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.orders {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.user-info {
|
||||
width: 33.3%; /* 1/3 от ширины страницы */
|
||||
}
|
||||
|
||||
|
||||
.card{
|
||||
margin:10px;
|
||||
padding:5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
footer {
|
||||
font-family: 'Correction Tape', sans-serif;
|
||||
background-color: #2F2727;
|
||||
color: #faebd7;
|
||||
padding-top: 20px;
|
||||
}
|
||||
.text-body-secondary {
|
||||
margin-bottom: 0.5em; /* Указывайте здесь нужное вам расстояние */
|
||||
}
|
||||
|
||||
.btn-primary{
|
||||
background-color: #ff5500;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.btn-primary:hover{
|
||||
background-color: #5233ff;
|
||||
color:aqua;
|
||||
}
|
||||
|
||||
.btn-secondary{
|
||||
background-color: #523352;
|
||||
color:#ff5500;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background-color: #006585;
|
||||
color:aqua;
|
||||
}
|
||||
|
||||
#remember-me:checked {
|
||||
background-color: #ff5500; /*изменяет фон чекбокса при выборе*/
|
||||
border-color: #5233ff; /*изменяет цвет рамки чекбокса*/
|
||||
}
|
||||
|
||||
/*видео,*/
|
||||
.video-foreground {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.video-foreground iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/*DEFAULT*/
|
||||
#menu-wrapper {
|
||||
position: fixed;
|
||||
top: 56px;
|
||||
left: -250px;
|
||||
width: 250px;
|
||||
height: 100%;
|
||||
background-color: #3E3333;
|
||||
transition: left 0.3s ease;
|
||||
z-index: 1;
|
||||
border-right: 3px solid #ff5500;
|
||||
border-top: 3px solid #523352; /* Добавляем белую границу справа */
|
||||
}
|
||||
|
||||
#menu-wrapper.open {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#menu {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#menu ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#menu li {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#menu a {
|
||||
text-decoration: none;
|
||||
color: #ff5500;
|
||||
font-size: 25px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#toggle-menu {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-left: 250px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*INDEX*/
|
||||
#video-container {
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
#video-container video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
#video-container .text-button-overlay {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center;
|
||||
color: #faebd7;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.text-button-overlay .text-overlay {
|
||||
font-size: 120px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text-button-overlay .underText-overlay {
|
||||
font-size: 50px;
|
||||
color: #faebd7;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.text-button-overlay .button-overlay {
|
||||
margin-top: 5%;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.button-overlay{
|
||||
background-color: #ff5500;
|
||||
border-color: #5233ff;
|
||||
border-radius: 4px;
|
||||
font-size: 25px;
|
||||
z-index: 1;
|
||||
}
|
||||
.button-overlay:hover {
|
||||
background-color: #5233ff;
|
||||
border-color: #ff5500;
|
||||
color:aqua;
|
||||
}
|
4
src/main/resources/public/favicon.svg
Normal file
4
src/main/resources/public/favicon.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-ticket-detailed" viewBox="0 0 16 16">
|
||||
<path d="M4 5.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5m0 5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5M5 7a1 1 0 0 0 0 2h6a1 1 0 1 0 0-2z"/>
|
||||
<path d="M0 4.5A1.5 1.5 0 0 1 1.5 3h13A1.5 1.5 0 0 1 16 4.5V6a.5.5 0 0 1-.5.5 1.5 1.5 0 0 0 0 3 .5.5 0 0 1 .5.5v1.5a1.5 1.5 0 0 1-1.5 1.5h-13A1.5 1.5 0 0 1 0 11.5V10a.5.5 0 0 1 .5-.5 1.5 1.5 0 1 0 0-3A.5.5 0 0 1 0 6zM1.5 4a.5.5 0 0 0-.5.5v1.05a2.5 2.5 0 0 1 0 4.9v1.05a.5.5 0 0 0 .5.5h13a.5.5 0 0 0 .5-.5v-1.05a2.5 2.5 0 0 1 0-4.9V4.5a.5.5 0 0 0-.5-.5z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 666 B |
BIN
src/main/resources/public/Мастер и Маргарита.jpg
Normal file
BIN
src/main/resources/public/Мастер и Маргарита.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 59 KiB |
BIN
src/main/resources/public/Сияние.jpg
Normal file
BIN
src/main/resources/public/Сияние.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 98 KiB |
64
src/main/resources/templates/cart.html
Normal file
64
src/main/resources/templates/cart.html
Normal file
@ -0,0 +1,64 @@
|
||||
<!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">
|
||||
<div class="d-flex flex-column align-items-center">
|
||||
<div class="mb-2 col-12 col-md-8 col-lg-6 d-flex align-items-center">
|
||||
<strong class="flex-fill">Корзина</strong>
|
||||
<form action="#" th:action="@{/cart/clear}" method="post">
|
||||
<button type="submit" class="btn btn-danger button-fixed-width"
|
||||
onclick="return confirm('Вы уверены?')">
|
||||
<i class="bi bi-x-lg"></i> Очистить
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card col-12 col-md-8 col-lg-6 align-items-center" th:each="cartItem : ${cart}">
|
||||
<div class="card-body col-12 p-2 d-flex flex-row align-items-center justify-content-center">
|
||||
<div class="col-9">
|
||||
[[${cartItem.iventName}]] ([[${#numbers.formatDecimal(cartItem.iventPrice, 1, 2)}]]) *
|
||||
[[${cartItem.quantity}]]
|
||||
=
|
||||
[[${#numbers.formatDecimal(cartItem.iventPrice * cartItem.quantity, 1, 2)}]]
|
||||
</div>
|
||||
<div class="col-3 d-flex justify-content-end">
|
||||
<div class="col-3 d-flex justify-content-end">
|
||||
<form action="#"
|
||||
th:action="@{/cart/toggle-quantity?iventId={iventId}&isIncrease=true(iventId=${cartItem.iventId})}"
|
||||
method="post">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-plus-lg"></i>
|
||||
</button>
|
||||
</form>
|
||||
<form action="#"
|
||||
th:action="@{/cart/toggle-quantity?iventId={iventId}&isIncrease=false(iventId=${cartItem.iventId})}"
|
||||
method="post">
|
||||
<button class="btn btn-danger">
|
||||
<i class="bi bi-dash-lg"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class=" mb-2 col-12 col-md-8 col-lg-6 d-flex justify-content-end">
|
||||
<!-- suda total -->
|
||||
</div>
|
||||
<div class="mb-2 col-12 col-md-8 col-lg-6 d-flex justify-content-center" th:if="${not #lists.isEmpty(cart)}">
|
||||
<form action="#" th:action="@{/cart/save}" method="post">
|
||||
<button type="submit" class="btn btn-primary" onclick="return confirm('Вы уверены?')">
|
||||
Оформить заказ
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
</html>
|
43
src/main/resources/templates/catalog.html
Normal file
43
src/main/resources/templates/catalog.html
Normal file
@ -0,0 +1,43 @@
|
||||
<!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">
|
||||
<div id="video-container">
|
||||
<div class="video-foreground">
|
||||
<iframe src="https://www.youtube.com/embed/XEEasR7hVhA?autoplay=1&controls=0&loop=1&showinfo=0&mute=1" allowfullscreen title="iframe_video"></iframe>
|
||||
</div>
|
||||
<div id="container" style="gap: 20px; overflow-y: auto; max-height: auto;">
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Данные отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<div class="mt-4" style="display: grid; grid-template-columns: repeat(4, 1fr)">
|
||||
<div class="card" th:each="ivent : ${items}">
|
||||
<script>
|
||||
var eventName = `[[${ivent.name}]]`;
|
||||
</script>
|
||||
<img class="img-card" th:src="@{'/' + ${ivent.name} + '.jpg'}" alt="s">
|
||||
<h5 class="card-title mb-0" th:text="${ivent.name}"></h5>
|
||||
<p class="card-text"><small class="text-body-secondary" th:text="${ivent.typeName}"></small></p>
|
||||
<p class="card-text"><small class="text-body-secondary" th:text="${ivent.placeName}"></small></p>
|
||||
<p class="card-text"><small class="text-body-secondary">[[${ivent.price}]] руб.</small></p>
|
||||
<form th:action="@{/buy?id={id}&isCartIvent={isCartIvent}(id=${ivent.id},isCartIvent=${ivent.isCartIvent})}" method="post">
|
||||
<button type="submit" class="btn btn-primary" th:text="'Добавить в корзину'"></button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url='',
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</th:block>
|
||||
</th:block>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
119
src/main/resources/templates/default.html
Normal file
119
src/main/resources/templates/default.html
Normal file
@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru" data-bs-theme="dark" xmlns:th="http://www.thymeleaf.org"
|
||||
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title layout:title-pattern="$LAYOUT_TITLE - $CONTENT_TITLE">TicketsBook</title>
|
||||
<script type="text/javascript" src="/webjars/bootstrap/5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<link rel="stylesheet" href="/webjars/bootstrap/5.3.3/dist/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="/webjars/bootstrap-icons/1.11.3/font/bootstrap-icons.min.css" />
|
||||
<link href="https://fonts.cdnfonts.com/css/correction-tape" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/css/style.css" />
|
||||
</head>
|
||||
|
||||
<body class="h-100 d-flex flex-column">
|
||||
<div id="menu-wrapper">
|
||||
<div id="menu">
|
||||
<ul class=" me-auto link" th:with="activeLink=${#objects.nullSafe(servletPath, '')}">
|
||||
<li>
|
||||
<a class="nav-link" href="/catalog"
|
||||
th:classappend="${activeLink.startsWith('/catalog') ? 'active' : ''}">
|
||||
◌ Каталог
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="/orders"
|
||||
th:classappend="${activeLink.startsWith('/orders') ? 'active' : ''}">
|
||||
◌ Личный кабинет
|
||||
</a>
|
||||
</li>
|
||||
<th:block sec:authorize="hasRole('ADMIN')">
|
||||
<li>
|
||||
<a class="nav-link" href="/admin/user"
|
||||
th:classappend="${activeLink.startsWith('/admin/user') ? 'active' : ''}">
|
||||
◌ Пользователи
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="/admin/type"
|
||||
th:classappend="${activeLink.startsWith('/admin/type') ? 'active' : ''}">
|
||||
◌ Типы
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="/admin/place"
|
||||
th:classappend="${activeLink.startsWith('/admin/place') ? 'active' : ''}">
|
||||
◌ Площадки
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="/admin/ivent"
|
||||
th:classappend="${activeLink.startsWith('/admin/ivent') ? 'active' : ''}">
|
||||
◌ Мероприятия
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="nav-link" href="/h2-console/" target="_blank">◌ Консоль H2</a>
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="navbar navbar-expand-md navbar-dark" data-bs-theme="dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" id="toggle-menu">
|
||||
<i class="bi bi-ticket-detailed" style="color:#faebd7;"onclick="toggleMenu()"></i>
|
||||
</a>
|
||||
<a class="navbar-brand" id="toggle-menu" style="color: #faebd7;" href="/">
|
||||
TicketsBook
|
||||
</a>
|
||||
<th:block sec:authorize="isAuthenticated()" th:with="userName=${#authentication.name}">
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#main-navbar"
|
||||
aria-controls="main-navbar" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="main-navbar">
|
||||
<ul class="navbar-nav me-auto link" th:with="activeLink=${#objects.nullSafe(servletPath, '')}">
|
||||
<a class="nav-link" href="/catalog"
|
||||
th:classappend="${activeLink.startsWith('/catalog') ? 'active' : ''}">
|
||||
Каталог
|
||||
</a>
|
||||
<a class="nav-link" href="/orders"
|
||||
th:classappend="${activeLink.startsWith('/orders') ? 'active' : ''}">
|
||||
Личный кабинет
|
||||
</a>
|
||||
</ul>
|
||||
<ul class="navbar-nav" th:if="${not #strings.isEmpty(userName)}">
|
||||
<form th:action="@{/logout}" method="post">
|
||||
<button type="submit" class="navbar-brand nav-link" style="color:#faebd7;" onclick="return confirm('Вы уверены?')">
|
||||
Выход ([[${userName}]])
|
||||
</button>
|
||||
</form>
|
||||
<a class="navbar-brand" style="color: #faebd7;" href="/cart">
|
||||
<i class="bi bi-bag-heart" style="color: #faebd7;"></i>
|
||||
[[${#numbers.formatDecimal(totalCart, 1, 2)}]] ₽
|
||||
</a>
|
||||
</ul>
|
||||
</div>
|
||||
</th:block>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="container-fluid" layout:fragment="content">
|
||||
</main>
|
||||
<footer class="footer mt-auto d-flex flex-shrink-0 justify-content-center align-items-center">
|
||||
Автор, [[${#dates.year(#dates.createNow())}]]
|
||||
</footer>
|
||||
<script>
|
||||
function toggleMenu() {
|
||||
var menuWrapper = document.getElementById("menu-wrapper");
|
||||
menuWrapper.classList.toggle("open");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
37
src/main/resources/templates/error.html
Normal file
37
src/main/resources/templates/error.html
Normal file
@ -0,0 +1,37 @@
|
||||
<!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">
|
||||
<ul class="list-group mb-2">
|
||||
<th:block th:if="${#strings.isEmpty(message)}">
|
||||
<li class="list-group-item">
|
||||
Неизвестная ошибка
|
||||
</li>
|
||||
</th:block>
|
||||
<th:block th:if="${not #strings.isEmpty(message)}">
|
||||
<li class="list-group-item">
|
||||
<strong>Ошибка:</strong> [[${message}]]
|
||||
</li>
|
||||
</th:block>
|
||||
<th:block th:if="${not #strings.isEmpty(url)}">
|
||||
<li class="list-group-item">
|
||||
<strong>Адрес:</strong> [[${url}]]
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<strong>Класс исключения:</strong> [[${exception}]]
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
[[${method}]] ([[${file}]]:[[${line}]])
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
<a class="btn btn-primary button-fixed-width" href="/">На главную</a>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
45
src/main/resources/templates/index.html
Normal file
45
src/main/resources/templates/index.html
Normal file
@ -0,0 +1,45 @@
|
||||
<!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">
|
||||
<div class="row">
|
||||
<div class="center">
|
||||
<div id="video-container">
|
||||
<div class="video-foreground">
|
||||
<iframe src="https://www.youtube.com/embed/UCCyoocDxBA?autoplay=1&controls=0&loop=1&showinfo=0&mute=1" allowfullscreen title="iframe_video"></iframe>
|
||||
</div>
|
||||
<div class="text-button-overlay">
|
||||
<div class="text-overlay">
|
||||
TicketsBook
|
||||
</div>
|
||||
<div class="underText-overlay">
|
||||
Сайт для покупки билетов
|
||||
</div>
|
||||
<div>
|
||||
<a class="btn button-overlay" href="/catalog">Начать покупку -></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var videos = [
|
||||
'https://www.youtube.com/embed/f0pdwd0miqs?autoplay=1&controls=0&loop=1&showinfo=0&mute=1',
|
||||
'https://www.youtube.com/embed/ijxk-fgcg7c?autoplay=1&controls=0&loop=1&showinfo=0&mute=1',
|
||||
'https://www.youtube.com/embed/UCCyoocDxBA?autoplay=1&controls=0&loop=1&showinfo=0&mute=1'
|
||||
];
|
||||
|
||||
const randomSource = videos[Math.floor(Math.random() * videos.length)];
|
||||
document.getElementById('w').src = randomSource;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
70
src/main/resources/templates/ivent-edit.html
Normal file
70
src/main/resources/templates/ivent-edit.html
Normal file
@ -0,0 +1,70 @@
|
||||
<!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/ivent/edit/{id}(id=${ivent.id})}" th:object="${ivent}" 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">
|
||||
<label for="date" class="form-label">Дата</label>
|
||||
<input type="text" th:field="*{date}" id="date" class="form-control" placeholder="yyyy-MM-dd">
|
||||
<div th:if="${#fields.hasErrors('date')}" th:errors="*{date}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Описание</label>
|
||||
<input type="text" th:field="*{description}" id="description" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('description')}" th:errors="*{description}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="other" class="form-label">Прочее</label>
|
||||
<input type="text" th:field="*{other}" id="other" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('other')}" th:errors="*{other}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="price" class="form-label">Цена</label>
|
||||
<input type="number" th:field="*{price}" id="price" class="form-control" min="1" step="1">
|
||||
<div th:if="${#fields.hasErrors('price')}" th:errors="*{price}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="typeId" class="form-label">Тип</label>
|
||||
<select th:field="*{typeId}" id="typeId" class="form-select">
|
||||
<option selected value="">Укажите категорию</option>
|
||||
<option th:each="type : ${types}" th:value="${type.id}">[[${type.name}]]</option>
|
||||
</select>
|
||||
<div th:if="${#fields.hasErrors('typeId')}" th:errors="*{typeId}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="placeId" class="form-label">Площадка</label>
|
||||
<select th:field="*{placeId}" id="placeId" class="form-select">
|
||||
<option selected value="">Укажите место</option>
|
||||
<option th:each="place : ${places}" th:value="${place.id}">[[${place.name}]]</option>
|
||||
</select>
|
||||
<div th:if="${#fields.hasErrors('placeId')}" th:errors="*{placeId}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<label for="image" class="form-label">Изображение</label>
|
||||
<input type="file" class="form-control" id="image" required>
|
||||
<div class="valid-feedback">Изображение загружено</div>
|
||||
<div 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/ivent">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
61
src/main/resources/templates/ivent.html
Normal file
61
src/main/resources/templates/ivent.html
Normal file
@ -0,0 +1,61 @@
|
||||
<!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/ivent/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-auto">Категория</th>
|
||||
<th scope="col" class="w-auto">Площадка</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="ivent : ${items}">
|
||||
<th scope="row" th:text="${ivent.id}"></th>
|
||||
<td th:text="${ivent.name}"></td>
|
||||
<td th:text="${ivent.typeName}"></td>
|
||||
<td th:text="${ivent.placeName}"></td>
|
||||
<td th:text="${ivent.price}"></td>
|
||||
<td>
|
||||
<form th:action="@{/admin/ivent/edit/{id}(id=${ivent.id})}" method="get">
|
||||
<button type="submit" class="btn btn-link button-link">Редактировать</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/admin/ivent/delete/{id}(id=${ivent.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>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url=${'admin/ivent'},
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
87
src/main/resources/templates/login.html
Normal file
87
src/main/resources/templates/login.html
Normal file
@ -0,0 +1,87 @@
|
||||
<!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">
|
||||
<div class="row">
|
||||
<main class="center">
|
||||
<div id="video-container">
|
||||
<div class="video-foreground">
|
||||
<iframe src="https://www.youtube.com/embed/f0pdwd0miqs?autoplay=1&controls=0&loop=1&showinfo=0&mute=1" allowfullscreen title="iframe_video"></iframe>
|
||||
</div>
|
||||
<div class="text-button-overlay">
|
||||
<div class="container mt-3">
|
||||
<form action="#" th:action="@{/login}" method="post">
|
||||
<div th:if="${param.error}" class="alert alert-danger">
|
||||
Неверный логин или пароль
|
||||
</div>
|
||||
<div th:if="${param.logout}" class="alert alert-success">
|
||||
Выход успешно произведен
|
||||
</div>
|
||||
<div th:if="${param.signup}" class="alert alert-success">
|
||||
Пользователь успешно создан
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Имя пользователя</label>
|
||||
<input type="text" id="username" name="username" class="form-control" required minlength="3"
|
||||
maxlength="20">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Пароль</label>
|
||||
<input type="password" id="password" name="password" class="form-control" required minlength="3"
|
||||
maxlength="20">
|
||||
</div>
|
||||
<div class="form-check mb-3">
|
||||
<input class="form-check-input" type="checkbox" id="remember-me" name="remember-me" checked>
|
||||
<label class="form-check-label" for="remember-me">Запомнить меня</label>
|
||||
</div>
|
||||
<div class="d-flex justify-content-center">
|
||||
<button class="btn btn-primary" type="submit" style="margin-right: 10px;">Войти</button>
|
||||
<a class="btn btn-secondary" href="/signup">Регистрация</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
/div>
|
||||
</main>
|
||||
</div>
|
||||
</main>
|
||||
<script>
|
||||
function getRandomVideo() {
|
||||
var videos = [
|
||||
"https://www.youtube.com/embed/f0pdwd0miqs?autoplay=1&controls=0&loop=1&showinfo=0&mute=1",
|
||||
"https://www.youtube.com/embed/f0pdwd0miqs?autoplay=1&controls=0&loop=1&showinfo=0&mute=1",
|
||||
"https://www.youtube.com/embed/f0pdwd0miqs?autoplay=1&controls=0&loop=1&showinfo=0&mute=1",
|
||||
"https://www.youtube.com/embed/f0pdwd0miqs?autoplay=1&controls=0&loop=1&showinfo=0&mute=1"
|
||||
];
|
||||
var randomIndex = Math.floor(Math.random() * videos.length);
|
||||
var randomVideo = videos[randomIndex];
|
||||
var videoElement = document.getElementById("video");
|
||||
videoElement.src = randomVideo;
|
||||
}
|
||||
|
||||
function setOverlayPosition() {
|
||||
var videoElement = document.getElementById("video");
|
||||
var textOverlayElement = document.getElementById("text-overlay");
|
||||
textOverlayElement.style.top = (videoElement.offsetHeight / 2 - textOverlayElement.offsetHeight / 2) + "px";
|
||||
textOverlayElement.style.left = (videoElement.offsetWidth / 2 - textOverlayElement.offsetWidth / 2) + "px";
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', (event) => {
|
||||
getRandomVideo();
|
||||
setOverlayPosition();
|
||||
});
|
||||
|
||||
window.addEventListener('resize', (event) => {
|
||||
setOverlayPosition();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
</html>
|
38
src/main/resources/templates/order-details.html
Normal file
38
src/main/resources/templates/order-details.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" layout:decorate="~{default}">
|
||||
|
||||
<body>
|
||||
<main layout:fragment="content">
|
||||
<button class="btn btn-link button-link" onclick="history.back()">Назад</button>
|
||||
<h1 style="color:#faebd7;">Заказ №[[${order.id}]], [[${#dates.format(order.date, 'HH:mm dd-MM-yyyy')}]]</h1>
|
||||
<th:block th:switch="${items.size()}">
|
||||
<h2 th:case="0">Товары заказа отсутствуют</h2>
|
||||
<th:block th:case="*">
|
||||
<table class="table mt-2">
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="w-auto" style="color:#faebd7;">Название товара</th>
|
||||
<th scope="col" class="w-10" style="color:#faebd7;">Цена</th>
|
||||
<th scope="col" class="w-10" style="color:#faebd7;">Количество</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="item : ${items}">
|
||||
<td th:text="${item.iventName}" style="color:#faebd7;"></td>
|
||||
<td th:text="${#numbers.formatDecimal(item.iventPrice, 1, 2)}" style="color:#faebd7;"></td>
|
||||
<td th:text="${item.quantity}" style="color:#faebd7;"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url='',
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
<h2 style="color:#faebd7;">Итого: [[${#numbers.formatDecimal(order.total, 1, 2)}]] руб</h2>
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
46
src/main/resources/templates/orders.html
Normal file
46
src/main/resources/templates/orders.html
Normal file
@ -0,0 +1,46 @@
|
||||
<!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="*">
|
||||
<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-auto"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr th:each="order : ${items}">
|
||||
<th scope="row" th:text="${order.id}"></th>
|
||||
<td th:text="${#dates.format(order.date, 'HH:mm dd-MM-yyyy')}"></td>
|
||||
<td>
|
||||
<form th:action="@{/orders/details/{id}(id=${order.id})}" method="get">
|
||||
<button type="submit" class="btn btn-link button-link">Детали</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url='',
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
<div class="mt-2 d-flex justify-content-center">
|
||||
<a class="btn btn-primary" href="/cart">Создать заказ</a>
|
||||
</div>
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
51
src/main/resources/templates/pagination.html
Normal file
51
src/main/resources/templates/pagination.html
Normal file
@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
|
||||
<body>
|
||||
|
||||
<th:block th:fragment="pagination (url, totalPages, currentPage)">
|
||||
<nav th:if="${totalPages > 1}" th:with="
|
||||
maxPage=2,
|
||||
currentPage=${currentPage + 1}">
|
||||
<ul class="pagination justify-content-center"
|
||||
th:with="
|
||||
seqFrom=${currentPage - maxPage < 1 ? 1 : currentPage - maxPage},
|
||||
seqTo=${currentPage + maxPage > totalPages ? totalPages : currentPage + maxPage}">
|
||||
<th:block th:if="${currentPage > maxPage + 1}">
|
||||
<li class="page-item">
|
||||
<a class="page-link" aria-label="Previous" th:href="@{/{url}?page=0(url=${url})}">
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-label="Previous">
|
||||
<span aria-hidden="true">…</span>
|
||||
</span>
|
||||
</li>
|
||||
</th:block>
|
||||
<li class="page-item" th:each="page : ${#numbers.sequence(seqFrom, seqTo)}"
|
||||
th:classappend="${page == currentPage} ? 'active' : ''">
|
||||
<a class=" page-link" th:href="@{/{url}?page={page}(url=${url},page=${page - 1})}">
|
||||
<span th:text="${page}" />
|
||||
</a>
|
||||
</li>
|
||||
<th:block th:if="${currentPage < totalPages - maxPage}">
|
||||
<li class="page-item disabled">
|
||||
<span class="page-link" aria-label="Previous">
|
||||
<span aria-hidden="true">…</span>
|
||||
</span>
|
||||
</li>
|
||||
<li class="page-item">
|
||||
<a class="page-link" aria-label="Next"
|
||||
th:href="@{/{url}?page={page}(url=${url},page=${totalPages - 1})}">
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
</li>
|
||||
</th:block>
|
||||
</ul>
|
||||
</nav>
|
||||
</th:block>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
29
src/main/resources/templates/place-edit.html
Normal file
29
src/main/resources/templates/place-edit.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!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/place/edit/{id}(id=${place.id})}" th:object="${place}"
|
||||
method="post">
|
||||
<div class="mb-3">
|
||||
<label for="id" class="form-label">ID</label>
|
||||
<input place="text" th:value="*{id}" id="id" class="form-control" readonly disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Название</label>
|
||||
<input place="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" place="submit">Сохранить</button>
|
||||
<a class="btn btn-secondary button-fixed-width" href="/admin/place">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
55
src/main/resources/templates/place.html
Normal file
55
src/main/resources/templates/place.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!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/place/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="place : ${items}">
|
||||
<th scope="row" th:text="${place.id}"></th>
|
||||
<td th:text="${place.name}"></td>
|
||||
<td>
|
||||
<form th:action="@{/admin/place/edit/{id}(id=${place.id})}" method="get">
|
||||
<button place="submit" class="btn btn-link button-link">Редактировать</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/admin/place/delete/{id}(id=${place.id})}" method="post">
|
||||
<button place="submit" class="btn btn-link button-link"
|
||||
onclick="return confirm('Вы уверены?')">Удалить</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</th:block>
|
||||
</th:block>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url=${'admin/place'},
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
40
src/main/resources/templates/signup.html
Normal file
40
src/main/resources/templates/signup.html
Normal file
@ -0,0 +1,40 @@
|
||||
<!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">
|
||||
<div class="video-foreground">
|
||||
<iframe src="https://www.youtube.com/embed/RRKJiM9Njr8?autoplay=1&controls=0&loop=1&showinfo=0&mute=1" allowfullscreen title="iframe_video"></iframe>
|
||||
</div>
|
||||
<form action="#" th:action="@{/signup}" th:object="${user}" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Почта</label>
|
||||
<input type="text" th:field="*{email}" id="email" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Пароль</label>
|
||||
<input type="password" th:field="*{password}" id="password" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="passwordConfirm" class="form-label">Пароль (подтверждение)</label>
|
||||
<input type="password" th:field="*{passwordConfirm}" id="passwordConfirm" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('passwordConfirm')}" th:errors="*{passwordConfirm}"
|
||||
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="/">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
</html>
|
29
src/main/resources/templates/type-edit.html
Normal file
29
src/main/resources/templates/type-edit.html
Normal file
@ -0,0 +1,29 @@
|
||||
<!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/type/edit/{id}(id=${type.id})}" th:object="${type}"
|
||||
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/type">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
55
src/main/resources/templates/type.html
Normal file
55
src/main/resources/templates/type.html
Normal file
@ -0,0 +1,55 @@
|
||||
<!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/type/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="type : ${items}">
|
||||
<th scope="row" th:text="${type.id}"></th>
|
||||
<td th:text="${type.name}"></td>
|
||||
<td>
|
||||
<form th:action="@{/admin/type/edit/{id}(id=${type.id})}" method="get">
|
||||
<button type="submit" class="btn btn-link button-link">Редактировать</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/admin/type/delete/{id}(id=${type.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>
|
||||
<th:block th:replace="~{ pagination :: pagination (
|
||||
url=${'admin/type'},
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
34
src/main/resources/templates/user-create.html
Normal file
34
src/main/resources/templates/user-create.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!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/user/create/(page=${page})}" th:object="${user}"
|
||||
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="login" class="form-label">Логин</label>
|
||||
<input type="text" th:field="*{login}" id="login" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('login')}" th:errors="*{login}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Пароль</label>
|
||||
<input type="text" th:field="*{password}" id="password" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('password')}" th:errors="*{password}" 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" th:href="@{/admin/user(page=${page})}">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
39
src/main/resources/templates/user-edit.html
Normal file
39
src/main/resources/templates/user-edit.html
Normal file
@ -0,0 +1,39 @@
|
||||
<!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/user/edit/{id}(id=${user.id},page=${page})}" th:object="${user}"
|
||||
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="handle" class="form-label">Логин</label>
|
||||
<input type="text" th:field="*{handle}" id="handle" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('handle')}" th:errors="*{handle}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">E-mail</label>
|
||||
<input type="text" th:field="*{email}" id="email" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="invalid-feedback"></div>
|
||||
</div>
|
||||
<div class="mb-3 visually-hidden">
|
||||
<label for="password" class="form-label">Пароль</label>
|
||||
<input type="text" th:field="*{password}" id="password" class="form-control">
|
||||
<div th:if="${#fields.hasErrors('password')}" th:errors="*{password}" 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" th:href="@{/admin/user(page=${page})}">Отмена</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
56
src/main/resources/templates/user.html
Normal file
56
src/main/resources/templates/user.html
Normal file
@ -0,0 +1,56 @@
|
||||
<!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/user/create/" 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="user : ${items}">
|
||||
<th scope="row" th:text="${user.id}"></th>
|
||||
<td th:text="${user.handle}"></td>
|
||||
<td>
|
||||
<form th:action="@{/admin/user/edit/{id}(id=${user.id})}" method="get">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<button type="submit" class="btn btn-link button-link">Редактировать</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>
|
||||
<form th:action="@{/admin/user/delete/{id}(id=${user.id})}" method="post">
|
||||
<input type="hidden" th:name="page" th:value="${page}">
|
||||
<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=${'admin/user'},
|
||||
totalPages=${totalPages},
|
||||
currentPage=${currentPage}) }" />
|
||||
</th:block>
|
||||
</main>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,13 +0,0 @@
|
||||
package com.example.demo;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class DemoApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
102
src/test/java/com/example/demo/IventServiceTests.java
Normal file
102
src/test/java/com/example/demo/IventServiceTests.java
Normal file
@ -0,0 +1,102 @@
|
||||
package com.example.demo;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
|
||||
import com.example.demo.ivents.model.IventEntity;
|
||||
import com.example.demo.ivents.service.IventService;
|
||||
import com.example.demo.places.model.PlaceEntity;
|
||||
import com.example.demo.places.service.PlaceService;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
|
||||
@SpringBootTest
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
class IventServiceTests {
|
||||
@Autowired
|
||||
private TypeService typeService;
|
||||
|
||||
@Autowired
|
||||
private PlaceService placeService;
|
||||
|
||||
@Autowired
|
||||
private IventService iventService;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void createTest() {
|
||||
final var type = typeService.create(new TypeEntity("newType"));
|
||||
final var place = placeService.create(new PlaceEntity("newPlace"));
|
||||
|
||||
var ivent = iventService.create(new IventEntity(
|
||||
"newIvent",
|
||||
type,
|
||||
LocalDate.of(2024, 10, 15),
|
||||
"Описание Slipknot",
|
||||
"Участники группы Slipknot",
|
||||
2500.33,
|
||||
30,
|
||||
place,
|
||||
null));
|
||||
Assertions.assertEquals(ivent, iventService.get(ivent.getId()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void createNullableTest() {
|
||||
var type = typeService.create(new TypeEntity("qwe"));
|
||||
var nullableIvent = new IventEntity(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null ,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
Assertions.assertThrows(DataIntegrityViolationException.class, () -> iventService.create(nullableIvent));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
void sortDates() {
|
||||
LocalDate start = LocalDate.of(2024, 9, 15);
|
||||
LocalDate end = LocalDate.of(2024, 12, 15);
|
||||
|
||||
List<IventEntity> list = iventService.getAll(0L, 0L, start, end);
|
||||
|
||||
Assertions.assertEquals(1, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
void updateTest() {
|
||||
var type = typeService.create(new TypeEntity("UpdateType"));
|
||||
var place = placeService.create(new PlaceEntity("UpdatePlace"));
|
||||
var ivent = iventService.create(new IventEntity("UpdateTest", type, LocalDate.of(2025, 10, 15), "desc", "other", 399.0, 3, place, null));
|
||||
var updatedIvent = iventService.update(ivent.getId(), new IventEntity("UpdatedTest", type, LocalDate.of(2025, 10, 15), "desc", "updateTestOther", 1399.0, 3, place, null));
|
||||
Assertions.assertEquals("UpdatedTest", updatedIvent.getName());
|
||||
Assertions.assertEquals("updateTestOther", updatedIvent.getOther());
|
||||
Assertions.assertEquals(1399.00, updatedIvent.getPrice());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
void deleteTest() {
|
||||
var type = typeService.create(new TypeEntity("DeleteType"));
|
||||
var place = placeService.create(new PlaceEntity("DeletePlace"));
|
||||
var ivent = iventService.create(new IventEntity("ToDelete", type, LocalDate.of(2026, 10, 15), "description", "otherInformation", 399.00, 5, place, null));
|
||||
iventService.delete(ivent.getId());
|
||||
Assertions.assertFalse(iventService.getAll(0L, 0L, null, null).stream().anyMatch(p -> p.getId().equals(ivent.getId())));
|
||||
}
|
||||
}
|
99
src/test/java/com/example/demo/OrderServiceTests.java
Normal file
99
src/test/java/com/example/demo/OrderServiceTests.java
Normal file
@ -0,0 +1,99 @@
|
||||
package com.example.demo;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import com.example.demo.core.error.NotFoundException;
|
||||
import com.example.demo.orders.api.OrderIventDto;
|
||||
import com.example.demo.orders.service.OrderService;
|
||||
import com.example.demo.places.model.PlaceEntity;
|
||||
import com.example.demo.ivents.model.IventEntity;
|
||||
import com.example.demo.ivents.service.IventService;
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
import com.example.demo.places.model.PlaceEntity;
|
||||
import com.example.demo.places.service.PlaceService;
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.service.UserService;
|
||||
|
||||
@SpringBootTest
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
public class OrderServiceTests {
|
||||
@Autowired
|
||||
private TypeService typeService;
|
||||
|
||||
@Autowired
|
||||
private PlaceService placeService;
|
||||
|
||||
@Autowired
|
||||
private IventService iventService;
|
||||
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Autowired
|
||||
private OrderService orderService;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void getTest() {
|
||||
Assertions.assertThrows(NotFoundException.class, () -> orderService.getAll(0L));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void createTest() {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
LocalDate _date = LocalDate.parse("2023-10-15", formatter);
|
||||
|
||||
final var type1 = typeService.create(new TypeEntity("type1"));
|
||||
final var type2 = typeService.create(new TypeEntity("type2"));
|
||||
|
||||
final var place1 = placeService.create(new PlaceEntity("place1"));
|
||||
final var place2 = placeService.create(new PlaceEntity("place2"));
|
||||
|
||||
final var ivent1 = iventService.create(new IventEntity(
|
||||
"ivent1",
|
||||
type1,
|
||||
_date,
|
||||
"Slipknot(test) - американская ню-метал группа, основанная в 1995 году. Они известны своим агрессивным звучанием, смешением хардкор-панк и тяжелого метала с индастриалом и элементами альтернативного рока.",
|
||||
"Участники группы Slipknot - это:<....>",
|
||||
2500.33,
|
||||
30,
|
||||
place1,
|
||||
null));
|
||||
|
||||
final var ivent2 = iventService.create(new IventEntity(
|
||||
"ivent2",
|
||||
type1,
|
||||
_date,
|
||||
"Placebo - британская альтернативная рок-группа, основанная в 1994 году. Они известны своим уникальным звучанием, погружающим слушателей в меланхоличную атмосферу и наполняющим музыку эмоциональным содержанием.",
|
||||
"Группа Placebo состояла из следующих участников:<....>",
|
||||
2500.33,
|
||||
30,
|
||||
place2,
|
||||
null));
|
||||
|
||||
final var user1 = userService.create(new UserEntity("user11", "user11@mail.com", "user11"));
|
||||
|
||||
OrderIventDto ivent10 = new OrderIventDto();
|
||||
ivent10.setIventId(ivent1.getId());
|
||||
ivent10.setQuantity(4);
|
||||
|
||||
OrderIventDto ivent20 = new OrderIventDto();
|
||||
ivent20.setIventId(ivent2.getId());
|
||||
ivent20.setQuantity(6);
|
||||
|
||||
orderService.create(user1.getId(), Arrays.asList(ivent10, ivent20));
|
||||
Assertions.assertEquals(1, orderService.getAll(user1.getId()).size());
|
||||
}
|
||||
}
|
60
src/test/java/com/example/demo/PlaceServiceTests.java
Normal file
60
src/test/java/com/example/demo/PlaceServiceTests.java
Normal file
@ -0,0 +1,60 @@
|
||||
package com.example.demo;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
|
||||
import com.example.demo.places.model.PlaceEntity;
|
||||
import com.example.demo.places.service.PlaceService;
|
||||
|
||||
@SpringBootTest
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
class PlaceServiceTests {
|
||||
@Autowired
|
||||
private PlaceService placeService;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void createTest() {
|
||||
placeService.create(new PlaceEntity("place10"));
|
||||
placeService.create(new PlaceEntity("place11"));
|
||||
Assertions.assertEquals(10, placeService.getAll().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void createNotUniqueTest() {
|
||||
final PlaceEntity nonUniquePlace = new PlaceEntity("Germany, Berlin");
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> placeService.create(nonUniquePlace));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
void createNullableTest() {
|
||||
final PlaceEntity nullablePlace = new PlaceEntity(null);
|
||||
Assertions.assertThrows(DataIntegrityViolationException.class, () -> placeService.create(nullablePlace));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
void updateTest() {
|
||||
final PlaceEntity newEntity = placeService.update(19L, new PlaceEntity("TEST"));
|
||||
Assertions.assertEquals(10, placeService.getAll().size());
|
||||
Assertions.assertEquals("TEST", newEntity.getName());
|
||||
Assertions.assertNotEquals("TESTS", newEntity.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
void deleteTest() {
|
||||
var del = placeService.create(new PlaceEntity("TESTSSS"));
|
||||
var test = placeService.get(del.getId());
|
||||
placeService.delete(test.getId());
|
||||
Assertions.assertEquals(10, placeService.getAll().size());
|
||||
}
|
||||
}
|
59
src/test/java/com/example/demo/TypeServiceTests.java
Normal file
59
src/test/java/com/example/demo/TypeServiceTests.java
Normal file
@ -0,0 +1,59 @@
|
||||
package com.example.demo;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
|
||||
import com.example.demo.types.model.TypeEntity;
|
||||
import com.example.demo.types.service.TypeService;
|
||||
|
||||
@SpringBootTest
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
class TypeServiceTests {
|
||||
@Autowired
|
||||
private TypeService typeService;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void createTest() {
|
||||
typeService.create(new TypeEntity("type10"));
|
||||
Assertions.assertEquals(10, typeService.getAll().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void createNotUniqueTest() {
|
||||
final TypeEntity nonUniqueType = new TypeEntity("Концерт");
|
||||
Assertions.assertThrows(IllegalArgumentException.class, () -> typeService.create(nonUniqueType));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
void createNullableTest() {
|
||||
final TypeEntity nullableType = new TypeEntity(null);
|
||||
Assertions.assertThrows(DataIntegrityViolationException.class, () -> typeService.create(nullableType));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
void updateTest() {
|
||||
final TypeEntity newEntity = typeService.update(3L, new TypeEntity("TEST"));
|
||||
Assertions.assertEquals(10, typeService.getAll().size());
|
||||
Assertions.assertEquals("TEST", newEntity.getName());
|
||||
Assertions.assertNotEquals("TESTS", newEntity.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(5)
|
||||
void deleteTest() {
|
||||
var del = typeService.create(new TypeEntity("TESTSSS"));
|
||||
var test = typeService.get(del.getId());
|
||||
typeService.delete(test.getId());
|
||||
Assertions.assertEquals(10, typeService.getAll().size());
|
||||
}
|
||||
}
|
51
src/test/java/com/example/demo/UserServiceTests.java
Normal file
51
src/test/java/com/example/demo/UserServiceTests.java
Normal file
@ -0,0 +1,51 @@
|
||||
package com.example.demo;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
|
||||
import com.example.demo.users.model.UserEntity;
|
||||
import com.example.demo.users.service.UserService;
|
||||
|
||||
@SpringBootTest
|
||||
@TestMethodOrder(OrderAnnotation.class)
|
||||
class UserServiceTests {
|
||||
@Autowired
|
||||
private UserService userService;
|
||||
|
||||
@Test
|
||||
@Order(1)
|
||||
void createTest() {
|
||||
Assertions.assertEquals(4, userService.getAll().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(2)
|
||||
void createNullableTest() {
|
||||
var nullableUser = new UserEntity(null, "userTest1", "password");
|
||||
Assertions.assertThrows(DataIntegrityViolationException.class, () -> userService.create(nullableUser));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(3)
|
||||
void updateTest() {
|
||||
var user = userService.create(new UserEntity("userTest2", "userTest2@mail.com", "pswrd"));
|
||||
var newEntity = userService.update(user.getId(),new UserEntity("userTest2123", "userTest2@mail.com", "password"));
|
||||
Assertions.assertEquals(5, userService.getAll().size());
|
||||
Assertions.assertEquals("userTest2123", newEntity.getHandle());
|
||||
Assertions.assertEquals("password", newEntity.getPassword());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Order(4)
|
||||
void deleteTest() {
|
||||
var user = userService.create(new UserEntity("Delete", "Delete", "Delete"));
|
||||
userService.delete(user.getId());
|
||||
Assertions.assertFalse(userService.getAll().stream().anyMatch(p -> p.getId().equals(user.getId())));
|
||||
}
|
||||
}
|
14
src/test/java/resources/application.properties
Normal file
14
src/test/java/resources/application.properties
Normal file
@ -0,0 +1,14 @@
|
||||
# Server
|
||||
spring.main.banner-mode=off
|
||||
|
||||
# Logger settings
|
||||
# Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF
|
||||
logging.level.com.example.demo=DEBUG
|
||||
|
||||
# JPA Settings
|
||||
spring.datasource.url=jdbc:h2:mem:testdb
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=password
|
||||
spring.datasource.driver-class-name=org.h2.Driver
|
||||
spring.jpa.hibernate.ddl-auto=create
|
||||
spring.jpa.open-in-view=false
|
Loading…
Reference in New Issue
Block a user