create jar file #2
98
README.md
98
README.md
@ -1,96 +1,2 @@
|
||||
# НТО 2025. II отборочный этап. Командные задания — Backend Решение
|
||||
## 📖 Предыстория
|
||||
|
||||
В компании S есть возможность бронирования мест в пространствах, предназначенных под общее использование (open-space). На данный момент для бронирования места используются различные способы бронирования, разработанные в каждом офисе индивидуально.
|
||||
Администрации компании S требуется мобильное приложение, как для рядовых сотрудников, так и для администрации с возможностью просмотра забронированных мест.
|
||||
|
||||
## 📑 Технологический стек
|
||||
|
||||
- Java 17
|
||||
- Spring Boot
|
||||
- H2
|
||||
- Liquibase
|
||||
|
||||
|
||||
## 🛠️ Техническое задание
|
||||
|
||||
Требуется разработать серверное приложение на Java с использованием Spring Boot, которое работает на основе протокола HTTP и взаимодействует с клиентами благодаря RESTful API.
|
||||
|
||||
Для хранения данных о сотрудниках и их посещениях должна использоваться реляционная база данных (H2). Схема БД должна создаваться liquibase-скриптами. ID-поля всех сущностей, сохраняемых в базе, должны выдаваться на уровне БД. Стратегия генерации ID - автоинкремент (1, 2, 3, 4…)
|
||||
|
||||
Сотрудникам не нужно регистрироваться, все данные в базе должны быть предзаполнены liquibase-скриптами при запуске серверного приложения. Данные для предзаполнения таблиц представлены ниже.
|
||||
|
||||
Картинки для аватаров пользователей не должны храниться в БД. Должны быть сохранены лишь URL-адреса на ресурсы, откуда в последующем мобильное приложение загрузит соответствующее изображение.
|
||||
|
||||
Сервер разрабатывается на основе предоставляемой заготовки проекта. Версии зависимостей и сами зависимости изменяться не должны.
|
||||
|
||||
|
||||
## 📂 Правила работы с проектом-заготовкой
|
||||
|
||||
В предоставленном проекте необходимо изучить, но никак не модифицировать, не перемещать и не удалять следующие файлы:
|
||||
- `pom.xml`
|
||||
- `application.yml`
|
||||
- все файлы из `db.changelog`
|
||||
|
||||
Кроме описанных выше файлов, в проекте уже созданы основные классы-сущности и добавлены пустые классы всех слоев. В этих классах необходимо будет написать программный код и добавить аннотации для реализации описанного задания. Наименования классов и прочий код уже написанный в предоставляемом проекте **изменять/удалять не нужно, необходимо их доработать**. Добавлять свои дополнительные классы в проект можно.
|
||||
|
||||
Создание таблиц в БД и предзаполнение их требуемыми данными уже реализовано в заготовке при помощи liquibase. В одной из сущностей добавлены аннотации для реализации связи “один ко многим”, обратите внимание, что в проекте потребуется еще связь данного типа.
|
||||
|
||||
|
||||
## 🌐 Где необходимо разместить сервер
|
||||
|
||||
Серверное приложение должно быть разработано и протестировано локально (**не требуется** размещать сервер удаленно и осуществлять его функционирование 24/7).
|
||||
|
||||
|
||||
## 📋 Технические требования к серверу и его ответам клиенту
|
||||
|
||||
Для конфигурирования Вашего сервера (его хоста/IP адреса) используйте константы из файла `Constants.kt`.
|
||||
|
||||
| **Тип запроса** | **Путь** | **Описание** | **Параметры/Тело** | **Ответы** |
|
||||
|-----------------|-----------------------|---------------------------|--------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **GET** | `api/<CODE>/auth` | Проверка авторизации | `<CODE>` - код для входа | `400` - что-то пошло не так<br>`401` - кода не существует<br>`200` - данный код существует - можно пользоваться приложением |
|
||||
| **GET** | `api/<CODE>/info` | Получение информации о пользователе | `<CODE>` - код для входа | `400` - что-то пошло не так<br>`401` - кода не существует<br>`200` - ОК<br><pre>{<br> "name":"Иванов Петр Федорович",<br> "photoUrl":"<https://funnyducks.ru/upload/iblock/0cd/0cdeb7ec3ed6fddda0f90fccee05557d.jpg>",<br> "booking":{<br> "2025-01-05": {"id":1,"place":"102"},<br> "2025-01-06": {"id":2,"place":"209.13"},<br> "2025-01-09": {"id":3,"place":"Зона 51. 50"}<br> }<br>}</pre> |
|
||||
| **GET** | `api/<CODE>/booking` | Получение доступных для бронирования мест | `<CODE>` - код для входа | `400` - что-то пошло не так<br>`401` - кода не существует<br>`200` - ОК<br><pre>{<br> "2025-01-05": [{"id": 1, "place": "102"},{"id": 2, "place": "209.13"}],<br> "2025-01-06": [{"id": 3, "place": "Зона 51. 50"}],<br> "2025-01-07": [{"id": 1, "place": "102"},{"id": 2, "place": "209.13"}],<br> "2025-01-08": [{"id": 2, "place": "209.13"}]<br>}</pre> **Список дат ограничен текущим + 3 днями** (ответ от сервера содержит 4 дня со свободными местами для каждого)
|
||||
| **POST** | `api/<CODE>/book` | Создание нового бронирования | `<CODE>` - код для входа<br>Тело: <br> <pre>{<br> “date”: “2025-01-05”,<br> “placeId”: 1 <br>}</pre> |`400` - что-то пошло не так<br>`401` - кода не существует<br>`409` - уже забронировано<br>`201` - бронирование успешно создано
|
||||
|
||||
|
||||
## 📊 Пример данных
|
||||
|
||||
Таблица сотрудников:
|
||||
|
||||
| **id** | **name** | **code** | **photo_url** |
|
||||
|--------|-----------|---------------------------------|---------------------------------------------------------------------------------------------|
|
||||
| 1 | Ivanov Ivan | 1111 | https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg |
|
||||
| 2 | Petrov Petr | 2222 | https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg |
|
||||
| 3 | Kozlov Oleg | 3333 | https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg |
|
||||
| 4 | Smirnova Anna | 4444 | https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg |
|
||||
|
||||
|
||||
Таблица мест для бронирования:
|
||||
|
||||
| **id** | **place_name** |
|
||||
|--------|-----------------|
|
||||
| 1 | K-19 |
|
||||
| 2 | M-16 |
|
||||
| 3 | T-1 |
|
||||
|
||||
|
||||
Таблица бронирований:
|
||||
|
||||
| **id** | **date** | **place_id** | **employee_id** |
|
||||
|--------|-----------|--------------|------------------|
|
||||
| 1 | 2025-11-08 | 1 | 1 |
|
||||
| 2 | 2025-11-10 | 2 | 2 |
|
||||
|
||||
|
||||
# 📝 Решение
|
||||
|
||||
Работу необходимо осуществлять в предоставленном проекте-заготовке (шаблоне).
|
||||
Когда завершите разработку, создайте пулреквест и запустите workflow в учебной системе.
|
||||
|
||||
|
||||
## ✅ Особенности оценивания
|
||||
|
||||
При тестировании сервера на него поочередно отправляются команды, описанные в API и ожидаются определенные корректные ответы.
|
||||
Сервер и приложение тестируются независимо.
|
||||
|
||||
username: user
|
||||
password: 123456789
|
||||
22
pom.xml
22
pom.xml
@ -56,6 +56,28 @@
|
||||
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
|
||||
<version>2.8.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
40
src/main/java/com/example/nto/config/WebSecurityConfig.java
Normal file
40
src/main/java/com/example/nto/config/WebSecurityConfig.java
Normal file
@ -0,0 +1,40 @@
|
||||
package com.example.nto.config;
|
||||
|
||||
import com.example.nto.filter.BaseAuthFilter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
public class WebSecurityConfig {
|
||||
@Bean
|
||||
public PasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||
return
|
||||
http
|
||||
.csrf(csrf -> csrf.disable())
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
.requestMatchers("/h2-console").permitAll()
|
||||
.requestMatchers("/register").permitAll()
|
||||
.anyRequest().authenticated()).
|
||||
addFilterBefore(new BaseAuthFilter(), UsernamePasswordAuthenticationFilter.class).
|
||||
httpBasic(Customizer.withDefaults())
|
||||
.build();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -1,12 +1,21 @@
|
||||
package com.example.nto.controller;
|
||||
|
||||
|
||||
import com.example.nto.controller.dto.EmployeeDto;
|
||||
import com.example.nto.entity.Employee;
|
||||
import com.example.nto.service.EmployeeService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.tomcat.util.net.openssl.ciphers.Authentication;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.web.server.authorization.AuthorizationContext;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static com.example.nto.controller.dto.EmployeeDto.toDto;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("api")
|
||||
@RequiredArgsConstructor
|
||||
@ -14,16 +23,14 @@ public class EmployeeController {
|
||||
|
||||
private final EmployeeService employeeService;
|
||||
|
||||
@GetMapping("/{code}/auth")
|
||||
@ResponseStatus(code = HttpStatus.OK)
|
||||
public void login(@PathVariable String code) {
|
||||
employeeService.auth(code);
|
||||
@GetMapping("/login")
|
||||
public Employee login(Authentication auth) {
|
||||
return employeeService.getByUsername((auth.getDeclaringClass()).getName());
|
||||
}
|
||||
|
||||
@GetMapping("/{code}/info")
|
||||
@ResponseStatus(code = HttpStatus.OK)
|
||||
public EmployeeDto getByCode(@PathVariable String code) {
|
||||
return employeeService.getByCode(code);
|
||||
@PostMapping("/register")
|
||||
public void register(@RequestBody Map<String, String> body) {
|
||||
employeeService.register(body.get("login"), body.get("password"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ import java.util.TreeMap;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EmployeeDto {
|
||||
private String name;
|
||||
private String username;
|
||||
private String photoUrl;
|
||||
private Map<LocalDate, PlaceDto> booking;
|
||||
|
||||
@ -26,6 +26,6 @@ public class EmployeeDto {
|
||||
dtoTreeMap.put(booking.getDate(), PlaceDto.toDto(booking.getPlace()));
|
||||
}
|
||||
|
||||
return new EmployeeDto(employee.getName(), employee.getPhotoUrl(), dtoTreeMap);
|
||||
return new EmployeeDto(employee.getUsername(), employee.getPhotoUrl(), dtoTreeMap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
package com.example.nto.controller.dto;
|
||||
|
||||
import com.example.nto.entity.Booking;
|
||||
import com.example.nto.entity.Employee;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EmployeeRegisterDto {
|
||||
private String username;
|
||||
private String password;
|
||||
}
|
||||
@ -5,7 +5,10 @@ import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@ -14,21 +17,26 @@ import java.util.List;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Table(name = "employee")
|
||||
public class Employee {
|
||||
public class Employee implements UserDetails {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private long id;
|
||||
|
||||
@Column(name = "name")
|
||||
private String name;
|
||||
@Column(name = "username")
|
||||
private String username;
|
||||
|
||||
@Column(name = "code")
|
||||
private String code;
|
||||
@Column(name = "password")
|
||||
private String password;
|
||||
|
||||
@Column(name = "photo_url")
|
||||
private String photoUrl;
|
||||
|
||||
@OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
private List<Booking> bookingList;
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> getAuthorities() {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
package com.example.nto.exception;
|
||||
|
||||
public class EmployeeAlreadyExistsException extends RuntimeException {
|
||||
public EmployeeAlreadyExistsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package com.example.nto.exception;
|
||||
|
||||
public class NoSuchUsernameException extends RuntimeException {
|
||||
public NoSuchUsernameException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,6 @@
|
||||
package com.example.nto.exception.handler;
|
||||
|
||||
import com.example.nto.exception.BookingAlreadyExistsException;
|
||||
import com.example.nto.exception.EmployeeNotFoundException;
|
||||
import com.example.nto.exception.PlaceNotFoundException;
|
||||
import com.example.nto.exception.*;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
@ -25,8 +23,19 @@ public class GlobalExceptionHandler {
|
||||
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
@ExceptionHandler(EmployeeAlreadyExistsException.class)
|
||||
public ResponseEntity<String> handleEmployeeAlreadyExistsException(EmployeeAlreadyExistsException e) {
|
||||
return new ResponseEntity<>(e.getMessage(), HttpStatus.CONFLICT);
|
||||
}
|
||||
|
||||
@ExceptionHandler(NoSuchUsernameException.class)
|
||||
public ResponseEntity<String> handleNoSuchUsernameException(NoSuchUsernameException e) {
|
||||
return new ResponseEntity<>(e.getMessage(), HttpStatus.CONFLICT);
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<String> handleGenericException(Exception e) {
|
||||
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
36
src/main/java/com/example/nto/filter/BaseAuthFilter.java
Normal file
36
src/main/java/com/example/nto/filter/BaseAuthFilter.java
Normal file
@ -0,0 +1,36 @@
|
||||
package com.example.nto.filter;
|
||||
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.web.filter.OncePerRequestFilter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
|
||||
public class BaseAuthFilter extends OncePerRequestFilter {
|
||||
@Override
|
||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||
String header = request.getHeader("Authorisation");
|
||||
if (header != null && header.startsWith("Base")) {
|
||||
try {
|
||||
String base64Token = header.substring(5);
|
||||
byte[] decoded = Base64.getDecoder().decode(base64Token);
|
||||
String credentials = new String(decoded, StandardCharsets.UTF_8);
|
||||
String[] values = credentials.split(" ", 2);
|
||||
String username = values[0];
|
||||
String password = values[1];
|
||||
|
||||
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(username, password, new ArrayList<>());
|
||||
SecurityContextHolder.getContext().setAuthentication(auth);
|
||||
} catch (Exception e) {
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,5 +8,5 @@ import java.util.Optional;
|
||||
|
||||
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
|
||||
@EntityGraph(attributePaths = {"bookingList", "bookingList.place"})
|
||||
Optional<Employee> findByCode(String code);
|
||||
Optional<Employee> findByUsername(String username);
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
package com.example.nto.service;
|
||||
|
||||
import com.example.nto.controller.dto.EmployeeDto;
|
||||
import com.example.nto.entity.Employee;
|
||||
|
||||
public interface EmployeeService {
|
||||
EmployeeDto getByCode(String code);
|
||||
Employee getByUsername(String username);
|
||||
|
||||
void auth(String code);
|
||||
void register(String login, String password);
|
||||
}
|
||||
|
||||
@ -30,7 +30,6 @@ public class BookingServiceImpl implements BookingService {
|
||||
private final BookingRepository bookingRepository;
|
||||
private final EmployeeRepository employeeRepository;
|
||||
private final PlaceRepository placeRepository;
|
||||
private final EmployeeService employeeService;
|
||||
|
||||
@Value("${booking.days-ahead}")
|
||||
private int daysAhead;
|
||||
@ -38,7 +37,6 @@ public class BookingServiceImpl implements BookingService {
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Map<LocalDate, List<PlaceDto>> getFreePlace(String code) {
|
||||
employeeService.auth(code);
|
||||
|
||||
List<Place> allPlaces = placeRepository.findAll();
|
||||
|
||||
@ -72,15 +70,15 @@ public class BookingServiceImpl implements BookingService {
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Booking create(String code, BookingCreateDto bookingCreateDto) {
|
||||
public Booking create(String username, BookingCreateDto bookingCreateDto) {
|
||||
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)
|
||||
.orElseThrow(() -> new EmployeeNotFoundException("Employee with " + code + " code not found!"));
|
||||
Employee employee = employeeRepository.findByUsername(username)
|
||||
.orElseThrow(() -> new EmployeeNotFoundException("Employee with " + username + " code not found!"));
|
||||
|
||||
long placeId = bookingCreateDto.getPlaceId();
|
||||
Place place = placeRepository.findById(placeId)
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
package com.example.nto.service.impl;
|
||||
|
||||
import com.example.nto.entity.Employee;
|
||||
import com.example.nto.exception.NoSuchUsernameException;
|
||||
import com.example.nto.repository.EmployeeRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class CustomUserDetailsServiceImpl implements UserDetailsService {
|
||||
@Autowired private EmployeeRepository employeeRepository;
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||
Employee employee = employeeRepository.findByUsername(username).orElseThrow(() -> new NoSuchUsernameException("No employee with " + username + " username!!!"));
|
||||
|
||||
return new org.springframework.security.core.userdetails.User(employee.getUsername(),
|
||||
employee.getPassword(),
|
||||
new ArrayList<>());
|
||||
}
|
||||
}
|
||||
@ -1,31 +1,37 @@
|
||||
package com.example.nto.service.impl;
|
||||
|
||||
import com.example.nto.controller.dto.EmployeeDto;
|
||||
import com.example.nto.exception.EmployeeNotFoundException;
|
||||
import com.example.nto.entity.Employee;
|
||||
import com.example.nto.exception.EmployeeAlreadyExistsException;
|
||||
import com.example.nto.exception.NoSuchUsernameException;
|
||||
import com.example.nto.repository.EmployeeRepository;
|
||||
import com.example.nto.service.EmployeeService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class EmployeeServiceImpl implements EmployeeService {
|
||||
|
||||
@Autowired
|
||||
private final EmployeeRepository employeeRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public EmployeeDto getByCode(String code) {
|
||||
return employeeRepository.findByCode(code).map(EmployeeDto::toDto)
|
||||
.orElseThrow(() -> new EmployeeNotFoundException("Employee with " + code + " code not found!"));
|
||||
public Employee getByUsername(String username) {
|
||||
return employeeRepository.findByUsername(username).orElseThrow(() -> new NoSuchUsernameException("No employee with " + username + " username!!!"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public void auth(String code) {
|
||||
if (employeeRepository.findByCode(code).isEmpty()) {
|
||||
throw new EmployeeNotFoundException("Employee with " + code + " code not found!");
|
||||
public void register(String login, String password) {
|
||||
if (employeeRepository.findByUsername(login).isPresent()){
|
||||
throw new EmployeeAlreadyExistsException("Username is already exists");
|
||||
}
|
||||
|
||||
Employee employee = new Employee();
|
||||
employee.setUsername(login);
|
||||
employee.setPassword(passwordEncoder.encode(password));
|
||||
}
|
||||
}
|
||||
|
||||
1
src/main/resources/application.properties
Normal file
1
src/main/resources/application.properties
Normal file
@ -0,0 +1 @@
|
||||
server.port=49155
|
||||
@ -21,10 +21,14 @@
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
|
||||
<column name="code" type="VARCHAR(100)">
|
||||
<column name="username" type="VARCHAR(100)">
|
||||
<constraints nullable="false" unique="true"/>
|
||||
</column>
|
||||
|
||||
<column name="password" type="VARCHAR(50)">
|
||||
<constraints nullable="false"/>
|
||||
</column>
|
||||
|
||||
<column name="photo_url" type="VARCHAR(100)"/>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.0.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
|
||||
|
||||
<changeSet id="2026-25-02--0004-authority" author="ggaydaenko">
|
||||
<preConditions onFail="MARK_RAN">
|
||||
<not>
|
||||
<tableExists tableName="authority"/>
|
||||
</not>
|
||||
</preConditions>
|
||||
|
||||
<createTable tableName="authority">
|
||||
<column name="id" type="BIGINT" autoIncrement="true">
|
||||
<constraints primaryKey="true" nullable="false"/>
|
||||
</column>
|
||||
|
||||
<column name="authority" type="VARCHAR(100)">
|
||||
<constraints nullable="false" unique="true"/>
|
||||
</column>
|
||||
</createTable>
|
||||
</changeSet>
|
||||
|
||||
</databaseChangeLog>
|
||||
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.0.xsd
|
||||
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
|
||||
|
||||
<changeSet id="2026-25-02--0004-authority-data" author="ggaydaenko">
|
||||
<loadData tableName="authority" file="db.changelog/data/csv/2026-25-02--0004-authority-data.csv"
|
||||
separator=";"
|
||||
quotchar='"'
|
||||
encoding="UTF-8"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
@ -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
|
||||
name;username;password;photo_url
|
||||
Ivanov Ivan;iivanov;YWRtaW4=;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg
|
||||
Petrov Petr;ppetrov;YWRtaW4=;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg
|
||||
Kozlov Oleg;okozlov;YWRtaW4=;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg
|
||||
Smirnova Anna;asmirnova;YWRtaW4=;https://catalog-cdn.detmir.st/media/2fe02057f9915e72a378795d32c79ea9.jpeg
|
||||
|
@ -0,0 +1,3 @@
|
||||
authority
|
||||
ROLE_USER
|
||||
ROLE_ROOM
|
||||
|
Loading…
x
Reference in New Issue
Block a user