diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..981aeb8 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/out \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..ab1f416 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/artifacts/NTO_2025_Backend_Team_Task_jar.xml b/.idea/artifacts/NTO_2025_Backend_Team_Task_jar.xml new file mode 100644 index 0000000..fee9c5a --- /dev/null +++ b/.idea/artifacts/NTO_2025_Backend_Team_Task_jar.xml @@ -0,0 +1,91 @@ + + + $PROJECT_DIR$/out/artifacts/NTO_2025_Backend_Team_Task_jar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..7c70b4f --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..63e9001 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..712ab9d --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..9dc782b --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 93eee07..018a1b2 100644 --- a/pom.xml +++ b/pom.xml @@ -58,4 +58,12 @@ + + + + org.springframework.boot + spring-boot-maven-plugin + + + \ No newline at end of file diff --git a/src/main/java/com/example/nto/controller/BookingController.java b/src/main/java/com/example/nto/controller/BookingController.java index a4ed8b1..1ce9439 100644 --- a/src/main/java/com/example/nto/controller/BookingController.java +++ b/src/main/java/com/example/nto/controller/BookingController.java @@ -20,16 +20,16 @@ public class BookingController { private final BookingService bookingService; - @GetMapping("/{code}/booking") + @GetMapping("/{code}/{password}/booking") @ResponseStatus(code = HttpStatus.OK) - public Map> getByDate(@PathVariable String code) { - return bookingService.getFreePlace(code); + public Map> getByDate(@PathVariable String code, @PathVariable String password) { + return bookingService.getFreePlace(code, password); } - @PostMapping("/{code}/book") + @PostMapping("/{code}/{password}/book") @ResponseStatus(code = HttpStatus.CREATED) - public void create(@PathVariable String code, @RequestBody BookingCreateDto bookingCreateDto) { - bookingService.create(code, bookingCreateDto); + public void create(@PathVariable String code, @RequestBody BookingCreateDto bookingCreateDto, @PathVariable String password) { + bookingService.create(code, password, bookingCreateDto); } } diff --git a/src/main/java/com/example/nto/controller/EmployeeController.java b/src/main/java/com/example/nto/controller/EmployeeController.java index 7f73702..e9e8f0e 100644 --- a/src/main/java/com/example/nto/controller/EmployeeController.java +++ b/src/main/java/com/example/nto/controller/EmployeeController.java @@ -14,16 +14,16 @@ public class EmployeeController { private final EmployeeService employeeService; - @GetMapping("/{code}/auth") - @ResponseStatus(code = HttpStatus.OK) - public void login(@PathVariable String code) { - employeeService.auth(code); - } - @GetMapping("/{code}/info") @ResponseStatus(code = HttpStatus.OK) - public EmployeeDto getByCode(@PathVariable String code) { - return employeeService.getByCode(code); + public EmployeeDto getByCode(@PathVariable String login) { + return employeeService.getByCode(login); + } + + @GetMapping("/login/{username}/{password}") + @ResponseStatus(code = HttpStatus.OK) + public EmployeeDto login(@PathVariable String username, @PathVariable String password){ + return employeeService.auth(username, password); } } diff --git a/src/main/java/com/example/nto/controller/dto/EmployeeCreateDto.java b/src/main/java/com/example/nto/controller/dto/EmployeeCreateDto.java new file mode 100644 index 0000000..ec7a585 --- /dev/null +++ b/src/main/java/com/example/nto/controller/dto/EmployeeCreateDto.java @@ -0,0 +1,18 @@ +package com.example.nto.controller.dto; + +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class EmployeeCreateDto { + @NotNull + private String username; + @NotNull + private String password; +} diff --git a/src/main/java/com/example/nto/controller/dto/EmployeeDto.java b/src/main/java/com/example/nto/controller/dto/EmployeeDto.java index 3c87566..710da9f 100644 --- a/src/main/java/com/example/nto/controller/dto/EmployeeDto.java +++ b/src/main/java/com/example/nto/controller/dto/EmployeeDto.java @@ -9,6 +9,7 @@ import lombok.NoArgsConstructor; import java.time.LocalDate; import java.util.Map; +import java.util.Optional; import java.util.TreeMap; @Data @@ -28,4 +29,13 @@ public class EmployeeDto { return new EmployeeDto(employee.getName(), employee.getPhotoUrl(), dtoTreeMap); } + + public static EmployeeDto toDtoOpt(Optional employee) { + Map dtoTreeMap = new TreeMap<>(); + for (Booking booking : employee.get().getBookingList()) { + dtoTreeMap.put(booking.getDate(), PlaceDto.toDto(booking.getPlace())); + } + + return new EmployeeDto(employee.get().getName(), employee.get().getPhotoUrl(), dtoTreeMap); + } } diff --git a/src/main/java/com/example/nto/entity/Employee.java b/src/main/java/com/example/nto/entity/Employee.java index e854a92..26ebe38 100644 --- a/src/main/java/com/example/nto/entity/Employee.java +++ b/src/main/java/com/example/nto/entity/Employee.java @@ -6,7 +6,9 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import java.util.Collection; import java.util.List; +import java.util.Set; @Data @Entity @@ -23,6 +25,12 @@ public class Employee { @Column(name = "name") private String name; + @Column(name = "username") + private String username; + + @Column(name = "password") + private String password; + @Column(name = "code") private String code; @@ -31,4 +39,7 @@ public class Employee { @OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private List bookingList; + + @ManyToMany + private Set roles; } diff --git a/src/main/java/com/example/nto/entity/Role.java b/src/main/java/com/example/nto/entity/Role.java new file mode 100644 index 0000000..75fa41d --- /dev/null +++ b/src/main/java/com/example/nto/entity/Role.java @@ -0,0 +1,21 @@ +package com.example.nto.entity; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Data +public class Role { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column(name = "role") + private String role; + +} diff --git a/src/main/java/com/example/nto/exception/EmployeeAlreadyExists.java b/src/main/java/com/example/nto/exception/EmployeeAlreadyExists.java new file mode 100644 index 0000000..7d64107 --- /dev/null +++ b/src/main/java/com/example/nto/exception/EmployeeAlreadyExists.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class EmployeeAlreadyExists extends RuntimeException { + public EmployeeAlreadyExists(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/nto/exception/InvalidPassword.java b/src/main/java/com/example/nto/exception/InvalidPassword.java new file mode 100644 index 0000000..352a305 --- /dev/null +++ b/src/main/java/com/example/nto/exception/InvalidPassword.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class InvalidPassword extends RuntimeException { + public InvalidPassword(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/nto/exception/InvalidUsername.java b/src/main/java/com/example/nto/exception/InvalidUsername.java new file mode 100644 index 0000000..72772e5 --- /dev/null +++ b/src/main/java/com/example/nto/exception/InvalidUsername.java @@ -0,0 +1,7 @@ +package com.example.nto.exception; + +public class InvalidUsername extends RuntimeException { + public InvalidUsername(String message) { + super(message); + } +} diff --git a/src/main/java/com/example/nto/repository/EmployeeRepository.java b/src/main/java/com/example/nto/repository/EmployeeRepository.java index d845a04..968e56f 100644 --- a/src/main/java/com/example/nto/repository/EmployeeRepository.java +++ b/src/main/java/com/example/nto/repository/EmployeeRepository.java @@ -3,10 +3,14 @@ package com.example.nto.repository; import com.example.nto.entity.Employee; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.List; import java.util.Optional; public interface EmployeeRepository extends JpaRepository { @EntityGraph(attributePaths = {"bookingList", "bookingList.place"}) Optional findByCode(String code); + + Optional findByUsername(String username); } diff --git a/src/main/java/com/example/nto/repository/PlaceRepository.java b/src/main/java/com/example/nto/repository/PlaceRepository.java index e7b84c9..5c67b85 100644 --- a/src/main/java/com/example/nto/repository/PlaceRepository.java +++ b/src/main/java/com/example/nto/repository/PlaceRepository.java @@ -1,7 +1,10 @@ package com.example.nto.repository; import com.example.nto.entity.Place; +import jdk.jfr.Registered; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +@Repository public interface PlaceRepository extends JpaRepository { } diff --git a/src/main/java/com/example/nto/service/BookingService.java b/src/main/java/com/example/nto/service/BookingService.java index 64e6ac6..37cd9f0 100644 --- a/src/main/java/com/example/nto/service/BookingService.java +++ b/src/main/java/com/example/nto/service/BookingService.java @@ -9,7 +9,7 @@ import java.util.List; import java.util.Map; public interface BookingService { - Map> getFreePlace(String code); + Map> getFreePlace(String code, String password); - Booking create(String code, BookingCreateDto bookingCreateDto); + Booking create(String code, String password, BookingCreateDto bookingCreateDto); } diff --git a/src/main/java/com/example/nto/service/EmployeeService.java b/src/main/java/com/example/nto/service/EmployeeService.java index 83144af..2a0994f 100644 --- a/src/main/java/com/example/nto/service/EmployeeService.java +++ b/src/main/java/com/example/nto/service/EmployeeService.java @@ -1,9 +1,11 @@ package com.example.nto.service; import com.example.nto.controller.dto.EmployeeDto; +import com.example.nto.exception.InvalidPassword; public interface EmployeeService { EmployeeDto getByCode(String code); - void auth(String code); + EmployeeDto auth(String username, String password) throws InvalidPassword; + } diff --git a/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java b/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java index ffc4f86..957d6e5 100644 --- a/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/BookingServiceImpl.java @@ -37,8 +37,8 @@ public class BookingServiceImpl implements BookingService { @Override @Transactional(readOnly = true) - public Map> getFreePlace(String code) { - employeeService.auth(code); + public Map> getFreePlace(String code, String password) { + employeeService.auth(code, password); List allPlaces = placeRepository.findAll(); @@ -72,14 +72,15 @@ public class BookingServiceImpl implements BookingService { @Override @Transactional - public Booking create(String code, BookingCreateDto bookingCreateDto) { + public Booking create(String code, String password, BookingCreateDto bookingCreateDto) { + employeeService.auth(code, password); LocalDate date = bookingCreateDto.getDate(); LocalDate today = LocalDate.now(ZoneId.systemDefault()); if (date.isBefore(today) || date.isAfter(today.plusDays(daysAhead))) { throw new IllegalArgumentException("Date is out of booking window"); } - Employee employee = employeeRepository.findByCode(code) + Employee employee = employeeRepository.findByUsername(code) .orElseThrow(() -> new EmployeeNotFoundException("Employee with " + code + " code not found!")); long placeId = bookingCreateDto.getPlaceId(); diff --git a/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java index 3085dc2..1ccec1e 100644 --- a/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java +++ b/src/main/java/com/example/nto/service/impl/EmployeeServiceImpl.java @@ -1,13 +1,22 @@ package com.example.nto.service.impl; import com.example.nto.controller.dto.EmployeeDto; +import com.example.nto.entity.Employee; import com.example.nto.exception.EmployeeNotFoundException; +import com.example.nto.exception.InvalidPassword; +import com.example.nto.exception.InvalidUsername; import com.example.nto.repository.EmployeeRepository; import com.example.nto.service.EmployeeService; + import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.Optional; + + +@Slf4j @Service @RequiredArgsConstructor public class EmployeeServiceImpl implements EmployeeService { @@ -21,11 +30,20 @@ public class EmployeeServiceImpl implements EmployeeService { .orElseThrow(() -> new EmployeeNotFoundException("Employee with " + code + " code not found!")); } + + @Override @Transactional(readOnly = true) - public void auth(String code) { - if (employeeRepository.findByCode(code).isEmpty()) { - throw new EmployeeNotFoundException("Employee with " + code + " code not found!"); + public EmployeeDto auth(String username, String password) throws InvalidPassword { + Optional employee = employeeRepository.findByUsername(username); + if(employee.isEmpty()){ + throw new InvalidUsername("Invalid Username"); } + + + if(!employee.get().getPassword().equals(password)){ + throw new InvalidPassword("Wrong password: " + password); + } + return EmployeeDto.toDtoOpt(employee); } } diff --git a/src/main/java/com/example/nto/utils/PasswordValidator.java b/src/main/java/com/example/nto/utils/PasswordValidator.java new file mode 100644 index 0000000..a490119 --- /dev/null +++ b/src/main/java/com/example/nto/utils/PasswordValidator.java @@ -0,0 +1,16 @@ +package com.example.nto.utils; + +import java.util.regex.Pattern; + +public class PasswordValidator { + public static boolean validate(String password){ + Pattern p = Pattern.compile("*[%?()*^$#@!/.,><|';:]*"); + if(password.length() < 8){ + return false; + } + if(!p.matcher(password).matches()){ + return false; + } + return true; + } +} diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..929f41e --- /dev/null +++ b/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: com.example.nto.App + diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8b68191..a2a2d7a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,4 +1,6 @@ spring: + profiles: + active: "dev" datasource: url: jdbc:h2:mem:testdb @@ -18,5 +20,8 @@ spring: enabled: true change-log: classpath:db.changelog/db.changelog-master.xml +server: + port : 49179 + booking: days-ahead: 3 \ No newline at end of file diff --git a/src/main/resources/db.changelog/1/0/2025-11-05--0001-employee.xml b/src/main/resources/db.changelog/1/0/2025-11-05--0001-employee.xml index d1f92f8..1017f74 100644 --- a/src/main/resources/db.changelog/1/0/2025-11-05--0001-employee.xml +++ b/src/main/resources/db.changelog/1/0/2025-11-05--0001-employee.xml @@ -21,6 +21,14 @@ + + + + + + + + diff --git a/src/main/resources/db.changelog/data/csv/2025-11-05--0001-employee-data.csv b/src/main/resources/db.changelog/data/csv/2025-11-05--0001-employee-data.csv index 87ddc6b..d2015e1 100644 --- a/src/main/resources/db.changelog/data/csv/2025-11-05--0001-employee-data.csv +++ b/src/main/resources/db.changelog/data/csv/2025-11-05--0001-employee-data.csv @@ -1,5 +1,5 @@ -name;code;photo_url -Ivanov Ivan;1111;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg -Petrov Petr;2222;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg -Kozlov Oleg;3333;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg -Smirnova Anna;4444;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg \ No newline at end of file +name;code;photo_url;password;username +Ivanov Ivan;1111;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg;bac12345;ivan +Petrov Petr;2222;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg;bac12345;petr +Kozlov Oleg;3333;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg;bac12345;oleg +Smirnova Anna;4444;https://catalog-cdn.udetmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg;bac12345;anna \ No newline at end of file