chore: Initial import of FLEX training material
This commit is contained in:
parent
c01246d4f7
commit
12235acc42
1020 changed files with 53940 additions and 0 deletions
4
flex-training-flexinale/flexinale-modulith-1-onion/.gitignore
vendored
Normal file
4
flex-training-flexinale/flexinale-modulith-1-onion/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# flex-training-filmfestival
|
||||
iSAQB Advanced Level FLEX training: This code contains our case study code for the "filmfestival" example.
|
||||
This is the main repo containing blueprint and solution.
|
||||
|
||||
## Architecture as a modulithic system
|
||||
Part 1 is in the folder flexinale-modulith-1-onion.
|
||||
Part 2 is in the folder flexinale-modulith-1-components.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FindBugsFilter>
|
||||
<Match>
|
||||
<!-- spotbugs message is Possible null pointer dereference of Kino.kinoSaele in equals method.
|
||||
But the if statements before that line guarantee not null. -->
|
||||
<Class name="de.accso.flexinale.domain.model.Kino" />
|
||||
<Bug pattern="NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE" />
|
||||
</Match>
|
||||
<Match>
|
||||
<Class name="de.accso.flexinale.infrastructure.FlexinaleSpringConfig" />
|
||||
<Bug pattern="CT_CONSTRUCTOR_THROW"/>
|
||||
</Match>
|
||||
</FindBugsFilter>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
FROM amazoncorretto:21.0.1-alpine3.18
|
||||
|
||||
WORKDIR /app
|
||||
COPY ../../target/flexinale-modulith-1-onion-2024.3.0-spring-boot-fat-jar.jar /app
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
CMD ["java", "-jar", "flexinale-modulith-1-onion-2024.3.0-spring-boot-fat-jar.jar"]
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
docker-compose \
|
||||
--project-name flexinale-modulith-1-onion \
|
||||
-f ../../../infrastructure/docker/flexinale-network.yml \
|
||||
-f ../../../infrastructure/docker/postgres/flexinale-postgres.yml \
|
||||
-f flexinale-modulith-1-onion.yml \
|
||||
up
|
||||
|
|
@ -0,0 +1 @@
|
|||
docker build -t de.accso/flexinale-modulith-1-onion:2024.3.0 -f Dockerfile ../../
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
version: '2.2'
|
||||
|
||||
services:
|
||||
|
||||
flexinale-modulith-1-onion:
|
||||
image: de.accso/flexinale-modulith-1-onion:2024.3.0
|
||||
depends_on:
|
||||
- flexinale-postgres
|
||||
ports:
|
||||
- "8080:8080"
|
||||
networks:
|
||||
- flexinale-network
|
||||
command: sh -c "java -Dspring.datasource.url=jdbc:postgresql://flexinale-postgres:5432/modulith-1 -jar flexinale-modulith-1-onion-2024.3.0-spring-boot-fat-jar.jar"
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
FROM amazoncorretto:21.0.1-alpine3.18
|
||||
|
||||
WORKDIR /app
|
||||
COPY ../../target/flexinale-modulith-1-onion-2024.3.0-spring-boot-fat-jar.jar /app
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
CMD ["java", "-jar", "flexinale-modulith-1-onion-2024.3.0-spring-boot-fat-jar.jar"]
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
version: '2.2'
|
||||
|
||||
services:
|
||||
|
||||
flexinale-modulith-1-onion:
|
||||
image: de.accso/flexinale-modulith-1-onion:2024.3.0
|
||||
depends_on:
|
||||
- flexinale-postgres
|
||||
ports:
|
||||
- "8080:8080"
|
||||
networks:
|
||||
- flexinale-network
|
||||
command: sh -c "java -Dspring.datasource.url=jdbc:postgresql://flexinale-postgres:5432/modulith-1 -jar flexinale-modulith-1-onion-2024.3.0-spring-boot-fat-jar.jar"
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
podman-compose ^
|
||||
--project-name flexinale-modulith-1-onion ^
|
||||
-f ..\..\..\infrastructure\podman\flexinale-network.yml ^
|
||||
-f ..\..\..\infrastructure\podman\postgres\flexinale-postgres.yml ^
|
||||
-f flexinale-modulith-1-onion.yml ^
|
||||
up
|
||||
|
|
@ -0,0 +1 @@
|
|||
podman build -t de.accso/flexinale-modulith-1-onion:2024.3.0 -f Dockerfile ../../
|
||||
118
flex-training-flexinale/flexinale-modulith-1-onion/pom.xml
Normal file
118
flex-training-flexinale/flexinale-modulith-1-onion/pom.xml
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>de.accso</groupId>
|
||||
<artifactId>flexinale</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<relativePath>../pom.xml</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<artifactId>flexinale-modulith-1-onion</artifactId>
|
||||
<version>2024.3.0</version>
|
||||
<name>Flexinale Modulith, Step 1, Onion</name>
|
||||
<description>FLEX case-study "film festival", modulith system, step 1 - onion layering</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf.extras</groupId>
|
||||
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>${org-postgres.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi</artifactId>
|
||||
<version>${apache-poi.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.poi</groupId>
|
||||
<artifactId>poi-ooxml</artifactId>
|
||||
<version>${apache-poi.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<!-- name extension of spring boot fat jar-->
|
||||
<configuration>
|
||||
<classifier>spring-boot-fat-jar</classifier>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs-maven-plugin</artifactId>
|
||||
<version>${spotbugs-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.h3xstream.findsecbugs</groupId>
|
||||
<artifactId>findsecbugs-plugin</artifactId>
|
||||
<version>${findsecbugs-maven-plugin.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<excludeFilterFile>SpotbugsExcludeFilter.xml</excludeFilterFile>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<!-- overwrite dependency on spotbugs if you want to specify the version of spotbugs -->
|
||||
<dependency>
|
||||
<groupId>com.github.spotbugs</groupId>
|
||||
<artifactId>spotbugs</artifactId>
|
||||
<version>${spotbugs.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Modulith 1 - Domain Classes Use Correct Interfaces And Internal Structure Test" type="JUnit" factoryName="JUnit" folderName="ArchitectureTests Modulith1">
|
||||
<module name="flexinale-modulith-1-onion" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="architecturetests.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="architecturetests" />
|
||||
<option name="MAIN_CLASS_NAME" value="architecturetests.InternalStructureOfDomainClassesUsingCorrectInterfacesAndAttributeTypesTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Modulith 1 - Entities Use Correct Interfaces And Internal Structure Test" type="JUnit" factoryName="JUnit" folderName="ArchitectureTests Modulith1">
|
||||
<module name="flexinale-modulith-1-onion" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="architecturetests.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="architecturetests" />
|
||||
<option name="MAIN_CLASS_NAME" value="architecturetests.InternalStructureOfEntityClassesUsingCorrectInterfacesAndAttributeTypesTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Modulith 1 - No Dependencies To Spring Test" type="JUnit" factoryName="JUnit" folderName="ArchitectureTests Modulith1">
|
||||
<module name="flexinale-modulith-1-onion" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="architecturetests.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="architecturetests" />
|
||||
<option name="MAIN_CLASS_NAME" value="architecturetests.NoDependenciesToSpringTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Modulith 1 - Onion Dependencies Test" type="JUnit" factoryName="JUnit" folderName="ArchitectureTests Modulith1">
|
||||
<module name="flexinale-modulith-1-onion" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="architecturetests.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="architecturetests" />
|
||||
<option name="MAIN_CLASS_NAME" value="architecturetests.OnionDependenciesTest" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Modulith 1 - REST upload to add filme" type="HttpClient.HttpRequestRunConfigurationType" factoryName="HTTP Request" folderName="REST upload Modulith 1" path="$PROJECT_DIR$/flexinale-modulith-1-onion/src/test/curl/rest-post-upload-to-add-and-update-filme.http" requestIdentifier="#1">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Modulith 1 - REST upload to add kinos" type="HttpClient.HttpRequestRunConfigurationType" factoryName="HTTP Request" folderName="REST upload Modulith 1" path="$PROJECT_DIR$/flexinale-modulith-1-onion/src/test/curl/rest-post-upload-to-add-and-update-kinos.http" requestIdentifier="#1">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Modulith 1 - REST upload to add vorfuehrungen" type="HttpClient.HttpRequestRunConfigurationType" factoryName="HTTP Request" folderName="REST upload Modulith 1" path="$PROJECT_DIR$/flexinale-modulith-1-onion/src/test/curl/rest-post-upload-to-add-and-update-vorfuehrungen.http" requestIdentifier="#1">
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Modulith 1 - delete all data from database" type="JUnit" factoryName="JUnit" folderName="Testdata Modulith 1">
|
||||
<module name="flexinale-modulith-1-onion" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="testdata.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="testdata" />
|
||||
<option name="MAIN_CLASS_NAME" value="testdata.DatabaseCleaner" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<option name="VM_PARAMETERS" value="-ea -Dspring.jpa.hibernate.ddl-auto=create" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Modulith 1 - load all data to database" type="JUnit" factoryName="JUnit" folderName="Testdata Modulith 1">
|
||||
<module name="flexinale-modulith-1-onion" />
|
||||
<extension name="coverage">
|
||||
<pattern>
|
||||
<option name="PATTERN" value="testdata.*" />
|
||||
<option name="ENABLED" value="true" />
|
||||
</pattern>
|
||||
</extension>
|
||||
<option name="PACKAGE_NAME" value="testdata" />
|
||||
<option name="MAIN_CLASS_NAME" value="testdata.TestDataLoader" />
|
||||
<option name="METHOD_NAME" value="" />
|
||||
<option name="TEST_OBJECT" value="class" />
|
||||
<option name="VM_PARAMETERS" value="-ea" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Modulith 1 - start app ("FlexinaleModulith1OnionApplication")" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" folderName="App Modulith 1">
|
||||
<module name="flexinale-modulith-1-onion" />
|
||||
<option name="SPRING_BOOT_MAIN_CLASS" value="de.accso.flexinale.FlexinaleModulith1OnionApplication" />
|
||||
<method v="2">
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package de.accso.flexinale;
|
||||
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
@SpringBootApplication
|
||||
public class FlexinaleModulith1OnionApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(FlexinaleModulith1OnionApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager(final EntityManagerFactory entityManagerFactory) {
|
||||
return new JpaTransactionManager(entityManagerFactory);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package de.accso.flexinale.api_in.rest;
|
||||
|
||||
import de.accso.flexinale.application.services.ExcelDataUploadService;
|
||||
import de.accso.flexinale.application.services.PersistMode;
|
||||
import de.accso.flexinale.shared_kernel.FlexinaleIllegalStateException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.util.Collection;
|
||||
|
||||
abstract class AbstractExcelRestController {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractExcelRestController.class);
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected RestUploadResult uploadViaRestCall(final ExcelDataUploadService excelDataUploadService, final MultipartFile file) {
|
||||
try(BufferedInputStream stream = new BufferedInputStream(file.getInputStream())) {
|
||||
if (!ExcelHelper.isExcelFile(stream)) {
|
||||
String message = "Please upload a valid Excel file!";
|
||||
throw new FlexinaleIllegalStateException(message);
|
||||
}
|
||||
|
||||
Collection uploadedData =
|
||||
excelDataUploadService.loadDataFromExcelSheetAndPersistAndReturn(stream, PersistMode.ADD_ONLY);
|
||||
|
||||
String message = "Uploaded the Excel file %s adding %d new entities (while not updating any existing data)."
|
||||
.formatted(file.getOriginalFilename(), uploadedData.size());
|
||||
|
||||
return new RestUploadResult(HttpStatus.OK, message);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
String message = "Could not upload the Excel file: %s: %s"
|
||||
.formatted(file.getOriginalFilename(), ex.getMessage());
|
||||
RestBadRequestException exception = new RestBadRequestException(message, ex);
|
||||
LOGGER.error(message, exception);
|
||||
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package de.accso.flexinale.api_in.rest;
|
||||
|
||||
import org.apache.poi.poifs.filesystem.FileMagic;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
final class ExcelHelper {
|
||||
private ExcelHelper() {
|
||||
}
|
||||
|
||||
static boolean isExcelFile(final InputStream stream) throws IOException {
|
||||
return (FileMagic.valueOf(stream) == FileMagic.OOXML);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package de.accso.flexinale.api_in.rest;
|
||||
|
||||
import de.accso.flexinale.application.services.FilmUploadService;
|
||||
import de.accso.flexinale.domain.model.Film;
|
||||
import de.accso.flexinale.application.services.FilmService;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class FilmRestController extends AbstractExcelRestController {
|
||||
|
||||
@Autowired
|
||||
private FilmService filmService;
|
||||
|
||||
@Autowired
|
||||
private FilmUploadService filmUploadService;
|
||||
|
||||
@GetMapping(value = "/rest/filme", produces = "application/json")
|
||||
@PreAuthorize("hasRole('ROLE_BESUCHER')")
|
||||
public List<Film> filme() {
|
||||
return filmService.filme();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/rest/film/{id}", produces = "application/json")
|
||||
@PreAuthorize("hasRole('ROLE_BESUCHER')")
|
||||
public Film film(@PathVariable final String id) {
|
||||
Identifiable.Id filmId = Identifiable.Id.of(id);
|
||||
Film film = filmService.film(filmId);
|
||||
if (film == null) {
|
||||
throw new RestResourceNotFoundException("no Film %s found.".formatted(id));
|
||||
}
|
||||
|
||||
return film;
|
||||
}
|
||||
|
||||
@PostMapping("/rest/filme")
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
public ResponseEntity<RestResponseMessage> uploadFilme(@RequestParam("file") final MultipartFile file) {
|
||||
RestUploadResult restUploadResult = uploadViaRestCall(filmUploadService, file);
|
||||
return ResponseEntity.status(restUploadResult.status())
|
||||
.body(new RestResponseMessage(restUploadResult.message()));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package de.accso.flexinale.api_in.rest;
|
||||
|
||||
import de.accso.flexinale.application.services.KinoUploadService;
|
||||
import de.accso.flexinale.application.services.KinoSaalUploadService;
|
||||
import de.accso.flexinale.domain.model.Kino;
|
||||
import de.accso.flexinale.domain.model.KinoSaal;
|
||||
import de.accso.flexinale.application.services.KinoService;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
public class KinoKinoSaalRestController extends AbstractExcelRestController {
|
||||
|
||||
@Autowired
|
||||
private KinoService kinoService;
|
||||
|
||||
@Autowired
|
||||
private KinoUploadService kinoUploadService;
|
||||
|
||||
@Autowired
|
||||
private KinoSaalUploadService kinoSaalUploadService;
|
||||
|
||||
@GetMapping(value = "/rest/kinos", produces = "application/json")
|
||||
@PreAuthorize("hasRole('ROLE_BESUCHER')")
|
||||
public List<Kino> kinos() {
|
||||
return kinoService.kinos();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/rest/kino/{id}", produces = "application/json")
|
||||
@PreAuthorize("hasRole('ROLE_BESUCHER')")
|
||||
public Kino kino(@PathVariable final String id) {
|
||||
Identifiable.Id kinoId = Identifiable.Id.of(id);
|
||||
Kino kino = kinoService.kino(kinoId);
|
||||
if (kino == null) {
|
||||
throw new RestResourceNotFoundException("no Kino %s found.".formatted(id));
|
||||
}
|
||||
|
||||
return kino;
|
||||
}
|
||||
|
||||
@PostMapping("/rest/kinos")
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
public ResponseEntity<RestResponseMessage> uploadKinosKinoSaele(@RequestParam("file") final MultipartFile file) {
|
||||
try(BufferedInputStream stream = new BufferedInputStream(file.getInputStream())) {
|
||||
|
||||
// load KinoSaele first
|
||||
Collection<KinoSaal> alleKinoSaele =
|
||||
kinoSaalUploadService.loadDataFromExcelSheet(stream);
|
||||
Map<Identifiable.Id, Collection<KinoSaal>> alleKinosUndIhreSaele =
|
||||
kinoSaalUploadService.mapKinoSaeleToKino(alleKinoSaele);
|
||||
|
||||
// .. then persist them as part of a Kino
|
||||
RestUploadResult restUploadResult;
|
||||
try {
|
||||
kinoUploadService.beforeLoad(alleKinosUndIhreSaele); // fix state of Kino-KinoSaal in map
|
||||
restUploadResult = uploadViaRestCall(kinoUploadService, file);
|
||||
}
|
||||
finally {
|
||||
kinoUploadService.afterLoad(); // clear map
|
||||
}
|
||||
|
||||
return ResponseEntity.status(restUploadResult.status())
|
||||
.body(new RestResponseMessage(restUploadResult.message()));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
String message = "Could not upload the Excel file: %s: %s".formatted(file.getOriginalFilename(), ex.getMessage());
|
||||
throw new RestBadRequestException(message, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package de.accso.flexinale.api_in.rest;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||
@SuppressWarnings("unused")
|
||||
public class RestBadRequestException extends RuntimeException {
|
||||
|
||||
// see https://www.springboottutorial.com/spring-boot-exception-handling-for-rest-services
|
||||
|
||||
public RestBadRequestException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public RestBadRequestException(final String message, final Exception ex) {
|
||||
super(message, ex);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package de.accso.flexinale.api_in.rest;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
|
||||
@ResponseStatus(HttpStatus.NOT_FOUND)
|
||||
public class RestResourceNotFoundException extends RuntimeException {
|
||||
|
||||
// see https://www.springboottutorial.com/spring-boot-exception-handling-for-rest-services
|
||||
|
||||
public RestResourceNotFoundException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package de.accso.flexinale.api_in.rest;
|
||||
|
||||
record RestResponseMessage(String message) {
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package de.accso.flexinale.api_in.rest;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
record RestUploadResult(HttpStatus status, String message) {
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package de.accso.flexinale.api_in.rest;
|
||||
|
||||
import de.accso.flexinale.application.services.VorfuehrungUploadService;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.application.services.VorfuehrungService;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
public class VorfuehrungRestController extends AbstractExcelRestController {
|
||||
|
||||
@Autowired
|
||||
private VorfuehrungService vorfuehrungService;
|
||||
|
||||
@Autowired
|
||||
private VorfuehrungUploadService vorfuehrungUploadService;
|
||||
|
||||
@GetMapping(value = "/rest/vorfuehrungen", produces = "application/json")
|
||||
@PreAuthorize("hasRole('ROLE_BESUCHER')")
|
||||
public List<Vorfuehrung> vorfuehrungen() {
|
||||
return vorfuehrungService.vorfuehrungen();
|
||||
}
|
||||
|
||||
@GetMapping(value = "/rest/vorfuehrungen/{id}", produces = "application/json")
|
||||
@PreAuthorize("hasRole('ROLE_BESUCHER')")
|
||||
public Vorfuehrung vorfuehrung(@PathVariable final String id) {
|
||||
Identifiable.Id vorfuehrungId = Identifiable.Id.of(id);
|
||||
Vorfuehrung vorfuehrung = vorfuehrungService.vorfuehrung(vorfuehrungId);
|
||||
if (vorfuehrung == null) {
|
||||
throw new RestResourceNotFoundException("no Vorfuehrung %s found.".formatted(id));
|
||||
}
|
||||
|
||||
return vorfuehrung;
|
||||
}
|
||||
|
||||
@PostMapping("/rest/vorfuehrungen")
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN')")
|
||||
public ResponseEntity<RestResponseMessage> uploadVorfuehrungen(@RequestParam("file") final MultipartFile file) {
|
||||
RestUploadResult restUploadResult = uploadViaRestCall(vorfuehrungUploadService, file);
|
||||
return ResponseEntity.status(restUploadResult.status())
|
||||
.body(new RestResponseMessage(restUploadResult.message()));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package de.accso.flexinale.api_in.web;
|
||||
|
||||
import de.accso.flexinale.application.services.FilmService;
|
||||
import de.accso.flexinale.domain.model.Besucher;
|
||||
import de.accso.flexinale.domain.model.Film;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.infrastructure.security.BenutzerDetailsService;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
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.server.ResponseStatusException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
public class FilmWebController {
|
||||
@Autowired
|
||||
private FilmService filmService;
|
||||
|
||||
@Autowired
|
||||
private BenutzerDetailsService benutzerService;
|
||||
|
||||
@GetMapping(value="/filme")
|
||||
@PreAuthorize("hasRole('ROLE_BESUCHER')")
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
public String filme(final Model model) {
|
||||
List<Film> filme = filmService.filme();
|
||||
model.addAttribute("filme", filme);
|
||||
return "filme";
|
||||
}
|
||||
|
||||
@GetMapping(value="/film/{id}")
|
||||
@PreAuthorize("hasRole('ROLE_BESUCHER')")
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
public String film(@PathVariable("id") final String id, final Model model) {
|
||||
Identifiable.Id filmId = Identifiable.Id.of(id);
|
||||
Film film = filmService.film(filmId);
|
||||
if (film == null) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "film %s not available".formatted(id));
|
||||
}
|
||||
else {
|
||||
model.addAttribute("film", film);
|
||||
List<Vorfuehrung> vorfuehrungenOfFilm = filmService.vorfuehrungenFuer(filmId);
|
||||
model.addAttribute("vorfuehrungen", vorfuehrungenOfFilm);
|
||||
|
||||
if (!vorfuehrungenOfFilm.isEmpty()) {
|
||||
Besucher loggedinBesucher = benutzerService.getLoggedInBesucher();
|
||||
List<String> vorfuehrungenMitUeberlapp =
|
||||
filmService.vorfuehrungenMitUeberlapp(vorfuehrungenOfFilm, loggedinBesucher)
|
||||
.stream().map(it -> it.id()).toList();
|
||||
List<String> vorfuehrungenMitTicket =
|
||||
filmService.vorfuehrungenFuerDieDerBenutzerEinTicketHat(vorfuehrungenOfFilm, loggedinBesucher)
|
||||
.stream().map(it -> it.id()).toList();
|
||||
|
||||
model.addAttribute("vorfuehrungenMitUeberlapp", vorfuehrungenMitUeberlapp);
|
||||
model.addAttribute("vorfuehrungenMitTicket", vorfuehrungenMitTicket);
|
||||
}
|
||||
}
|
||||
|
||||
return "film";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package de.accso.flexinale.api_in.web;
|
||||
|
||||
import de.accso.flexinale.application.Config;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
|
||||
@Controller
|
||||
public class IndexWebController {
|
||||
@Autowired
|
||||
private Config config;
|
||||
|
||||
@GetMapping(value="/")
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
public String index(final Model model) {
|
||||
model.addAttribute("applicationTitle", config.getApplicationTitle());
|
||||
model.addAttribute("buildVersion", config.getBuildVersion());
|
||||
model.addAttribute("buildDate", config.getBuildDate());
|
||||
|
||||
return "index";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package de.accso.flexinale.api_in.web;
|
||||
|
||||
import de.accso.flexinale.domain.model.Kino;
|
||||
import de.accso.flexinale.application.services.KinoService;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
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.server.ResponseStatusException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
public class KinoWebController {
|
||||
|
||||
@Autowired
|
||||
private KinoService kinoService;
|
||||
|
||||
@GetMapping(value="/kinos")
|
||||
@PreAuthorize("hasRole('ROLE_BESUCHER')")
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
public String kinos(final Model model) {
|
||||
List<Kino> kinos = kinoService.kinos();
|
||||
model.addAttribute("kinos", kinos);
|
||||
return "kinos";
|
||||
}
|
||||
|
||||
@GetMapping(value="/kino/{id}")
|
||||
@PreAuthorize("hasRole('ROLE_BESUCHER')")
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
public String kino(@PathVariable("id") final String id, final Model model) {
|
||||
Identifiable.Id kinoId = Identifiable.Id.of(id);
|
||||
Kino kino = kinoService.kino(kinoId);
|
||||
if (kino == null) {
|
||||
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "kino %s not available".formatted(id));
|
||||
}
|
||||
else {
|
||||
model.addAttribute("kino", kino);
|
||||
return "kino";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package de.accso.flexinale.api_in.web;
|
||||
|
||||
import de.accso.flexinale.domain.model.Ticket;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static de.accso.flexinale.shared_kernel.RawWrapper.getRawOrNull;
|
||||
|
||||
class TicketSorter {
|
||||
|
||||
/**
|
||||
* return a list of list of tickets, sorted by time (inner list) and date (outer list).
|
||||
* each list contains the tickets for a day
|
||||
*/
|
||||
static List<List<VorfuehrungMitAnzahlTicketsTO>> sortAndMapTicketsPerDay(final List<Ticket> allTickets) {
|
||||
if (allTickets == null || allTickets.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 1) tickets by day
|
||||
List<List<Ticket>> ticketsProTag = new ArrayList<>();
|
||||
|
||||
LocalDate previousDate = LocalDate.MIN; // far past :-)
|
||||
for (Ticket ticket : allTickets) {
|
||||
LocalDate currentDate = ((LocalDateTime) getRawOrNull(ticket.vorfuehrung.zeit)).toLocalDate();
|
||||
|
||||
if (currentDate.equals(previousDate)) {
|
||||
ticketsProTag.getLast().add(ticket);
|
||||
}
|
||||
else {
|
||||
List<Ticket> tickets = new ArrayList<>();
|
||||
tickets.add(ticket);
|
||||
ticketsProTag.add(tickets);
|
||||
}
|
||||
previousDate = currentDate;
|
||||
}
|
||||
|
||||
// 2) inner structure: Vorfuehrung and tickets
|
||||
List<List<VorfuehrungMitAnzahlTicketsTO>> vorfuehrungUndAnzahlTicketsFuerVorfuehrungProTag = new ArrayList<>();
|
||||
|
||||
for (List<Ticket> ticketsForOneDay : ticketsProTag) {
|
||||
List<VorfuehrungMitAnzahlTicketsTO> vorfuehrungenUndAnzahlTicketsFuerEinenTag = new ArrayList<>();
|
||||
int anzahlTicketsFuerVorfuehrung = 0;
|
||||
Vorfuehrung previousVorfuehrung = ticketsForOneDay.getFirst().vorfuehrung;
|
||||
|
||||
for (Ticket ticket : ticketsForOneDay) {
|
||||
// inside each day, vorfuehrungen are ordered by zeit.
|
||||
// There is never more than one vorfuehrung at a zeit for a user.
|
||||
// So tickets for the same vorfuehrung are all in a row - that's why the following works.
|
||||
Vorfuehrung currentVorfuehrung = ticket.vorfuehrung;
|
||||
|
||||
if (currentVorfuehrung.equals(previousVorfuehrung)) {
|
||||
anzahlTicketsFuerVorfuehrung++;
|
||||
}
|
||||
else {
|
||||
// add the information for previous Vorfuehrung
|
||||
VorfuehrungMitAnzahlTicketsTO to = mapVorfuehrungToTO(
|
||||
anzahlTicketsFuerVorfuehrung, previousVorfuehrung);
|
||||
vorfuehrungenUndAnzahlTicketsFuerEinenTag.add(to);
|
||||
anzahlTicketsFuerVorfuehrung = 1; // ... we also have a new ticket for the next Vorfuehrung
|
||||
}
|
||||
|
||||
previousVorfuehrung = currentVorfuehrung;
|
||||
}
|
||||
|
||||
// Also add the last Vorfuehrung an anzahlTickets when day is over
|
||||
VorfuehrungMitAnzahlTicketsTO to = mapVorfuehrungToTO(
|
||||
anzahlTicketsFuerVorfuehrung, previousVorfuehrung);
|
||||
vorfuehrungenUndAnzahlTicketsFuerEinenTag.add(to);
|
||||
vorfuehrungUndAnzahlTicketsFuerVorfuehrungProTag.add(vorfuehrungenUndAnzahlTicketsFuerEinenTag);
|
||||
}
|
||||
return vorfuehrungUndAnzahlTicketsFuerVorfuehrungProTag;
|
||||
}
|
||||
|
||||
private static VorfuehrungMitAnzahlTicketsTO mapVorfuehrungToTO(final int anzahlTicketsFuerVorfuehrung,
|
||||
final Vorfuehrung previousVorfuehrung) {
|
||||
return new VorfuehrungMitAnzahlTicketsTO(
|
||||
new VorfuehrungMitAnzahlTicketsTO.Zeit(getRawOrNull(previousVorfuehrung.zeit)),
|
||||
previousVorfuehrung.film,
|
||||
previousVorfuehrung.kinoSaal,
|
||||
new VorfuehrungMitAnzahlTicketsTO.AnzahlTickets(anzahlTicketsFuerVorfuehrung)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
package de.accso.flexinale.api_in.web;
|
||||
|
||||
import de.accso.flexinale.application.services.TicketService;
|
||||
import de.accso.flexinale.application.services.VorfuehrungService;
|
||||
import de.accso.flexinale.domain.model.KontingentBereitsAusgeschoepftException;
|
||||
import de.accso.flexinale.domain.model.Besucher;
|
||||
import de.accso.flexinale.domain.model.Film;
|
||||
import de.accso.flexinale.domain.model.KinoSaal;
|
||||
import de.accso.flexinale.domain.model.Ticket;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.infrastructure.security.BenutzerDetailsService;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.shared_kernel.RawWrapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
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.servlet.mvc.support.RedirectAttributes;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Controller
|
||||
public class TicketWebController {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TicketWebController.class);
|
||||
|
||||
@Autowired
|
||||
private TicketService ticketService;
|
||||
|
||||
@Autowired
|
||||
private BenutzerDetailsService benutzerService;
|
||||
|
||||
@Autowired
|
||||
private VorfuehrungService vorfuehrungService;
|
||||
|
||||
@GetMapping(value="/tickets")
|
||||
@PreAuthorize("hasRole('ROLE_BESUCHER')")
|
||||
public String listTickets(final Model model) {
|
||||
Besucher loggedInBenutzer = benutzerService.getLoggedInBesucher();
|
||||
|
||||
List<Ticket> allTickets = ticketService.findByBesucherOrderByZeit(loggedInBenutzer);
|
||||
List<List<VorfuehrungMitAnzahlTicketsTO>> ticketsByDay = TicketSorter.sortAndMapTicketsPerDay(allTickets);
|
||||
|
||||
model.addAttribute("ticketsByDay", ticketsByDay);
|
||||
|
||||
int totalNumberOfTickets = ticketService.gesamtZahlDerTicketsFuer(loggedInBenutzer);
|
||||
model.addAttribute("totalNumberOfTickets", totalNumberOfTickets);
|
||||
|
||||
return ("tickets");
|
||||
}
|
||||
|
||||
@PostMapping("/vorfuehrung/loeseGutscheineOnlineEin")
|
||||
@PreAuthorize("hasRole('ROLE_BESUCHER')")
|
||||
public String loeseGutscheineOnlineEin(
|
||||
@RequestParam("vorfuehrungId") final String vId,
|
||||
@RequestParam("anzahl") final int anzahl,
|
||||
final RedirectAttributes redirAttrs) {
|
||||
Besucher loggedInBesucher = benutzerService.getLoggedInBesucher();
|
||||
|
||||
Identifiable.Id vorfuehrungId = Identifiable.Id.of(vId);
|
||||
try {
|
||||
ticketService.loeseGutscheineOnlineFuerVorfuehrungEin(vorfuehrungId, loggedInBesucher, anzahl);
|
||||
}
|
||||
catch (KontingentBereitsAusgeschoepftException kbaEx) {
|
||||
String message = "Kontingent exceeded: no %d tickets for Vorfuehrung %s and Besucher %s available"
|
||||
.formatted(anzahl, vorfuehrungId, loggedInBesucher.login);
|
||||
LOGGER.info(message);
|
||||
|
||||
Vorfuehrung vorfuehrung = vorfuehrungService.vorfuehrung(vorfuehrungId);
|
||||
String filmId = vorfuehrung.film.id().id();
|
||||
|
||||
redirAttrs.addFlashAttribute("error", "Keine %d Tickets mehr vorhanden".formatted(anzahl));
|
||||
return "redirect:/film/" + filmId;
|
||||
}
|
||||
redirAttrs.addFlashAttribute("success", "%d Ticket(s) erfolgreich gekauft".formatted(anzahl));
|
||||
return "redirect:/tickets";
|
||||
}
|
||||
}
|
||||
|
||||
// class used to "flatten" Vorfuehrung and number of Tickets for Vorfuehrung for Clients
|
||||
record VorfuehrungMitAnzahlTicketsTO(Zeit zeit, Film film, KinoSaal kinoSaal, AnzahlTickets anzahlTickets) {
|
||||
public record Zeit(LocalDateTime raw) implements RawWrapper<LocalDateTime> {
|
||||
public Zeit(LocalDateTime raw) {
|
||||
this.raw = raw.withNano(0); // precision is second
|
||||
}
|
||||
}
|
||||
|
||||
public record AnzahlTickets(Integer raw) implements RawWrapper<Integer> {}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package de.accso.flexinale.application;
|
||||
|
||||
public interface Config { //TODO split into business and technical configuration
|
||||
int getQuoteOnline();
|
||||
int getMinZeitZwischenVorfuehrungenInMinuten();
|
||||
|
||||
String getApplicationTitle();
|
||||
String getBuildVersion();
|
||||
String getBuildDate();
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package de.accso.flexinale.application;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class UeberlappendeVorfuehrungException extends RuntimeException {
|
||||
public UeberlappendeVorfuehrungException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public UeberlappendeVorfuehrungException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public UeberlappendeVorfuehrungException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public UeberlappendeVorfuehrungException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
protected UeberlappendeVorfuehrungException(final String message, final Throwable cause,
|
||||
final boolean enableSuppression, final boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package de.accso.flexinale.application.services;
|
||||
|
||||
import de.accso.flexinale.domain.dao.AbstractDao;
|
||||
import de.accso.flexinale.shared_kernel.DoNotCheckInArchitectureTests;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@DoNotCheckInArchitectureTests
|
||||
public abstract class AbstractExcelDataUploadService<E extends Identifiable> implements ExcelDataUploadService<E> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractExcelDataUploadService.class);
|
||||
|
||||
@Override
|
||||
public void beforeLoad(Object... o) {}
|
||||
|
||||
@Override
|
||||
public void afterLoad(Object... o) {}
|
||||
|
||||
@Override
|
||||
public final Collection<E> loadDataFromExcelSheet(final String resourceName) throws IOException {
|
||||
try (InputStream stream = AbstractExcelDataUploadService.class.getResourceAsStream(resourceName)) {
|
||||
if (stream == null) {
|
||||
throw new FileNotFoundException("Resource '" + resourceName + "' not found!");
|
||||
}
|
||||
return loadDataFromExcelSheet(stream);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Collection<E> loadDataFromExcelSheet(final InputStream stream) throws IOException {
|
||||
Collection<E> dataSet = new HashSet<>();
|
||||
|
||||
// Excel sheet (the sheet, not the file!) needs to be named exactly like the entity's class name
|
||||
// (example: "Kino")
|
||||
String sheetName = getNameOfExcelDataType();
|
||||
|
||||
try (Workbook workbook = new XSSFWorkbook(stream)) {
|
||||
// iterate through each row of first/desired sheet from the workbook
|
||||
for (Row excelRow : workbook.getSheet(sheetName)) {
|
||||
String firstCell = excelRow.getCell(0).getStringCellValue();
|
||||
|
||||
// ignore comments, starting with # , load all other rows
|
||||
if (!firstCell.startsWith("#")) {
|
||||
E data = createDataFromExcelRow(excelRow);
|
||||
dataSet.add(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dataSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int loadDataFromExcelSheetAndPersist(
|
||||
final String resourceName, final PersistMode mode) throws IOException {
|
||||
try (InputStream stream = AbstractExcelDataUploadService.class.getResourceAsStream(resourceName)) {
|
||||
if (stream == null) {
|
||||
throw new IOException("Resource '" + resourceName + "' not found!");
|
||||
}
|
||||
return loadDataFromExcelSheetAndPersist(stream, mode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int loadDataFromExcelSheetAndPersist(
|
||||
final InputStream stream, final PersistMode mode) throws IOException {
|
||||
return loadDataFromExcelSheetAndPersistAndReturn(stream, mode).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Collection<E> loadDataFromExcelSheetAndPersistAndReturn(
|
||||
final InputStream stream, final PersistMode mode) throws IOException {
|
||||
Collection<E> dataCollection = loadDataFromExcelSheet(stream);
|
||||
Collection<E> dataLoaded = new HashSet<>();
|
||||
AbstractDao<E> dao = getDao();
|
||||
|
||||
// in mode "ADD_ONLY" no updates are allowed
|
||||
for (E dataFromExcelSheet : dataCollection) {
|
||||
Identifiable.Id id = dataFromExcelSheet.id();
|
||||
|
||||
if (mode == PersistMode.ADD_ONLY && dao.findById(id).isPresent()) {
|
||||
String sheetName = getNameOfExcelDataType();
|
||||
|
||||
LOGGER.warn("%s %s already exists. Will not be persisted because 'ADD' is active".formatted(sheetName, id));
|
||||
} else {
|
||||
dao.save(dataFromExcelSheet);
|
||||
dataLoaded.add(dataFromExcelSheet);
|
||||
}
|
||||
}
|
||||
|
||||
return dataLoaded; // only return what really has been loaded
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package de.accso.flexinale.application.services;
|
||||
|
||||
import de.accso.flexinale.domain.dao.AbstractDao;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collection;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public interface ExcelDataUploadService<E extends Identifiable> {
|
||||
Collection<E> loadDataFromExcelSheet(final String resourceName) throws IOException;
|
||||
Collection<E> loadDataFromExcelSheet(final InputStream stream) throws IOException;
|
||||
|
||||
int loadDataFromExcelSheetAndPersist(final String resourceName, PersistMode mode) throws IOException;
|
||||
int loadDataFromExcelSheetAndPersist(final InputStream stream, PersistMode mode) throws IOException;
|
||||
Collection<E> loadDataFromExcelSheetAndPersistAndReturn(final InputStream stream, PersistMode mode) throws IOException;
|
||||
|
||||
AbstractDao<E> getDao();
|
||||
String getNameOfExcelDataType();
|
||||
|
||||
void beforeLoad(Object... o);
|
||||
void afterLoad(Object... o);
|
||||
E createDataFromExcelRow(final Row row);
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package de.accso.flexinale.application.services;
|
||||
|
||||
import de.accso.flexinale.domain.model.Besucher;
|
||||
import de.accso.flexinale.domain.model.Film;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Transactional
|
||||
public class FilmService {
|
||||
|
||||
private final de.accso.flexinale.domain.services.FilmService filmService;
|
||||
|
||||
public FilmService(de.accso.flexinale.domain.services.FilmService filmService) {
|
||||
this.filmService = filmService;
|
||||
}
|
||||
|
||||
public Film film(Identifiable.Id filmId) {
|
||||
return filmService.film(filmId);
|
||||
}
|
||||
|
||||
public List<Film> filme() {
|
||||
return filmService.filme();
|
||||
}
|
||||
|
||||
public List<Vorfuehrung> vorfuehrungenFuer(Identifiable.Id filmId) {
|
||||
return filmService.vorfuehrungenFuer(filmId);
|
||||
}
|
||||
|
||||
public List<Identifiable.Id> vorfuehrungenMitUeberlapp(List<Vorfuehrung> vorfuehrungenOfFilm, Besucher loggedinBesucher) {
|
||||
return filmService.vorfuehrungenMitUeberlapp(vorfuehrungenOfFilm, loggedinBesucher);
|
||||
}
|
||||
|
||||
|
||||
public List<Identifiable.Id> vorfuehrungenFuerDieDerBenutzerEinTicketHat(List<Vorfuehrung> vorfuehrungenOfFilm, Besucher loggedinBesucher) {
|
||||
return filmService.vorfuehrungenFuerDieDerBenutzerEinTicketHat(vorfuehrungenOfFilm, loggedinBesucher);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package de.accso.flexinale.application.services;
|
||||
|
||||
import de.accso.flexinale.domain.dao.AbstractDao;
|
||||
import de.accso.flexinale.domain.dao.FilmDao;
|
||||
import de.accso.flexinale.domain.model.Film;
|
||||
import de.accso.flexinale.shared_kernel.FlexinaleIllegalArgumentException;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Transactional
|
||||
public class FilmUploadService extends AbstractExcelDataUploadService<Film> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(FilmUploadService.class);
|
||||
|
||||
private final FilmDao filmDao;
|
||||
|
||||
public FilmUploadService(final FilmDao filmDao) {
|
||||
this.filmDao = filmDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractDao<Film> getDao() {
|
||||
return filmDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameOfExcelDataType() {
|
||||
return Film.class.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Film createDataFromExcelRow(final Row excelRow) {
|
||||
Identifiable.Id filmId = Identifiable.Id.of(excelRow.getCell(0).getStringCellValue());
|
||||
|
||||
Cell cellDauer = excelRow.getCell(3);
|
||||
int dauerInMinuten =
|
||||
switch(cellDauer.getCellType()) {
|
||||
case NUMERIC -> (int) cellDauer.getNumericCellValue();
|
||||
case STRING -> Integer.parseInt(cellDauer.getStringCellValue());
|
||||
default -> throw new FlexinaleIllegalArgumentException("Dauer is not a number");
|
||||
};
|
||||
|
||||
Film film = new Film(
|
||||
filmId,
|
||||
new Film.Titel(excelRow.getCell(1).getStringCellValue()),
|
||||
new Film.ImdbUrl(excelRow.getCell(2).getStringCellValue()),
|
||||
new Film.DauerInMinuten(dauerInMinuten)
|
||||
);
|
||||
|
||||
LOGGER.debug("New Film created " + film);
|
||||
|
||||
return film;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
package de.accso.flexinale.application.services;
|
||||
|
||||
import de.accso.flexinale.domain.dao.AbstractDao;
|
||||
import de.accso.flexinale.domain.dao.KinoSaalDao;
|
||||
import de.accso.flexinale.domain.model.Kino;
|
||||
import de.accso.flexinale.domain.model.KinoSaal;
|
||||
import de.accso.flexinale.shared_kernel.FlexinaleIllegalArgumentException;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.apache.poi.ss.usermodel.Cell;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
@Transactional
|
||||
public class KinoSaalUploadService extends AbstractExcelDataUploadService<KinoSaal> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(KinoSaalUploadService.class);
|
||||
|
||||
private final KinoSaalDao kinoSaalDao;
|
||||
|
||||
public KinoSaalUploadService(KinoSaalDao kinoSaalDao) {
|
||||
this.kinoSaalDao = kinoSaalDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractDao<KinoSaal> getDao() {
|
||||
return kinoSaalDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameOfExcelDataType() {
|
||||
return KinoSaal.class.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public KinoSaal createDataFromExcelRow(final Row excelRow) {
|
||||
String kinoSaalIdAsString = excelRow.getCell(0).getStringCellValue();
|
||||
|
||||
Identifiable.Id kinoId = Identifiable.Id.of(excelRow.getCell(3).getStringCellValue());
|
||||
|
||||
Cell cellAnzahlPlaetze = excelRow.getCell(2);
|
||||
int anzahlPlaetze =
|
||||
switch(cellAnzahlPlaetze.getCellType()) {
|
||||
case NUMERIC -> (int) cellAnzahlPlaetze.getNumericCellValue();
|
||||
case STRING -> Integer.parseInt(cellAnzahlPlaetze.getStringCellValue());
|
||||
default -> throw new FlexinaleIllegalArgumentException("Anzahl Plaetze is not a number");
|
||||
};
|
||||
|
||||
KinoSaal kinoSaal = new KinoSaal(
|
||||
Identifiable.Id.of(kinoSaalIdAsString),
|
||||
new KinoSaal.Name(excelRow.getCell(1).getStringCellValue()),
|
||||
new KinoSaal.AnzahlPlaetze(anzahlPlaetze),
|
||||
new Kino(kinoId)
|
||||
);
|
||||
|
||||
LOGGER.debug("New KinoSaal created: " + kinoSaal);
|
||||
|
||||
return kinoSaal;
|
||||
}
|
||||
|
||||
public Map<Identifiable.Id, Collection<KinoSaal>> mapKinoSaeleToKino(final Collection<KinoSaal> kinoSaele) {
|
||||
Map<Identifiable.Id, Collection<KinoSaal>> saeleUndKinos = new HashMap<>();
|
||||
for (KinoSaal saal : kinoSaele) {
|
||||
Identifiable.Id key = saal.kino.id();
|
||||
|
||||
Collection<KinoSaal> kinoSaeleAlreadyMapped = saeleUndKinos.get(key);
|
||||
if (kinoSaeleAlreadyMapped == null) {
|
||||
HashSet<KinoSaal> saele = new HashSet<>();
|
||||
saele.add(saal);
|
||||
saeleUndKinos.put(key, saele);
|
||||
}
|
||||
else {
|
||||
kinoSaeleAlreadyMapped.add(saal);
|
||||
saeleUndKinos.put(key, kinoSaeleAlreadyMapped);
|
||||
}
|
||||
}
|
||||
return saeleUndKinos;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package de.accso.flexinale.application.services;
|
||||
|
||||
import de.accso.flexinale.domain.model.Kino;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Transactional
|
||||
public class KinoService {
|
||||
|
||||
private final de.accso.flexinale.domain.services.KinoService kinoService;
|
||||
|
||||
public KinoService(de.accso.flexinale.domain.services.KinoService kinoService) {
|
||||
this.kinoService = kinoService;
|
||||
}
|
||||
|
||||
public Kino kino(Identifiable.Id kinoId) {
|
||||
return kinoService.kino(kinoId);
|
||||
}
|
||||
|
||||
public List<Kino> kinos() {
|
||||
return kinoService.kinos();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package de.accso.flexinale.application.services;
|
||||
|
||||
import de.accso.flexinale.domain.dao.AbstractDao;
|
||||
import de.accso.flexinale.domain.dao.KinoDao;
|
||||
import de.accso.flexinale.domain.model.Kino;
|
||||
import de.accso.flexinale.domain.model.KinoSaal;
|
||||
import de.accso.flexinale.shared_kernel.DeveloperMistakeException;
|
||||
import de.accso.flexinale.shared_kernel.FlexinaleIllegalStateException;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Transactional
|
||||
public class KinoUploadService extends AbstractExcelDataUploadService<Kino> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(KinoUploadService.class);
|
||||
|
||||
private final KinoDao kinoDao;
|
||||
|
||||
private final Map<Identifiable.Id, Collection<KinoSaal>> alleKinosUndIhreSaele = new HashMap<>();
|
||||
|
||||
public KinoUploadService(KinoDao kinoDao) {
|
||||
this.kinoDao = kinoDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractDao<Kino> getDao() {
|
||||
return kinoDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameOfExcelDataType() {
|
||||
return Kino.class.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeLoad(Object... o) {
|
||||
if ((o.length != 1) && (!(o[0] instanceof Map))) {
|
||||
throw new DeveloperMistakeException("wrong type: expecting a " +
|
||||
"Map<Identifiable.Id, Collection<KinoSaal>> of Kino and their KinoSaele");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<Identifiable.Id, Collection<KinoSaal>> newKinoAndKinoSaele = (Map<Identifiable.Id, Collection<KinoSaal>>) o[0];
|
||||
this.alleKinosUndIhreSaele.putAll(newKinoAndKinoSaele);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterLoad(Object... o) {
|
||||
alleKinosUndIhreSaele.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Kino createDataFromExcelRow(final Row excelRow) {
|
||||
String kinoId = excelRow.getCell(0).getStringCellValue();
|
||||
|
||||
Collection<KinoSaal> saeleImKino = alleKinosUndIhreSaele.get(Identifiable.Id.of(kinoId));
|
||||
if (saeleImKino == null || saeleImKino.isEmpty()) {
|
||||
throw new FlexinaleIllegalStateException("no Saele for Kino %s found".formatted(kinoId));
|
||||
}
|
||||
|
||||
Kino kino = new Kino(
|
||||
Identifiable.Id.of(kinoId),
|
||||
new Kino.Name(excelRow.getCell(1).getStringCellValue()),
|
||||
new Kino.Adresse(excelRow.getCell(2).getStringCellValue()),
|
||||
new Kino.EmailAdresse(excelRow.getCell(3).getStringCellValue()),
|
||||
Set.copyOf(saeleImKino)
|
||||
);
|
||||
|
||||
LOGGER.debug("New Kino created: " + kino);
|
||||
|
||||
return kino;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package de.accso.flexinale.application.services;
|
||||
|
||||
public enum PersistMode {
|
||||
// updates on existing entities allowed (beware of inconsistencies with derived entities!)
|
||||
UPDATE,
|
||||
|
||||
// only new data can be added, no updates on existing entities allowed
|
||||
ADD_ONLY
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package de.accso.flexinale.application.services;
|
||||
|
||||
import de.accso.flexinale.domain.model.Besucher;
|
||||
import de.accso.flexinale.domain.model.Ticket;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Transactional
|
||||
public class TicketService {
|
||||
|
||||
private final de.accso.flexinale.domain.services.TicketService ticketService;
|
||||
|
||||
public TicketService(de.accso.flexinale.domain.services.TicketService ticketService) {
|
||||
this.ticketService = ticketService;
|
||||
}
|
||||
|
||||
public List<Ticket> findByBesucherOrderByZeit(Besucher loggedInBenutzer) {
|
||||
return ticketService.findByBesucherOrderByZeit(loggedInBenutzer);
|
||||
}
|
||||
|
||||
public int gesamtZahlDerTicketsFuer(Besucher loggedInBenutzer) {
|
||||
return ticketService.gesamtZahlDerTicketsFuer(loggedInBenutzer);
|
||||
}
|
||||
|
||||
public void loeseGutscheineOnlineFuerVorfuehrungEin(Identifiable.Id vorfuehrungId, Besucher loggedInBesucher, int anzahl) {
|
||||
ticketService.loeseGutscheineOnlineFuerVorfuehrungEin(vorfuehrungId, loggedInBesucher, anzahl);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package de.accso.flexinale.application.services;
|
||||
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Transactional
|
||||
public class VorfuehrungService {
|
||||
|
||||
private final de.accso.flexinale.domain.services.VorfuehrungService vorfuehrungService;
|
||||
|
||||
public VorfuehrungService(de.accso.flexinale.domain.services.VorfuehrungService vorfuehrungService) {
|
||||
this.vorfuehrungService = vorfuehrungService;
|
||||
}
|
||||
|
||||
public Vorfuehrung vorfuehrung(Identifiable.Id vorfuehrungId) {
|
||||
return vorfuehrungService.vorfuehrung(vorfuehrungId);
|
||||
}
|
||||
|
||||
public List<Vorfuehrung> vorfuehrungen() {
|
||||
return vorfuehrungService.vorfuehrungen();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package de.accso.flexinale.application.services;
|
||||
|
||||
import de.accso.flexinale.application.Config;
|
||||
import de.accso.flexinale.domain.dao.AbstractDao;
|
||||
import de.accso.flexinale.domain.dao.KinoSaalDao;
|
||||
import de.accso.flexinale.domain.dao.VorfuehrungDao;
|
||||
import de.accso.flexinale.domain.model.Film;
|
||||
import de.accso.flexinale.domain.model.KinoSaal;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import jakarta.transaction.Transactional;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Transactional
|
||||
public class VorfuehrungUploadService extends AbstractExcelDataUploadService<Vorfuehrung> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(VorfuehrungUploadService.class);
|
||||
|
||||
private final KinoSaalDao kinoSaalDao;
|
||||
private final VorfuehrungDao vorfuehrungDao;
|
||||
|
||||
private final Config config;
|
||||
|
||||
public VorfuehrungUploadService(final VorfuehrungDao vorfuehrungDao, final KinoSaalDao kinoSaalDao, final Config config) {
|
||||
this.kinoSaalDao = kinoSaalDao;
|
||||
this.vorfuehrungDao = vorfuehrungDao;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractDao<Vorfuehrung> getDao() {
|
||||
return vorfuehrungDao;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameOfExcelDataType() {
|
||||
return Vorfuehrung.class.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vorfuehrung createDataFromExcelRow(final Row excelRow) {
|
||||
String vorfuehrungIdAsString = excelRow.getCell(0).getStringCellValue();
|
||||
|
||||
// expects date/time in format like for example 2023-02-20T17:45
|
||||
LocalDateTime zeit = LocalDateTime.parse(excelRow.getCell(1).getStringCellValue());
|
||||
|
||||
String filmIdAsString = excelRow.getCell(2).getStringCellValue();
|
||||
Film film = new Film(Identifiable.Id.of(filmIdAsString));
|
||||
String kinoSaalId = excelRow.getCell(3).getStringCellValue();
|
||||
KinoSaal kinoSaal = kinoSaalDao.findById(Identifiable.Id.of(kinoSaalId)).orElseThrow(IllegalStateException::new);
|
||||
|
||||
Vorfuehrung vorfuehrung = new Vorfuehrung(Identifiable.Id.of(vorfuehrungIdAsString),
|
||||
new Vorfuehrung.Zeit(zeit), film, kinoSaal, config.getQuoteOnline());
|
||||
|
||||
LOGGER.debug("New Vorfuehrung created: " + vorfuehrung);
|
||||
|
||||
return vorfuehrung;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package de.accso.flexinale.domain.dao;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public interface AbstractDao<E extends Identifiable> {
|
||||
List<E> findAll();
|
||||
|
||||
Optional<E> findById(final Identifiable.Id id);
|
||||
|
||||
E save(final E entity);
|
||||
|
||||
void delete(final E data);
|
||||
void deleteById(final Identifiable.Id id);
|
||||
void deleteAll();
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package de.accso.flexinale.domain.dao;
|
||||
|
||||
import de.accso.flexinale.domain.model.Film;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface FilmDao extends AbstractDao<Film> {
|
||||
|
||||
@Override
|
||||
Optional<Film> findById(final Identifiable.Id id);
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package de.accso.flexinale.domain.dao;
|
||||
|
||||
import de.accso.flexinale.domain.model.Kino;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface KinoDao extends AbstractDao<Kino> {
|
||||
|
||||
@Override
|
||||
Optional<Kino> findById(final Identifiable.Id id);
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package de.accso.flexinale.domain.dao;
|
||||
|
||||
import de.accso.flexinale.domain.model.KinoSaal;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface KinoSaalDao extends AbstractDao<KinoSaal> {
|
||||
|
||||
@Override
|
||||
Optional<KinoSaal> findById(final Identifiable.Id id);
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package de.accso.flexinale.domain.dao;
|
||||
|
||||
import de.accso.flexinale.domain.model.Kontingent;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface KontingentDao extends AbstractDao<Kontingent> {
|
||||
|
||||
@Override
|
||||
Optional<Kontingent> findById(final Identifiable.Id id);
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package de.accso.flexinale.domain.dao;
|
||||
|
||||
import de.accso.flexinale.domain.model.Besucher;
|
||||
import de.accso.flexinale.domain.model.Ticket;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface TicketDao extends AbstractDao<Ticket> {
|
||||
|
||||
@Override
|
||||
Optional<Ticket> findById(final Identifiable.Id id);
|
||||
|
||||
List<Ticket> findByBesucherOrderByZeit(final Besucher benutzer);
|
||||
|
||||
int gesamtZahlDerTicketsFuer(final Besucher benutzer);
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package de.accso.flexinale.domain.dao;
|
||||
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface VorfuehrungDao extends AbstractDao<Vorfuehrung> {
|
||||
|
||||
@Override
|
||||
Optional<Vorfuehrung> findById(final Identifiable.Id id);
|
||||
|
||||
List<Vorfuehrung> findByFilmId(final Identifiable.Id filmId);
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package de.accso.flexinale.domain.model;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.EqualsByContent;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.shared_kernel.RawWrapper;
|
||||
import de.accso.flexinale.shared_kernel.Versionable;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
public class Besucher implements Identifiable, Versionable, EqualsByContent {
|
||||
public record Login(String raw) implements RawWrapper<String> {}
|
||||
public record EmailAdresse(String raw) implements RawWrapper<String> {}
|
||||
public record Name(String raw) implements RawWrapper<String> {}
|
||||
public record Vorname(String raw) implements RawWrapper<String> {}
|
||||
|
||||
public final Id id;
|
||||
public final Version version;
|
||||
|
||||
public final Login login;
|
||||
public final EmailAdresse emailAdresse;
|
||||
public final Name name;
|
||||
public final Vorname vorname;
|
||||
|
||||
public Besucher(final Id id,
|
||||
final Login login, final EmailAdresse emailAdresse, final Name name, final Vorname vorname) {
|
||||
this(id, Versionable.unknownVersion(), login, emailAdresse, name, vorname);
|
||||
}
|
||||
|
||||
public Besucher(final Id id, final Version version,
|
||||
final Login login, final EmailAdresse emailAdresse, final Name name, final Vorname vorname) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.login = login;
|
||||
this.emailAdresse = emailAdresse;
|
||||
this.name = name;
|
||||
this.vorname = vorname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (!equalsByContent(o)) return false;
|
||||
|
||||
if (this == o) {return true;}
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
Besucher that = (Besucher) o;
|
||||
return new EqualsBuilder().append(version, that.version).isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsByContent(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
Besucher that = (Besucher) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(id, that.id)
|
||||
.append(login, that.login)
|
||||
.append(emailAdresse, that.emailAdresse).append(name, that.name)
|
||||
.append(vorname, that.vorname).isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(login).append(emailAdresse).append(name).append(vorname).toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package de.accso.flexinale.domain.model;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.EqualsByContent;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.shared_kernel.RawWrapper;
|
||||
import de.accso.flexinale.shared_kernel.Versionable;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
public class Film implements Identifiable, Versionable, EqualsByContent {
|
||||
public record Titel(String raw) implements RawWrapper<String> {}
|
||||
public record ImdbUrl(String raw) implements RawWrapper<String> {}
|
||||
public record DauerInMinuten(Integer raw) implements RawWrapper<Integer> {}
|
||||
|
||||
public final Id id;
|
||||
public final Version version;
|
||||
|
||||
public Titel titel;
|
||||
public ImdbUrl imdbUrl;
|
||||
public DauerInMinuten dauerInMinuten;
|
||||
|
||||
public Film(final Id id) {
|
||||
this(id, Versionable.unknownVersion());
|
||||
}
|
||||
|
||||
public Film(final Id id, final Version version) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Film(final Id id,
|
||||
final Titel titel, final ImdbUrl imdbUrl, final DauerInMinuten dauerInMinuten) {
|
||||
this(id, Versionable.unknownVersion(), titel, imdbUrl, dauerInMinuten);
|
||||
}
|
||||
|
||||
public Film(final Id id, final Version version,
|
||||
final Titel titel, final ImdbUrl imdbUrl, final DauerInMinuten dauerInMinuten) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.titel = titel;
|
||||
this.imdbUrl = imdbUrl;
|
||||
this.dauerInMinuten = dauerInMinuten;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (!equalsByContent(o)) return false;
|
||||
|
||||
if (this == o) {return true;}
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
final Film that = (Film) o;
|
||||
return new EqualsBuilder().append(version, that.version).isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsByContent(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
final Film that = (Film) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(id, that.id)
|
||||
.append(titel, that.titel).append(imdbUrl, that.imdbUrl)
|
||||
.append(dauerInMinuten, that.dauerInMinuten).isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(titel).append(imdbUrl).append(dauerInMinuten).toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
package de.accso.flexinale.domain.model;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.*;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class Kino implements Identifiable, Versionable, EqualsByContent {
|
||||
public record Name(String raw) implements RawWrapper<String> {}
|
||||
public record Adresse(String raw) implements RawWrapper<String> {}
|
||||
public record EmailAdresse(String raw) implements RawWrapper<String> {}
|
||||
|
||||
public final Id id;
|
||||
public final Version version;
|
||||
|
||||
public Name name;
|
||||
public Adresse adresse;
|
||||
public EmailAdresse emailAdresse;
|
||||
@DoNotCheckInArchitectureTests
|
||||
public Set<KinoSaal> kinoSaele = new HashSet<>();
|
||||
|
||||
public Kino(final Id id) {
|
||||
this(id, Versionable.unknownVersion());
|
||||
}
|
||||
|
||||
public Kino(final Id id, final Version version) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Kino(final Id id,
|
||||
final Name name, final Adresse adresse,
|
||||
final EmailAdresse emailAdresse, final Set<KinoSaal> kinoSaele) {
|
||||
this(id, Versionable.unknownVersion(), name, adresse, emailAdresse, kinoSaele);
|
||||
}
|
||||
|
||||
public Kino(final Id id, final Version version,
|
||||
final Name name, final Adresse adresse,
|
||||
final EmailAdresse emailAdresse, final Set<KinoSaal> kinoSaele) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.name = name;
|
||||
this.adresse = adresse;
|
||||
this.emailAdresse = emailAdresse;
|
||||
this.kinoSaele = kinoSaele;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void addSaele(final Collection<KinoSaal> saele) {
|
||||
kinoSaele.addAll(saele);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (!equalsByContent(o)) return false;
|
||||
|
||||
if (this == o) {return true;}
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
Kino that = (Kino) o;
|
||||
return new EqualsBuilder().append(version, that.version).isEquals();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantValue")
|
||||
@Override
|
||||
public boolean equalsByContent(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
Kino that = (Kino) o;
|
||||
|
||||
boolean result = new EqualsBuilder()
|
||||
.append(id, that.id)
|
||||
.append(name, that.name)
|
||||
.append(adresse, that.adresse).append(emailAdresse, that.emailAdresse)
|
||||
.isEquals();
|
||||
if (!result) return false;
|
||||
|
||||
if (kinoSaele == null && that.kinoSaele == null) return true;
|
||||
if (kinoSaele == null && that.kinoSaele != null) return false;
|
||||
if (kinoSaele != null && that.kinoSaele == null) return false;
|
||||
|
||||
List<KinoSaal> thisKSList = kinoSaele.stream().toList();
|
||||
List<KinoSaal> thatKSList = that.kinoSaele.stream().toList();
|
||||
if (thisKSList.size() != thatKSList.size()) return false;
|
||||
for (int counter = 0; counter < thisKSList.size(); counter++) {
|
||||
if (!thisKSList.get(counter).equalsByContent(thatKSList.get(counter))) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(name).append(adresse).append(emailAdresse)
|
||||
.append(kinoSaele.stream().toList()) // need to use list, as Set does not support deep equals
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
package de.accso.flexinale.domain.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import de.accso.flexinale.shared_kernel.*;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
||||
public class KinoSaal implements Identifiable, Versionable, EqualsByContent {
|
||||
public record Name(String raw) implements RawWrapper<String> {}
|
||||
public record AnzahlPlaetze(Integer raw) implements RawWrapper<Integer> {}
|
||||
|
||||
public final Id id;
|
||||
public final Version version;
|
||||
|
||||
public Name name;
|
||||
public AnzahlPlaetze anzahlPlaetze = new AnzahlPlaetze(0); //TODO why do we initialize this and not the rest?
|
||||
|
||||
@JsonIgnore
|
||||
@DoNotCheckInArchitectureTests
|
||||
public Kino kino;
|
||||
|
||||
public KinoSaal(final Id id) {
|
||||
this(id, Versionable.unknownVersion());
|
||||
}
|
||||
|
||||
public KinoSaal(final Id id, final Version version) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public KinoSaal(final Id id,
|
||||
final Name name, final AnzahlPlaetze anzahlPlaetze, final Kino kino) {
|
||||
this(id, Versionable.unknownVersion(), name, anzahlPlaetze, kino);
|
||||
}
|
||||
|
||||
public KinoSaal(final Id id, final Version version,
|
||||
final Name name, final AnzahlPlaetze anzahlPlaetze, final Kino kino) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.name = name;
|
||||
this.anzahlPlaetze = anzahlPlaetze;
|
||||
this.kino = kino;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KinoSaal{" +
|
||||
"id='" + id + '\'' +
|
||||
", version=" + version +
|
||||
", name='" + name + '\'' +
|
||||
", anzahlPlaetze=" + anzahlPlaetze +
|
||||
(kino != null ? ", kino.id=" + kino.id : ", kino=null") +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (!equalsByContent(o)) return false;
|
||||
|
||||
if (this == o) {return true;}
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
KinoSaal that = (KinoSaal) o;
|
||||
return new EqualsBuilder().append(version, that.version).isEquals();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantValue")
|
||||
@Override
|
||||
public boolean equalsByContent(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
KinoSaal that = (KinoSaal) o;
|
||||
|
||||
boolean result = new EqualsBuilder()
|
||||
.append(id, that.id)
|
||||
.append(name, that.name)
|
||||
.append(anzahlPlaetze, that.anzahlPlaetze).
|
||||
isEquals();
|
||||
if (!result) return false;
|
||||
|
||||
if (kino == null && that.kino == null) return true;
|
||||
if (kino == null && that.kino != null) return false;
|
||||
if (kino != null) {
|
||||
result = kino.id.equals(that.kino.id); // do not check kino but only its id (otherwise Stackoverflow error)
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder builder = new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(name).append(anzahlPlaetze);
|
||||
if (kino != null) {
|
||||
builder.append(kino.id); // do not use kino but only its id (otherwise Stackoverflow error)
|
||||
}
|
||||
return builder.toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
package de.accso.flexinale.domain.model;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.*;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import static de.accso.flexinale.shared_kernel.RawWrapper.getRawOrNull;
|
||||
|
||||
public class Kontingent implements Identifiable, Versionable, EqualsByContent {
|
||||
public record RestKontingent(Integer raw) implements RawWrapper<Integer> {}
|
||||
|
||||
public enum KontingentType {
|
||||
ONLINE,
|
||||
ZENTRAL,
|
||||
KINOKASSE
|
||||
}
|
||||
|
||||
public final Id id;
|
||||
public final Version version;
|
||||
|
||||
private final RestKontingent restKontingentOnline;
|
||||
private final RestKontingent restKontingentZentral;
|
||||
private final RestKontingent restKontingentKinokasse;
|
||||
|
||||
public Kontingent(final Id id,
|
||||
final RestKontingent restKontingentOnline,
|
||||
final RestKontingent restKontingentZentral,
|
||||
final RestKontingent restKontingentKinokasse) {
|
||||
this(id, Versionable.unknownVersion(),
|
||||
restKontingentOnline, restKontingentZentral, restKontingentKinokasse);
|
||||
}
|
||||
|
||||
public Kontingent(final Id id, final Version version,
|
||||
final RestKontingent restKontingentOnline,
|
||||
final RestKontingent restKontingentZentral,
|
||||
final RestKontingent restKontingentKinokasse) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.restKontingentOnline = restKontingentOnline;
|
||||
this.restKontingentZentral = restKontingentZentral;
|
||||
this.restKontingentKinokasse = restKontingentKinokasse;
|
||||
}
|
||||
|
||||
public Kontingent(final Id id,
|
||||
final int gesamtKapazitaetVerkauf, final int quoteOnline) {
|
||||
this(id, Versionable.unknownVersion(), gesamtKapazitaetVerkauf, quoteOnline);
|
||||
}
|
||||
|
||||
public Kontingent(final Id id, final Version version,
|
||||
final int gesamtKapazitaetVerkauf, final int quoteOnline) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
|
||||
restKontingentOnline = new RestKontingent((int) Math.ceil ((float)(gesamtKapazitaetVerkauf * quoteOnline)/100));
|
||||
restKontingentZentral = new RestKontingent((int) Math.floor((float)(gesamtKapazitaetVerkauf - restKontingentOnline.raw)/2));
|
||||
restKontingentKinokasse = new RestKontingent(gesamtKapazitaetVerkauf - restKontingentZentral.raw - restKontingentOnline.raw);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public RestKontingent getRestKontingentOnline() {
|
||||
return restKontingentOnline;
|
||||
}
|
||||
|
||||
public RestKontingent getRestKontingentZentral() {
|
||||
return restKontingentZentral;
|
||||
}
|
||||
|
||||
public RestKontingent getRestKontingentKinokasse() {
|
||||
return restKontingentKinokasse;
|
||||
}
|
||||
|
||||
public Kontingent reduziereKontingent(final KontingentType kontingentType, final int kontingentReduktion) {
|
||||
if (kontingentReduktion < 0) {
|
||||
throw new FlexinaleIllegalArgumentException("reduction of Kontingent has to be >=0 but is "
|
||||
+ kontingentReduktion);
|
||||
}
|
||||
|
||||
return switch (kontingentType) {
|
||||
case ONLINE ->
|
||||
new Kontingent(this.id, this.version,
|
||||
reduziereDiesesKontingent(getRawOrNull(restKontingentOnline), kontingentReduktion),
|
||||
this.restKontingentZentral,
|
||||
this.restKontingentKinokasse
|
||||
);
|
||||
case ZENTRAL ->
|
||||
new Kontingent(this.id, this.version,
|
||||
this.restKontingentOnline,
|
||||
reduziereDiesesKontingent(getRawOrNull(restKontingentZentral), kontingentReduktion),
|
||||
this.restKontingentKinokasse
|
||||
);
|
||||
case KINOKASSE ->
|
||||
new Kontingent(this.id, this.version,
|
||||
this.restKontingentOnline,
|
||||
this.restKontingentZentral,
|
||||
reduziereDiesesKontingent(getRawOrNull(restKontingentKinokasse), kontingentReduktion)
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
private RestKontingent reduziereDiesesKontingent(final int kontingent, final int reduziereUm) {
|
||||
int reduziertesKontingent = kontingent - reduziereUm;
|
||||
if (reduziertesKontingent < 0) {
|
||||
throw new KontingentBereitsAusgeschoepftException();
|
||||
}
|
||||
|
||||
return new RestKontingent(reduziertesKontingent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (!equalsByContent(o)) return false;
|
||||
|
||||
if (this == o) {return true;}
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
Kontingent that = (Kontingent) o;
|
||||
return new EqualsBuilder().append(version, that.version).isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsByContent(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
Kontingent that = (Kontingent) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(id, that.id)
|
||||
.append(restKontingentOnline, that.restKontingentOnline)
|
||||
.append(restKontingentZentral, that.restKontingentZentral)
|
||||
.append(restKontingentKinokasse, that.restKontingentKinokasse)
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(restKontingentOnline)
|
||||
.append(restKontingentZentral).append(restKontingentKinokasse)
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package de.accso.flexinale.domain.model;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.DoNotCheckInArchitectureTests;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@DoNotCheckInArchitectureTests
|
||||
public class KontingentBereitsAusgeschoepftException extends RuntimeException {
|
||||
|
||||
//TODO is this exception needed? replace by boolean value in the "Ticketing" interface's method, which is currently void
|
||||
// if we keep the exception, then perhaps move as inner class to "Kontingent"?
|
||||
|
||||
public KontingentBereitsAusgeschoepftException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public KontingentBereitsAusgeschoepftException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public KontingentBereitsAusgeschoepftException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public KontingentBereitsAusgeschoepftException(final Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
protected KontingentBereitsAusgeschoepftException(final String message, final Throwable cause,
|
||||
final boolean enableSuppression, final boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
package de.accso.flexinale.domain.model;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.DoNotCheckInArchitectureTests;
|
||||
import de.accso.flexinale.shared_kernel.EqualsByContent;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.shared_kernel.Versionable;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
public class Ticket implements Identifiable, Versionable, EqualsByContent {
|
||||
|
||||
public enum VerkaufsKanal {
|
||||
ONLINE,
|
||||
ZENTRAL,
|
||||
KINOKASSE
|
||||
}
|
||||
|
||||
public final Id id;
|
||||
public final Version version;
|
||||
|
||||
@DoNotCheckInArchitectureTests
|
||||
public final Film film;
|
||||
@DoNotCheckInArchitectureTests
|
||||
public final Vorfuehrung vorfuehrung;
|
||||
@DoNotCheckInArchitectureTests
|
||||
public final Besucher besucher;
|
||||
@DoNotCheckInArchitectureTests
|
||||
public VerkaufsKanal verkaufsKanal;
|
||||
|
||||
public Ticket(final Id id,
|
||||
final Film film, final Vorfuehrung vorfuehrung, final Besucher besucher,
|
||||
final VerkaufsKanal verkaufsKanal) {
|
||||
this(id, Versionable.unknownVersion(), film, vorfuehrung, besucher, verkaufsKanal);
|
||||
}
|
||||
|
||||
public Ticket(final Id id, final Version version,
|
||||
final Film film, final Vorfuehrung vorfuehrung, final Besucher besucher,
|
||||
final VerkaufsKanal verkaufsKanal) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.film = film;
|
||||
this.vorfuehrung = vorfuehrung;
|
||||
this.besucher = besucher;
|
||||
this.verkaufsKanal = verkaufsKanal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (!equalsByContent(o)) return false;
|
||||
|
||||
if (this == o) {return true;}
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
Ticket that = (Ticket) o;
|
||||
return new EqualsBuilder().append(version, that.version).isEquals();
|
||||
}
|
||||
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
@Override
|
||||
public boolean equalsByContent(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
Ticket that = (Ticket) o;
|
||||
|
||||
boolean result = new EqualsBuilder()
|
||||
.append(id, that.id)
|
||||
.append(verkaufsKanal, that.verkaufsKanal)
|
||||
.isEquals();
|
||||
if (!result) return false;
|
||||
|
||||
if (film == null && that.film == null) result = true;
|
||||
if (film == null && that.film != null) return false;
|
||||
if (film != null) {
|
||||
result = film.equalsByContent(that.film);
|
||||
if (!result) return false;
|
||||
}
|
||||
|
||||
if (vorfuehrung == null && that.vorfuehrung == null) result = true;
|
||||
if (vorfuehrung == null && that.vorfuehrung != null) return false;
|
||||
if (vorfuehrung != null) {
|
||||
result = vorfuehrung.equalsByContent(that.vorfuehrung);
|
||||
if (!result) return false;
|
||||
}
|
||||
|
||||
if (besucher == null && that.besucher == null) result = true;
|
||||
if (besucher == null && that.besucher != null) return false;
|
||||
if (besucher != null) {
|
||||
result = besucher.equalsByContent(that.besucher);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(film).append(vorfuehrung).append(besucher)
|
||||
.append(verkaufsKanal)
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,190 @@
|
|||
package de.accso.flexinale.domain.model;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.*;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static de.accso.flexinale.shared_kernel.RawWrapper.getRawOrNull;
|
||||
|
||||
public final class Vorfuehrung implements Identifiable, Versionable, EqualsByContent { // TODO is only final as otherwise Spotbugs complains, get rid of Exception in constructor!
|
||||
public record Zeit(LocalDateTime raw) implements RawWrapper<LocalDateTime> {
|
||||
public Zeit(LocalDateTime raw) {
|
||||
this.raw = raw.withNano(0); // precision is second
|
||||
}
|
||||
}
|
||||
|
||||
public final Id id;
|
||||
public final Version version;
|
||||
|
||||
public final Zeit zeit;
|
||||
|
||||
@DoNotCheckInArchitectureTests
|
||||
public final Film film;
|
||||
@DoNotCheckInArchitectureTests
|
||||
public final KinoSaal kinoSaal;
|
||||
|
||||
public Kontingent getKontingent() {
|
||||
return kontingent;
|
||||
}
|
||||
|
||||
@DoNotCheckInArchitectureTests
|
||||
private Kontingent kontingent; // kontingent is not final because when tickets are sold, the kontingent may change.
|
||||
|
||||
public Vorfuehrung(final Id id,
|
||||
final Zeit zeit,
|
||||
final Film film, final KinoSaal kinoSaal, final Kontingent kontingent) {
|
||||
this(id, Versionable.unknownVersion(), zeit, film, kinoSaal, kontingent);
|
||||
}
|
||||
|
||||
public Vorfuehrung(final Id id, final Version version,
|
||||
final Zeit zeit,
|
||||
final Film film, final KinoSaal kinoSaal, final Kontingent kontingent) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.zeit = zeit;
|
||||
this.film = film;
|
||||
this.kinoSaal = kinoSaal;
|
||||
this.kontingent = kontingent;
|
||||
|
||||
//TODO this validation should not be done here. If class is not final, Spotbugs complains. For DB entities use "nullable=false" and "optional=false" for DB checks. In domain classes check with jakarta.annotation NonNull? Also: Why is KinoSaal obligatory but not Film?
|
||||
if (kinoSaal == null) {
|
||||
throw new FlexinaleIllegalArgumentException("KinoSaal of Vorfuehrung " + this +
|
||||
" must not be null");
|
||||
}
|
||||
//TODO this validation should not be done here but in KinoSaal itself
|
||||
if (kinoSaal.anzahlPlaetze == null) {
|
||||
throw new FlexinaleIllegalArgumentException("KinoSaal's Anzahl Plaetze of Vorfuehrung " + this +
|
||||
" must not be null");
|
||||
}
|
||||
if (kontingent == null) {
|
||||
throw new FlexinaleIllegalArgumentException("Kontingent of Vorfuehrung " + this +
|
||||
" must not be null");
|
||||
}
|
||||
}
|
||||
|
||||
public Vorfuehrung(final Id id,
|
||||
final Zeit zeit, final Film film, final KinoSaal kinoSaal, int quoteOnline) {
|
||||
this(id, Versionable.unknownVersion(), zeit, film, kinoSaal, quoteOnline);
|
||||
}
|
||||
|
||||
public Vorfuehrung(final Id id, final Version version,
|
||||
final Zeit zeit, final Film film, final KinoSaal kinoSaal, int quoteOnline) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.zeit = zeit;
|
||||
this.film = film;
|
||||
this.kinoSaal = kinoSaal;
|
||||
|
||||
if (kinoSaal == null) {
|
||||
throw new FlexinaleIllegalArgumentException("KinoSaal of Vorfuehrung " + this +
|
||||
" must not be null");
|
||||
}
|
||||
if (kinoSaal.anzahlPlaetze == null) {
|
||||
throw new FlexinaleIllegalArgumentException("KinoSaal's Anzahl Plaetze of Vorfuehrung " + this +
|
||||
" must not be null");
|
||||
}
|
||||
|
||||
kontingent = new Kontingent(Identifiable.Id.of("K" + id), getRawOrNull(kinoSaal.anzahlPlaetze), quoteOnline);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public Integer getGesamtkapazitaetVerkauf() {
|
||||
return getRawOrNull(kinoSaal.anzahlPlaetze);
|
||||
}
|
||||
|
||||
public void verkaufeTickets(final Ticket.VerkaufsKanal verkaufsKanal, final int anzahlGutscheine) {
|
||||
switch (verkaufsKanal) {
|
||||
case ONLINE ->
|
||||
this.kontingent = kontingent.reduziereKontingent(Kontingent.KontingentType.ONLINE, anzahlGutscheine);
|
||||
case ZENTRAL ->
|
||||
this.kontingent = kontingent.reduziereKontingent(Kontingent.KontingentType.ZENTRAL, anzahlGutscheine);
|
||||
case KINOKASSE ->
|
||||
this.kontingent = kontingent.reduziereKontingent(Kontingent.KontingentType.KINOKASSE, anzahlGutscheine);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean ueberlapptMit(Vorfuehrung other, long puffer) {
|
||||
LocalDateTime begin = getRawOrNull(this.zeit);
|
||||
LocalDateTime beginOther = getRawOrNull(other.zeit);
|
||||
LocalDateTime end = begin.plusMinutes(this.film.dauerInMinuten.raw());
|
||||
LocalDateTime endOther = beginOther.plusMinutes(other.film.dauerInMinuten.raw());
|
||||
|
||||
return ((begin.isBefore(endOther.plusMinutes(puffer)))
|
||||
&& (end.isAfter(beginOther.minusMinutes(puffer))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (!equalsByContent(o)) return false;
|
||||
|
||||
if (this == o) {return true;}
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
Vorfuehrung that = (Vorfuehrung) o;
|
||||
return new EqualsBuilder().append(version, that.version).isEquals();
|
||||
}
|
||||
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
@Override
|
||||
public boolean equalsByContent(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
Vorfuehrung that = (Vorfuehrung) o;
|
||||
|
||||
boolean result = new EqualsBuilder()
|
||||
.append(id, that.id)
|
||||
.append(zeit, that.zeit)
|
||||
.isEquals();
|
||||
if (!result) return false;
|
||||
|
||||
if (film == null && that.film == null) result = true;
|
||||
if (film == null && that.film != null) return false;
|
||||
if (film != null) {
|
||||
result = film.equalsByContent(that.film);
|
||||
if (!result) return false;
|
||||
}
|
||||
|
||||
if (kinoSaal == null && that.kinoSaal == null) result = true;
|
||||
if (kinoSaal == null && that.kinoSaal != null) return false;
|
||||
if (kinoSaal != null) {
|
||||
result = kinoSaal.equalsByContent(that.kinoSaal);
|
||||
if (!result) return false;
|
||||
}
|
||||
|
||||
if (kontingent == null && that.kontingent == null) result = true;
|
||||
if (kontingent == null && that.kontingent != null) return false;
|
||||
if (kontingent != null) {
|
||||
result = kontingent.equalsByContent(that.kontingent);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(zeit).append(film).append(kinoSaal)
|
||||
.append(kontingent)
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package de.accso.flexinale.domain.services;
|
||||
|
||||
import de.accso.flexinale.domain.dao.FilmDao;
|
||||
import de.accso.flexinale.domain.dao.TicketDao;
|
||||
import de.accso.flexinale.domain.dao.VorfuehrungDao;
|
||||
import de.accso.flexinale.domain.model.Besucher;
|
||||
import de.accso.flexinale.domain.model.Film;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FilmService {
|
||||
|
||||
private final FilmDao filmDao;
|
||||
|
||||
private final VorfuehrungDao vorfuehrungDao;
|
||||
|
||||
private final TicketDao ticketDao;
|
||||
|
||||
private final long minZeitZwischenVorfuehrungenInMinuten;
|
||||
|
||||
public FilmService(final FilmDao filmDao, final VorfuehrungDao vorfuehrungDao, final TicketDao ticketDao,
|
||||
final long minZeitZwischenVorfuehrungenInMinuten) {
|
||||
this.filmDao = filmDao;
|
||||
this.vorfuehrungDao = vorfuehrungDao;
|
||||
this.ticketDao = ticketDao;
|
||||
this.minZeitZwischenVorfuehrungenInMinuten = minZeitZwischenVorfuehrungenInMinuten;
|
||||
}
|
||||
|
||||
public List<Film> filme() {
|
||||
return filmDao.findAll();
|
||||
}
|
||||
|
||||
public Film film(Identifiable.Id id) {
|
||||
return filmDao.findById(id).orElse(null);
|
||||
}
|
||||
|
||||
public List<Vorfuehrung> vorfuehrungenFuer(final Identifiable.Id filmId) {
|
||||
return vorfuehrungDao.findByFilmId(filmId);
|
||||
}
|
||||
|
||||
public List<Identifiable.Id> vorfuehrungenMitUeberlapp(final List<Vorfuehrung> vorfuehrungen, final Besucher besucher) {
|
||||
List<Identifiable.Id> vorfuehrungenMitUeberlapp = new ArrayList<>();
|
||||
|
||||
TicketBundle ticketBundle = new TicketBundle(besucher, ticketDao, minZeitZwischenVorfuehrungenInMinuten);
|
||||
|
||||
for (Vorfuehrung vorfuehrung : vorfuehrungen) {
|
||||
if (ticketBundle.mindestensEinTicketImTicketBundleUeberlapptMit(vorfuehrung)) {
|
||||
vorfuehrungenMitUeberlapp.add(vorfuehrung.id);
|
||||
}
|
||||
}
|
||||
return vorfuehrungenMitUeberlapp;
|
||||
}
|
||||
|
||||
public List<Identifiable.Id> vorfuehrungenFuerDieDerBenutzerEinTicketHat(final List<Vorfuehrung> vorfuehrungen, final Besucher besucher) {
|
||||
List<Identifiable.Id> vorfuehrungenMitTicket = new ArrayList<>();
|
||||
|
||||
TicketBundle ticketBundle = new TicketBundle(besucher, ticketDao, minZeitZwischenVorfuehrungenInMinuten);
|
||||
|
||||
for (Vorfuehrung vorfuehrung : vorfuehrungen) {
|
||||
if (ticketBundle.hatSchonTicketFuer(vorfuehrung)) {
|
||||
vorfuehrungenMitTicket.add(vorfuehrung.id);
|
||||
}
|
||||
}
|
||||
return vorfuehrungenMitTicket;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package de.accso.flexinale.domain.services;
|
||||
|
||||
import de.accso.flexinale.domain.dao.KinoDao;
|
||||
import de.accso.flexinale.domain.model.Kino;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KinoService {
|
||||
|
||||
private final KinoDao kinoDao;
|
||||
|
||||
public KinoService(final KinoDao kinoDao) {
|
||||
this.kinoDao = kinoDao;
|
||||
}
|
||||
|
||||
public List<Kino> kinos() {
|
||||
return kinoDao.findAll();
|
||||
}
|
||||
|
||||
public Kino kino(final Identifiable.Id id) {
|
||||
return kinoDao.findById(id).orElse(null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package de.accso.flexinale.domain.services;
|
||||
|
||||
import de.accso.flexinale.domain.dao.TicketDao;
|
||||
import de.accso.flexinale.domain.model.Besucher;
|
||||
import de.accso.flexinale.domain.model.Ticket;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TicketBundle {
|
||||
|
||||
private final Besucher besucher;
|
||||
|
||||
private final TicketDao ticketDao;
|
||||
|
||||
private final long minZeitZwischenVorfuehrungenInMinuten;
|
||||
|
||||
public TicketBundle(final Besucher besucher, final TicketDao ticketDao, final long minZeitZwischenVorfuehrungenInMinuten) {
|
||||
this.besucher = besucher;
|
||||
this.ticketDao = ticketDao;
|
||||
this.minZeitZwischenVorfuehrungenInMinuten = minZeitZwischenVorfuehrungenInMinuten;
|
||||
}
|
||||
|
||||
public boolean mindestensEinTicketImTicketBundleUeberlapptMit(final Vorfuehrung vorfuehrung) {
|
||||
List<Ticket> tickets = ticketDao.findByBesucherOrderByZeit(besucher);
|
||||
return tickets.stream().anyMatch(ticket ->
|
||||
ticket.vorfuehrung.ueberlapptMit(vorfuehrung, minZeitZwischenVorfuehrungenInMinuten));
|
||||
}
|
||||
|
||||
public boolean hatSchonTicketFuer(final Vorfuehrung vorfuehrung) {
|
||||
List<Ticket> tickets = ticketDao.findByBesucherOrderByZeit(besucher);
|
||||
return tickets.stream()
|
||||
.map(ticket -> ticket.vorfuehrung).toList().contains(vorfuehrung);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package de.accso.flexinale.domain.services;
|
||||
|
||||
import de.accso.flexinale.domain.dao.KontingentDao;
|
||||
import de.accso.flexinale.domain.dao.TicketDao;
|
||||
import de.accso.flexinale.domain.dao.VorfuehrungDao;
|
||||
import de.accso.flexinale.domain.model.Besucher;
|
||||
import de.accso.flexinale.domain.model.Ticket;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class TicketService {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TicketService.class);
|
||||
|
||||
private final TicketDao ticketDao;
|
||||
|
||||
private final VorfuehrungDao vorfuehrungDao;
|
||||
|
||||
private final KontingentDao kontingentDao;
|
||||
|
||||
public TicketService(final TicketDao ticketDao, final VorfuehrungDao vorfuehrungDao,
|
||||
final KontingentDao kontingentDao) {
|
||||
this.ticketDao = ticketDao;
|
||||
this.vorfuehrungDao = vorfuehrungDao;
|
||||
this.kontingentDao = kontingentDao;
|
||||
}
|
||||
|
||||
public void loeseGutscheineOnlineFuerVorfuehrungEin(final Identifiable.Id vorfuehrungId, final Besucher besucher, final int anzahlGutscheine) {
|
||||
loeseGutscheineFuerVorfuehrungEin(vorfuehrungId, besucher, anzahlGutscheine, Ticket.VerkaufsKanal.ONLINE);
|
||||
}
|
||||
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private void loeseGutscheineFuerVorfuehrungEin(final Identifiable.Id vorfuehrungId, final Besucher besucher,
|
||||
final int anzahlGutscheine,
|
||||
final Ticket.VerkaufsKanal verkaufsKanal) {
|
||||
Vorfuehrung vorfuehrung = vorfuehrungDao.findById(vorfuehrungId).get(); //TODO check isPresent() first!
|
||||
vorfuehrung.verkaufeTickets(verkaufsKanal, anzahlGutscheine);
|
||||
|
||||
for (int i = 0; i < anzahlGutscheine; i++) {
|
||||
Ticket ticket = new Ticket(Identifiable.Id.of(), vorfuehrung.film, vorfuehrung, besucher, verkaufsKanal);
|
||||
ticketDao.save(ticket);
|
||||
}
|
||||
|
||||
// save reduction of the Kontingent reduction
|
||||
kontingentDao.save(vorfuehrung.getKontingent());
|
||||
|
||||
LOGGER.info("%d ticket(s) bought for Vorfuehrung %s, Besucher %s, using Verkaufskanal %s"
|
||||
.formatted(anzahlGutscheine, vorfuehrung.id, besucher.login, verkaufsKanal));
|
||||
}
|
||||
|
||||
public int gesamtZahlDerTicketsFuer(final Besucher eingeloggterBesucher) {
|
||||
return ticketDao.gesamtZahlDerTicketsFuer(eingeloggterBesucher);
|
||||
}
|
||||
|
||||
public List<Ticket> findByBesucherOrderByZeit(final Besucher eingeloggterBesucher) {
|
||||
return ticketDao.findByBesucherOrderByZeit(eingeloggterBesucher);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package de.accso.flexinale.domain.services;
|
||||
|
||||
import de.accso.flexinale.domain.dao.VorfuehrungDao;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class VorfuehrungService {
|
||||
|
||||
private final VorfuehrungDao vorfuehrungDao;
|
||||
|
||||
public VorfuehrungService(final VorfuehrungDao vorfuehrungDao) {
|
||||
this.vorfuehrungDao = vorfuehrungDao;
|
||||
}
|
||||
|
||||
public List<Vorfuehrung> vorfuehrungen() {
|
||||
return vorfuehrungDao.findAll();
|
||||
}
|
||||
|
||||
public Vorfuehrung vorfuehrung(final Identifiable.Id id) {
|
||||
return vorfuehrungDao.findById(id).orElse(null);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
package de.accso.flexinale.infrastructure;
|
||||
|
||||
import de.accso.flexinale.application.Config;
|
||||
import de.accso.flexinale.shared_kernel.FlexinaleIllegalArgumentException;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class FlexinaleSpringConfig implements Config {
|
||||
@Value("${application.title}")
|
||||
public String applicationTitle;
|
||||
|
||||
@Value("${build.version}")
|
||||
public String buildVersion;
|
||||
|
||||
@Value("${build.date}")
|
||||
public String buildDate;
|
||||
|
||||
@Override
|
||||
public String getApplicationTitle() {
|
||||
return applicationTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBuildVersion() {
|
||||
return buildVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBuildDate() {
|
||||
return buildDate;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
private final int quoteOnline;
|
||||
|
||||
@Override
|
||||
public int getQuoteOnline() {
|
||||
return quoteOnline;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
private final int minZeitZwischenVorfuehrungenInMinuten;
|
||||
|
||||
@Override
|
||||
public int getMinZeitZwischenVorfuehrungenInMinuten() {
|
||||
return minZeitZwischenVorfuehrungenInMinuten;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------------------------
|
||||
|
||||
public FlexinaleSpringConfig(@Value("${de.accso.flexinale.kontingent.quote.online:33}") final int quoteOnline,
|
||||
@Value("${de.accso.flexinale.vorfuehrung.min-zeit-zwischen-vorfuehrungen-in-minuten:30}") final int minZeitZwischenVorfuehrungenInMinuten) {
|
||||
if (quoteOnline < 0 || quoteOnline > 100) {
|
||||
String message = "Quote online should be a percentage, i.e. 0 <= quote online <= 100, but was " + quoteOnline;
|
||||
throw new FlexinaleIllegalArgumentException(message);
|
||||
}
|
||||
this.quoteOnline = quoteOnline;
|
||||
|
||||
if (minZeitZwischenVorfuehrungenInMinuten < 0) {
|
||||
String message = "minZeitZwischenVorfuehrungenInMinuten should be positiv, but was "
|
||||
+ minZeitZwischenVorfuehrungenInMinuten;
|
||||
throw new FlexinaleIllegalArgumentException(message);
|
||||
}
|
||||
this.minZeitZwischenVorfuehrungenInMinuten = minZeitZwischenVorfuehrungenInMinuten;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
package de.accso.flexinale.infrastructure;
|
||||
|
||||
import de.accso.flexinale.application.Config;
|
||||
import de.accso.flexinale.application.services.FilmUploadService;
|
||||
import de.accso.flexinale.application.services.KinoUploadService;
|
||||
import de.accso.flexinale.application.services.KinoSaalUploadService;
|
||||
import de.accso.flexinale.application.services.VorfuehrungUploadService;
|
||||
import de.accso.flexinale.domain.dao.*;
|
||||
import de.accso.flexinale.domain.services.FilmService;
|
||||
import de.accso.flexinale.domain.services.KinoService;
|
||||
import de.accso.flexinale.domain.services.TicketService;
|
||||
import de.accso.flexinale.domain.services.VorfuehrungService;
|
||||
import de.accso.flexinale.infrastructure.persistence.*;
|
||||
import de.accso.flexinale.infrastructure.security.BenutzerDao;
|
||||
import de.accso.flexinale.infrastructure.security.BenutzerJpaRepository;
|
||||
import de.accso.flexinale.infrastructure.security.BenutzerJpaRepositoryDelegate;
|
||||
import de.accso.flexinale.infrastructure.security.services.BenutzerUploadService;
|
||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
@Configuration
|
||||
@EnableJpaRepositories({"de.accso.flexinale.infrastructure.persistence",
|
||||
"de.accso.flexinale.infrastructure.security"})
|
||||
@EnableTransactionManagement
|
||||
@EntityScan(basePackages={"de.accso.flexinale.infrastructure.persistence",
|
||||
"de.accso.flexinale.infrastructure.security"})
|
||||
public class FlexinaleSpringFactory {
|
||||
|
||||
@Bean
|
||||
public FilmService createFilmService(final FilmDao filmDao,
|
||||
final TicketDao ticketDao,
|
||||
final VorfuehrungDao vorfuehrungDao,
|
||||
final Config config) {
|
||||
return new FilmService(filmDao, vorfuehrungDao, ticketDao, config.getMinZeitZwischenVorfuehrungenInMinuten());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilmUploadService createFilmUploadService(final FilmDao filmDao) {
|
||||
return new FilmUploadService(filmDao);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public de.accso.flexinale.application.services.FilmService createApplicationFilmService(final FilmService filmService){
|
||||
return new de.accso.flexinale.application.services.FilmService(filmService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilmDao createFilmDao(final FilmJpaRepository filmJpaRepository) {
|
||||
return new FilmJpaRepositoryDelegate(filmJpaRepository);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@Bean
|
||||
public KinoService createKinoService(final KinoDao KinoDao) {
|
||||
return new KinoService(KinoDao);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public de.accso.flexinale.application.services.KinoService createApplicationKinoService(final KinoService kinoService){
|
||||
return new de.accso.flexinale.application.services.KinoService(kinoService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KinoUploadService createKinoUploadService(final KinoDao kinoDao) {
|
||||
return new KinoUploadService(kinoDao);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KinoSaalUploadService createKinoSaalUploadService(final KinoSaalDao kinoSaalDao) {
|
||||
return new KinoSaalUploadService(kinoSaalDao);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KinoDao createKinoDao(final KinoJpaRepository kinoJpaRepository) {
|
||||
return new KinoJpaRepositoryDelegate(kinoJpaRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public KinoSaalDao createKinoSaalDao(final KinoSaalJpaRepository kinoSaalJpaRepository) {
|
||||
return new KinoSaalJpaRepositoryDelegate(kinoSaalJpaRepository);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@Bean
|
||||
public VorfuehrungService createVorfuehrungService(final VorfuehrungDao vorfuehrungDao) {
|
||||
return new VorfuehrungService(vorfuehrungDao);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public de.accso.flexinale.application.services.VorfuehrungService createApplicationVorfuehrungService(final VorfuehrungService vorfuehrungService){
|
||||
return new de.accso.flexinale.application.services.VorfuehrungService(vorfuehrungService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VorfuehrungUploadService createVorfuehrungUploadService(final VorfuehrungDao vorfuehrungDao,
|
||||
final KinoSaalDao kinoSaalDao,
|
||||
final Config config) {
|
||||
return new VorfuehrungUploadService(vorfuehrungDao, kinoSaalDao, config);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VorfuehrungDao createVorfuehrungDao(final VorfuehrungJpaRepository vorfuehrungJpaRepository) {
|
||||
return new VorfuehrungJpaRepositoryDelegate(vorfuehrungJpaRepository);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@Bean
|
||||
public KontingentDao createKontingentDao(final KontingentJpaRepository kontingentJpaRepository) {
|
||||
return new KontingentJpaRepositoryDelegate(kontingentJpaRepository);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@Bean
|
||||
public TicketService createTicketService(final TicketDao ticketDao,
|
||||
final VorfuehrungDao vorfuehrungDao,
|
||||
final KontingentDao kontingentDao) {
|
||||
return new TicketService(ticketDao, vorfuehrungDao, kontingentDao);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public de.accso.flexinale.application.services.TicketService createApplicationTicketService(final TicketService ticketService){
|
||||
return new de.accso.flexinale.application.services.TicketService(ticketService);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TicketDao createTicketDao(final TicketJpaRepository ticketJpaRepository) {
|
||||
return new TicketJpaRepositoryDelegate(ticketJpaRepository);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@Bean
|
||||
public BenutzerUploadService createBesucherUploadService(final BenutzerDao benutzerDao) {
|
||||
return new BenutzerUploadService(benutzerDao);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BenutzerDao createBesucherDao(final BenutzerJpaRepository benutzerJpaRepository) {
|
||||
return new BenutzerJpaRepositoryDelegate(benutzerJpaRepository);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.shared_kernel.Versionable;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@jakarta.persistence.Entity(name = "Film")
|
||||
public class FilmEntity implements Identifiable, Versionable, Serializable {
|
||||
@jakarta.persistence.Id
|
||||
public String id; // primary key
|
||||
|
||||
@jakarta.persistence.Column
|
||||
public String titel;
|
||||
|
||||
@jakarta.persistence.Column
|
||||
public String imdbUrl;
|
||||
|
||||
@jakarta.persistence.Column
|
||||
public Integer dauerInMinuten;
|
||||
|
||||
@jakarta.persistence.Version
|
||||
private Integer version = 0;
|
||||
|
||||
protected FilmEntity() {
|
||||
}
|
||||
|
||||
public FilmEntity(final String id, final Integer version,
|
||||
final String titel, final String imdbUrl, final Integer dauerInMinuten) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.titel = titel;
|
||||
this.imdbUrl = imdbUrl;
|
||||
this.dauerInMinuten = dauerInMinuten;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return Identifiable.Id.of(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return Versionable.Version.of(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
FilmEntity that = (FilmEntity) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(id, that.id).append(version, that.version)
|
||||
.append(titel, that.titel)
|
||||
.append(imdbUrl, that.imdbUrl)
|
||||
.append(dauerInMinuten, that.dauerInMinuten)
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(titel).append(imdbUrl).append(dauerInMinuten).toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface FilmJpaRepository extends JpaRepository<FilmEntity, String> {
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import de.accso.flexinale.domain.dao.FilmDao;
|
||||
import de.accso.flexinale.domain.model.Film;
|
||||
import de.accso.flexinale.infrastructure.persistence.mapper.FilmEntity2FilmMapper;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class FilmJpaRepositoryDelegate implements FilmDao {
|
||||
|
||||
private final FilmJpaRepository filmJpaRepository;
|
||||
|
||||
public FilmJpaRepositoryDelegate(FilmJpaRepository filmJpaRepository) {
|
||||
this.filmJpaRepository = filmJpaRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Film> findAll() {
|
||||
List<FilmEntity> filmEntities = filmJpaRepository.findAll();
|
||||
|
||||
return filmEntities.stream()
|
||||
.map(FilmEntity2FilmMapper::map)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Film> findById(final Identifiable.Id id) {
|
||||
Optional<FilmEntity> filmEntity = filmJpaRepository.findById(id.id());
|
||||
return FilmEntity2FilmMapper.map(filmEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Film save(final Film film) {
|
||||
FilmEntity filmEntity = FilmEntity2FilmMapper.map(film);
|
||||
FilmEntity savedEntity = filmJpaRepository.save(filmEntity);
|
||||
return FilmEntity2FilmMapper.map(savedEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(final Film film) {
|
||||
FilmEntity filmEntity = FilmEntity2FilmMapper.map(film);
|
||||
filmJpaRepository.delete(filmEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(final Identifiable.Id id) {
|
||||
filmJpaRepository.deleteById(id.id());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
filmJpaRepository.deleteAll();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.shared_kernel.Versionable;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.FetchType;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@jakarta.persistence.Entity(name = "Kino")
|
||||
public class KinoEntity implements Identifiable, Versionable, Serializable {
|
||||
@jakarta.persistence.Id
|
||||
public String id; // primary key
|
||||
|
||||
@jakarta.persistence.Column
|
||||
public String name;
|
||||
|
||||
@jakarta.persistence.Column
|
||||
public String adresse;
|
||||
|
||||
@jakarta.persistence.Column
|
||||
public String emailAdresse;
|
||||
|
||||
@jakarta.persistence.Version
|
||||
private Integer version = 0;
|
||||
|
||||
@jakarta.persistence.OneToMany(mappedBy = "kino", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
|
||||
public Set<KinoSaalEntity> kinoSaele = new HashSet<>();
|
||||
|
||||
protected KinoEntity() {
|
||||
}
|
||||
|
||||
public KinoEntity(final String id, final Integer version,
|
||||
final String name, final String adresse, final String emailAdresse) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.name = name;
|
||||
this.adresse = adresse;
|
||||
this.emailAdresse = emailAdresse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return Identifiable.Id.of(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return Versionable.Version.of(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
KinoEntity that = (KinoEntity) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(id, that.id).append(version, that.version)
|
||||
.append(name, that.name)
|
||||
.append(adresse, that.adresse)
|
||||
.append(emailAdresse, that.emailAdresse)
|
||||
.append(kinoSaele.stream().toList(), that.kinoSaele.stream().toList()) // need to use list, as Set does not support deep equals
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(name).append(adresse)
|
||||
.append(emailAdresse)
|
||||
.append(kinoSaele.stream().toList()) // need to use list, as Set does not support deep equals
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface KinoJpaRepository extends JpaRepository<KinoEntity, String> {
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import de.accso.flexinale.domain.dao.KinoDao;
|
||||
import de.accso.flexinale.domain.model.Kino;
|
||||
import de.accso.flexinale.infrastructure.persistence.mapper.KinoKinoSaalEntity2KinoKinoSaalMapper;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class KinoJpaRepositoryDelegate implements KinoDao {
|
||||
|
||||
private final KinoJpaRepository kinoJpaRepository;
|
||||
|
||||
public KinoJpaRepositoryDelegate(KinoJpaRepository KinoJpaRepository) {
|
||||
this.kinoJpaRepository = KinoJpaRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Kino> findAll() {
|
||||
List<KinoEntity> kinoEntities = kinoJpaRepository.findAll();
|
||||
|
||||
return kinoEntities.stream()
|
||||
.map(KinoKinoSaalEntity2KinoKinoSaalMapper::mapKino)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Kino> findById(final Identifiable.Id id) {
|
||||
Optional<KinoEntity> kinoEntity = kinoJpaRepository.findById(id.id());
|
||||
return KinoKinoSaalEntity2KinoKinoSaalMapper.mapKino(kinoEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Kino save(final Kino kino) {
|
||||
KinoEntity kinoEntity = KinoKinoSaalEntity2KinoKinoSaalMapper.mapKino(kino);
|
||||
KinoEntity savedEntity = kinoJpaRepository.save(kinoEntity);
|
||||
return KinoKinoSaalEntity2KinoKinoSaalMapper.mapKino(savedEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(final Kino kino) {
|
||||
KinoEntity kinoEntity = KinoKinoSaalEntity2KinoKinoSaalMapper.mapKino(kino);
|
||||
kinoJpaRepository.delete(kinoEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(final Identifiable.Id id) {
|
||||
kinoJpaRepository.deleteById(id.id());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
kinoJpaRepository.deleteAll();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.shared_kernel.Versionable;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@jakarta.persistence.Entity(name = "KinoSaal")
|
||||
public class KinoSaalEntity implements Identifiable, Versionable, Serializable {
|
||||
@jakarta.persistence.Id
|
||||
public String id; // primary key
|
||||
|
||||
@jakarta.persistence.Column
|
||||
public String name;
|
||||
|
||||
@jakarta.persistence.Column
|
||||
public Integer anzahlPlaetze = 0;
|
||||
|
||||
@jakarta.persistence.ManyToOne
|
||||
@jakarta.persistence.JoinColumn(name = "kino", nullable = false)
|
||||
public KinoEntity kino;
|
||||
|
||||
@jakarta.persistence.Version
|
||||
private Integer version = 0;
|
||||
|
||||
protected KinoSaalEntity() {
|
||||
}
|
||||
|
||||
public KinoSaalEntity(final String id, final Integer version,
|
||||
final String name, final Integer anzahlPlaetze, final KinoEntity kino) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.name = name;
|
||||
this.anzahlPlaetze = anzahlPlaetze;
|
||||
this.kino = kino;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return Identifiable.Id.of(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return Versionable.Version.of(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "KinoSaalEntity{" +
|
||||
"id='" + id + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", anzahlPlaetze=" + anzahlPlaetze +
|
||||
(kino != null ? ", kino.id=" + kino.id : ", kino=null") +
|
||||
", version=" + version +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
KinoSaalEntity that = (KinoSaalEntity) o;
|
||||
|
||||
EqualsBuilder builder = new EqualsBuilder()
|
||||
.append(id, that.id).append(version, that.version)
|
||||
.append(name, that.name).append(anzahlPlaetze, that.anzahlPlaetze);
|
||||
if (kino != null) {
|
||||
builder.append(kino.id, that.kino.id); // do not check kino but only its id (otherwise Stackoverflow error)
|
||||
}
|
||||
return builder.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
HashCodeBuilder builder = new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(name).append(anzahlPlaetze);
|
||||
if (kino != null) {
|
||||
builder.append(kino.id); // do not check kino but only its id (otherwise Stackoverflow error)
|
||||
}
|
||||
return builder.toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface KinoSaalJpaRepository extends JpaRepository<KinoSaalEntity, String> {
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import de.accso.flexinale.domain.dao.KinoSaalDao;
|
||||
import de.accso.flexinale.domain.model.KinoSaal;
|
||||
import de.accso.flexinale.infrastructure.persistence.mapper.KinoKinoSaalEntity2KinoKinoSaalMapper;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class KinoSaalJpaRepositoryDelegate implements KinoSaalDao {
|
||||
|
||||
private final KinoSaalJpaRepository kinoSaalJpaRepository;
|
||||
|
||||
public KinoSaalJpaRepositoryDelegate(final KinoSaalJpaRepository kinoSaalJpaRepository) {
|
||||
this.kinoSaalJpaRepository = kinoSaalJpaRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KinoSaal> findAll() {
|
||||
List<KinoSaalEntity> kinoSaalEntities = kinoSaalJpaRepository.findAll();
|
||||
|
||||
return kinoSaalEntities.stream()
|
||||
.map(KinoKinoSaalEntity2KinoKinoSaalMapper::mapKinoSaal)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<KinoSaal> findById(final Identifiable.Id id) {
|
||||
Optional<KinoSaalEntity> kinoSaalEntity = kinoSaalJpaRepository.findById(id.id());
|
||||
return KinoKinoSaalEntity2KinoKinoSaalMapper.mapToKinoSaal(kinoSaalEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KinoSaal save(final KinoSaal kinoSaal) {
|
||||
KinoSaalEntity kinoSaalEntity = KinoKinoSaalEntity2KinoKinoSaalMapper.mapKinoSaal(kinoSaal);
|
||||
KinoSaalEntity savedEntity = kinoSaalJpaRepository.save(kinoSaalEntity);
|
||||
return KinoKinoSaalEntity2KinoKinoSaalMapper.mapKinoSaal(savedEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(final KinoSaal kinoSaal) {
|
||||
KinoSaalEntity kinoSaalEntity = KinoKinoSaalEntity2KinoKinoSaalMapper.mapKinoSaal(kinoSaal);
|
||||
kinoSaalJpaRepository.delete(kinoSaalEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(final Identifiable.Id id) {
|
||||
kinoSaalJpaRepository.deleteById(id.id());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
kinoSaalJpaRepository.deleteAll();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.shared_kernel.Versionable;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@jakarta.persistence.Entity(name="Kontingent")
|
||||
public class KontingentEntity implements Identifiable, Versionable, Serializable {
|
||||
@jakarta.persistence.Id
|
||||
public String id;
|
||||
|
||||
@jakarta.persistence.Column
|
||||
private Integer restKontingentOnline = 0;
|
||||
|
||||
@jakarta.persistence.Column
|
||||
private Integer restKontingentZentral = 0;
|
||||
|
||||
@jakarta.persistence.Column
|
||||
private Integer restKontingentKinokasse = 0;
|
||||
|
||||
@jakarta.persistence.Version
|
||||
private Integer version = 0;
|
||||
|
||||
protected KontingentEntity() {
|
||||
}
|
||||
|
||||
public KontingentEntity(final String id, final Integer version,
|
||||
final Integer restKontingentOnline,
|
||||
final Integer restKontingentZentral,
|
||||
final Integer restKontingentKinokasse) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.restKontingentOnline = restKontingentOnline;
|
||||
this.restKontingentZentral = restKontingentZentral;
|
||||
this.restKontingentKinokasse = restKontingentKinokasse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return Identifiable.Id.of(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return Versionable.Version.of(version);
|
||||
}
|
||||
|
||||
public Integer getRestKontingentOnline() {
|
||||
return restKontingentOnline;
|
||||
}
|
||||
|
||||
public Integer getRestKontingentZentral() {
|
||||
return restKontingentZentral;
|
||||
}
|
||||
|
||||
public Integer getRestKontingentKinokasse() {
|
||||
return restKontingentKinokasse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
KontingentEntity that = (KontingentEntity) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(id, that.id).append(version, that.version)
|
||||
.append(restKontingentOnline, that.restKontingentOnline)
|
||||
.append(restKontingentZentral, that.restKontingentZentral)
|
||||
.append(restKontingentKinokasse, that.restKontingentKinokasse)
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(restKontingentOnline)
|
||||
.append(restKontingentZentral).append(restKontingentKinokasse)
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface KontingentJpaRepository extends JpaRepository<KontingentEntity, String> {
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import de.accso.flexinale.domain.dao.KontingentDao;
|
||||
import de.accso.flexinale.domain.model.Kontingent;
|
||||
import de.accso.flexinale.infrastructure.persistence.mapper.KontingentEntity2KontingentMapper;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class KontingentJpaRepositoryDelegate implements KontingentDao {
|
||||
|
||||
private final KontingentJpaRepository kontingentJpaRepository;
|
||||
|
||||
public KontingentJpaRepositoryDelegate(KontingentJpaRepository kontingentJpaRepository) {
|
||||
this.kontingentJpaRepository = kontingentJpaRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Kontingent> findAll() {
|
||||
List<KontingentEntity> KontingentEntities = kontingentJpaRepository.findAll();
|
||||
|
||||
return KontingentEntities.stream()
|
||||
.map(KontingentEntity2KontingentMapper::map)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Kontingent> findById(final Identifiable.Id id) {
|
||||
Optional<KontingentEntity> kontingentEntity = kontingentJpaRepository.findById(id.id());
|
||||
return KontingentEntity2KontingentMapper.map(kontingentEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Kontingent save(final Kontingent kontingent) {
|
||||
KontingentEntity kontingentEntity = KontingentEntity2KontingentMapper.map(kontingent);
|
||||
KontingentEntity savedEntity = kontingentJpaRepository.save(kontingentEntity);
|
||||
return KontingentEntity2KontingentMapper.map(savedEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(final Kontingent kontingent) {
|
||||
KontingentEntity kontingentEntity = KontingentEntity2KontingentMapper.map(kontingent);
|
||||
kontingentJpaRepository.delete(kontingentEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(final Identifiable.Id id) {
|
||||
kontingentJpaRepository.deleteById(id.id());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
kontingentJpaRepository.deleteAll();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import de.accso.flexinale.domain.model.Ticket;
|
||||
import de.accso.flexinale.infrastructure.security.BenutzerEntity;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.shared_kernel.Versionable;
|
||||
import jakarta.persistence.EnumType;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@jakarta.persistence.Entity(name="Ticket")
|
||||
public class TicketEntity implements Identifiable, Versionable, Serializable {
|
||||
@jakarta.persistence.Id
|
||||
public String id; // primary key
|
||||
|
||||
@jakarta.persistence.ManyToOne
|
||||
public FilmEntity film;
|
||||
|
||||
@jakarta.persistence.ManyToOne
|
||||
public VorfuehrungEntity vorfuehrung;
|
||||
|
||||
@jakarta.persistence.ManyToOne
|
||||
public BenutzerEntity benutzer;
|
||||
|
||||
@jakarta.persistence.Enumerated(EnumType.STRING)
|
||||
public Ticket.VerkaufsKanal verkaufsKanal;
|
||||
|
||||
@jakarta.persistence.Version
|
||||
private Integer version = 0;
|
||||
|
||||
protected TicketEntity() {
|
||||
}
|
||||
|
||||
public TicketEntity(final String id, final Integer version,
|
||||
final FilmEntity film,
|
||||
final VorfuehrungEntity vorfuehrung,
|
||||
final BenutzerEntity benutzer,
|
||||
final Ticket.VerkaufsKanal verkaufsKanal) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.film = film;
|
||||
this.vorfuehrung = vorfuehrung;
|
||||
this.benutzer = benutzer;
|
||||
this.verkaufsKanal = verkaufsKanal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return Identifiable.Id.of(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return Versionable.Version.of(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
TicketEntity that = (TicketEntity) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(id, that.id).append(version, that.version)
|
||||
.append(film, that.film)
|
||||
.append(vorfuehrung, that.vorfuehrung).append(benutzer, that.benutzer)
|
||||
.append(verkaufsKanal, that.verkaufsKanal)
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(film).append(vorfuehrung).append(benutzer)
|
||||
.append(verkaufsKanal)
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface TicketJpaRepository extends JpaRepository<TicketEntity, String> {
|
||||
|
||||
@Query("SELECT t FROM Ticket t WHERE t.benutzer.id = :benutzerId ORDER BY t.vorfuehrung.zeit")
|
||||
List<TicketEntity> findByBenutzerOrderByZeit(String benutzerId);
|
||||
|
||||
@Query("SELECT count(t) FROM Ticket t WHERE t.benutzer.id = :benutzerId")
|
||||
int gesamtZahlDerTicketsFuer(String benutzerId);
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import de.accso.flexinale.domain.dao.TicketDao;
|
||||
import de.accso.flexinale.domain.model.Besucher;
|
||||
import de.accso.flexinale.domain.model.Ticket;
|
||||
import de.accso.flexinale.infrastructure.persistence.mapper.TicketEntity2TicketMapper;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class TicketJpaRepositoryDelegate implements TicketDao {
|
||||
|
||||
private final TicketJpaRepository ticketJpaRepository;
|
||||
|
||||
public TicketJpaRepositoryDelegate(TicketJpaRepository ticketJpaRepository) {
|
||||
this.ticketJpaRepository = ticketJpaRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Ticket> findAll() {
|
||||
List<TicketEntity> ticketEntities = ticketJpaRepository.findAll();
|
||||
return TicketEntity2TicketMapper.map(ticketEntities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Ticket> findById(final Identifiable.Id id) {
|
||||
Optional<TicketEntity> ticketEntity = ticketJpaRepository.findById(id.id());
|
||||
return TicketEntity2TicketMapper.map(ticketEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Ticket> findByBesucherOrderByZeit(final Besucher besucher) {
|
||||
List<TicketEntity> ticketEntitiesForBesucher = ticketJpaRepository.findByBenutzerOrderByZeit(besucher.id().id());
|
||||
return TicketEntity2TicketMapper.map(ticketEntitiesForBesucher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int gesamtZahlDerTicketsFuer(final Besucher besucher) {
|
||||
return ticketJpaRepository.gesamtZahlDerTicketsFuer(besucher.id().id());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ticket save(final Ticket ticket) {
|
||||
TicketEntity ticketEntity = TicketEntity2TicketMapper.map(ticket);
|
||||
TicketEntity savedEntity = ticketJpaRepository.save(ticketEntity);
|
||||
return TicketEntity2TicketMapper.map(savedEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(final Ticket ticket) {
|
||||
TicketEntity ticketEntity = TicketEntity2TicketMapper.map(ticket);
|
||||
ticketJpaRepository.delete(ticketEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(final Identifiable.Id id) {
|
||||
ticketJpaRepository.deleteById(id.id());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
ticketJpaRepository.deleteAll();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.DateTimeHelper;
|
||||
import de.accso.flexinale.shared_kernel.FlexinaleIllegalArgumentException;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.shared_kernel.Versionable;
|
||||
import jakarta.persistence.CascadeType;
|
||||
import jakarta.persistence.FetchType;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@jakarta.persistence.Entity(name = "Vorfuehrung")
|
||||
public final class VorfuehrungEntity implements Identifiable, Versionable, Serializable { // TODO is only final as otherwise Spotbugs
|
||||
@jakarta.persistence.Id
|
||||
public String id; // primary key
|
||||
|
||||
@jakarta.persistence.Column
|
||||
public Long zeit; // save EPOCH seconds instead of Date/Time (Timezone is UTC)
|
||||
|
||||
@jakarta.persistence.ManyToOne
|
||||
public FilmEntity film;
|
||||
|
||||
@jakarta.persistence.ManyToOne
|
||||
public KinoSaalEntity kinoSaal;
|
||||
|
||||
@jakarta.persistence.OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
|
||||
public KontingentEntity kontingent;
|
||||
|
||||
@jakarta.persistence.Version
|
||||
private Integer version = 0;
|
||||
|
||||
protected VorfuehrungEntity() {
|
||||
}
|
||||
|
||||
public VorfuehrungEntity(final String id, final Integer version,
|
||||
final LocalDateTime zeit,
|
||||
final FilmEntity film,
|
||||
final KinoSaalEntity kinoSaal,
|
||||
final KontingentEntity kontingent) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.zeit = DateTimeHelper.toEpochSeconds(zeit);
|
||||
this.film = film;
|
||||
this.kinoSaal = kinoSaal;
|
||||
this.kontingent = kontingent;
|
||||
|
||||
//TODO this validation should not be done here. If class is not final, Spotbugs complains. For DB entities use "nullable=false" and "optional=false" for DB checks. In domain classes check with jakarta.annotation NonNull? Also: Why is KinoSaal obligatory but not Film?
|
||||
if (kinoSaal == null) {
|
||||
throw new FlexinaleIllegalArgumentException("KinoSaal of Vorfuehrung " + this +
|
||||
" must not be null");
|
||||
}
|
||||
//TODO this validation should not be done here but in KinoSaal itself
|
||||
if (kinoSaal.anzahlPlaetze == null) {
|
||||
throw new FlexinaleIllegalArgumentException("KinoSaal's Anzahl Plaetze of Vorfuehrung " + this +
|
||||
" must not be null");
|
||||
}
|
||||
if (kontingent == null) {
|
||||
throw new FlexinaleIllegalArgumentException("Kontingent of Vorfuehrung " + this +
|
||||
" must not be null");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return Identifiable.Id.of(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return Versionable.Version.of(version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
VorfuehrungEntity that = (VorfuehrungEntity) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(id, that.id).append(version, that.version)
|
||||
.append(zeit, that.zeit)
|
||||
.append(film, that.film).append(kinoSaal, that.kinoSaal)
|
||||
.append(kontingent, that.kontingent)
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(zeit).append(film).append(kinoSaal)
|
||||
.append(kontingent)
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface VorfuehrungJpaRepository extends JpaRepository<VorfuehrungEntity, String> {
|
||||
|
||||
@Query("SELECT v FROM Vorfuehrung v WHERE v.film.id = :filmId ORDER BY v.zeit")
|
||||
List<VorfuehrungEntity> findByFilmId(String filmId);
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package de.accso.flexinale.infrastructure.persistence;
|
||||
|
||||
import de.accso.flexinale.domain.dao.VorfuehrungDao;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.infrastructure.persistence.mapper.VorfuehrungEntity2VorfuehrungMapper;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class VorfuehrungJpaRepositoryDelegate implements VorfuehrungDao {
|
||||
|
||||
private final VorfuehrungJpaRepository vorfuehrungJpaRepository;
|
||||
|
||||
public VorfuehrungJpaRepositoryDelegate(VorfuehrungJpaRepository vorfuehrungJpaRepository) {
|
||||
this.vorfuehrungJpaRepository = vorfuehrungJpaRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Vorfuehrung> findAll() {
|
||||
List<VorfuehrungEntity> vorfuehrungEntities = vorfuehrungJpaRepository.findAll();
|
||||
|
||||
return vorfuehrungEntities.stream()
|
||||
.map(VorfuehrungEntity2VorfuehrungMapper::map)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Vorfuehrung> findById(final Identifiable.Id id) {
|
||||
Optional<VorfuehrungEntity> vorfuehrungEntity = vorfuehrungJpaRepository.findById(id.id());
|
||||
return VorfuehrungEntity2VorfuehrungMapper.map(vorfuehrungEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Vorfuehrung> findByFilmId(final Identifiable.Id filmId) {
|
||||
List<VorfuehrungEntity> vorfuehrungEntities = vorfuehrungJpaRepository.findByFilmId(filmId.id());
|
||||
return VorfuehrungEntity2VorfuehrungMapper.map(vorfuehrungEntities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vorfuehrung save(final Vorfuehrung vorfuehrung) {
|
||||
VorfuehrungEntity vorfuehrungEntity = VorfuehrungEntity2VorfuehrungMapper.map(vorfuehrung);
|
||||
VorfuehrungEntity savedEntity = vorfuehrungJpaRepository.save(vorfuehrungEntity);
|
||||
return VorfuehrungEntity2VorfuehrungMapper.map(savedEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(final Vorfuehrung vorfuehrung) {
|
||||
VorfuehrungEntity vorfuehrungEntity = VorfuehrungEntity2VorfuehrungMapper.map(vorfuehrung);
|
||||
vorfuehrungJpaRepository.delete(vorfuehrungEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(final Identifiable.Id id) {
|
||||
vorfuehrungJpaRepository.deleteById(id.id());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
vorfuehrungJpaRepository.deleteAll();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package de.accso.flexinale.infrastructure.persistence.mapper;
|
||||
|
||||
import de.accso.flexinale.domain.model.Film;
|
||||
import de.accso.flexinale.infrastructure.persistence.FilmEntity;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static de.accso.flexinale.shared_kernel.RawWrapper.getRawOrNull;
|
||||
|
||||
public final class FilmEntity2FilmMapper {
|
||||
|
||||
public static Film map(final FilmEntity filmEntity) {
|
||||
return new Film(filmEntity.id(), filmEntity.version(),
|
||||
new Film.Titel(filmEntity.titel), new Film.ImdbUrl(filmEntity.imdbUrl),
|
||||
new Film.DauerInMinuten(filmEntity.dauerInMinuten));
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public static Optional<Film> map(final Optional<FilmEntity> optionalFilmEntity) {
|
||||
if (optionalFilmEntity.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
else {
|
||||
FilmEntity filmEntity = optionalFilmEntity.get();
|
||||
return Optional.of(map(filmEntity));
|
||||
}
|
||||
}
|
||||
|
||||
public static FilmEntity map(final Film film) {
|
||||
return new FilmEntity(film.id().id(), film.version().version(),
|
||||
getRawOrNull(film.titel), getRawOrNull(film.imdbUrl), getRawOrNull(film.dauerInMinuten));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package de.accso.flexinale.infrastructure.persistence.mapper;
|
||||
|
||||
import de.accso.flexinale.domain.model.Kino;
|
||||
import de.accso.flexinale.domain.model.KinoSaal;
|
||||
import de.accso.flexinale.infrastructure.persistence.KinoEntity;
|
||||
import de.accso.flexinale.infrastructure.persistence.KinoSaalEntity;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static de.accso.flexinale.shared_kernel.RawWrapper.getRawOrNull;
|
||||
|
||||
public final class KinoKinoSaalEntity2KinoKinoSaalMapper {
|
||||
|
||||
public static Kino mapKino(final KinoEntity kinoEntity) {
|
||||
final Kino kino = new Kino(Identifiable.Id.of(kinoEntity.id), kinoEntity.version());
|
||||
Set<KinoSaal> mappedKinoSaele = kinoEntity.kinoSaele.stream()
|
||||
.map(kinoSaalEntity -> mapKinoSaal(kinoSaalEntity, kino))
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
|
||||
kino.name = new Kino.Name(kinoEntity.name);
|
||||
kino.adresse = new Kino.Adresse(kinoEntity.adresse);
|
||||
kino.emailAdresse = new Kino.EmailAdresse(kinoEntity.emailAdresse);
|
||||
kino.kinoSaele = mappedKinoSaele;
|
||||
|
||||
return kino;
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public static Optional<Kino> mapKino(final Optional<KinoEntity> optionalKinoEntity) {
|
||||
if (optionalKinoEntity.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
else {
|
||||
KinoEntity kinoEntity = optionalKinoEntity.get();
|
||||
return Optional.of(mapKino(kinoEntity));
|
||||
}
|
||||
}
|
||||
|
||||
public static KinoEntity mapKino(final Kino kino) {
|
||||
final KinoEntity kinoEntity = new KinoEntity(kino.id().id(), kino.version().version(),
|
||||
getRawOrNull(kino.name), getRawOrNull(kino.adresse), getRawOrNull(kino.emailAdresse));
|
||||
|
||||
kinoEntity.kinoSaele = kino.kinoSaele.stream()
|
||||
.map(kinoSaal -> mapKinoSaal(kinoSaal, kinoEntity))
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
|
||||
return kinoEntity;
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public static Optional<KinoSaal> mapToKinoSaal(final Optional<KinoSaalEntity> kinoSaalEntity) {
|
||||
return kinoSaalEntity.map(KinoKinoSaalEntity2KinoKinoSaalMapper::mapKinoSaal);
|
||||
}
|
||||
|
||||
public static KinoSaal mapKinoSaal(final KinoSaalEntity kinoSaalEntity) {
|
||||
KinoEntity kinoEntity = kinoSaalEntity.kino;
|
||||
Kino mappedKino = mapKino(kinoEntity);
|
||||
return new KinoSaal(kinoSaalEntity.id(), kinoEntity.version(),
|
||||
new KinoSaal.Name(kinoSaalEntity.name), new KinoSaal.AnzahlPlaetze(kinoSaalEntity.anzahlPlaetze),
|
||||
mappedKino);
|
||||
}
|
||||
|
||||
private static KinoSaal mapKinoSaal(final KinoSaalEntity kinoSaalEntity, final Kino kino) {
|
||||
return new KinoSaal(kinoSaalEntity.id(), kinoSaalEntity.version(),
|
||||
new KinoSaal.Name(kinoSaalEntity.name), new KinoSaal.AnzahlPlaetze(kinoSaalEntity.anzahlPlaetze),
|
||||
kino);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"OptionalUsedAsFieldOrParameterType", "unused"})
|
||||
public static Optional<KinoSaalEntity> mapFromKinoSaal(final Optional<KinoSaal> kinoSaal) {
|
||||
return kinoSaal.map(KinoKinoSaalEntity2KinoKinoSaalMapper::mapKinoSaal);
|
||||
}
|
||||
|
||||
public static KinoSaalEntity mapKinoSaal(final KinoSaal kinoSaal) {
|
||||
Kino kino = kinoSaal.kino;
|
||||
KinoEntity mappedKino = mapKino(kino);
|
||||
return new KinoSaalEntity(kinoSaal.id().id(), kinoSaal.version().version(),
|
||||
getRawOrNull(kinoSaal.name), getRawOrNull(kinoSaal.anzahlPlaetze), mappedKino);
|
||||
}
|
||||
|
||||
private static KinoSaalEntity mapKinoSaal(final KinoSaal kinoSaal, final KinoEntity kinoEntity) {
|
||||
return new KinoSaalEntity(kinoSaal.id().id(), kinoEntity.version().version(),
|
||||
getRawOrNull(kinoSaal.name), getRawOrNull(kinoSaal.anzahlPlaetze), kinoEntity);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package de.accso.flexinale.infrastructure.persistence.mapper;
|
||||
|
||||
import de.accso.flexinale.domain.model.Kontingent;
|
||||
import de.accso.flexinale.infrastructure.persistence.KontingentEntity;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static de.accso.flexinale.shared_kernel.RawWrapper.getRawOrNull;
|
||||
|
||||
public final class KontingentEntity2KontingentMapper {
|
||||
|
||||
public static Kontingent map(final KontingentEntity kontingentEntity) {
|
||||
return new Kontingent(kontingentEntity.id(),
|
||||
kontingentEntity.version(),
|
||||
new Kontingent.RestKontingent(kontingentEntity.getRestKontingentOnline()),
|
||||
new Kontingent.RestKontingent(kontingentEntity.getRestKontingentZentral()),
|
||||
new Kontingent.RestKontingent(kontingentEntity.getRestKontingentKinokasse()));
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public static Optional<Kontingent> map(final Optional<KontingentEntity> optionalKontingentEntity) {
|
||||
if (optionalKontingentEntity.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
else {
|
||||
KontingentEntity kontingentEntity = optionalKontingentEntity.get();
|
||||
return Optional.of(map(kontingentEntity));
|
||||
}
|
||||
}
|
||||
|
||||
public static KontingentEntity map(final Kontingent kontingent) {
|
||||
return new KontingentEntity(kontingent.id().id(),
|
||||
kontingent.version().version(),
|
||||
getRawOrNull(kontingent.getRestKontingentOnline()),
|
||||
getRawOrNull(kontingent.getRestKontingentZentral()),
|
||||
getRawOrNull(kontingent.getRestKontingentKinokasse()));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package de.accso.flexinale.infrastructure.persistence.mapper;
|
||||
|
||||
import de.accso.flexinale.domain.model.Besucher;
|
||||
import de.accso.flexinale.domain.model.Film;
|
||||
import de.accso.flexinale.domain.model.Ticket;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.infrastructure.persistence.FilmEntity;
|
||||
import de.accso.flexinale.infrastructure.persistence.TicketEntity;
|
||||
import de.accso.flexinale.infrastructure.persistence.VorfuehrungEntity;
|
||||
import de.accso.flexinale.infrastructure.security.BenutzerEntity;
|
||||
import de.accso.flexinale.infrastructure.security.mapper.BenutzerEntity2BesucherMapper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class TicketEntity2TicketMapper {
|
||||
|
||||
public static Ticket map(final TicketEntity ticketEntity) {
|
||||
Film mappedFilm = FilmEntity2FilmMapper.map(ticketEntity.film);
|
||||
Vorfuehrung mappedVorfuehrung = VorfuehrungEntity2VorfuehrungMapper.map(ticketEntity.vorfuehrung);
|
||||
Besucher mappedBesucher = BenutzerEntity2BesucherMapper.map(ticketEntity.benutzer);
|
||||
|
||||
return new Ticket(ticketEntity.id(), ticketEntity.version(),
|
||||
mappedFilm, mappedVorfuehrung, mappedBesucher,
|
||||
ticketEntity.verkaufsKanal);
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public static Optional<Ticket> map(final Optional<TicketEntity> optionalTicketEntity) {
|
||||
if (optionalTicketEntity.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
else {
|
||||
TicketEntity ticketEntity = optionalTicketEntity.get();
|
||||
return Optional.of(map(ticketEntity));
|
||||
}
|
||||
}
|
||||
|
||||
public static TicketEntity map(final Ticket ticket) {
|
||||
FilmEntity mappedFilm = FilmEntity2FilmMapper.map(ticket.film);
|
||||
VorfuehrungEntity mappedVorfuehrung = VorfuehrungEntity2VorfuehrungMapper.map(ticket.vorfuehrung);
|
||||
BenutzerEntity mappedBenutzer = BenutzerEntity2BesucherMapper.map(ticket.besucher);
|
||||
|
||||
return new TicketEntity(ticket.id().id(), ticket.version().version(),
|
||||
mappedFilm, mappedVorfuehrung, mappedBenutzer,
|
||||
ticket.verkaufsKanal);
|
||||
}
|
||||
|
||||
public static List<Ticket> map(final List<TicketEntity> ticketEntities) {
|
||||
return ticketEntities.stream().map(TicketEntity2TicketMapper::map)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package de.accso.flexinale.infrastructure.persistence.mapper;
|
||||
|
||||
import de.accso.flexinale.domain.model.Film;
|
||||
import de.accso.flexinale.domain.model.KinoSaal;
|
||||
import de.accso.flexinale.domain.model.Kontingent;
|
||||
import de.accso.flexinale.domain.model.Vorfuehrung;
|
||||
import de.accso.flexinale.infrastructure.persistence.FilmEntity;
|
||||
import de.accso.flexinale.infrastructure.persistence.KinoSaalEntity;
|
||||
import de.accso.flexinale.infrastructure.persistence.KontingentEntity;
|
||||
import de.accso.flexinale.infrastructure.persistence.VorfuehrungEntity;
|
||||
import de.accso.flexinale.shared_kernel.DateTimeHelper;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static de.accso.flexinale.shared_kernel.RawWrapper.getRawOrNull;
|
||||
|
||||
public class VorfuehrungEntity2VorfuehrungMapper {
|
||||
|
||||
public static Vorfuehrung map(final VorfuehrungEntity vorfuehrungEntity) {
|
||||
Film mappedFilm = FilmEntity2FilmMapper.map(vorfuehrungEntity.film);
|
||||
KinoSaal mappedKinoSaal = KinoKinoSaalEntity2KinoKinoSaalMapper.mapKinoSaal(vorfuehrungEntity.kinoSaal);
|
||||
Kontingent mappedKontingent = KontingentEntity2KontingentMapper.map(vorfuehrungEntity.kontingent);
|
||||
|
||||
return new Vorfuehrung(Identifiable.Id.of(vorfuehrungEntity.id), vorfuehrungEntity.version(),
|
||||
new Vorfuehrung.Zeit(DateTimeHelper.fromEpochSeconds(vorfuehrungEntity.zeit)),
|
||||
mappedFilm, mappedKinoSaal, mappedKontingent);
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public static Optional<Vorfuehrung> map(final Optional<VorfuehrungEntity> optionalVorfuehrungEntity) {
|
||||
if (optionalVorfuehrungEntity.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
else {
|
||||
VorfuehrungEntity vorfuehrungEntity = optionalVorfuehrungEntity.get();
|
||||
return Optional.of(map(vorfuehrungEntity));
|
||||
}
|
||||
}
|
||||
|
||||
public static VorfuehrungEntity map(final Vorfuehrung vorfuehrung) {
|
||||
FilmEntity mappedFilm = FilmEntity2FilmMapper.map(vorfuehrung.film);
|
||||
KinoSaalEntity mappedKinoSaal = KinoKinoSaalEntity2KinoKinoSaalMapper.mapKinoSaal(vorfuehrung.kinoSaal);
|
||||
KontingentEntity mappedKontingent = KontingentEntity2KontingentMapper.map(vorfuehrung.getKontingent());
|
||||
|
||||
return new VorfuehrungEntity(vorfuehrung.id().id(), vorfuehrung.version().version(),
|
||||
getRawOrNull(vorfuehrung.zeit), mappedFilm, mappedKinoSaal, mappedKontingent);
|
||||
}
|
||||
|
||||
public static List<Vorfuehrung> map(final List<VorfuehrungEntity> vorfuehrungEntities) {
|
||||
return vorfuehrungEntities.stream().map(VorfuehrungEntity2VorfuehrungMapper::map)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package de.accso.flexinale.infrastructure.security;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationManager;
|
||||
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
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;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class ApplicationSecurityConfiguration {
|
||||
|
||||
@Autowired
|
||||
private UserDetailsService userDetailsService;
|
||||
|
||||
@Bean
|
||||
public PasswordEncoder encoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DaoAuthenticationProvider authenticationProvider() {
|
||||
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
||||
authProvider.setUserDetailsService(userDetailsService);
|
||||
authProvider.setPasswordEncoder(encoder());
|
||||
return authProvider;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AuthenticationManager authManager(final HttpSecurity http) throws Exception {
|
||||
AuthenticationManagerBuilder authenticationManagerBuilder =
|
||||
http.getSharedObject(AuthenticationManagerBuilder.class);
|
||||
authenticationManagerBuilder.authenticationProvider(authenticationProvider());
|
||||
return authenticationManagerBuilder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(final HttpSecurity http) throws Exception {
|
||||
http.authorizeRequests()
|
||||
.anyRequest()
|
||||
.authenticated()
|
||||
.and()
|
||||
.formLogin(Customizer.withDefaults())
|
||||
.httpBasic(Customizer.withDefaults());
|
||||
|
||||
// see here: https://www.baeldung.com/spring-security-csrf
|
||||
// otherwise, the rest POST requests fail with 401 errors
|
||||
http.csrf(c -> c.ignoringRequestMatchers("/rest/**"));
|
||||
return http.build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package de.accso.flexinale.infrastructure.security;
|
||||
|
||||
import de.accso.flexinale.domain.dao.AbstractDao;
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface BenutzerDao extends AbstractDao<BenutzerEntity> {
|
||||
|
||||
@Override
|
||||
Optional<BenutzerEntity> findById(final Identifiable.Id id);
|
||||
|
||||
List<BenutzerEntity> findByName(String name);
|
||||
|
||||
Optional<BenutzerEntity> findByLogin(String login);
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package de.accso.flexinale.infrastructure.security;
|
||||
|
||||
import de.accso.flexinale.domain.model.Besucher;
|
||||
import de.accso.flexinale.infrastructure.security.mapper.BenutzerEntity2BesucherMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
public class BenutzerDetailsService implements UserDetailsService {
|
||||
|
||||
@Autowired
|
||||
private BenutzerJpaRepository benutzerJpaRepository;
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(final String login) throws UsernameNotFoundException {
|
||||
Optional<BenutzerEntity> benutzer = benutzerJpaRepository.findByLogin(login);
|
||||
if (benutzer.isEmpty()) { // login is unique
|
||||
throw new UsernameNotFoundException("No Benutzer %s found".formatted(login));
|
||||
}
|
||||
return new UserPrincipal(benutzer.get());
|
||||
}
|
||||
|
||||
public Besucher getLoggedInBesucher() {
|
||||
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||
String userLogin = authentication.getName();
|
||||
Optional<BenutzerEntity> benutzer = benutzerJpaRepository.findByLogin(userLogin);
|
||||
|
||||
if (benutzer.isEmpty()) {
|
||||
throw new ResponseStatusException(HttpStatus.EXPECTATION_FAILED,
|
||||
"None or more than one Besucher with login " + userLogin);
|
||||
}
|
||||
else {
|
||||
return BenutzerEntity2BesucherMapper.map(benutzer.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
package de.accso.flexinale.infrastructure.security;
|
||||
|
||||
import de.accso.flexinale.shared_kernel.Identifiable;
|
||||
import de.accso.flexinale.shared_kernel.Versionable;
|
||||
import jakarta.persistence.EnumType;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@jakarta.persistence.Entity(name="Benutzer")
|
||||
@SuppressWarnings("unused")
|
||||
public class BenutzerEntity implements Identifiable, Versionable, Serializable {
|
||||
@SuppressWarnings("unused")
|
||||
public enum Rolle { // Spring Security needs "ROLE_" as default prefix! Do not change.
|
||||
ROLE_UNBEKANNT,
|
||||
ROLE_BESUCHER,
|
||||
ROLE_ADMIN
|
||||
}
|
||||
|
||||
@jakarta.persistence.Id
|
||||
public String id; // primary key
|
||||
|
||||
@jakarta.persistence.Column(unique = true)
|
||||
public String login;
|
||||
|
||||
@jakarta.persistence.Column(unique = true)
|
||||
public String emailAdresse;
|
||||
|
||||
@jakarta.persistence.Column
|
||||
public String name;
|
||||
|
||||
@jakarta.persistence.Column
|
||||
public String vorname;
|
||||
|
||||
@jakarta.persistence.Column
|
||||
@jakarta.persistence.Enumerated(EnumType.STRING)
|
||||
public Rolle rolle;
|
||||
|
||||
@jakarta.persistence.Column
|
||||
private String passwort;
|
||||
|
||||
@jakarta.persistence.Version
|
||||
private Integer version = 0;
|
||||
|
||||
protected BenutzerEntity() {
|
||||
}
|
||||
|
||||
public BenutzerEntity(final String id,
|
||||
final String login, final String emailAdresse, final String name, final String vorname) {
|
||||
this(id, Versionable.unknownVersion().version(), login, emailAdresse, name, vorname, Rolle.ROLE_UNBEKANNT);
|
||||
}
|
||||
|
||||
public BenutzerEntity(final String id, final Integer version,
|
||||
final String login, final String emailAdresse, final String name, final String vorname) {
|
||||
this(id, version, login, emailAdresse, name, vorname, Rolle.ROLE_UNBEKANNT);
|
||||
}
|
||||
|
||||
public BenutzerEntity(final String id,
|
||||
final String login, final String emailAdresse, final String name, final String vorname, Rolle rolle) {
|
||||
this(id, Versionable.unknownVersion().version(), login, emailAdresse, name, vorname, rolle);
|
||||
}
|
||||
|
||||
public BenutzerEntity(final String id, final Integer version,
|
||||
final String login, final String emailAdresse, final String name, final String vorname, Rolle rolle) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
this.login = login;
|
||||
this.emailAdresse = emailAdresse;
|
||||
this.name = name;
|
||||
this.vorname = vorname;
|
||||
this.rolle = rolle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Id id() {
|
||||
return Identifiable.Id.of(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Version version() {
|
||||
return Versionable.Version.of(version);
|
||||
}
|
||||
|
||||
public String getPasswort() { //TODO do we need this?
|
||||
return passwort;
|
||||
}
|
||||
|
||||
public void encryptAndSetPassword(final String passwort) {
|
||||
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
this.passwort = encoder.encode(passwort);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {return true;}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {return false;}
|
||||
|
||||
BenutzerEntity that = (BenutzerEntity) o;
|
||||
|
||||
return new EqualsBuilder()
|
||||
.append(id, that.id).append(version, that.version)
|
||||
.append(login, that.login)
|
||||
.append(emailAdresse, that.emailAdresse)
|
||||
.append(name, that.name)
|
||||
.append(vorname, that.vorname).isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37)
|
||||
.append(id).append(version)
|
||||
.append(login)
|
||||
.append(emailAdresse).append(name).append(vorname).toHashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package de.accso.flexinale.infrastructure.security;
|
||||
|
||||
import org.springframework.data.annotation.Immutable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Immutable
|
||||
@Repository
|
||||
public interface BenutzerJpaRepository extends JpaRepository<BenutzerEntity, String> {
|
||||
|
||||
@Query("SELECT b FROM Benutzer b WHERE b.name = :name")
|
||||
List<BenutzerEntity> findByName(@Param("name") String name);
|
||||
|
||||
@Query("SELECT b FROM Benutzer b WHERE b.login = :login")
|
||||
Optional<BenutzerEntity> findByLogin(@Param("login") String login);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue