Merge pull request 'develop' (#3) from develop into master

Reviewed-on: #3
This commit is contained in:
Petr 2025-02-19 12:37:35 +00:00
commit 18664d196b
62 changed files with 959 additions and 9 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

View File

@ -25,7 +25,7 @@ public class Passage {
@Column(name = "passage", unique = true)
@NotBlank(message = "Название не может быть пустым!")
@Size(max = 100, message = "Максимальная длина названия 100 символов!")
private String passage;
private String passageName;
@OneToMany(mappedBy = "passage")
private List<Visit> visits;

View File

@ -31,6 +31,7 @@ public class Terminal {
// Мне нужна была рандомная генерация кода для терминала, так что я мог сделать кривую реализацию через самописную функцию,
// но боялся, что она будет генерировать не уникальные значения. (я очень смутно представляю, как эта штука работает)
// Код взят отсюда: https://stackoverflow.com/questions/25082244/auto-generate-unique-random-string-in-spring-mvc-hibernate
// todo: Понять что это такое.......................................................................................
@GenericGenerator(name = "uuid-gen", strategy = "uuid")
@GeneratedValue(generator = "uuid-gen")
@NotBlank(message = "Код не может быть пустым!")

View File

@ -30,7 +30,7 @@ public class Visit {
private LocalDateTime endVisit;
@Column(name = "is_finished", nullable = false)
private final boolean isFinished = false;
private boolean isFinished = false;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "start_terminal_id", referencedColumnName = "code", nullable = false)

View File

@ -0,0 +1,8 @@
package com.example.nto.domain.exception;
public class AccessDeniedException extends RuntimeException {
public AccessDeniedException(String message) {
// Доступ запрещен.
super();
}
}

View File

@ -0,0 +1,8 @@
package com.example.nto.domain.exception;
public class ConflictResourceException extends RuntimeException {
public ConflictResourceException(String message) {
// Вызывается, когда данные в таблицах конфликтуют.
super(message);
}
}

View File

@ -0,0 +1,8 @@
package com.example.nto.domain.exception;
public class ImageSendException extends RuntimeException {
public ImageSendException(String message) {
// Вызывается при ошибке во время отправки изображения.
super(message);
}
}

View File

@ -0,0 +1,7 @@
package com.example.nto.domain.exception;
public class ImageUploadException extends RuntimeException {
public ImageUploadException(String message) {
super(message);
}
}

View File

@ -0,0 +1,8 @@
package com.example.nto.domain.exception;
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
// Вызывается, когда что-то не найдено в бд.
super(message);
}
}

View File

@ -0,0 +1,36 @@
package com.example.nto.domain.exception.advice;
import com.amazonaws.SdkBaseException;
import com.amazonaws.SdkClientException;
import com.amazonaws.services.s3.model.AmazonS3Exception;
import com.example.nto.domain.exception.model.ApiError;
import com.example.nto.utils.Utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.util.concurrent.ExecutionException;
import static com.example.nto.utils.DataFormatType.DATE_TIME;
@Slf4j
@ControllerAdvice
public class ExceptionApiHandler {
@ExceptionHandler({AmazonS3Exception.class, SdkClientException.class})
public ResponseEntity<ApiError> amazonS3Exception(SdkBaseException e) {
return new ResponseEntity<>(
new ApiError("500", "Что-то пошло не так с AWS SDK.", e.getMessage(), Utils.nowTime(DATE_TIME)),
HttpStatus.INTERNAL_SERVER_ERROR
);
}
@ExceptionHandler({InterruptedException.class, ExecutionException.class})
public ResponseEntity<ApiError> runtimeException(RuntimeException e) {
return new ResponseEntity<>(
new ApiError("500", "Один из потоков завершился исключением.", e.getMessage(), Utils.nowTime(DATE_TIME)),
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}

View File

@ -1,8 +1,7 @@
package com.example.nto.domain.exception.advice;
import com.example.nto.domain.exception.CodeNotFoundException;
import com.example.nto.domain.exception.EmployeeNotFoundException;
import com.example.nto.domain.exception.SomethingWentWrongException;
import com.amazonaws.services.organizations.model.ConstraintViolationException;
import com.example.nto.domain.exception.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
@ -27,4 +26,36 @@ public class GlobalExceptionalHandler {
public ResponseEntity<HttpStatus> handlerSomethingWentWrongException(SomethingWentWrongException e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<String> handlerAccessDeniedException(AccessDeniedException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.UNAUTHORIZED);
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<String> handlerAccessDeniedException(ResourceNotFoundException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.NOT_FOUND);
}
@ExceptionHandler(ConflictResourceException.class)
public ResponseEntity<String> handlerConflictResourceException(ConflictResourceException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.CONFLICT);
}
@ExceptionHandler(ImageUploadException.class)
public ResponseEntity<String> handlerImageUploadException(ImageUploadException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(ImageSendException.class)
public ResponseEntity<String> handlerImageSendException(ImageSendException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
@ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<String> handlerConstraintViolationException(ConstraintViolationException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}

View File

@ -0,0 +1,14 @@
package com.example.nto.domain.exception.model;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ApiError {
private String status; // Код статуса HTTP-ответа
private String reason; // Общее описание причины ошибки
private String message; // Сообщение об ошибке
private String timestamp; // Дата и время когда произошла ошибка (в формате "yyyy-MM-dd HH:mm:ss")
}

View File

@ -24,4 +24,6 @@ public class VisitDTO {
// превратить обратно можно с помощью LocalDateTime.parse().
private String durationVisit;
private String officeName;
private String typePassage; // Строка либо PASSAGE_CARD, либо PASSAGE_TELEPHONE
}

View File

@ -16,6 +16,7 @@ public class EmployeeDTO {
private String name;
private String surname;
private String patronymic;
private String aboutMe;
private String telephone;
private String email;
@ -23,7 +24,7 @@ public class EmployeeDTO {
private String officeName;
private String officeImageUrl;
private String position; // Название должности
private String positionName; // Название должности
private String role; // строка либо ROLE_USER, либо ROLE_ADMIN
private String profileImageUrl;

View File

@ -1,4 +1,48 @@
package com.example.nto.dto.mappers;
import com.example.nto.domain.entity.Office;
import com.example.nto.dto.entity.OfficeDTO;
import com.example.nto.dto.mappers.employee.EmployeeItemMapper;
import lombok.experimental.UtilityClass;
import java.util.stream.Collectors;
@UtilityClass
public class OfficeMapper {
public static OfficeDTO convertToDTO(Office office) {
OfficeDTO officeDTO = new OfficeDTO();
officeDTO.setId(office.getId());
officeDTO.setName(office.getName());
officeDTO.setDescription(office.getDescription());
officeDTO.setAddress(office.getAddress());
officeDTO.setLatitude(office.getLatitude());
officeDTO.setLongitude(office.getLongitude());
officeDTO.setLinkLogo(office.getLinkLogo());
officeDTO.setTelephone(office.getTelephone());
officeDTO.setEmail(office.getEmail());
officeDTO.setEmployees(office.getEmployeeList().stream().map(EmployeeItemMapper::convertToDTO).collect(Collectors.toList()));
officeDTO.setTerminals(office.getTerminals().stream().map(TerminalMapper::convertToDTO).collect(Collectors.toList()));
return officeDTO;
}
public static Office convertFromDTO(OfficeDTO officeDTO) {
Office office = new Office();
office.setId(officeDTO.getId());
office.setName(officeDTO.getName());
office.setDescription(officeDTO.getDescription());
office.setAddress(officeDTO.getAddress());
office.setLatitude(officeDTO.getLatitude());
office.setLongitude(officeDTO.getLongitude());
office.setLinkLogo(officeDTO.getLinkLogo());
office.setTelephone(officeDTO.getTelephone());
office.setEmail(officeDTO.getEmail());
// todo: Проверить, возможно, здесь нужно office.setEmployeeList() и office.setTerminals()
return office;
}
}

View File

@ -1,4 +1,21 @@
package com.example.nto.dto.mappers;
import com.example.nto.domain.entity.Position;
import com.example.nto.dto.entity.PositionDTO;
import com.example.nto.dto.mappers.employee.EmployeeItemMapper;
import lombok.experimental.UtilityClass;
import java.util.stream.Collectors;
@UtilityClass
public class PositionMapper {
}
public static PositionDTO convertToDTO(Position position) {
PositionDTO positionDTO = new PositionDTO();
positionDTO.setId(position.getId());
positionDTO.setName(position.getName());
positionDTO.setEmployeeItemDTOList(position.getEmployees().stream().map(EmployeeItemMapper::convertToDTO).collect(Collectors.toList()));
return positionDTO;
}
}

View File

@ -1,4 +1,29 @@
package com.example.nto.dto.mappers;
import com.example.nto.domain.entity.Office;
import com.example.nto.domain.entity.Terminal;
import com.example.nto.dto.entity.TerminalDTO;
import lombok.experimental.UtilityClass;
@UtilityClass
public class TerminalMapper {
public static TerminalDTO convertToDTO(Terminal terminal) {
TerminalDTO terminalDTO = new TerminalDTO();
terminalDTO.setId(terminal.getId());
terminalDTO.setName(terminal.getName());
terminalDTO.setCode(terminal.getCode());
terminalDTO.setOfficeName(terminal.getOffice().getName());
return terminalDTO;
}
public static Terminal convertFromDTO(TerminalDTO terminalDTO, Office office) {
Terminal terminal = new Terminal();
terminal.setName(terminalDTO.getName());
terminal.setOffice(office);
return terminal;
}
}

View File

@ -1,4 +1,42 @@
package com.example.nto.dto.mappers;
import com.example.nto.domain.entity.Employee;
import com.example.nto.domain.entity.Passage;
import com.example.nto.domain.entity.Terminal;
import com.example.nto.domain.entity.Visit;
import com.example.nto.dto.entity.VisitDTO;
import com.example.nto.utils.Utils;
import lombok.experimental.UtilityClass;
import java.time.LocalDateTime;
@UtilityClass
public class VisitMapper {
public static VisitDTO convertToDTO(Visit visit) {
VisitDTO visitDTO = new VisitDTO();
visitDTO.setId(visit.getId());
visitDTO.setStartVisit(visit.getStartVisit().toString());
visitDTO.setEndVisit(visit.getEndVisit().toString());
visitDTO.setFinished(visit.isFinished());
visitDTO.setDurationVisit(Utils.period(visit.getStartVisit(), visit.getEndVisit()).toString());
visitDTO.setOfficeName(visit.getStartTerminal().getOffice().getName());
visitDTO.setTypePassage(visit.getPassage().getPassageName());
return visitDTO;
}
public static Visit convertFromDTO(Terminal terminal, Employee employee, Passage passage) {
Visit visit = new Visit();
visit.setEmployee(employee);
visit.setStartVisit(LocalDateTime.now());
visit.setFinished(false);
visit.setStartTerminal(terminal);
visit.setPassage(passage);
return visit;
}
}

View File

@ -26,6 +26,7 @@ public class EmployeeCreateMapper {
employee.setPosition(position);
employee.setRole(role);
employee.setProfileImageUrl(Utils.getRandomUrlProfileImage());
employee.setBlocked(false);
return employee;
}

View File

@ -1,4 +1,31 @@
package com.example.nto.dto.mappers.employee;
import com.example.nto.domain.entity.Employee;
import com.example.nto.domain.entity.Visit;
import com.example.nto.dto.entity.employee.EmployeeItemDTO;
import com.example.nto.utils.Utils;
import lombok.experimental.UtilityClass;
import java.util.List;
@UtilityClass
public class EmployeeItemMapper {
public static EmployeeItemDTO convertToDTO(Employee employee) {
EmployeeItemDTO employeeItemDTO = new EmployeeItemDTO();
employeeItemDTO.setId(employee.getId());
employeeItemDTO.setName(employee.getName());
employeeItemDTO.setSurname(employee.getSurname());
employeeItemDTO.setPatronymic(employee.getPatronymic());
employeeItemDTO.setProfileImageUrl(employee.getProfileImageUrl());
employeeItemDTO.setOfficeName(employee.getOffice().getName());
employeeItemDTO.setPosition(employee.getPosition().getName());
employeeItemDTO.setBlocked(employee.isBlocked());
// todo: Протестировать работу!
List<Visit> visitsLast30Days = Utils.filterDateLast30Days(employee.getVisits());
employeeItemDTO.setVisitStatus(visitsLast30Days.stream().anyMatch(visit -> !visit.isFinished()));
return employeeItemDTO;
}
}

View File

@ -1,4 +1,81 @@
package com.example.nto.dto.mappers.employee;
import com.example.nto.domain.entity.*;
import com.example.nto.dto.entity.employee.EmployeeDTO;
import com.example.nto.utils.Utils;
import lombok.experimental.UtilityClass;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@UtilityClass
public class EmployeeMapper {
public static EmployeeDTO convertToDTO(Employee employee) {
EmployeeDTO employeeDTO = new EmployeeDTO();
employeeDTO.setId(employee.getId());
employeeDTO.setName(employee.getName());
employeeDTO.setSurname(employee.getSurname());
employeeDTO.setPatronymic(employee.getPatronymic());
employeeDTO.setAboutMe(employeeDTO.getAboutMe());
employeeDTO.setTelephone(employee.getTelephone());
employeeDTO.setEmail(employee.getEmail());
employeeDTO.setOfficeId(employee.getOffice().getId());
employeeDTO.setOfficeName(employee.getOffice().getName());
employeeDTO.setOfficeImageUrl(employee.getOffice().getLinkLogo());
employeeDTO.setPositionName(employee.getPosition().getName());
employeeDTO.setRole(employee.getRole().getRoleName());
employeeDTO.setRole(employee.getProfileImageUrl());
employeeDTO.setBlocked(employee.isBlocked());
List<Visit> visitsLast30Days = Utils.filterDateLast30Days(employee.getVisits());
boolean visitStatus = false;
String startVisitDateTime = null;
String currentOfficeName = null;
List<Long> visitsIdLast30Days = new ArrayList<>(visitsLast30Days.size());
List<List<LocalDateTime>> periods = new ArrayList<>(visitsLast30Days.size());
for (Visit visit : visitsLast30Days) {
if (!visit.isFinished()) {
if (visitStatus) throw new IllegalStateException("У работника два посещения одновременно!");
visitStatus = true;
startVisitDateTime = visit.getStartVisit().toString();
currentOfficeName = visit.getStartTerminal().getOffice().getName();
}
visitsIdLast30Days.add(visit.getId());
periods.add(List.of(visit.getStartVisit(), visit.getEndVisit()));
}
employeeDTO.setVisitStatus(visitStatus);
employeeDTO.setStartVisitDateTime(startVisitDateTime);
employeeDTO.setCurrentOfficeName(currentOfficeName);
employeeDTO.setVisitsIdLast30Days(visitsIdLast30Days);
employeeDTO.setTotalTimeVisitsLast30Days(Utils.periods(periods));
employeeDTO.setCreateAt(employee.getCreatedAt().toString());
return employeeDTO;
}
public static Employee convertFromDTO(EmployeeDTO employeeDTO, Office office, Position position, Role role) {
Employee employee = new Employee();
employee.setName(employeeDTO.getName());
employee.setSurname(employeeDTO.getSurname());
employee.setPatronymic(employeeDTO.getPatronymic());
employeeDTO.setAboutMe(employee.getAboutMe());
employee.setTelephone(employeeDTO.getTelephone());
employee.setEmail(employeeDTO.getEmail());
employee.setOffice(office);
employee.setPosition(position);
employee.setRole(role);
employee.setProfileImageUrl(employeeDTO.getProfileImageUrl());
employee.setBlocked(employeeDTO.isBlocked());
return employee;
}
}

View File

@ -0,0 +1,11 @@
package com.example.nto.repository;
import com.example.nto.domain.entity.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
Optional<Employee> findByEmail(String email);
Optional<Employee> findByTelephone(String telephone);
}

View File

@ -0,0 +1,10 @@
package com.example.nto.repository;
import com.example.nto.domain.entity.Office;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface OfficeRepository extends JpaRepository<Office, Long> {
Optional<Office> findByName(String name);
}

View File

@ -0,0 +1,10 @@
package com.example.nto.repository;
import com.example.nto.domain.entity.Passage;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface PassageRepository extends JpaRepository<Passage, Long> {
Optional<Passage> findByPassageName(String passageName);
}

View File

@ -0,0 +1,10 @@
package com.example.nto.repository;
import com.example.nto.domain.entity.Position;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface PositionRepository extends JpaRepository<Position, Long> {
Optional<Position> findByName(String name);
}

View File

@ -0,0 +1,10 @@
package com.example.nto.repository;
import com.example.nto.domain.entity.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface RoleRepository extends JpaRepository<Role, Long> {
Optional<Role> findByRoleName(String roleName);
}

View File

@ -0,0 +1,10 @@
package com.example.nto.repository;
import com.example.nto.domain.entity.Terminal;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface TerminalRepository extends JpaRepository<Terminal, Long> {
Optional<Terminal> findByCode(String code);
}

View File

@ -0,0 +1,7 @@
package com.example.nto.repository;
import com.example.nto.domain.entity.Visit;
import org.springframework.data.jpa.repository.JpaRepository;
public interface VisitRepository extends JpaRepository<Visit, Long> {
}

View File

@ -0,0 +1,23 @@
package com.example.nto.service;
import com.example.nto.dto.entity.employee.EmployeeCreateDTO;
import com.example.nto.dto.entity.employee.EmployeeDTO;
import com.example.nto.dto.entity.employee.EmployeeItemDTO;
import java.util.List;
public interface EmployeeService {
List<EmployeeItemDTO> getAll();
List<EmployeeItemDTO> getWorkingEmployee(boolean isWorking);
EmployeeDTO getById(long employeeId);
EmployeeDTO getByEmail(String email);
EmployeeDTO getByTelephone(String telephone);
EmployeeDTO create(EmployeeCreateDTO employeeCreateDTO);
EmployeeDTO update(long employeeId, EmployeeDTO employeeDTO);
void delete(long employeeId);
void patchProfileImage(long employeeId, String imageUrl);
void patchBlockEmployee(long employeeId, boolean blockStatus);
}

View File

@ -0,0 +1,15 @@
package com.example.nto.service;
import com.example.nto.dto.entity.OfficeDTO;
import java.util.List;
public interface OfficeService {
List<OfficeDTO> getAllOffice();
List<OfficeDTO> getAllSortedDistance(double userLatitude, double userLongitude);
OfficeDTO getById(long officeId);
OfficeDTO create(OfficeDTO officeDTO);
OfficeDTO update(long officeId, OfficeDTO officeDTO);
void delete(long officeId);
}

View File

@ -0,0 +1,16 @@
package com.example.nto.service;
import com.example.nto.dto.entity.TerminalDTO;
import java.util.List;
public interface TerminalService {
List<TerminalDTO> getAllTerminal();
TerminalDTO getById(long terminalId);
TerminalDTO create(TerminalDTO terminalDTO);
TerminalDTO update(long terminalId, TerminalDTO terminalDTO);
void delete(long terminalId);
TerminalDTO checkCode(String code);
}

View File

@ -0,0 +1,21 @@
package com.example.nto.service;
import com.example.nto.dto.entity.VisitDTO;
import java.time.LocalDateTime;
import java.util.List;
public interface VisitService {
List<VisitDTO> getAll();
List<VisitDTO> getAllVisitByEmployee(long employeeId);
List<VisitDTO> getAllVisitByPeriod(LocalDateTime start, LocalDateTime stop);
List<VisitDTO> getAllFinished(boolean isFinished);
VisitDTO getById(long visitId);
VisitDTO open(long employeeId, long startTerminalId, String passageName);
void exit(long employeeId, long endTerminalId);
// update не нужен он не будет использоваться :)
void delete(long visitId);
}

View File

@ -0,0 +1,156 @@
package com.example.nto.service.impl;
import com.example.nto.aspect.annotation.LogExample;
import com.example.nto.domain.entity.*;
import com.example.nto.domain.exception.ConflictResourceException;
import com.example.nto.domain.exception.ResourceNotFoundException;
import com.example.nto.dto.entity.employee.EmployeeCreateDTO;
import com.example.nto.dto.entity.employee.EmployeeDTO;
import com.example.nto.dto.entity.employee.EmployeeItemDTO;
import com.example.nto.dto.mappers.employee.EmployeeCreateMapper;
import com.example.nto.dto.mappers.employee.EmployeeItemMapper;
import com.example.nto.dto.mappers.employee.EmployeeMapper;
import com.example.nto.repository.EmployeeRepository;
import com.example.nto.repository.OfficeRepository;
import com.example.nto.repository.PositionRepository;
import com.example.nto.repository.RoleRepository;
import com.example.nto.service.EmployeeService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class EmployeeServiceImpl implements EmployeeService {
private final EmployeeRepository employeeRepository;
private final OfficeRepository officeRepository;
private final RoleRepository roleRepository;
private final PositionRepository positionRepository;
private final PasswordEncoder passwordEncoder;
@Override
@LogExample
public List<EmployeeItemDTO> getAll() {
return employeeRepository.findAll().stream().map(EmployeeItemMapper::convertToDTO).collect(Collectors.toList());
}
@Override
@LogExample
public List<EmployeeItemDTO> getWorkingEmployee(boolean isWorking) {
return employeeRepository.findAll().stream().filter(employee -> checkWorking(employee, isWorking))
.map(EmployeeItemMapper::convertToDTO).collect(Collectors.toList());
}
@Override
@LogExample
public EmployeeDTO getById(long employeeId) {
return employeeRepository.findById(employeeId).map(EmployeeMapper::convertToDTO)
.orElseThrow(() -> new ResourceNotFoundException("Работник с id (" + employeeId + ") не найден!"));
}
@Override
@LogExample
public EmployeeDTO getByEmail(String email) {
return employeeRepository.findByEmail(email).map(EmployeeMapper::convertToDTO)
.orElseThrow(() -> new ResourceNotFoundException("Работник с email (" + email + ") не найден!"));
}
@Override
@LogExample
public EmployeeDTO getByTelephone(String telephone) {
return employeeRepository.findByTelephone(telephone).map(EmployeeMapper::convertToDTO)
.orElseThrow(() -> new ResourceNotFoundException("Работник с telephone (" + telephone + ") не найден!"));
}
@Override
@LogExample
public EmployeeDTO create(EmployeeCreateDTO employeeCreateDTO) {
Optional<Employee> employeeEmail = employeeRepository.findByEmail(employeeCreateDTO.getEmail());
Optional<Employee> employeeTelephone = employeeRepository.findByTelephone(employeeCreateDTO.getTelephone());
if (employeeEmail.isPresent() || employeeTelephone.isPresent()) {
throw new ConflictResourceException("Пользователь с таким email или телефоном уже существует!");
}
Office office = officeRepository.findByName(employeeCreateDTO.getOfficeName())
.orElseThrow(() -> new ResourceNotFoundException("Офис с именем (" + employeeCreateDTO.getOfficeName() + ") не найден!"));
Position position = positionRepository.findByName(employeeCreateDTO.getPositionName())
.orElseThrow(() -> new ResourceNotFoundException("Позиция с именем (" + employeeCreateDTO.getPositionName() + ") не найден!"));
Role role = roleRepository.findByRoleName(employeeCreateDTO.getRole())
.orElseThrow(() -> new ResourceNotFoundException("Роль с названием (" + employeeCreateDTO.getRole() + ") не найден!"));
return EmployeeMapper.convertToDTO(employeeRepository.save(
EmployeeCreateMapper.convertFromDTO(employeeCreateDTO, passwordEncoder.encode(employeeCreateDTO.getPassword()), office, position, role)));
}
@Override
@LogExample
public EmployeeDTO update(long employeeId, EmployeeDTO employeeDTO) {
Employee employee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new ResourceNotFoundException("Работник с id (" + employeeId + ") не найден!"));
Optional<Employee> employeeEmail = employeeRepository.findByEmail(employeeDTO.getEmail());
Optional<Employee> employeeTelephone = employeeRepository.findByTelephone(employeeDTO.getTelephone());
if (employeeEmail.isPresent() || employeeTelephone.isPresent()) {
throw new ConflictResourceException("Пользователь с таким email или телефоном уже существует!");
}
Office office = officeRepository.findByName(employeeDTO.getOfficeName())
.orElseThrow(() -> new ResourceNotFoundException("Офис с именем (" + employeeDTO.getOfficeName() + ") не найден!"));
Position position = positionRepository.findByName(employeeDTO.getPositionName())
.orElseThrow(() -> new ResourceNotFoundException("Позиция с именем (" + employeeDTO.getPositionName() + ") не найден!"));
employee.setName(employeeDTO.getName());
employee.setSurname(employeeDTO.getSurname());
employee.setPatronymic(employeeDTO.getPatronymic());
employee.setAboutMe(employeeDTO.getAboutMe());
employee.setTelephone(employee.getTelephone());
employee.setEmail(employee.getEmail());
employee.setOffice(office);
employee.setPosition(position);
// profileImageUrl меняется в отдельном эндпоинте
employee.setBlocked(employeeDTO.isBlocked());
return EmployeeMapper.convertToDTO(employeeRepository.save(employee));
}
@Override
@LogExample
public void delete(long employeeId) {
employeeRepository.findById(employeeId).orElseThrow(() -> new ResourceNotFoundException("Работник с id (" + employeeId + ") не найден!"));
employeeRepository.deleteById(employeeId);
}
@Override
@LogExample
public void patchProfileImage(long employeeId, String imageUrl) {
Employee employee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new ResourceNotFoundException("Работник с id (" + employeeId + ") не найден!"));
employee.setProfileImageUrl(imageUrl);
employeeRepository.save(employee);
}
@Override
@LogExample
public void patchBlockEmployee(long employeeId, boolean blockStatus) {
Employee employee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new ResourceNotFoundException("Работник с id (" + employeeId + ") не найден!"));
employee.setBlocked(blockStatus);
employeeRepository.save(employee);
}
private static boolean checkWorking(Employee employee, boolean isWorking) {
List<Visit> visits = employee.getVisits().stream().filter(visit -> !visit.isFinished()).collect(Collectors.toList());
if (visits.isEmpty()) return !isWorking;
else if (visits.size() == 1) return isWorking;
else throw new IllegalStateException("Пользователь не может находится в суперпозиции");
}
}

View File

@ -0,0 +1,84 @@
package com.example.nto.service.impl;
import com.example.nto.aspect.annotation.LogExample;
import com.example.nto.domain.entity.Office;
import com.example.nto.domain.exception.ResourceNotFoundException;
import com.example.nto.dto.entity.OfficeDTO;
import com.example.nto.dto.mappers.OfficeMapper;
import com.example.nto.repository.OfficeRepository;
import com.example.nto.service.OfficeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.awt.geom.Point2D;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class OfficeServiceImpl implements OfficeService {
private final OfficeRepository officeRepository;
@Override
@LogExample
public List<OfficeDTO> getAllOffice() {
return officeRepository.findAll().stream().map(OfficeMapper::convertToDTO).collect(Collectors.toList());
}
@Override
@LogExample
public List<OfficeDTO> getAllSortedDistance(double userLatitude, double userLongitude) {
return officeRepository.findAll().stream().map(OfficeMapper::convertToDTO)
.sorted(createComparator(new Point2D.Double(userLatitude, userLongitude))).collect(Collectors.toList());
}
@Override
@LogExample
public OfficeDTO getById(long officeId) {
return officeRepository.findById(officeId).map(OfficeMapper::convertToDTO)
.orElseThrow(() -> new ResourceNotFoundException("Офис с id (" + officeId + ") не найден!"));
}
@Override
@LogExample
public OfficeDTO create(OfficeDTO officeDTO) {
return OfficeMapper.convertToDTO(officeRepository.save(OfficeMapper.convertFromDTO(officeDTO)));
}
@Override
@LogExample
public OfficeDTO update(long officeId, OfficeDTO officeDTO) {
Office office = officeRepository.findById(officeId)
.orElseThrow(() -> new ResourceNotFoundException("Офис с id (" + officeId + ") не найден!"));
office.setName(officeDTO.getName());
office.setDescription(officeDTO.getDescription());
office.setAddress(officeDTO.getAddress());
office.setLatitude(officeDTO.getLatitude());
office.setLongitude(officeDTO.getLongitude());
// linkLogo меняется в отдельном запросе
office.setTelephone(officeDTO.getTelephone());
office.setEmail(officeDTO.getEmail());
return OfficeMapper.convertToDTO(officeRepository.save(office));
}
@Override
@LogExample
public void delete(long officeId) {
officeRepository.findById(officeId).orElseThrow(() -> new ResourceNotFoundException("Офис с id (" + officeId + ") не найден!"));
officeRepository.deleteById(officeId);
}
private static Comparator<OfficeDTO> createComparator(Point2D point) {
return (office0, office1) -> {
Point2D point0 = new Point2D.Double(office0.getLatitude(), office0.getLongitude());
Point2D point1 = new Point2D.Double(office1.getLatitude(), office1.getLongitude());
return Double.compare(point0.distanceSq(point), point1.distanceSq(point));
};
}
}

View File

@ -0,0 +1,79 @@
package com.example.nto.service.impl;
import com.example.nto.aspect.annotation.LogExample;
import com.example.nto.domain.entity.Office;
import com.example.nto.domain.entity.Terminal;
import com.example.nto.domain.exception.ResourceNotFoundException;
import com.example.nto.dto.entity.TerminalDTO;
import com.example.nto.dto.mappers.TerminalMapper;
import com.example.nto.repository.OfficeRepository;
import com.example.nto.repository.TerminalRepository;
import com.example.nto.service.TerminalService;
import com.example.nto.utils.Utils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class TerminalServiceImpl implements TerminalService {
private final TerminalRepository terminalRepository;
private final OfficeRepository officeRepository;
@Override
@LogExample
public List<TerminalDTO> getAllTerminal() {
return terminalRepository.findAll().stream().map(TerminalMapper::convertToDTO).collect(Collectors.toList());
}
@Override
@LogExample
public TerminalDTO getById(long terminalId) {
return terminalRepository.findById(terminalId).map(TerminalMapper::convertToDTO)
.orElseThrow(() -> new ResourceNotFoundException("Терминал с id (" + terminalId + ") не найден!"));
}
@Override
@LogExample
public TerminalDTO create(TerminalDTO terminalDTO) {
Office office = officeRepository.findByName(terminalDTO.getOfficeName())
.orElseThrow(() -> new ResourceNotFoundException("Офис с названием (" + terminalDTO.getOfficeName() + ") не найден!"));
Terminal terminal = new Terminal();
terminal.setName(terminalDTO.getName());
terminal.setOffice(office);
return TerminalMapper.convertToDTO(terminalRepository.save(terminal));
}
@Override
@LogExample
public TerminalDTO update(long terminalId, TerminalDTO terminalDTO) {
Terminal terminal = terminalRepository.findById(terminalId)
.orElseThrow(() -> new ResourceNotFoundException("Терминал с id (" + terminalId + ") не найден!"));
Office office = officeRepository.findByName(terminalDTO.getOfficeName())
.orElseThrow(() -> new ResourceNotFoundException("Офис с названием (" + terminalDTO.getOfficeName() + ") не найден!"));
terminal.setName(terminalDTO.getName());
terminal.setCode(Utils.generateUniqueName());
terminal.setOffice(office);
return TerminalMapper.convertToDTO(terminalRepository.save(terminal));
}
@Override
@LogExample
public void delete(long terminalId) {
terminalRepository.findById(terminalId).orElseThrow(() -> new ResourceNotFoundException("Терминал с id (" + terminalId + ") не найден!"));
terminalRepository.deleteById(terminalId);
}
@Override
@LogExample
public TerminalDTO checkCode(String code) {
return terminalRepository.findByCode(code).map(TerminalMapper::convertToDTO)
.orElseThrow(() -> new ResourceNotFoundException("Терминал с кодом (" + code + ") не найден!"));
}
}

View File

@ -1,14 +1,24 @@
package com.example.nto.service.impl;
import com.example.nto.domain.entity.Employee;
import com.example.nto.repository.EmployeeRepository;
import lombok.AllArgsConstructor;
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 java.util.Optional;
@Service
@AllArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {
private final EmployeeRepository employeeRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
Optional<Employee> employee = employeeRepository.findByEmail(email);
if (employee.isEmpty()) throw new UsernameNotFoundException("Employee not found!");
return employee.get();
}
}

View File

@ -0,0 +1,125 @@
package com.example.nto.service.impl;
import com.example.nto.aspect.annotation.LogExample;
import com.example.nto.domain.entity.Employee;
import com.example.nto.domain.entity.Passage;
import com.example.nto.domain.entity.Terminal;
import com.example.nto.domain.entity.Visit;
import com.example.nto.domain.exception.ResourceNotFoundException;
import com.example.nto.dto.entity.VisitDTO;
import com.example.nto.dto.mappers.VisitMapper;
import com.example.nto.repository.EmployeeRepository;
import com.example.nto.repository.PassageRepository;
import com.example.nto.repository.TerminalRepository;
import com.example.nto.repository.VisitRepository;
import com.example.nto.service.VisitService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class VisitServiceImpl implements VisitService {
private final EmployeeRepository employeeRepository;
private final VisitRepository visitRepository;
private final TerminalRepository terminalRepository;
private final PassageRepository passageRepository;
@Override
@LogExample
public List<VisitDTO> getAll() {
return visitRepository.findAll().stream().map(VisitMapper::convertToDTO).collect(Collectors.toList());
}
@Override
@LogExample
public List<VisitDTO> getAllVisitByEmployee(long employeeId) {
return visitRepository.findAll().stream()
.filter(visit -> checkEmployeeVisit(visit, employeeId))
.map(VisitMapper::convertToDTO).collect(Collectors.toList());
}
@Override
@LogExample
public List<VisitDTO> getAllVisitByPeriod(LocalDateTime start, LocalDateTime stop) {
// todo: Доделать если останется время
return List.of();
}
@Override
@LogExample
public List<VisitDTO> getAllFinished(boolean isFinished) {
return visitRepository.findAll().stream()
.filter(visit -> checkFinishVisit(visit, isFinished))
.map(VisitMapper::convertToDTO).collect(Collectors.toList());
}
@Override
@LogExample
public VisitDTO getById(long visitId) {
return visitRepository.findById(visitId).map(VisitMapper::convertToDTO)
.orElseThrow(() -> new ResourceNotFoundException("Посещение с id (" + visitId + ") не найден!"));
}
@Override
@LogExample
public void delete(long visitId) {
visitRepository.findById(visitId).orElseThrow(() -> new ResourceNotFoundException("Посещение с id (" + visitId + ") не найден!"));
visitRepository.deleteById(visitId);
}
@Override
@LogExample
public VisitDTO open(long employeeId, long startTerminalId, String passageName) {
Employee employee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new ResourceNotFoundException("Работник с id (" + employeeId + ") не найден!"));
Terminal terminal = terminalRepository.findById(startTerminalId)
.orElseThrow(() -> new ResourceNotFoundException("Терминал с id (" + startTerminalId + ") не найден!"));
Passage passage = passageRepository.findByPassageName(passageName)
.orElseThrow(() -> new ResourceNotFoundException("Тип входа с названием (" + passageName + ") не найден!"));
Visit visit = new Visit();
visit.setEmployee(employee);
visit.setStartVisit(LocalDateTime.now());
visit.setFinished(false);
visit.setStartTerminal(terminal);
visit.setPassage(passage);
return VisitMapper.convertToDTO(visitRepository.save(visit));
}
@Override
@LogExample
public void exit(long employeeId, long endTerminalId) {
Employee employee = employeeRepository.findById(employeeId)
.orElseThrow(() -> new ResourceNotFoundException("Работник с id (" + employeeId + ") не найден!"));
Terminal terminal = terminalRepository.findById(endTerminalId)
.orElseThrow(() -> new ResourceNotFoundException("Терминал с id (" + endTerminalId + ") не найден!"));
Visit activeVisit = getActiveVisit(employee);
activeVisit.setStartVisit(LocalDateTime.now());
activeVisit.setFinished(true);
activeVisit.setEndTerminal(terminal);
visitRepository.save(activeVisit);
}
private static boolean checkEmployeeVisit(Visit visit, long employeeId) {
return visit.getEmployee().getId() == employeeId;
}
private static boolean checkFinishVisit(Visit visit, boolean isFinished) {
return visit.isFinished() == isFinished;
}
private static Visit getActiveVisit(Employee employee) {
List<Visit> visits = employee.getVisits().stream().filter(Visit::isFinished).collect(Collectors.toList());
if (visits.size() == 1) return visits.get(0);
else throw new IllegalStateException("Пользователь не может находится в суперпозиции");
}
}