Improvements

This commit is contained in:
Maksim Skobaro 2025-06-05 06:51:14 +03:00
parent d298a5f1f4
commit 3a25996280
178 changed files with 5277 additions and 2759 deletions

View File

@ -10,15 +10,14 @@
<version>0.0.1</version>
</parent>
<groupId>ru.mskobaro</groupId>
<artifactId>server</artifactId>
<version>0.0.1</version>
<name>TDMS::SERVER</name>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>23</maven.compiler.source>
<maven.compiler.target>23</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<lombok.version>1.18.34</lombok.version>
</properties>
@ -127,6 +126,8 @@
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
<source>23</source>
<target>23</target>
</configuration>
</plugin>
<plugin>

View File

@ -15,7 +15,7 @@ public class TdmsApplication {
Thread.currentThread().setName("spring-bootstrapper");
ConfigurableApplicationContext ctx = SpringApplication.run(TdmsApplication.class, args);
Environment environment = ctx.getEnvironment();
log.info("Static files location: {}", environment.getProperty("spring.web.resources.static-locations"));
log.info("Server listening on port: {}", environment.getProperty("server.port"));
ctx.start();
}
}

View File

@ -0,0 +1,21 @@
package ru.mskobaro.tdms.business.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import lombok.Getter;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
@Embeddable
@Getter
public class AuditInfo {
@CreationTimestamp
@Column(name = "created_at")
private LocalDateTime createdAt;
@UpdateTimestamp
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}

View File

@ -0,0 +1,25 @@
package ru.mskobaro.tdms.business.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "commission_member_data")
@Getter
@Setter
public class CommissionMemberData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne
@JoinColumn(name = "partic_id", referencedColumnName = "id")
private Participant participant;
private String workPlace;
private String workPosition;
@Embedded
private AuditInfo auditInfo;
}

View File

@ -0,0 +1,40 @@
package ru.mskobaro.tdms.business.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDate;
import java.util.List;
@Entity
@Table(name = "defense")
@Getter
@Setter
public class Defense {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany
@JoinTable(
name = "defense_commission",
joinColumns = @JoinColumn(name = "defense_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "commission_member_data_id", referencedColumnName = "id"))
private List<CommissionMemberData> commissionMembers;
private LocalDate defenseDate;
@OneToMany(mappedBy = "defense")
private List<Group> groups;
@ManyToMany
@JoinTable(
name = "defense_best_student_works",
joinColumns = @JoinColumn(name = "defense_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "student_data_id", referencedColumnName = "id"))
private List<StudentData> bestWorks;
@Embedded
private AuditInfo auditInfo;
}

View File

@ -0,0 +1,35 @@
package ru.mskobaro.tdms.business.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@Entity
@Table(name = "diploma_topic")
public class DiplomaTopic {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@ManyToOne
@JoinColumn(name = "teacher_id")
private TeacherData teacher;
@ManyToOne
@JoinColumn(name = "direction_of_preparation_id")
private DirectionOfPreparation directionOfPreparation;
@Embedded
private AuditInfo auditInfo;
}

View File

@ -0,0 +1,27 @@
package ru.mskobaro.tdms.business.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Entity
@Table(name = "direction_of_preparation")
@Getter
@Setter
public class DirectionOfPreparation {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String code;
@OneToMany(mappedBy = "directionOfPreparation")
private List<DiplomaTopic> diplomaTopic;
@Embedded
private AuditInfo auditInfo;
}

View File

@ -0,0 +1,40 @@
package ru.mskobaro.tdms.business.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@ToString
@Entity
@Table(name = "`group`")
public class Group {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@OneToMany(mappedBy = "group")
private List<StudentData> students = new ArrayList<>();
@ManyToOne
@JoinColumn(name = "direction_of_preparation_id")
private DirectionOfPreparation directionOfPreparation;
@ManyToOne
@JoinColumn(name = "defense_id")
private Defense defense;
@Embedded
private AuditInfo auditInfo;
}

View File

@ -0,0 +1,67 @@
package ru.mskobaro.tdms.business.entity;
import io.micrometer.common.util.StringUtils;
import jakarta.persistence.*;
import lombok.*;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Entity
@Table(name = "participant")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class Participant {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstName;
private String lastName;
private String middleName;
private String email;
private String numberPhone;
@OneToOne(mappedBy = "participant")
private User user;
private boolean deleted;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "participant_role",
joinColumns = @JoinColumn(name = "partic_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private List<Role> roles;
@Embedded
private AuditInfo auditInfo;
@Transient
public String getFullName() {
return Stream.of(lastName, firstName, middleName)
.filter(StringUtils::isNotBlank)
.collect(Collectors.joining(" "));
}
@Transient
public String getShortName() {
StringBuilder builder = new StringBuilder();
if (StringUtils.isNotBlank(lastName)) {
builder.append(lastName);
}
if (StringUtils.isNotBlank(firstName)) {
builder.append(" ").append(firstName.charAt(0)).append(".");
}
if (StringUtils.isNotBlank(middleName)) {
builder.append(" ").append(middleName.charAt(0)).append(".");
}
return builder.toString().trim();
}
}

View File

@ -1,14 +1,11 @@
package ru.mskobaro.tdms.domain.entity;
package ru.mskobaro.tdms.business.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.*;
import org.springframework.security.core.GrantedAuthority;
@ -18,6 +15,7 @@ import org.springframework.security.core.GrantedAuthority;
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "`role`")
@EqualsAndHashCode(of = "id")
public class Role implements GrantedAuthority {
@Id
@Column(name = "id")

View File

@ -0,0 +1,50 @@
package ru.mskobaro.tdms.business.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import ru.mskobaro.tdms.integration.database.TeacherDataRepository;
@Getter
@Setter
@ToString(exclude = "group")
@Entity
@Table(name = "student_data")
public class StudentData {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne
@JoinColumn(name = "partic_id")
private Participant participant;
@ManyToOne
@JoinColumn(name = "group_id")
private Group group;
@ManyToOne
@JoinColumn(name = "study_form_id")
private StudyForm form;
private Integer protectionOrder;
private Integer protectionDay;
private Integer markComment;
private Integer markPractice;
@ManyToOne
@JoinColumn(name = "curator_id")
private TeacherData curator;
@ManyToOne
@JoinColumn(name = "diploma_topic_id")
private DiplomaTopic diplomaTopic;
@Embedded
private AuditInfo auditInfo;
}

View File

@ -1,23 +1,21 @@
package ru.mskobaro.tdms.domain.entity;
package ru.mskobaro.tdms.business.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Entity
@Table(name = "study_form")
@Getter
@Setter
@ToString
@Entity
@Table(name = "diploma_topic")
public class DiplomaTopic {
public class StudyForm {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
}
@Embedded
private AuditInfo auditInfo;
}

View File

@ -0,0 +1,41 @@
package ru.mskobaro.tdms.business.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
import ru.mskobaro.tdms.business.taskfields.TaskFields;
@Entity
@Table(name = "task")
@NoArgsConstructor
@Getter
@Setter
public class Task {
public enum Type {
DIPLOMA_TOPIC_AGREEMENT,
}
public enum Status {
WAIT_FOR_TOPIC_AGREEMENT,
DONE,
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Enumerated(EnumType.STRING)
private Type type;
@Enumerated(EnumType.STRING)
private Status status;
@JdbcTypeCode(SqlTypes.JSON)
private TaskFields fields;
@Embedded
private AuditInfo auditInfo;
}

View File

@ -0,0 +1,24 @@
package ru.mskobaro.tdms.business.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "teacher_data")
@Getter
@Setter
public class TeacherData {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne
@JoinColumn(name = "participant_id", referencedColumnName = "id")
private Participant participant;
private String degree;
@Embedded
private AuditInfo auditInfo;
}

View File

@ -1,4 +1,4 @@
package ru.mskobaro.tdms.domain.entity;
package ru.mskobaro.tdms.business.entity;
import jakarta.persistence.*;
@ -6,13 +6,10 @@ import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.List;
@ -32,33 +29,26 @@ public class User implements UserDetails {
private String login;
@Column(name = "password")
private String password;
@Column(name = "full_name")
private String fullName;
@Column(name = "email")
private String email;
@Column(name = "number_phone")
private String numberPhone;
@Column(name = "created_at")
@CreationTimestamp
private ZonedDateTime createdAt;
@Column(name = "updated_at")
@UpdateTimestamp
private ZonedDateTime updatedAt;
@ManyToMany
@JoinTable(
name = "user_role",
joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "role_id", referencedColumnName = "id"))
private List<Role> roles;
@OneToOne
@JoinColumn(name = "partic_id")
private Participant participant;
@Embedded
private AuditInfo auditInfo;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return roles.stream()
return participant.getRoles().stream()
.map(Role::getAuthority)
.map(SimpleGrantedAuthority::new)
.toList();
}
@Override
public boolean isEnabled() {
return !participant.isDeleted();
}
@Override
public String getUsername() {
return login;

View File

@ -1,12 +1,16 @@
package ru.mskobaro.tdms.domain.exception;
package ru.mskobaro.tdms.business.exception;
import ru.mskobaro.tdms.presentation.payload.ErrorDTO;
import ru.mskobaro.tdms.presentation.controller.payload.ErrorDTO;
public class AccessDeniedException extends BusinessException {
public AccessDeniedException() {
super("Access denied");
}
public AccessDeniedException(String message) {
super(message);
}
@Override
public ErrorDTO.ErrorCode getErrorCode() {
return ErrorDTO.ErrorCode.ACCESS_DENIED;

View File

@ -1,6 +1,6 @@
package ru.mskobaro.tdms.domain.exception;
package ru.mskobaro.tdms.business.exception;
import ru.mskobaro.tdms.presentation.payload.ErrorDTO;
import ru.mskobaro.tdms.presentation.controller.payload.ErrorDTO;
public class BusinessException extends RuntimeException {
public BusinessException(String message) {

View File

@ -1,18 +1,18 @@
package ru.mskobaro.tdms.domain.exception;
package ru.mskobaro.tdms.business.exception;
import ru.mskobaro.tdms.presentation.payload.ErrorDTO;
import ru.mskobaro.tdms.presentation.controller.payload.ErrorDTO;
public class NotFoundException extends BusinessException {
public NotFoundException(Class<?> entityClass, Object id) {
super(entityClass.getSimpleName() + " с идентификатором " + id + " не наеден");
super(entityClass.getSimpleName() + " с идентификатором " + id + " не существует");
}
public NotFoundException(Class<?> entityClass) {
super(entityClass.getSimpleName() + " не найден");
super(entityClass.getSimpleName() + " не существует");
}
public NotFoundException() {
super("Не найдено");
super("Не существует");
}
public static void throwIfNull(Object object) {

View File

@ -1,4 +1,4 @@
package ru.mskobaro.tdms.domain.service;
package ru.mskobaro.tdms.business.service;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
@ -6,10 +6,14 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
@Service
@Slf4j
public class AuthenticationService {
@ -17,25 +21,41 @@ public class AuthenticationService {
private HttpServletRequest request;
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private SessionRegistry sessionRegistry;
public void logout() {
log.info("Logging out user: {}", SecurityContextHolder.getContext().getAuthentication().getName());
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return;
}
String username = authentication.getName();
HttpSession session = request.getSession(false);
if(session != null) {
if (session != null) {
session.invalidate();
}
SecurityContextHolder.clearContext();
log.info("User logged out");
log.info("User {} logged out", username);
}
public void logout(String username) {
sessionRegistry.getAllSessions(username, false).forEach(session -> {
log.info("Invalidating session for user {}: {}", username, session.getSessionId());
session.expireNow();
sessionRegistry.removeSessionInformation(session.getSessionId());
});
}
@Transactional
public void login(String username, String password) {
log.info("Logging in user: {}, ip: {}", username, request.getRemoteAddr());
var token = new UsernamePasswordAuthenticationToken(username, password);
var authenticated = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authenticated);
log.info("User {} logged in", username);
request.getSession().setAttribute(SPRING_SECURITY_CONTEXT_KEY, SecurityContextHolder.getContext());
log.info("User {} logged in, ip: {}", username, request.getRemoteAddr());
}
}

View File

@ -0,0 +1,20 @@
package ru.mskobaro.tdms.business.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.mskobaro.tdms.integration.database.DefenceRepository;
import ru.mskobaro.tdms.presentation.controller.payload.DefenceDTO;
import java.util.List;
@Service
@Transactional
public class DefenceService {
@Autowired
private DefenceRepository defenceRepository;
public List<DefenceDTO> getAllDefences() {
return defenceRepository.findAll().stream().map(DefenceDTO::from).toList();
}
}

View File

@ -0,0 +1,55 @@
package ru.mskobaro.tdms.business.service;
import jakarta.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.mskobaro.tdms.business.entity.DiplomaTopic;
import ru.mskobaro.tdms.business.entity.DirectionOfPreparation;
import ru.mskobaro.tdms.business.entity.TeacherData;
import ru.mskobaro.tdms.integration.database.DiplomaTopicRepository;
import ru.mskobaro.tdms.integration.database.PreparationDirectionRepository;
import ru.mskobaro.tdms.integration.database.TeacherDataRepository;
import ru.mskobaro.tdms.presentation.controller.payload.DiplomaTopicDTO;
import java.util.List;
@Service
@Transactional
public class DiplomaTopicService {
@Autowired
private DiplomaTopicRepository diplomaTopicRepository;
@Autowired
private TeacherDataRepository teacherDataRepository;
@Autowired
private PreparationDirectionRepository preparationDirectionRepository;
public List<DiplomaTopic> findAll() {
return diplomaTopicRepository.findAll();
}
public void save(DiplomaTopicDTO diplomaTopicDTO) {
boolean isEdit = diplomaTopicDTO.getId() != null;
DiplomaTopic diplomaTopic;
if (isEdit) {
diplomaTopic = diplomaTopicRepository.findByIdThrow(diplomaTopicDTO.getId());
} else {
diplomaTopic = new DiplomaTopic();
}
diplomaTopic.setName(diplomaTopicDTO.getName());
if (diplomaTopicDTO.getTeacher() != null) {
TeacherData teacherData = teacherDataRepository.findByIdThrow(diplomaTopicDTO.getTeacher().getId());
diplomaTopic.setTeacher(teacherData);
}
if (diplomaTopicDTO.getPreparationDirection() != null) {
DirectionOfPreparation directionOfPreparation = preparationDirectionRepository.findByIdThrow(diplomaTopicDTO.getPreparationDirection().getId());
diplomaTopic.setDirectionOfPreparation(directionOfPreparation);
}
diplomaTopicRepository.save(diplomaTopic);
}
public List<DiplomaTopic> findAllForStudent(Long studentId) {
return diplomaTopicRepository.findAllForStudentId(studentId);
}
}

View File

@ -0,0 +1,78 @@
package ru.mskobaro.tdms.business.service;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import ru.mskobaro.tdms.business.entity.DirectionOfPreparation;
import ru.mskobaro.tdms.business.entity.Group;
import ru.mskobaro.tdms.business.entity.StudentData;
import ru.mskobaro.tdms.business.exception.BusinessException;
import ru.mskobaro.tdms.integration.database.GroupRepository;
import ru.mskobaro.tdms.integration.database.PreparationDirectionRepository;
import ru.mskobaro.tdms.integration.database.StudentDataRepository;
import ru.mskobaro.tdms.presentation.controller.payload.GroupDTO;
import ru.mskobaro.tdms.presentation.controller.payload.StudentDataDTO;
import java.util.Collection;
import java.util.List;
@Service
@Transactional
@Slf4j
public class GroupService {
@Autowired
private GroupRepository groupRepository;
@Autowired
private RoleService roleService;
@Autowired
private StudentDataRepository studentDataRepository;
@Autowired
private PreparationDirectionRepository preparationDirectionRepository;
public Collection<GroupDTO> getAllGroups() {
List<Group> groups = groupRepository.findAll();
return groups.stream().map(g -> GroupDTO.from(g, true)).toList();
}
public void save(@Valid GroupDTO groupDTO) {
boolean editMode = groupDTO.getId() != null;
log.info("Saving group: {}. Edit?: {}", groupDTO, editMode);
if (!editMode && groupRepository.existsByName(groupDTO.getName())) {
throw new BusinessException("Группа с именем " + groupDTO.getName() + " уже существует");
}
Group group = editMode ? groupRepository.findByIdThrow(groupDTO.getId()) : new Group();
group.setName(groupDTO.getName());
studentDataRepository.findAllByGroup_Id(group.getId()).forEach(s -> {
s.setGroup(null);
studentDataRepository.save(s);
});
group.getStudents().clear();
List<Long> studentIds = groupDTO.getStudents().stream().map(StudentDataDTO::getId).toList();
if (!CollectionUtils.isEmpty(studentIds)) {
List<StudentData> students = studentDataRepository.findAllById(studentIds);
students.stream()
.filter(s -> roleService.isParticInAuthority(s.getParticipant(), RoleService.Authority.STUDENT))
.forEach(s -> {
group.getStudents().add(s);
s.setGroup(group);
});
}
if (groupDTO.getPreparationDirection() != null) {
DirectionOfPreparation directionOfPreparation = preparationDirectionRepository.findByIdThrow(groupDTO.getPreparationDirection().getId());
group.setDirectionOfPreparation(directionOfPreparation);
}
groupRepository.save(group);
}
public void deleteGroup(Long groupId) {
groupRepository.deleteById(groupId);
}
}

View File

@ -0,0 +1,220 @@
package ru.mskobaro.tdms.business.service;
import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
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;
import ru.mskobaro.tdms.business.entity.*;
import ru.mskobaro.tdms.business.exception.AccessDeniedException;
import ru.mskobaro.tdms.business.exception.BusinessException;
import ru.mskobaro.tdms.integration.database.*;
import ru.mskobaro.tdms.presentation.controller.payload.ParticipantSaveDTO;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Transactional
@Slf4j
public class ParticipantService {
@Autowired
private ParticipantRepository participantRepository;
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private UserRepository userRepository;
@Autowired
private GroupRepository groupRepository;
@Autowired
private StudentDataRepository studentDataRepository;
@Autowired
private AuthenticationService authenticationService;
@Autowired
private TeacherDataRepository teacherDataRepository;
public ParticipantService(ParticipantRepository participantRepository) {
this.participantRepository = participantRepository;
}
public Collection<Participant> getAllParticipants() {
return participantRepository.findAll();
}
public void saveParticipant(ParticipantSaveDTO participantSaveDTO) {
boolean editMode = participantSaveDTO.getId() != null;
log.info("Saving participant: {}. Edit: {}", participantSaveDTO, editMode);
Participant existingParticipant = null;
if (editMode)
existingParticipant = participantRepository.findByIdThrow(participantSaveDTO.getId());
persistParticipant(participantSaveDTO, existingParticipant, editMode);
}
private void persistParticipant(ParticipantSaveDTO participantSaveDTO, Participant existingParticipant, boolean editMode) {
Participant participant = !editMode ? new Participant() : existingParticipant;
User callerUser = userService.getCallerUser();
if (callerUser == null)
throw new AccessDeniedException();
participant.setFirstName(participantSaveDTO.getFirstName());
participant.setLastName(participantSaveDTO.getLastName());
participant.setMiddleName(participantSaveDTO.getMiddleName());
participant.setNumberPhone(participantSaveDTO.getNumberPhone());
participant.setEmail(participantSaveDTO.getEmail());
List<Role> roles = persistRoles(participantSaveDTO, existingParticipant, editMode, callerUser, participant);
boolean credentialsChanged = persistUserData(participantSaveDTO, existingParticipant, editMode, participant);
persistStudentData(participantSaveDTO, existingParticipant, editMode, roles, participant);
persistTeacherData(participantSaveDTO, existingParticipant, editMode, roles, participant);
// TODO: notification task
Participant saved = participantRepository.save(participant);
log.info("Participant saved: {}", saved.getFullName());
if (credentialsChanged) {
log.info("User {} changed credentials, logging out", saved.getUser().getUsername());
authenticationService.logout(saved.getUser().getUsername());
}
}
private List<Role> persistRoles(ParticipantSaveDTO participantSaveDTO, Participant existingParticipant, boolean editMode, User callerUser, Participant participant) {
boolean isAdmin = roleService.isParticInAuthority(callerUser.getParticipant(), RoleService.Authority.ADMIN);
boolean isSecretary = roleService.isParticInAuthority(callerUser.getParticipant(), RoleService.Authority.SECRETARY);
boolean isOwner = existingParticipant != null && existingParticipant.getUser() != null
&& existingParticipant.getUser().getId().equals(callerUser.getId());
if (!isAdmin && !isSecretary && !isOwner)
throw new AccessDeniedException();
if (participantSaveDTO.getAuthorities() != null && participantSaveDTO.getAuthorities().contains(RoleService.Authority.ADMIN) && !isAdmin)
throw new AccessDeniedException("Недостаточно прав для назначения роли администратора");
List<Role> roles = participantSaveDTO.getAuthorities() != null
? participantSaveDTO.getAuthorities()
.stream()
.map(roleService::getRoleByAuthority)
.collect(Collectors.toList())
: null;
if (editMode && isOwner && !isAdmin && roles != null && !ListUtils.isEqualList(roles, existingParticipant.getRoles())) {
throw new AccessDeniedException("Вы не можете изменять свои роли");
} else if (roles != null) {
participant.setRoles(roles);
}
return roles;
}
private boolean persistUserData(ParticipantSaveDTO participantSaveDTO, Participant existingParticipant, boolean editMode, Participant participant) {
boolean credentialsChanged = false;
if (participantSaveDTO.getUserData() == null) {
return credentialsChanged;
}
User user;
boolean wasBefore = false;
if (editMode && existingParticipant.getUser() != null) {
user = existingParticipant.getUser();
wasBefore = true;
} else {
user = new User();
}
String login = participantSaveDTO.getUserData().getLogin();
if (StringUtils.isNotBlank(login)) {
user.setLogin(login);
credentialsChanged = true;
} else if (!wasBefore) {
throw new BusinessException("Логин не может быть пустым");
}
String password = participantSaveDTO.getUserData().getPassword();
if (StringUtils.isNotBlank(password)) {
user.setPassword(passwordEncoder.encode(password));
credentialsChanged = true;
} else if (!wasBefore) {
throw new BusinessException("Пароль не может быть пустым");
}
user = userRepository.save(user);
participant.setUser(user);
user.setParticipant(participant);
return credentialsChanged;
}
private void persistTeacherData(ParticipantSaveDTO participantSaveDTO, Participant existingParticipant, boolean editMode, List<Role> roles, Participant participant) {
boolean shouldPersistTeacherData = participantSaveDTO.getTeacherData() != null && roles != null
&& CollectionUtils.containsAny(roles, roleService.getRoleByAuthority(RoleService.Authority.TEACHER));
if (!shouldPersistTeacherData) {
return;
}
boolean alreadyExists = editMode && teacherDataRepository.existsByParticipant_IdAndParticipant_DeletedFalse(existingParticipant.getId());
TeacherData teacherData;
if (alreadyExists) {
teacherData = teacherDataRepository.findByParticipant_Id(existingParticipant.getId());
} else {
teacherData = new TeacherData();
}
teacherData.setDegree(participantSaveDTO.getTeacherData().getDegree());
teacherData = teacherDataRepository.save(teacherData);
teacherData.setParticipant(participant);
}
private void persistStudentData(ParticipantSaveDTO participantSaveDTO, Participant existingParticipant, boolean editMode, List<Role> roles, Participant participant) {
boolean shouldPersistStudentData = participantSaveDTO.getStudentData() != null && roles != null
&& CollectionUtils.containsAny(roles, roleService.getRoleByAuthority(RoleService.Authority.STUDENT));
if (!shouldPersistStudentData) {
return;
}
boolean alreadyExists = editMode && studentDataRepository.existsByParticipant_IdAndParticipant_DeletedFalse(existingParticipant.getId());
StudentData studentData;
if (alreadyExists) {
studentData = studentDataRepository.findStudentDataByParticipant_Id(existingParticipant.getId());
} else {
studentData = new StudentData();
}
if (participantSaveDTO.getStudentData().getGroupId() != null) {
Group group = groupRepository.findByIdThrow(participantSaveDTO.getStudentData().getGroupId());
studentData.setGroup(group);
if (!group.getStudents().contains(studentData))
group.getStudents().add(studentData);
} else {
if (editMode) {
Group group = groupRepository.findByStudentsContaining(Collections.singletonList(studentData));
if (group != null)
group.getStudents().remove(studentData);
}
studentData.setGroup(null);
}
if (participantSaveDTO.getStudentData().getCuratorId() != null) {
TeacherData teacherData = teacherDataRepository.findByIdThrow(participantSaveDTO.getStudentData().getCuratorId());
studentData.setCurator(teacherData);
} else {
studentData.setCurator(null);
}
studentData = studentDataRepository.save(studentData);
studentData.setParticipant(participant);
}
public void deleteParticipant(Long id) {
if (id == 1) {
throw new AccessDeniedException();
}
Participant partic = participantRepository.findByIdThrow(id);
partic.setDeleted(true);
}
}

View File

@ -0,0 +1,34 @@
package ru.mskobaro.tdms.business.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.mskobaro.tdms.business.entity.DirectionOfPreparation;
import ru.mskobaro.tdms.integration.database.PreparationDirectionRepository;
import ru.mskobaro.tdms.presentation.controller.payload.PreparationDirectionDTO;
import java.util.List;
@Service
@Transactional
public class PreparationDirectionService {
@Autowired
private PreparationDirectionRepository preparationDirectionRepository;
public List<DirectionOfPreparation> getAll() {
return preparationDirectionRepository.findAll();
}
public void save(PreparationDirectionDTO preparationDirectionDTO) {
boolean editMode = preparationDirectionDTO.getId() != null;
DirectionOfPreparation preparationDirection;
if (editMode) {
preparationDirection = preparationDirectionRepository.findByIdThrow(preparationDirectionDTO.getId());
} else {
preparationDirection = new DirectionOfPreparation();
}
preparationDirection.setName(preparationDirectionDTO.getName());
preparationDirection.setCode(preparationDirectionDTO.getCode());
preparationDirectionRepository.save(preparationDirection);
}
}

View File

@ -1,5 +1,7 @@
package ru.mskobaro.tdms.domain.service;
package ru.mskobaro.tdms.business.service;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import jakarta.annotation.PostConstruct;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -7,7 +9,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.mskobaro.tdms.domain.entity.Role;
import ru.mskobaro.tdms.business.entity.Participant;
import ru.mskobaro.tdms.business.entity.Role;
import ru.mskobaro.tdms.integration.database.RoleRepository;
import java.util.Map;
@ -17,17 +20,23 @@ import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class RoleService {
@RequiredArgsConstructor
@Getter
public enum Authority {
ADMIN("ROLE_ADMINISTRATOR"),
COMM_MEMBER("ROLE_COMMISSION_MEMBER"),
TEACHER("ROLE_TEACHER"),
PLAGIARISM_CHECKER("ROLE_PLAGIARISM_CHECKER"),
SECRETARY("ROLE_SECRETARY"),
STUDENT("ROLE_STUDENT"),
;
private final String authority;
@JsonValue
public String getAuthority() {
return authority;
}
@JsonCreator
public static Authority from(String authority) {
for (Authority value : values()) {
if (value.getAuthority().equals(authority)) {
@ -37,7 +46,8 @@ public class RoleService {
throw new IllegalArgumentException("No such authority: " + authority);
}
}
public transient Map<String, Role> roles;
private final Map<String, Role> roles = new ConcurrentHashMap<>();
@Autowired
private RoleRepository roleRepository;
@ -45,7 +55,6 @@ public class RoleService {
@PostConstruct
@Transactional
public void bootstrapRolesCache() {
roles = new ConcurrentHashMap<>();
roleRepository.findAll().forEach(role -> roles.put(role.getAuthority(), role));
log.info("Roles initialized: {}", roles);
}
@ -53,4 +62,21 @@ public class RoleService {
public Role getRoleByAuthority(Authority authority) {
return roles.get(authority.getAuthority());
}
public boolean isParticInAuthority(Participant participant, Authority authority) {
return participant.getRoles().stream()
.anyMatch(role -> role.getAuthority().equals(authority.getAuthority()));
}
public boolean isParticInAnyAuthority(Participant participant, Authority... authority) {
return participant.getRoles().stream()
.anyMatch(role -> {
for (Authority auth : authority) {
if (role.getAuthority().equals(auth.getAuthority())) {
return true;
}
}
return false;
});
}
}

View File

@ -0,0 +1,29 @@
package ru.mskobaro.tdms.business.service;
import jakarta.transaction.Transactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.mskobaro.tdms.business.entity.Participant;
import ru.mskobaro.tdms.business.entity.StudentData;
import ru.mskobaro.tdms.integration.database.StudentDataRepository;
import ru.mskobaro.tdms.integration.database.UserRepository;
import java.util.Collection;
import java.util.List;
@Service
@Transactional
@Slf4j
public class StudentDataService {
@Autowired
private StudentDataRepository studentDataRepository;
public StudentData getStudentByParticIdThrow(Long particId) {
return studentDataRepository.findStudentDataByParticipant_Id(particId);
}
public Collection<StudentData> getAllStudentsWithoutGroup() {
return studentDataRepository.findByGroupIsNull();
}
}

View File

@ -1,4 +1,4 @@
package ru.mskobaro.tdms.domain.service;
package ru.mskobaro.tdms.business.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

View File

@ -0,0 +1,63 @@
package ru.mskobaro.tdms.business.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.mskobaro.tdms.business.entity.StudentData;
import ru.mskobaro.tdms.business.entity.Task;
import ru.mskobaro.tdms.business.entity.User;
import ru.mskobaro.tdms.business.taskfields.DiplomaTopicAgreementTaskFields;
import ru.mskobaro.tdms.business.taskfields.TaskFields;
import ru.mskobaro.tdms.integration.database.StudentDataRepository;
import ru.mskobaro.tdms.integration.database.TaskRepository;
import ru.mskobaro.tdms.presentation.controller.TaskController;
import java.util.List;
@Service
@Transactional
public class TaskService {
@Autowired
private TaskRepository taskRepository;
@Autowired
private UserService userService;
@Autowired
private StudentDataRepository studentDataRepository;
public void createTask(Task.Type type, Task.Status status, TaskFields taskFields) {
Task task = new Task();
task.setType(type);
task.setStatus(status);
task.setFields(taskFields);
taskRepository.save(task);
}
public Task findDiplomaTopicAgreementTaskCallerMaker() {
User user = userService.getCallerUser();
List<Task> diplomaTopicAgreementTaskByMakerId = taskRepository.findDiplomaTopicAgreementTaskByMakerId(
user.getParticipant().getId(), Task.Type.DIPLOMA_TOPIC_AGREEMENT
);
if (diplomaTopicAgreementTaskByMakerId.isEmpty()) {
return null;
}
if (diplomaTopicAgreementTaskByMakerId.size() > 1) {
throw new IllegalStateException();
}
return diplomaTopicAgreementTaskByMakerId;
}
public void createDiplomaAgreementTask(TaskController.DiplomaTopicAgreementDTO diplomaTopicAgreementDTO) {
DiplomaTopicAgreementTaskFields taskFields = new DiplomaTopicAgreementTaskFields();
User user = userService.getCallerUser();
StudentData studentData = studentDataRepository.findStudentDataByParticipant_Id(user.getParticipant().getId());
taskFields.setCheckerParticipantId(user.getParticipant().getId());
taskFields.setDiplomaTopicId(diplomaTopicAgreementDTO.getDiplomaTopicId());
taskFields.setDiplomaTopicName(diplomaTopicAgreementDTO.getDiplomaTopicName());
taskFields.setCheckerParticipantId(studentData.getCurator().getId());
createTask(Task.Type.DIPLOMA_TOPIC_AGREEMENT, Task.Status.WAIT_FOR_TOPIC_AGREEMENT, taskFields);
}
}

View File

@ -0,0 +1,30 @@
package ru.mskobaro.tdms.business.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.mskobaro.tdms.business.entity.TeacherData;
import ru.mskobaro.tdms.business.exception.NotFoundException;
import ru.mskobaro.tdms.integration.database.TeacherDataRepository;
import java.util.List;
@Service
@Transactional
public class TeacherDataService {
@Autowired
private TeacherDataRepository teacherDataRepository;
public List<TeacherData> findAll() {
return teacherDataRepository.findAll();
}
public TeacherData getTeacherDataByParticipantId(Long participantId) {
TeacherData teacher = teacherDataRepository.findByParticipant_Id(participantId);
if (teacher == null) {
throw new NotFoundException(TeacherData.class, participantId);
}
return teacher;
}
}

View File

@ -0,0 +1,50 @@
package ru.mskobaro.tdms.business.service;
import jakarta.transaction.Transactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import ru.mskobaro.tdms.business.entity.User;
import ru.mskobaro.tdms.integration.database.UserRepository;
import ru.mskobaro.tdms.presentation.controller.payload.UserDTO;
import java.util.List;
@Service
@Transactional
@Slf4j
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public User loadUserByUsername(String username) throws UsernameNotFoundException {
log.debug("Loading user with username: {}", username);
User user = userRepository.findUserByLogin(username).orElseThrow(
() -> new UsernameNotFoundException("User with login " + username + " not found"));
log.debug("User with login {} loaded", username);
return user;
}
public List<UserDTO> getAllUsers() {
log.debug("Loading all users");
List<UserDTO> users = userRepository.findAll().stream()
.map(UserDTO::fromEntity)
.toList();
log.info("{} users loaded", users.size());
return users;
}
public User getCallerUser() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (!(principal instanceof User)) {
return null;
}
return (User) principal;
}
}

View File

@ -0,0 +1,11 @@
package ru.mskobaro.tdms.business.taskfields;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class DiplomaTopicAgreementTaskFields extends MakerCheckerTaskFields {
private String diplomaTopicName;
private Long diplomaTopicId;
}

View File

@ -0,0 +1,13 @@
package ru.mskobaro.tdms.business.taskfields;
import lombok.Getter;
import lombok.Setter;
import java.time.LocalDateTime;
@Getter
@Setter
public class MakerCheckerTaskFields extends MakerTaskFields {
private Long checkerParticipantId;
private LocalDateTime approvedAt;
}

View File

@ -0,0 +1,10 @@
package ru.mskobaro.tdms.business.taskfields;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class MakerTaskFields extends TaskFields {
private Long makerParticipantId;
}

View File

@ -0,0 +1,10 @@
package ru.mskobaro.tdms.business.taskfields;
import lombok.Getter;
import java.time.LocalDateTime;
@Getter
public class TaskFields {
private LocalDateTime createdAt;
}

View File

@ -1,35 +0,0 @@
package ru.mskobaro.tdms.domain.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.ZonedDateTime;
@Getter
@Setter
@ToString
@Entity
@Table(name = "`group`")
public class Group {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String name;
@ManyToOne
@JoinColumn(name = "curator_teacher_id")
private Teacher groupCurator;
@Column(name = "created_at")
@CreationTimestamp
private ZonedDateTime createdAt;
@Column(name = "updated_at")
@UpdateTimestamp
private ZonedDateTime updatedAt;
}

View File

@ -1,68 +0,0 @@
package ru.mskobaro.tdms.domain.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.ZonedDateTime;
@Getter
@Setter
@ToString
@Entity
@Table(name = "student")
public class Student {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "form")
private Boolean form;
@Column(name = "protection_order")
private Integer protectionOrder;
@Column(name = "magistracy")
private String magistracy;
@Column(name = "digital_format_present")
private Boolean digitalFormatPresent;
@Column(name = "mark_comment")
private Integer markComment;
@Column(name = "mark_practice")
private Integer markPractice;
@Column(name = "predefence_comment")
private String predefenceComment;
@Column(name = "normal_control")
private String normalControl;
@Column(name = "anti_plagiarism")
private Integer antiPlagiarism;
@Column(name = "note")
private String note;
@Column(name = "record_book_returned")
private Boolean recordBookReturned;
@Column(name = "work")
private String work;
@OneToOne
@JoinColumn(name = "user_id")
private User user;
@ManyToOne
@JoinColumn(name = "diploma_topic_id")
private DiplomaTopic diplomaTopic;
// Научный руководитель
@ManyToOne
@JoinColumn(name = "adviser_teacher_id")
private Teacher adviser;
@ManyToOne
@JoinColumn(name = "group_id")
private Group group;
@Column(name = "created_at")
@CreationTimestamp
private ZonedDateTime createdAt;
@Column(name = "updated_at")
@UpdateTimestamp
private ZonedDateTime updatedAt;
}

View File

@ -1,36 +0,0 @@
package ru.mskobaro.tdms.domain.entity;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.ZonedDateTime;
import java.util.List;
@Getter
@Setter
@ToString
@Entity
@Table(name = "teacher")
public class Teacher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "groupCurator")
private List<Group> curatingGroups;
@OneToMany(mappedBy = "adviser")
private List<Student> advisingStudents;
@Column(name = "created_at")
@CreationTimestamp
private ZonedDateTime createdAt;
@Column(name = "updated_at")
@UpdateTimestamp
private ZonedDateTime updatedAt;
}

View File

@ -1,9 +0,0 @@
package ru.mskobaro.tdms.domain.service;
import jakarta.transaction.Transactional;
import org.springframework.stereotype.Service;
@Service
@Transactional
public class DiplomaTopicService {
}

View File

@ -1,67 +0,0 @@
package ru.mskobaro.tdms.domain.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.mskobaro.tdms.domain.entity.Group;
import ru.mskobaro.tdms.domain.entity.User;
import ru.mskobaro.tdms.domain.exception.BusinessException;
import ru.mskobaro.tdms.integration.database.GroupRepository;
import ru.mskobaro.tdms.presentation.payload.GroupDTO;
import ru.mskobaro.tdms.presentation.payload.GroupEditDTO;
import java.util.Collection;
import java.util.List;
@Service
@Transactional
@Slf4j
public class GroupService {
@Autowired
private GroupRepository groupRepository;
@Autowired
private UserService userService;
public Collection<GroupDTO> getAllGroups() {
log.info("Getting all groups");
List<Group> groups = groupRepository.findAll();
User callerUser = userService.getCallerUser();
List<GroupDTO> result = groups.stream().map(group -> {
GroupDTO groupDTO = new GroupDTO();
groupDTO.setName(group.getName());
groupDTO.setId(group.getId());
if (group.getGroupCurator() != null) {
groupDTO.setCuratorName(group.getGroupCurator().getUser().getFullName());
if (callerUser != null) {
groupDTO.setIAmCurator(group.getGroupCurator().getUser().equals(callerUser));
}
}
return groupDTO;
}).toList();
log.info("Found {} groups", result.size());
return result;
}
public void createGroup(String groupName) {
log.info("Creating group with name {}", groupName);
if (groupRepository.existsByName(groupName)) {
throw new BusinessException("Группа с именем " + groupName + " уже существует");
}
Group group = new Group();
group.setName(groupName);
Group saved = groupRepository.save(group);
log.info("Group saved: {}", saved);
}
public void editGroup(GroupEditDTO groupEditDTO) {
log.info("Updating group with dto: {}", groupEditDTO);
Group group = groupRepository.findByIdThrow(groupEditDTO.getId());
group.setName(groupEditDTO.getName());
log.info("Group updated: {}", group);
}
}

View File

@ -1,57 +0,0 @@
package ru.mskobaro.tdms.domain.service;
import jakarta.transaction.Transactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.mskobaro.tdms.domain.entity.Student;
import ru.mskobaro.tdms.domain.entity.User;
import ru.mskobaro.tdms.domain.exception.BusinessException;
import ru.mskobaro.tdms.domain.exception.NotFoundException;
import ru.mskobaro.tdms.integration.database.StudentRepository;
import ru.mskobaro.tdms.integration.database.UserRepository;
import ru.mskobaro.tdms.presentation.payload.StudentDTO;
@Service
@Transactional
@Slf4j
public class StudentService {
@Autowired
private StudentRepository studentRepository;
@Autowired
private UserService userService;
@Autowired
private UserRepository userRepository;
public Student getCallerStudentThrow() {
userService.sureCallerInAnyRole(RoleService.Authority.STUDENT);
return studentRepository.findByUser(userService.getCallerUser());
}
public StudentDTO getCallerStudentDtoThrow() {
Student callerStudent = getCallerStudentThrow();
if (callerStudent == null) {
throw new BusinessException("Вызывающий пользователь является студентом, но ассоциированный с ним студент не найден");
}
return StudentDTO.from(callerStudent);
}
public StudentDTO getStudentByUserIdThrow(Long id) {
User callerUser = userService.getCallerUser();
if (callerUser == null) {
throw new NotFoundException();
}
if (callerUser.getId().equals(id)) {
return getCallerStudentDtoThrow();
} else if (userService.isCallerInRole(RoleService.Authority.ADMIN, RoleService.Authority.TEACHER, RoleService.Authority.SECRETARY)) {
User user = userRepository.findByIdThrow(id);
Student student = studentRepository.findByUser(user);
NotFoundException.throwIfNull(student, Student.class, user.getId());
return StudentDTO.from(student);
} else {
throw new NotFoundException(Student.class, id);
}
}
}

View File

@ -1,181 +0,0 @@
package ru.mskobaro.tdms.domain.service;
import jakarta.transaction.Transactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import ru.mskobaro.tdms.domain.entity.*;
import ru.mskobaro.tdms.domain.exception.AccessDeniedException;
import ru.mskobaro.tdms.domain.exception.NotFoundException;
import ru.mskobaro.tdms.integration.database.GroupRepository;
import ru.mskobaro.tdms.integration.database.StudentRepository;
import ru.mskobaro.tdms.integration.database.TeacherRepository;
import ru.mskobaro.tdms.integration.database.UserRepository;
import ru.mskobaro.tdms.presentation.payload.RegistrationDTO;
import ru.mskobaro.tdms.presentation.payload.UserDTO;
import java.util.List;
import java.util.Optional;
@Service
@Transactional
@Slf4j
public class UserService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Autowired
private GroupRepository groupRepository;
@Autowired
private StudentRepository studentRepository;
@Autowired
private RoleService roleService;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private TeacherRepository teacherRepository;
@Override
public User loadUserByUsername(String username) throws UsernameNotFoundException {
log.debug("Loading user with username: {}", username);
User user = userRepository.findUserByLogin(username).orElseThrow(
() -> new UsernameNotFoundException("User with login " + username + " not found"));
log.debug("User with login {} loaded", username);
return user;
}
public List<UserDTO> getAllUsers() {
log.debug("Loading all users");
List<UserDTO> users = userRepository.findAll().stream()
.map(UserDTO::from)
.toList();
log.info("{} users loaded", users.size());
return users;
}
public void registerUser(RegistrationDTO registrationDTO) {
log.info("Registering user: {}", registrationDTO);
User user = transientUser(registrationDTO);
fillRoles(user, registrationDTO);
userRepository.save(user);
if (userInAnyRole(user, RoleService.Authority.STUDENT)) {
sureCallerInAnyRole(RoleService.Authority.ADMIN, RoleService.Authority.SECRETARY);
Student student = transientStudent(registrationDTO.getStudentData());
student.setUser(user);
student = studentRepository.save(student);
log.info("New user is student: {}", student);
} else if (userInAnyRole(user, RoleService.Authority.TEACHER)) {
sureCallerInAnyRole(RoleService.Authority.ADMIN, RoleService.Authority.SECRETARY);
Teacher teacher = transientTeacher(registrationDTO.getTeacherData());
teacher.setUser(user);
teacher = teacherRepository.save(teacher);
log.info("New user is teacher: {}", teacher);
} else if (userInAnyRole(user, RoleService.Authority.ADMIN)) {
sureCallerInAnyRole(RoleService.Authority.ADMIN);
log.info("New user is administrator");
} else {
throw new UnsupportedOperationException("Role not supported: " + user.getAuthorities());
}
}
private Teacher transientTeacher(RegistrationDTO.TeacherRegistrationDTO teacherData) {
if (teacherData == null)
throw new NullPointerException("Teacher data is null");
if (teacherData.getCuratingGroups() == null)
teacherData.setCuratingGroups(List.of());
if (teacherData.getAdvisingStudents() == null)
teacherData.setAdvisingStudents(List.of());
Teacher teacher = new Teacher();
List<Group> groups = groupRepository.findAllById(teacherData.getCuratingGroups());
if (groups.size() != teacherData.getCuratingGroups().size()) {
throw new NotFoundException(Teacher.class);
}
List<Student> students = studentRepository.findAllById(teacherData.getAdvisingStudents());
if (students.size() != teacherData.getAdvisingStudents().size()) {
throw new NotFoundException(Student.class);
}
teacher.setCuratingGroups(groups);
teacher.setAdvisingStudents(students);
return teacher;
}
private User transientUser(RegistrationDTO registrationDTO) {
User user = new User();
user.setLogin(registrationDTO.getLogin());
user.setPassword(passwordEncoder.encode(registrationDTO.getPassword()));
user.setFullName(registrationDTO.getFullName());
user.setEmail(registrationDTO.getEmail());
user.setNumberPhone(registrationDTO.getNumberPhone());
return user;
}
private Student transientStudent(RegistrationDTO.StudentRegistrationDTO studentData) {
if (studentData == null)
throw new NullPointerException("Student data is null");
Student student = new Student();
if (studentData.getGroupId() != null) {
student.setGroup(groupRepository.findByIdThrow(studentData.getGroupId()));
}
return student;
}
private void fillRoles(User user, RegistrationDTO registrationDTO) {
RoleService.Authority accountType = registrationDTO.getAccountType();
Role role = roleService.getRoleByAuthority(accountType);
if (role == null) {
throw new IllegalArgumentException("Role not found for authority: " + accountType);
}
user.setRoles(List.of(role));
}
public boolean userInAnyRole(User user, RoleService.Authority... authority) {
if (user == null || authority == null) {
return false;
}
List<RoleService.Authority> toCheckAuthorities = List.of(authority);
return user.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.map(RoleService.Authority::from)
.anyMatch(toCheckAuthorities::contains);
}
public User getCallerUser() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (!(principal instanceof User)) {
return null;
}
return (User) principal;
}
public Optional<User> getCallerOptional() {
return Optional.ofNullable(getCallerUser());
}
public UserDTO getCallerUserDTO() {
return getCallerOptional().map(UserDTO::from).orElse(UserDTO.unauthenticated());
}
public boolean isCallerInRole(RoleService.Authority... authorities) {
return userInAnyRole(getCallerUser(), authorities);
}
public void sureCallerInAnyRole(RoleService.Authority... authority) {
if (!isCallerInRole(authority)) {
throw new AccessDeniedException();
}
}
}

View File

@ -1,4 +1,25 @@
package ru.mskobaro.tdms.integration.database;
public interface DefenceRepository {
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import ru.mskobaro.tdms.business.entity.Defense;
import ru.mskobaro.tdms.business.exception.NotFoundException;
import java.util.Optional;
@Repository
public interface DefenceRepository extends JpaRepository<Defense, Long> {
default Defense findByIdThrow(Long id) {
return this.findById(id).orElseThrow(() -> new NotFoundException(Defense.class, id));
}
@Override
@Query("SELECT d from Defense d " +
"left join fetch d.bestWorks bw " +
"left join fetch d.commissionMembers cm " +
"where d.id = :id " +
"and bw.participant.deleted = false " +
"and cm.participant.deleted = false")
Optional<Defense> findById(Long id);
}

View File

@ -1,13 +1,29 @@
package ru.mskobaro.tdms.integration.database;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import ru.mskobaro.tdms.domain.entity.DiplomaTopic;
import ru.mskobaro.tdms.domain.exception.NotFoundException;
import ru.mskobaro.tdms.business.entity.DiplomaTopic;
import ru.mskobaro.tdms.business.entity.DirectionOfPreparation;
import ru.mskobaro.tdms.business.entity.StudentData;
import ru.mskobaro.tdms.business.exception.NotFoundException;
import java.util.List;
import java.util.Optional;
@Repository
public interface DiplomaTopicRepository extends JpaRepository<DiplomaTopic, Integer> {
default DiplomaTopic findByIdThrow(Integer id) {
public interface DiplomaTopicRepository extends JpaRepository<DiplomaTopic, Long> {
default DiplomaTopic findByIdThrow(Long id) {
return this.findById(id).orElseThrow(() -> new NotFoundException(DiplomaTopic.class, id));
}
@Override
@Query("SELECT d FROM DiplomaTopic d WHERE d.id = :id AND d.teacher.participant.deleted = false")
Optional<DiplomaTopic> findById(Long id);
@Query("SELECT d FROM DiplomaTopic d " +
"inner join d.directionOfPreparation dp " +
"inner join StudentData sd on sd.id = :studentIdwhere " +
"where dp = sd.group.directionOfPreparation")
List<DiplomaTopic> findAllForStudentId(Long studentId);
}

View File

@ -1,9 +1,14 @@
package ru.mskobaro.tdms.integration.database;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import ru.mskobaro.tdms.domain.entity.Group;
import ru.mskobaro.tdms.domain.exception.NotFoundException;
import ru.mskobaro.tdms.business.entity.Group;
import ru.mskobaro.tdms.business.entity.StudentData;
import ru.mskobaro.tdms.business.exception.NotFoundException;
import java.util.List;
import java.util.Optional;
@Repository
public interface GroupRepository extends JpaRepository<Group, Long> {
@ -11,5 +16,12 @@ public interface GroupRepository extends JpaRepository<Group, Long> {
return this.findById(id).orElseThrow(() -> new NotFoundException(Group.class, id));
}
@Override
@Query("SELECT g FROM Group g left join fetch g.students sd WHERE g.id = :id")
Optional<Group> findById(Long id);
boolean existsByName(String name);
@Query("SELECT g FROM Group g left join fetch g.students sd WHERE sd IN :students")
Group findByStudentsContaining(List<StudentData> students);
}

View File

@ -0,0 +1,27 @@
package ru.mskobaro.tdms.integration.database;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import ru.mskobaro.tdms.business.entity.DiplomaTopic;
import ru.mskobaro.tdms.business.entity.Participant;
import ru.mskobaro.tdms.business.exception.NotFoundException;
import java.util.List;
import java.util.Optional;
@Repository
public interface ParticipantRepository extends JpaRepository<Participant, Long> {
default Participant findByIdThrow(Long id) {
return this.findById(id).orElseThrow(() -> new NotFoundException(DiplomaTopic.class, id));
}
@Override
@Query("SELECT p FROM Participant p WHERE p.id = :id AND p.deleted = false")
Optional<Participant> findById(Long id);
@Override
@Query("SELECT p from Participant p where p.deleted = false")
List<Participant> findAll();
}

View File

@ -0,0 +1,13 @@
package ru.mskobaro.tdms.integration.database;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import ru.mskobaro.tdms.business.entity.DirectionOfPreparation;
import ru.mskobaro.tdms.business.exception.NotFoundException;
@Repository
public interface PreparationDirectionRepository extends JpaRepository<DirectionOfPreparation, Long> {
default DirectionOfPreparation findByIdThrow(Long id) {
return this.findById(id).orElseThrow(() -> new NotFoundException(DirectionOfPreparation.class, id));
}
}

View File

@ -2,7 +2,7 @@ package ru.mskobaro.tdms.integration.database;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import ru.mskobaro.tdms.domain.entity.Role;
import ru.mskobaro.tdms.business.entity.Role;
@Repository
public interface RoleRepository extends JpaRepository<Role, Long> {

View File

@ -0,0 +1,39 @@
package ru.mskobaro.tdms.integration.database;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import ru.mskobaro.tdms.business.entity.StudentData;
import ru.mskobaro.tdms.business.exception.NotFoundException;
import java.util.List;
import java.util.Optional;
@Repository
public interface StudentDataRepository extends JpaRepository<StudentData, Long> {
default StudentData findByIdThrow(Long id) {
return this.findById(id).orElseThrow(() -> new NotFoundException(StudentData.class, id));
}
@EntityGraph(type = EntityGraph.EntityGraphType.LOAD, attributePaths = {"group.students"})
@Query("SELECT s FROM StudentData s join fetch s.participant p WHERE p.id = :id AND p.deleted = false")
StudentData findStudentDataByParticipant_Id(Long id);
boolean existsByParticipant_IdAndParticipant_DeletedFalse(Long id);
@Override
@EntityGraph(type = EntityGraph.EntityGraphType.LOAD, attributePaths = {"participant.roles"})
@Query("SELECT s FROM StudentData s join fetch s.participant p WHERE s.id in :ids AND p.deleted = false")
List<StudentData> findAllById(Iterable<Long> ids);
@Query("SELECT s FROM StudentData s join fetch s.participant p WHERE s.group is null and p.deleted = false")
List<StudentData> findByGroupIsNull();
@Query("SELECT s FROM StudentData s join fetch s.participant p join fetch s.group g WHERE g.id = :id AND p.deleted = false")
List<StudentData> findAllByGroup_Id(Long id);
@Override
@Query("SELECT s FROM StudentData s join fetch s.participant p WHERE s.id = :id AND p.deleted = false")
Optional<StudentData> findById(Long id);
}

View File

@ -1,16 +0,0 @@
package ru.mskobaro.tdms.integration.database;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import ru.mskobaro.tdms.domain.entity.Student;
import ru.mskobaro.tdms.domain.entity.User;
import ru.mskobaro.tdms.domain.exception.NotFoundException;
@Repository
public interface StudentRepository extends JpaRepository<Student, Long> {
default Student findByIdThrow(Long id) {
return this.findById(id).orElseThrow(() -> new NotFoundException(Student.class, id));
}
Student findByUser(User user);
}

View File

@ -0,0 +1,21 @@
package ru.mskobaro.tdms.integration.database;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import ru.mskobaro.tdms.business.entity.Task;
import ru.mskobaro.tdms.business.exception.NotFoundException;
import java.util.List;
@Repository
public interface TaskRepository extends JpaRepository<Task, Long> {
default Task findByIdThrow(Long id) {
return findById(id).orElseThrow(() -> new NotFoundException(Task.class, id));
}
@Query(value = "SELECT t FROM task t " +
"WHERE t.type = :type " +
"and t.fields->>'makerParticipantId' = :id", nativeQuery = true)
List<Task> findDiplomaTopicAgreementTaskByMakerId(Long id, Task.Type type);
}

View File

@ -0,0 +1,30 @@
package ru.mskobaro.tdms.integration.database;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import ru.mskobaro.tdms.business.entity.TeacherData;
import ru.mskobaro.tdms.business.exception.NotFoundException;
import java.util.List;
import java.util.Optional;
@Repository
public interface TeacherDataRepository extends JpaRepository<TeacherData, Long> {
default TeacherData findByIdThrow(Long id) {
return this.findById(id).orElseThrow(() -> new NotFoundException(TeacherData.class, id));
}
@Override
@Query("SELECT t FROM TeacherData t WHERE t.id = :id AND t.participant.deleted = false")
Optional<TeacherData> findById(Long id);
boolean existsByParticipant_IdAndParticipant_DeletedFalse(Long id);
@Query("SELECT t FROM TeacherData t WHERE t.participant.id = :id AND t.participant.deleted = false")
TeacherData findByParticipant_Id(Long id);
@Override
@Query("select t from TeacherData t where t.participant.deleted = false")
List<TeacherData> findAll();
}

View File

@ -1,13 +0,0 @@
package ru.mskobaro.tdms.integration.database;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import ru.mskobaro.tdms.domain.entity.Teacher;
import ru.mskobaro.tdms.domain.exception.NotFoundException;
@Repository
public interface TeacherRepository extends JpaRepository<Teacher, Long> {
default Teacher findByIdThrow(Long id) {
return this.findById(id).orElseThrow(() -> new NotFoundException(Teacher.class, id));
}
}

View File

@ -1,9 +1,10 @@
package ru.mskobaro.tdms.integration.database;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import ru.mskobaro.tdms.domain.entity.User;
import ru.mskobaro.tdms.domain.exception.NotFoundException;
import ru.mskobaro.tdms.business.entity.User;
import ru.mskobaro.tdms.business.exception.NotFoundException;
import java.util.Optional;
@ -12,5 +13,11 @@ public interface UserRepository extends JpaRepository<User, Long> {
default User findByIdThrow(Long id) {
return this.findById(id).orElseThrow(() -> new NotFoundException(User.class, id));
}
@Override
@Query("SELECT u from User u join fetch u.participant p where u.id = :id and p.deleted = false")
Optional<User> findById(Long id);
@Query("SELECT u from User u join fetch u.participant p where u.login = :login and p.deleted = false")
Optional<User> findUserByLogin(String login);
}

View File

@ -0,0 +1,22 @@
package ru.mskobaro.tdms.presentation.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.mskobaro.tdms.business.service.DefenceService;
import ru.mskobaro.tdms.presentation.controller.payload.DefenceDTO;
import java.util.List;
@RestController
@RequestMapping("/api/v1/defence")
public class DefenceController {
@Autowired
private DefenceService defenceService;
@GetMapping("/all")
public List<DefenceDTO> getAllDefences() {
return defenceService.getAllDefences();
}
}

View File

@ -1,13 +1,34 @@
package ru.mskobaro.tdms.presentation.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import ru.mskobaro.tdms.business.service.DiplomaTopicService;
import ru.mskobaro.tdms.presentation.controller.payload.DiplomaTopicDTO;
import java.util.List;
@RestController
@RequestMapping("/api/v1/diploma-topic/")
@Validated
public class DiplomaTopicController {
@Autowired
private DiplomaTopicService diplomaTopicService;
@GetMapping("/all")
public List<DiplomaTopicDTO> getAll() {
return diplomaTopicService.findAll().stream().map(DiplomaTopicDTO::from).toList();
}
@PostMapping("/save")
public void save(@RequestBody DiplomaTopicDTO diplomaTopicDTO) {
diplomaTopicService.save(diplomaTopicDTO);
}
@GetMapping("/all-for-student")
public List<DiplomaTopicDTO> getAllForStudent(@RequestParam Long studentId) {
return diplomaTopicService.findAllForStudent(studentId).stream().map(DiplomaTopicDTO::from).toList();
}
}

View File

@ -3,10 +3,8 @@ package ru.mskobaro.tdms.presentation.controller;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import ru.mskobaro.tdms.domain.service.GroupService;
import ru.mskobaro.tdms.presentation.payload.GroupCreateDTO;
import ru.mskobaro.tdms.presentation.payload.GroupDTO;
import ru.mskobaro.tdms.presentation.payload.GroupEditDTO;
import ru.mskobaro.tdms.business.service.GroupService;
import ru.mskobaro.tdms.presentation.controller.payload.GroupDTO;
import java.util.Collection;
@ -21,13 +19,13 @@ public class GroupController {
return groupService.getAllGroups();
}
@PostMapping("/create-group")
public void createGroup(@RequestBody @Valid GroupCreateDTO groupCreateDTO) {
groupService.createGroup(groupCreateDTO.getName());
@PostMapping("/save")
public void save(@RequestBody @Valid GroupDTO groupDTO) {
groupService.save(groupDTO);
}
@PostMapping("/edit-group")
public void editGroup(@RequestBody @Valid GroupEditDTO groupEditDTO) {
groupService.editGroup(groupEditDTO);
@PostMapping("/delete")
public void delete(@RequestParam(value = "id") Long groupId) {
groupService.deleteGroup(groupId);
}
}

View File

@ -0,0 +1,35 @@
package ru.mskobaro.tdms.presentation.controller;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import ru.mskobaro.tdms.business.service.ParticipantService;
import ru.mskobaro.tdms.presentation.controller.payload.IdDto;
import ru.mskobaro.tdms.presentation.controller.payload.ParticipantDTO;
import ru.mskobaro.tdms.presentation.controller.payload.ParticipantSaveDTO;
import java.util.Collection;
@RestController
@RequestMapping("/api/v1/participant")
public class ParticipantController {
@Autowired
private ParticipantService participantService;
@GetMapping("/get-all-participants")
public Collection<ParticipantDTO> getAllParticipants() {
return participantService.getAllParticipants().stream()
.map(ParticipantDTO::fromEntity)
.toList();
}
@PostMapping("/save")
public void registerParticipant(@Valid @RequestBody ParticipantSaveDTO participantSaveDTO) {
participantService.saveParticipant(participantSaveDTO);
}
@PostMapping("/delete")
public void deleteParticipant(@RequestBody IdDto id) {
participantService.deleteParticipant(id.getId());
}
}

View File

@ -0,0 +1,26 @@
package ru.mskobaro.tdms.presentation.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import ru.mskobaro.tdms.business.service.PreparationDirectionService;
import ru.mskobaro.tdms.presentation.controller.payload.PreparationDirectionDTO;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/v1/prep-direction")
public class PreparationDirectionController {
@Autowired
private PreparationDirectionService preparationDirectionService;
@GetMapping("/get-all")
public List<PreparationDirectionDTO> getAll() {
return preparationDirectionService.getAll().stream().map(PreparationDirectionDTO::from).collect(Collectors.toList());
}
@PostMapping("save")
public void save(@RequestBody PreparationDirectionDTO preparationDirectionDTO) {
preparationDirectionService.save(preparationDirectionDTO);
}
}

View File

@ -5,22 +5,29 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import ru.mskobaro.tdms.domain.service.StudentService;
import ru.mskobaro.tdms.presentation.payload.StudentDTO;
import ru.mskobaro.tdms.business.entity.StudentData;
import ru.mskobaro.tdms.business.service.StudentDataService;
import ru.mskobaro.tdms.presentation.controller.payload.GroupDTO;
import ru.mskobaro.tdms.presentation.controller.payload.StudentDataDTO;
import java.util.Collection;
@RestController
@RequestMapping("/api/v1/student/")
public class StudentController {
@Autowired
private StudentService studentService;
private StudentDataService studentDataService;
@GetMapping("/current")
public StudentDTO getCurrentStudent() {
return studentService.getCallerStudentDtoThrow();
@GetMapping(value = "/by-partic-id")
public StudentDataDTO getCurrentStudent(@RequestParam(value = "id") Long particId) {
StudentData studentData = studentDataService.getStudentByParticIdThrow(particId);
return StudentDataDTO.from(studentData, true);
}
@GetMapping("/by-user-id")
public StudentDTO getStudentByUserId(@RequestParam Long id) {
return studentService.getStudentByUserIdThrow(id);
@GetMapping(value = "all-without-group")
public Collection<StudentDataDTO> getAllStudentsWithoutGroup() {
return studentDataService.getAllStudentsWithoutGroup().stream()
.map(sd -> StudentDataDTO.from(sd, true))
.toList();
}
}

View File

@ -4,7 +4,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import ru.mskobaro.tdms.domain.service.SysInfoService;
import ru.mskobaro.tdms.business.service.SysInfoService;
@RestController
@RequestMapping("/api/v1/sysinfo")

View File

@ -0,0 +1,34 @@
package ru.mskobaro.tdms.presentation.controller;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import ru.mskobaro.tdms.business.service.TaskService;
import ru.mskobaro.tdms.presentation.controller.payload.TaskDto;
@RestController
@RequestMapping("/api/v1/task")
public class TaskController {
@Autowired
private TaskService taskService;
@GetMapping("/diploma-agreement-maker")
public TaskDto diplomaTopicAgreementMaker() {
return TaskDto.from(taskService.findDiplomaTopicAgreementTaskCallerMaker());
}
@PostMapping("/create-topic-agreement")
public void createDiplomaTopicAgreement(@RequestBody DiplomaTopicAgreementDTO diplomaTopicAgreementDTO) {
taskService.createDiplomaAgreementTask(diplomaTopicAgreementDTO);
}
@NoArgsConstructor
@Getter
@Setter
public static class DiplomaTopicAgreementDTO {
private String diplomaTopicName;
private Long diplomaTopicId;
}
}

View File

@ -0,0 +1,28 @@
package ru.mskobaro.tdms.presentation.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import ru.mskobaro.tdms.business.service.TeacherDataService;
import ru.mskobaro.tdms.presentation.controller.payload.TeacherDataDTO;
import java.util.List;
@RestController
@RequestMapping("/api/v1/teacher-data")
public class TeacherDataController {
@Autowired
private TeacherDataService teacherDataService;
@GetMapping("/get-all")
public List<TeacherDataDTO> getAllTeacherData() {
return teacherDataService.findAll().stream().map(TeacherDataDTO::from).toList();
}
@GetMapping("/by-partic-id")
public TeacherDataDTO getTeacherDataByParticipantId(@RequestParam Long id) {
return TeacherDataDTO.from(teacherDataService.getTeacherDataByParticipantId(id));
}
}

View File

@ -3,14 +3,14 @@ package ru.mskobaro.tdms.presentation.controller;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import ru.mskobaro.tdms.domain.service.AuthenticationService;
import ru.mskobaro.tdms.domain.service.UserService;
import ru.mskobaro.tdms.presentation.payload.LoginDTO;
import ru.mskobaro.tdms.presentation.payload.RegistrationDTO;
import ru.mskobaro.tdms.presentation.payload.UserDTO;
import java.util.List;
import ru.mskobaro.tdms.business.entity.User;
import ru.mskobaro.tdms.business.service.AuthenticationService;
import ru.mskobaro.tdms.business.service.UserService;
import ru.mskobaro.tdms.presentation.controller.payload.LoginDTO;
import ru.mskobaro.tdms.presentation.controller.payload.UserDTO;
@RestController
@RequestMapping("/api/v1/user")
@ -22,8 +22,11 @@ public class UserController {
private UserService userService;
@GetMapping("/current")
public UserDTO getCurrentUser() {
return userService.getCallerUserDTO();
public ResponseEntity<UserDTO> getCurrentUser() {
User user = userService.getCallerUser();
return user == null
? ResponseEntity.status(HttpStatus.UNAUTHORIZED).build()
: ResponseEntity.ok(UserDTO.fromEntity(user));
}
@PostMapping("/logout")
@ -35,14 +38,4 @@ public class UserController {
public void login(@RequestBody @Valid LoginDTO loginDTO) {
authenticationService.login(loginDTO.getLogin(), loginDTO.getPassword());
}
@PostMapping("/register")
public void register(@RequestBody @Valid RegistrationDTO registrationDTO) {
userService.registerUser(registrationDTO);
}
@GetMapping("/get-all")
public List<UserDTO> getAllUsers() {
return userService.getAllUsers();
}
}

View File

@ -0,0 +1,31 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import ru.mskobaro.tdms.business.entity.CommissionMemberData;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
public class CommissionMemberDTO {
private Long id;
private ParticipantDTO participant;
private String workPlace;
private String workPosition;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public static CommissionMemberDTO from(CommissionMemberData commissionMemberData) {
CommissionMemberDTO dto = new CommissionMemberDTO();
dto.setId(commissionMemberData.getId());
dto.setParticipant(ParticipantDTO.fromEntity(commissionMemberData.getParticipant()));
dto.setWorkPlace(commissionMemberData.getWorkPlace());
dto.setWorkPosition(commissionMemberData.getWorkPosition());
dto.setCreatedAt(commissionMemberData.getAuditInfo().getCreatedAt());
dto.setUpdatedAt(commissionMemberData.getAuditInfo().getUpdatedAt());
return dto;
}
}

View File

@ -0,0 +1,39 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections4.CollectionUtils;
import ru.mskobaro.tdms.business.entity.Defense;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
@Getter
@Setter
public class DefenceDTO {
private Long id;
private List<CommissionMemberDTO> commissionMembers;
private List<GroupDTO> groups;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public static DefenceDTO from(Defense defense) {
DefenceDTO dto = new DefenceDTO();
dto.setId(defense.getId());
if (CollectionUtils.isNotEmpty(defense.getCommissionMembers())) {
dto.setCommissionMembers(
defense.getCommissionMembers().stream()
.map(CommissionMemberDTO::from)
.collect(Collectors.toList())
);
}
dto.setGroups(defense.getGroups().stream().map(g -> GroupDTO.from(g, true)).toList());
dto.setCreatedAt(defense.getAuditInfo().getCreatedAt());
dto.setUpdatedAt(defense.getAuditInfo().getUpdatedAt());
return dto;
}
}

View File

@ -0,0 +1,37 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import ru.mskobaro.tdms.business.entity.DiplomaTopic;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
@ToString
public class DiplomaTopicDTO {
private Long id;
private String name;
private TeacherDataDTO teacher;
private PreparationDirectionDTO preparationDirection;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public static DiplomaTopicDTO from(DiplomaTopic diplomaTopic) {
DiplomaTopicDTO dto = new DiplomaTopicDTO();
dto.setId(diplomaTopic.getId());
dto.setName(diplomaTopic.getName());
if (diplomaTopic.getTeacher() != null) {
dto.setTeacher(TeacherDataDTO.from(diplomaTopic.getTeacher()));
}
if (diplomaTopic.getDirectionOfPreparation() != null) {
dto.setPreparationDirection(PreparationDirectionDTO.from(diplomaTopic.getDirectionOfPreparation()));
}
dto.setCreatedAt(diplomaTopic.getAuditInfo().getCreatedAt());
dto.setUpdatedAt(diplomaTopic.getAuditInfo().getUpdatedAt());
return dto;
}
}

View File

@ -1,4 +1,4 @@
package ru.mskobaro.tdms.presentation.payload;
package ru.mskobaro.tdms.presentation.controller.payload;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

View File

@ -0,0 +1,47 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import ru.mskobaro.tdms.business.entity.Group;
import java.time.LocalDateTime;
import java.util.List;
@Getter
@Setter
@ToString
public class GroupDTO {
private Long id;
@NotEmpty(message = "Имя группы не может быть пустым")
@Size(min = 3, max = 50, message = "Имя группы должно быть от 3 до 50 символов")
@Pattern(regexp = "^[а-яА-ЯёЁ0-9_-]*$", message = "Имя группы должно содержать только русские буквы, дефис, нижнее подчеркивание и цифры")
private String name;
private List<StudentDataDTO> students;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
private PreparationDirectionDTO preparationDirection;
public static GroupDTO from(Group group, boolean includeStudents) {
GroupDTO dto = new GroupDTO();
dto.setId(group.getId());
dto.setName(group.getName());
if (includeStudents && group.getStudents() != null) {
dto.setStudents(
group.getStudents()
.stream()
.filter(sd -> !sd.getParticipant().isDeleted())
.map(sd -> StudentDataDTO.from(sd, false))
.toList());
}
if (group.getDirectionOfPreparation() != null) {
dto.setPreparationDirection(PreparationDirectionDTO.from(group.getDirectionOfPreparation()));
}
dto.setCreatedAt(group.getAuditInfo().getCreatedAt());
dto.setUpdatedAt(group.getAuditInfo().getUpdatedAt());
return dto;
}
}

View File

@ -0,0 +1,14 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@NoArgsConstructor
@Getter
@Setter
@ToString
public class IdDto {
private Long id;
}

View File

@ -1,4 +1,4 @@
package ru.mskobaro.tdms.presentation.payload;
package ru.mskobaro.tdms.presentation.controller.payload;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Pattern;

View File

@ -0,0 +1,48 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import ru.mskobaro.tdms.business.entity.Participant;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
import java.util.List;
@Getter
@Setter
@ToString
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public class ParticipantDTO {
private Long id;
private String firstName;
private String lastName;
private String middleName;
private String email;
private String numberPhone;
private UserDTO user;
private List<RoleDTO> roles;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public static ParticipantDTO fromEntity(Participant participant) {
ParticipantDTO participantDTO = new ParticipantDTO();
participantDTO.setId(participant.getId());
participantDTO.setFirstName(participant.getFirstName());
participantDTO.setLastName(participant.getLastName());
participantDTO.setMiddleName(participant.getMiddleName());
participantDTO.setEmail(participant.getEmail());
participantDTO.setNumberPhone(participant.getNumberPhone());
participantDTO.setRoles(RoleDTO.from(participant));
participantDTO.setCreatedAt(participant.getAuditInfo().getCreatedAt());
participantDTO.setUpdatedAt(participant.getAuditInfo().getUpdatedAt());
if (participant.getUser() != null) {
participantDTO.setUser(UserDTO.fromEntity(participant.getUser(), participantDTO));
}
return participantDTO;
}
}

View File

@ -0,0 +1,64 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import lombok.Getter;
import lombok.ToString;
import ru.mskobaro.tdms.business.service.RoleService.Authority;
import java.util.List;
@Getter
@ToString
public class ParticipantSaveDTO {
private Long id;
@NotEmpty(message = "Имя не может быть пустым")
@Pattern(regexp = "^[a-zA-Zа-яА-ЯёЁ\\s-]+$", message = "Имя должно содержать только буквы английского или русского алфавита, пробелы и дефис")
private String firstName;
@NotEmpty(message = "Фамилия не может быть пустой")
@Pattern(regexp = "^[a-zA-Zа-яА-ЯёЁ\\s-]+$", message = "Фамилия должна содержать только буквы английского или русского алфавита, пробелы и дефис")
private String lastName;
private String middleName;
@NotNull(message = "Почта не может быть пустой")
@Email(message = "Почта должна быть валидным адресом электронной почты")
private String email;
@NotNull(message = "Номер телефона не может быть пустым")
@Pattern(regexp = "^\\+[1-9]\\d{6,14}$", message = "Номер телефона должен начинаться с '+' и содержать от 7 до 15 цифр")
private String numberPhone;
private List<Authority> authorities;
@Valid
private UserRegistrationDTO userData;
@Valid
private StudentRegistrationDTO studentData;
@Valid
private TeacherRegistrationDTO teacherData;
@Getter
@ToString
public static class UserRegistrationDTO {
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "Логин должен содержать только латинские буквы, цифры и знак подчеркивания")
@Size(min = 5, message = "Логин должен содержать минимум 5 символов")
@Size(max = 32, message = "Логин должен содержать максимум 32 символов")
private String login;
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$", message = "Пароль должен содержать хотя бы одну цифру, одну заглавную и одну строчную букву, минимум 8 символов")
private String password;
}
@Getter
@ToString
public static class StudentRegistrationDTO {
private Long groupId;
private Long curatorId;
private Long diplomaTopicId;
}
@Getter
@ToString
public static class TeacherRegistrationDTO {
private List<Long> curatingGroups;
private List<Long> advisingStudents;
private String degree;
}
}

View File

@ -0,0 +1,29 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import ru.mskobaro.tdms.business.entity.DirectionOfPreparation;
import java.time.LocalDateTime;
@NoArgsConstructor
@Getter
@Setter
public class PreparationDirectionDTO {
private Long id;
private String name;
private String code;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public static PreparationDirectionDTO from(DirectionOfPreparation preparationDirection) {
PreparationDirectionDTO dto = new PreparationDirectionDTO();
dto.setId(preparationDirection.getId());
dto.setName(preparationDirection.getName());
dto.setCode(preparationDirection.getCode());
dto.setCreatedAt(preparationDirection.getAuditInfo().getCreatedAt());
dto.setUpdatedAt(preparationDirection.getAuditInfo().getUpdatedAt());
return dto;
}
}

View File

@ -0,0 +1,19 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import ru.mskobaro.tdms.business.entity.Participant;
import ru.mskobaro.tdms.business.entity.Role;
import java.util.List;
public record RoleDTO(String name, String code) {
public static RoleDTO from(Role role) {
return new RoleDTO(role.getName(), role.getAuthority());
}
public static List<RoleDTO> from(Participant participant) {
return participant.getRoles().stream().map(RoleDTO::from).toList();
}
}

View File

@ -0,0 +1,31 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import lombok.Getter;
import lombok.Setter;
import ru.mskobaro.tdms.business.entity.StudentData;
@Setter
@Getter
public class StudentDataDTO {
private Long id;
private GroupDTO group;
private ParticipantDTO participant;
private TeacherDataDTO curator;
private DiplomaTopicDTO diplomaTopic;
public static StudentDataDTO from(StudentData studentData, boolean includeGroup) {
StudentDataDTO dto = new StudentDataDTO();
dto.setId(studentData.getId());
if (includeGroup && studentData.getGroup() != null) {
dto.setGroup(GroupDTO.from(studentData.getGroup(), false));
}
dto.setParticipant(ParticipantDTO.fromEntity(studentData.getParticipant()));
if (studentData.getCurator() != null) {
dto.setCurator(TeacherDataDTO.from(studentData.getCurator()));
}
if (studentData.getDiplomaTopic() != null) {
dto.setDiplomaTopic(DiplomaTopicDTO.from(studentData.getDiplomaTopic()));
}
return dto;
}
}

View File

@ -0,0 +1,33 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import ru.mskobaro.tdms.business.entity.Task;
import ru.mskobaro.tdms.business.taskfields.TaskFields;
import java.time.LocalDateTime;
@NoArgsConstructor
@Getter
@Setter
public class TaskDto {
private Long id;
private Task.Type type;
private Task.Status status;
private TaskFields fields;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
public static TaskDto from(Task task) {
TaskDto dto = new TaskDto();
dto.setId(task.getId());
dto.setType(task.getType());
dto.setStatus(task.getStatus());
dto.setFields(task.getFields());
dto.setCreatedAt(task.getAuditInfo().getCreatedAt());
dto.setUpdatedAt(task.getAuditInfo().getUpdatedAt());
return dto;
}
}

View File

@ -0,0 +1,27 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import ru.mskobaro.tdms.business.entity.TeacherData;
@Getter
@Setter
@NoArgsConstructor
public class TeacherDataDTO {
private Long id;
private ParticipantDTO participant;
private String degree;
private String createdAt;
private String updatedAt;
public static TeacherDataDTO from(TeacherData teacherData) {
TeacherDataDTO dto = new TeacherDataDTO();
dto.setId(teacherData.getId());
dto.setParticipant(ParticipantDTO.fromEntity(teacherData.getParticipant()));
dto.setDegree(teacherData.getDegree());
dto.setCreatedAt(teacherData.getAuditInfo().getCreatedAt().toString());
dto.setUpdatedAt(teacherData.getAuditInfo().getUpdatedAt().toString());
return dto;
}
}

View File

@ -0,0 +1,41 @@
package ru.mskobaro.tdms.presentation.controller.payload;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import lombok.Builder;
import ru.mskobaro.tdms.business.entity.User;
import java.time.LocalDateTime;
import java.time.ZonedDateTime;
@Builder
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
public record UserDTO(
Long id,
String login,
ParticipantDTO participant,
LocalDateTime createdAt,
LocalDateTime updatedAt
) {
public static UserDTO fromEntity(User user) {
return UserDTO.builder()
.id(user.getId())
.login(user.getLogin())
.participant(ParticipantDTO.fromEntity(user.getParticipant()))
.createdAt(user.getAuditInfo().getCreatedAt())
.updatedAt(user.getAuditInfo().getUpdatedAt())
.build();
}
public static UserDTO fromEntity(User user, ParticipantDTO participant) {
return UserDTO.builder()
.id(user.getId())
.login(user.getLogin())
.participant(participant)
.createdAt(user.getAuditInfo().getCreatedAt())
.updatedAt(user.getAuditInfo().getUpdatedAt())
.build();
}
}

View File

@ -9,16 +9,16 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import ru.mskobaro.tdms.domain.exception.AccessDeniedException;
import ru.mskobaro.tdms.domain.exception.BusinessException;
import ru.mskobaro.tdms.presentation.payload.ErrorDTO;
import ru.mskobaro.tdms.business.exception.AccessDeniedException;
import ru.mskobaro.tdms.business.exception.BusinessException;
import ru.mskobaro.tdms.presentation.controller.payload.ErrorDTO;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.springframework.http.HttpStatus.*;
import static org.springframework.http.HttpStatus.NOT_FOUND;
import static ru.mskobaro.tdms.presentation.payload.ErrorDTO.ErrorCode.*;
import static ru.mskobaro.tdms.presentation.controller.payload.ErrorDTO.ErrorCode.*;
@RestControllerAdvice
@Slf4j

View File

@ -1,14 +0,0 @@
package ru.mskobaro.tdms.presentation.payload;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import lombok.Getter;
@Getter
public class GroupCreateDTO {
@NotEmpty(message = "Имя группы не может быть пустым")
@Size(min = 3, max = 50, message = "Имя группы должно быть от 3 до 50 символов")
@Pattern(regexp = "^[а-яА-ЯёЁ0-9_-]*$", message = "Имя группы должно содержать только русские буквы, дефис, нижнее подчеркивание и цифры")
private String name;
}

View File

@ -1,15 +0,0 @@
package ru.mskobaro.tdms.presentation.payload;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
public class GroupDTO {
private Long id;
private String name;
private String curatorName;
private Boolean iAmCurator;
}

View File

@ -1,17 +0,0 @@
package ru.mskobaro.tdms.presentation.payload;
import jakarta.validation.constraints.*;
import lombok.Getter;
import lombok.ToString;
@Getter
@ToString
public class GroupEditDTO {
@NotNull(message = "Идентификатор группы не может быть пустым")
@Min(value = 1, message = "Идентификатор группы должен быть больше 0")
private Long id;
@NotEmpty(message = "Имя группы не может быть пустым")
@Size(min = 3, max = 50, message = "Имя группы должно быть от 3 до 50 символов")
@Pattern(regexp = "^[а-яА-ЯёЁ0-9_-]*$", message = "Имя группы должно содержать только русские буквы, дефис, нижнее подчеркивание и цифры")
private String name;
}

View File

@ -1,54 +0,0 @@
package ru.mskobaro.tdms.presentation.payload;
import jakarta.validation.constraints.*;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.hibernate.validator.constraints.Length;
import ru.mskobaro.tdms.domain.service.RoleService.Authority;
import java.util.List;
@Getter
@ToString
public class RegistrationDTO {
@NotEmpty(message = "Логин не может быть пустым")
@Pattern(regexp = "^[a-zA-Z0-9_]+$", message = "Логин должен содержать только латинские буквы, цифры и знак подчеркивания")
@Size(min = 5, message = "Логин должен содержать минимум 5 символов")
@Size(max = 32, message = "Логин должен содержать максимум 32 символов")
private String login;
@NotEmpty(message = "Пароль не может быть пустым")
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{8,}$", message = "Пароль должен содержать хотя бы одну цифру, одну заглавную и одну строчную букву, минимум 8 символов")
private String password;
@NotEmpty(message = "Имя не может быть пустым")
@Length(min = 3, message = "Имя должно содержать минимум 3 символа")
@Pattern(regexp = "^[a-zA-Zа-яА-ЯёЁ\\s]+$", message = "Имя должно содержать только буквы английского или русского алфавита и пробелы")
private String fullName;
@NotNull(message = "Почта не может быть пустой")
@Email(message = "Почта должна быть валидным адресом электронной почты")
private String email;
@NotNull(message = "Номер телефона не может быть пустым")
@Pattern(regexp = "^\\+[1-9]\\d{6,14}$", message = "Номер телефона должен начинаться с '+' и содержать от 7 до 15 цифр")
private String numberPhone;
@NotNull(message = "Тип аккаунта не может быть пустым")
private Authority accountType;
private StudentRegistrationDTO studentData;
private TeacherRegistrationDTO teacherData;
@Getter
@ToString
public static class StudentRegistrationDTO {
@NotNull(message = "Группа не может быть пустой")
private Long groupId;
}
@Getter
@Setter
@ToString
public static class TeacherRegistrationDTO {
private List<Long> curatingGroups;
private List<Long> advisingStudents;
}
}

View File

@ -1,19 +0,0 @@
package ru.mskobaro.tdms.presentation.payload;
import ru.mskobaro.tdms.domain.entity.Role;
import ru.mskobaro.tdms.domain.entity.User;
import java.util.List;
public record RoleDTO(String name, String authority) {
public static RoleDTO from(Role role) {
return new RoleDTO(role.getName(), role.getAuthority());
}
public static List<RoleDTO> from(User user) {
return user.getRoles().stream().map(RoleDTO::from).toList();
}
}

View File

@ -1,47 +0,0 @@
package ru.mskobaro.tdms.presentation.payload;
import lombok.Data;
import ru.mskobaro.tdms.domain.entity.Student;
@Data
public class StudentDTO {
private Long id;
private Boolean form;
private Integer protectionOrder;
private String magistracy;
private Boolean digitalFormatPresent;
private Integer markComment;
private Integer markPractice;
private String predefenceComment;
private String normalControl;
private Integer antiPlagiarism;
private String note;
private Boolean recordBookReturned;
private String work;
private UserDTO user;
private String diplomaTopic;
private UserDTO mentorUser;
private GroupDTO group;
public static StudentDTO from(Student student) {
StudentDTO studentDTO = new StudentDTO();
studentDTO.setId(student.getId());
studentDTO.setForm(student.getForm());
studentDTO.setProtectionOrder(student.getProtectionOrder());
studentDTO.setMagistracy(student.getMagistracy());
studentDTO.setDigitalFormatPresent(student.getDigitalFormatPresent());
studentDTO.setMarkComment(student.getMarkComment());
studentDTO.setMarkPractice(student.getMarkPractice());
studentDTO.setPredefenceComment(student.getPredefenceComment());
studentDTO.setNormalControl(student.getNormalControl());
studentDTO.setAntiPlagiarism(student.getAntiPlagiarism());
studentDTO.setNote(student.getNote());
studentDTO.setRecordBookReturned(student.getRecordBookReturned());
studentDTO.setWork(student.getWork());
studentDTO.setUser(UserDTO.from(student.getUser()));
studentDTO.setDiplomaTopic(student.getDiplomaTopic().getName());
return studentDTO;
}
}

View File

@ -1,45 +0,0 @@
package ru.mskobaro.tdms.presentation.payload;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Builder;
import org.springframework.security.core.GrantedAuthority;
import ru.mskobaro.tdms.domain.entity.User;
import java.time.ZonedDateTime;
import java.util.List;
@Builder
@JsonInclude(JsonInclude.Include.NON_ABSENT)
public record UserDTO(
Long id,
boolean authenticated,
String login,
String fullName,
String email,
String phone,
ZonedDateTime createdAt,
ZonedDateTime updatedAt,
List<String> authorities
) {
public static UserDTO unauthenticated() {
return UserDTO.builder()
.authenticated(false)
.build();
}
public static UserDTO from(User user) {
return UserDTO.builder()
.id(user.getId())
.authenticated(true)
.login(user.getLogin())
.fullName(user.getFullName())
.email(user.getEmail())
.phone(user.getNumberPhone())
.createdAt(user.getCreatedAt())
.updatedAt(user.getUpdatedAt())
.authorities(user.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList())
.build();
}
}

View File

@ -11,31 +11,28 @@ import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextHolderFilter;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import ru.mskobaro.tdms.system.web.DevAuthenticationRequestFilter;
import ru.mskobaro.tdms.system.web.LoggingRequestFilter;
import java.time.Duration;
import java.util.List;
import static ru.mskobaro.tdms.domain.service.RoleService.Authority.*;
import static ru.mskobaro.tdms.business.service.RoleService.Authority.ADMIN;
import static ru.mskobaro.tdms.business.service.RoleService.Authority.SECRETARY;
@Slf4j
@ -45,15 +42,15 @@ public class SecurityConfig {
private Environment environment;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, AuthenticationManager authenticationManager, CorsConfigurationSource cors) throws Exception {
if (environment.matchesProfiles("dev")) {
httpSecurity.addFilterAfter(new DevAuthenticationRequestFilter(), SecurityContextHolderFilter.class);
}
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity,
AuthenticationManager authenticationManager,
CorsConfigurationSource cors
) throws Exception {
return httpSecurity
.addFilterAfter(new LoggingRequestFilter(), AnonymousAuthenticationFilter.class)
.addFilterAfter(new LoggingRequestFilter(), SecurityContextHolderFilter.class)
.authorizeHttpRequests(this::configureHttpAuthorization)
.csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
// .csrf(csrf -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()))
.csrf(AbstractHttpConfigurer::disable)
.cors(a -> a.configurationSource(cors))
.authenticationManager(authenticationManager)
.sessionManagement(cfg -> {
@ -65,7 +62,11 @@ public class SecurityConfig {
@Bean
@Primary
public CorsConfigurationSource corsConfigurationProd(@Value("${application.domain}") String domain, @Value("${application.port}") String port, @Value("${application.protocol}") String protocol) {
public CorsConfigurationSource corsConfiguration(
@Value("${application.domain}") String domain,
@Value("${application.port}") String port,
@Value("${application.protocol}") String protocol
) {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowedMethods(List.of(HttpMethod.GET.name(), HttpMethod.POST.name(), HttpMethod.OPTIONS.name()));
corsConfiguration.setAllowedHeaders(List.of("Authorization", "Content-Type"));
@ -85,7 +86,14 @@ public class SecurityConfig {
@Bean
public AuthenticationManager authenticationManager(UserDetailsService userDetailsService) {
return new ProviderManager(authenticationProvider(userDetailsService));
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(passwordEncoder());
provider.setUserDetailsService(userDetailsService);
return new ProviderManager(provider);
}
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
@ -93,32 +101,42 @@ public class SecurityConfig {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
private void configureHttpAuthorization(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry httpAuthorization) {
/* SysInfoController */
private void configureHttpAuthorization(
AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry httpAuthorization
) {
httpAuthorization.requestMatchers("/api/v1/sysinfo/**").permitAll();
/* UserController */
httpAuthorization.requestMatchers("/api/v1/user/logout").authenticated();
httpAuthorization.requestMatchers("/api/v1/user/login").anonymous();
httpAuthorization.requestMatchers("/api/v1/user/login").permitAll();
httpAuthorization.requestMatchers("/api/v1/user/current").permitAll();
httpAuthorization.requestMatchers("/api/v1/user/get-all").hasAuthority(ADMIN.getAuthority());
httpAuthorization.requestMatchers("/api/v1/user/register").hasAuthority(ADMIN.getAuthority());
httpAuthorization.requestMatchers("/api/v1/user/validate-registration").hasAuthority(ADMIN.getAuthority());
/* StudentController */
httpAuthorization.requestMatchers("/api/v1/student/current").permitAll();
httpAuthorization.requestMatchers("api/v1/student/by-user-id").hasAnyAuthority(
SECRETARY.getAuthority(), ADMIN.getAuthority(), STUDENT.getAuthority(), TEACHER.getAuthority());
/* GroupController */
httpAuthorization.requestMatchers("/api/v1/participant/get-all-participants").permitAll();
httpAuthorization.requestMatchers("/api/v1/participant/get-possible-curators").permitAll();
// Сложная логика авторизации, так что проверяем явно в ParticipantService
httpAuthorization.requestMatchers("/api/v1/participant/save").authenticated();
httpAuthorization.requestMatchers("/api/v1/participant/delete").hasAuthority(ADMIN.getAuthority());
httpAuthorization.requestMatchers("/api/v1/group/get-all-groups").permitAll();
httpAuthorization.requestMatchers("/api/v1/group/create-group").hasAuthority(ADMIN.getAuthority());
/* deny all other api requests */
httpAuthorization.requestMatchers("/api/v1/group/save").hasAnyAuthority(ADMIN.getAuthority(), SECRETARY.getAuthority());
httpAuthorization.requestMatchers("/api/v1/group/delete").hasAnyAuthority(ADMIN.getAuthority());
httpAuthorization.requestMatchers("/api/v1/student/by-partic-id").permitAll();
httpAuthorization.requestMatchers("/api/v1/student/all-without-group").permitAll();
httpAuthorization.requestMatchers("/api/v1/teacher-data/get-all").permitAll();
httpAuthorization.requestMatchers("/api/v1/teacher-data/by-partic-id").permitAll();
httpAuthorization.requestMatchers("/api/v1/prep-direction/get-all").permitAll();
httpAuthorization.requestMatchers("/api/v1/prep-direction/save").hasAnyAuthority(ADMIN.getAuthority(), SECRETARY.getAuthority());
httpAuthorization.requestMatchers("/api/v1/defence/all").permitAll();
httpAuthorization.requestMatchers("/api/v1/diploma-topic/all").permitAll();
httpAuthorization.requestMatchers("/api/v1/diploma-topic/all-for-student").permitAll();
httpAuthorization.requestMatchers("/api/v1/diploma-topic/save").hasAnyAuthority(ADMIN.getAuthority(), SECRETARY.getAuthority());
httpAuthorization.requestMatchers("/api/**").denyAll();
/* since api already blocked, all other requests are static resources */
httpAuthorization.requestMatchers("/**").permitAll();
}
private AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider(passwordEncoder());
provider.setUserDetailsService(userDetailsService);
return provider;
}
}

View File

@ -0,0 +1,28 @@
package ru.mskobaro.tdms.system.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.PathResourceResolver;
import java.io.IOException;
import org.springframework.core.io.Resource;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/")
.resourceChain(true)
.addResolver(new PathResourceResolver() {
@Override
protected Resource getResource(String resourcePath, Resource location) throws IOException {
Resource requestedResource = location.createRelative(resourcePath);
return requestedResource.exists() && requestedResource.isReadable()
? requestedResource
: location.createRelative("index.html");
}
});
}
}

View File

@ -0,0 +1,20 @@
package ru.mskobaro.tdms.system.web;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerExceptionResolver;
@Component
public class AccessDeniedExceptionHandler implements AccessDeniedHandler {
@Autowired
private HandlerExceptionResolver handlerExceptionResolver;
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) {
handlerExceptionResolver.resolveException(request, response, null, new ru.mskobaro.tdms.business.exception.AccessDeniedException());
}
}

View File

@ -1,46 +0,0 @@
package ru.mskobaro.tdms.system.web;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.TransientSecurityContext;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.web.filter.OncePerRequestFilter;
import ru.mskobaro.tdms.domain.entity.Role;
import ru.mskobaro.tdms.domain.entity.User;
import java.io.IOException;
import java.util.List;
import static ru.mskobaro.tdms.domain.service.RoleService.Authority.ADMIN;
@Slf4j
public class DevAuthenticationRequestFilter extends OncePerRequestFilter {
public DevAuthenticationRequestFilter() {
log.warn("!!!ANY REQUEST WILL BE AUTHENTICATED AS DEV_ADMIN, IF YOU SEE THIS IN PRODUCTION, CHECK YOUR CONFIGURATION!!!");
log.warn("!!!ANY REQUEST WILL BE AUTHENTICATED AS DEV_ADMIN, IF YOU SEE THIS IN PRODUCTION, CHECK YOUR CONFIGURATION!!!");
log.warn("!!!ANY REQUEST WILL BE AUTHENTICATED AS DEV_ADMIN, IF YOU SEE THIS IN PRODUCTION, CHECK YOUR CONFIGURATION!!!");
log.warn("!!!ANY REQUEST WILL BE AUTHENTICATED AS DEV_ADMIN, IF YOU SEE THIS IN PRODUCTION, CHECK YOUR CONFIGURATION!!!");
log.warn("!!!ANY REQUEST WILL BE AUTHENTICATED AS DEV_ADMIN, IF YOU SEE THIS IN PRODUCTION, CHECK YOUR CONFIGURATION!!!");
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
Role role = new Role(-1L, "dev_admin_role", ADMIN.getAuthority());
User admin = new User();
admin.setRoles(List.of(role));
admin.setId(-1L);
admin.setLogin("dev_admin");
admin.setEmail("dev_admin@main.mail");
admin.setFullName("dev_admin");
admin.setNumberPhone("+79999999999");
admin.setPassword("{bcrypt}$2a$06$BHHQMjwQB2KI9sDdC9rRHOuYkTskjDt9WAyrscWP/Dcn7my3Jr77K");
var auth = new PreAuthenticatedAuthenticationToken(admin, null, admin.getAuthorities());
var context = new TransientSecurityContext(auth);
SecurityContextHolder.setContext(context);
filterChain.doFilter(request, response);
}
}

View File

@ -6,24 +6,30 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import java.io.IOException;
import java.util.UUID;
@Slf4j
public class LoggingRequestFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
UUID uuid = UUID.randomUUID();
long startTime = System.currentTimeMillis();
String username = SecurityContextHolder.getContext().getAuthentication() != null ?
SecurityContextHolder.getContext().getAuthentication().getName() : "anonymousUser";
HttpSession session = request.getSession(false);
log.info("Request received: [{}] {} user: {}, session: {}, remote ip: {}",
request.getMethod(), request.getRequestURI(), request.getRemoteUser(),
session == null ? "no" : session.getId(), request.getRemoteAddr());
log.info("Request received: [{}] {} user: {}, session: {}, remote ip: {} [{}]",
request.getMethod(), request.getRequestURI(), username,
session == null ? "no" : session.getId(), request.getRemoteAddr(), uuid);
try {
filterChain.doFilter(request, response);
} finally {
long duration = System.currentTimeMillis() - startTime;
log.info("Response with {} status duration: {} ms", response.getStatus(), duration);
log.info("Response with {} status duration: {} ms [{}]", response.getStatus(), duration, uuid);
}
}
}

View File

@ -30,11 +30,6 @@ spring:
schemas: ${db.schema}
banner:
location: banner.txt
web:
resources:
static-locations: classpath:/static/
# autoconfigure:
# exclude: org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
server:
port: ${application.port}
address: ${application.domain}

View File

@ -0,0 +1,280 @@
CREATE TABLE defense
(
id BIGSERIAL PRIMARY KEY,
defense_date DATE,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
CREATE TABLE defense_best_student_works
(
defense_id BIGINT,
student_data_id BIGINT
);
CREATE TABLE defense_commission
(
defense_id BIGINT,
commission_member_data_id BIGINT
);
CREATE TABLE diploma_topic
(
id BIGSERIAL PRIMARY KEY,
name TEXT,
teacher_id BIGINT,
direction_of_preparation_id BIGINT,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
CREATE TABLE direction_of_preparation
(
id BIGSERIAL PRIMARY KEY,
name TEXT,
code TEXT,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
CREATE TABLE "group"
(
id BIGSERIAL PRIMARY KEY,
name TEXT,
defense_id BIGINT,
direction_of_preparation_id BIGINT,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
CREATE TABLE participant
(
id BIGSERIAL PRIMARY KEY,
first_name TEXT,
last_name TEXT,
middle_name TEXT,
email TEXT,
number_phone TEXT,
deleted BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
CREATE TABLE participant_role
(
partic_id BIGINT NOT NULL,
role_id BIGINT NOT NULL
);
CREATE TABLE role
(
id BIGINT PRIMARY KEY,
name TEXT,
authority TEXT
);
/* todo */
CREATE TABLE student_data
(
id BIGSERIAL PRIMARY KEY,
partic_id BIGINT,
study_form_id BIGINT,
curator_id BIGINT,
protection_order INTEGER,
protection_day INTEGER,
mark_comment INTEGER,
mark_practice INTEGER,
predefnese_comment TEXT,
normal_control BOOLEAN,
anti_plagiarism INTEGER,
record_book_returned BOOLEAN,
work TEXT,
diploma_topic_id BIGINT,
adviser_teacher_partic_id BIGINT,
group_id BIGINT,
marks_3 BIGINT,
marks_4 BIGINT,
marks_5 BIGINT,
commission_mark BIGINT,
estimated BOOLEAN,
diploma_with_honors BOOLEAN,
magistracy_recommendation BOOLEAN,
magistracy_wanted BOOLEAN,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
/* not implemented */
create table questionnaire
(
id BIGSERIAL PRIMARY KEY,
student_data_id BIGINT,
data TEXT,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
create table study_form
(
id BIGSERIAL PRIMARY KEY,
name TEXT,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
/* todo */
create table stud_comment
(
id BIGSERIAL PRIMARY KEY,
comment TEXT,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
/* todo */
create table student_data_comment
(
id BIGSERIAL PRIMARY KEY,
student_data_id BIGINT,
stud_comment_id BIGINT,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
CREATE TABLE teacher_data
(
id BIGSERIAL PRIMARY KEY,
participant_id BIGINT,
degree TEXT NOT NULL,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
CREATE TABLE commission_member_data
(
id BIGSERIAL PRIMARY KEY,
partic_id BIGINT,
work_place TEXT,
work_position TEXT,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
CREATE TABLE "user"
(
id BIGSERIAL PRIMARY KEY,
login TEXT,
password TEXT,
partic_id BIGINT,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
CREATE TABLE task
(
id BIGSERIAL PRIMARY KEY,
type TEXT,
status TEXT,
fields jsonb,
created_at TIMESTAMP WITHOUT TIME ZONE,
updated_at TIMESTAMP WITHOUT TIME ZONE
);
ALTER TABLE defense_commission
ADD CONSTRAINT FK_DEFCOM_ON_DEFNESE FOREIGN KEY (defense_id) REFERENCES defense (id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE defense_commission
ADD CONSTRAINT FK_DEFCOM_ON_COMMEMD FOREIGN KEY (commission_member_data_id) REFERENCES commission_member_data (id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "group"
ADD CONSTRAINT FK_GROUP_ON_defnese FOREIGN KEY (defense_id) REFERENCES defense (id) ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE "group"
ADD CONSTRAINT FK_GROUP_ON_DOP FOREIGN KEY (direction_of_preparation_id) REFERENCES direction_of_preparation (id) ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE participant_role
ADD CONSTRAINT FK_PARROL_ON_PARTICIPANT FOREIGN KEY (partic_id) REFERENCES participant (id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE participant_role
ADD CONSTRAINT FK_PARROL_ON_ROLE FOREIGN KEY (role_id) REFERENCES role (id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE student_data
ADD CONSTRAINT UC_STUDENT_DATA_PARTIC UNIQUE (partic_id);
ALTER TABLE student_data
ADD CONSTRAINT FK_STUDENT_DATA_ON_ADVISER_TEACHER_PARTIC FOREIGN KEY (adviser_teacher_partic_id) REFERENCES participant (id) ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE student_data
ADD CONSTRAINT FK_STUDENT_DATA_ON_DIPLOMA_TOPIC FOREIGN KEY (diploma_topic_id) REFERENCES diploma_topic (id) ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE student_data
ADD CONSTRAINT FK_STUDENT_DATA_ON_GROUP FOREIGN KEY (group_id) REFERENCES "group" (id) ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE student_data
ADD CONSTRAINT FK_STUDENT_DATA_ON_PARTIC FOREIGN KEY (partic_id) REFERENCES participant (id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE participant
ADD CONSTRAINT UC_PARTICIPANT_EMAIL UNIQUE (email);
ALTER TABLE participant
ADD CONSTRAINT UC_PARTICIPANT_NUMBER_PHONE UNIQUE (number_phone);
ALTER TABLE "user"
ADD CONSTRAINT UC_USER_LOGIN UNIQUE (login);
ALTER TABLE "user"
ADD CONSTRAINT UC_USER_PARTIC UNIQUE (partic_id);
ALTER TABLE "user"
ADD CONSTRAINT FK_USER_ON_PARTIC FOREIGN KEY (partic_id) REFERENCES participant (id);
ALTER TABLE teacher_data
ADD CONSTRAINT UC_TEACHER_DATA_PARTIC UNIQUE (participant_id);
ALTER TABLE teacher_data
ADD CONSTRAINT FK_TEACHER_DATA_ON_PARTIC FOREIGN KEY (participant_id) REFERENCES participant (id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE commission_member_data
ADD CONSTRAINT UC_COMMISION_MEMBER_DATA_PARTIC UNIQUE (partic_id);
ALTER TABLE commission_member_data
ADD CONSTRAINT FK_COMMISION_MEMBER_DATA_ON_PARTIC FOREIGN KEY (partic_id) REFERENCES participant (id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE direction_of_preparation
ADD CONSTRAINT UC_DIRECTION_OF_PREPARATION_NAME UNIQUE (name);
ALTER TABLE direction_of_preparation
ADD CONSTRAINT UC_DIRECTION_OF_PREPARATION_CODE UNIQUE (code);
ALTER TABLE diploma_topic
ADD CONSTRAINT UC_DIPLOMA_TOPIC_NAME FOREIGN KEY (direction_of_preparation_id) REFERENCES direction_of_preparation (id) ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE defense_best_student_works
ADD CONSTRAINT FK_DEFENSE_BEST_STUDENT_WORKS_ON_DEFENSE FOREIGN KEY (defense_id) REFERENCES defense (id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE defense_best_student_works
ADD CONSTRAINT FK_DEFENSE_BEST_STUDENT_WORKS_ON_STUDENT_DATA FOREIGN KEY (student_data_id) REFERENCES student_data (id) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE stud_comment
ADD CONSTRAINT UC_STUD_COMMENT_COMMENT UNIQUE (comment);
ALTER TABLE student_data
ADD CONSTRAINT UC_STUDENT_DATA_DIPLOMA_TOPIC FOREIGN KEY (diploma_topic_id) REFERENCES diploma_topic (id) ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE student_data
ADD CONSTRAINT UC_STUDENT_DATA_STUDY_FORM FOREIGN KEY (study_form_id) REFERENCES study_form (id) ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE student_data
ADD CONSTRAINT UC_STUDENT_DATA_CURATOR FOREIGN KEY (curator_id) REFERENCES teacher_data (id) ON DELETE SET NULL ON UPDATE CASCADE;
ALTER TABLE study_form
ADD CONSTRAINT UC_STUDY_FORM_NAME UNIQUE (name);
alter table questionnaire
add constraint UC_QUESTIONNAIRE_STUDENT_DATA_ID unique (student_data_id);
alter table questionnaire
add constraint FK_QUESTIONNAIRE_STUDENT_DATA foreign key (student_data_id) references student_data (id) on delete cascade on update cascade;

View File

@ -1,13 +0,0 @@
create table role
(
id bigint primary key,
name text not null unique,
authority text not null unique
);
-- COMMENTS
comment on table role is 'Таблица ролей пользователей';
comment on column role.name is 'Человекочитаемое имя роли';
comment on column role.authority is 'Имя роли в системе';

View File

@ -1,22 +0,0 @@
create table "user"
(
id bigserial primary key,
login text not null unique,
password text not null,
full_name text not null,
email text not null unique,
number_phone text not null unique,
created_at timestamptz not null,
updated_at timestamptz
);
-- COMMENTS
comment on table "user" is 'Таблица пользователей';
comment on column "user".login is 'Логин пользователя';
comment on column "user".password is 'Пароль пользователя';
comment on column "user".full_name is 'Полное имя пользователя в формате Фамилия Имя Отчество';
comment on column "user".email is 'Почта пользователя';
comment on column "user".number_phone is 'Номер телефона пользователя';

View File

@ -1,24 +0,0 @@
create table user_role
(
id bigserial primary key,
user_id bigint not null,
role_id bigint not null
);
-- FOREIGN KEY
alter table user_role
add constraint fk_user_role_user_id
foreign key (user_id) references "user" (id)
on delete cascade on update cascade;
alter table user_role
add constraint fk_user_role_role_id
foreign key (role_id) references role (id)
on delete restrict on update cascade;
-- COMMENTS
comment on table user_role is 'Таблица связи пользователей и ролей';
comment on column user_role.user_id is 'Идентификатор пользователя';
comment on column user_role.role_id is 'Идентификатор роли';

View File

@ -1,11 +0,0 @@
create table diploma_topic
(
id bigserial primary key,
name text not null
);
-- COMMENTS
comment on table diploma_topic is 'Таблица тем дипломных работ';
comment on column diploma_topic.name is 'Название темы дипломной работы';

View File

@ -1,19 +0,0 @@
create table teacher
(
id bigserial primary key,
user_id bigint not null,
created_at timestamptz not null,
updated_at timestamptz
);
-- FOREIGN KEY
alter table teacher
add constraint fk_teacher_user_id
foreign key (user_id) references "user" (id)
on delete cascade on update cascade;
-- COMMENTS
comment on table teacher is 'Таблица преподавателей';
comment on column teacher.user_id is 'Идентификатор пользователя';

View File

@ -1,22 +0,0 @@
create table "group"
(
id bigserial primary key,
name text not null unique,
curator_teacher_id bigint,
created_at timestamptz not null,
updated_at timestamptz
);
-- FOREIGN KEY
alter table "group"
add constraint fk_group_curator_teacher_id
foreign key (curator_teacher_id) references teacher (id)
on delete set null on update cascade;
-- COMMENTS
comment on table "group" is 'Таблица групп студентов';
comment on column "group".name is 'Название группы';
comment on column "group".curator_teacher_id is 'Идентификатор куратора группы';

View File

@ -1,65 +0,0 @@
create table student
(
id bigserial primary key,
user_id bigint not null,
diploma_topic_id bigint,
adviser_teacher_id bigint,
group_id bigint,
form boolean,
protection_day int,
protection_order int,
magistracy text,
digital_format_present boolean,
mark_comment int,
mark_practice int,
predefence_comment text,
normal_control text,
anti_plagiarism int,
note text,
record_book_returned boolean,
work text,
created_at timestamptz not null,
updated_at timestamptz
);
-- FOREIGN KEY
alter table student
add constraint fk_student_user_id
foreign key (user_id) references "user" (id)
on delete cascade on update cascade;
alter table student
add constraint fk_student_diploma_topic_id
foreign key (diploma_topic_id) references diploma_topic (id)
on delete set null on update cascade;
alter table student
add constraint fk_student_adviser_teacher_id
foreign key (adviser_teacher_id) references teacher (id)
on delete set null on update cascade;
alter table student
add constraint fk_student_group_id
foreign key (group_id) references "group" (id)
on delete set null on update cascade;
-- COMMENTS
comment on table student is 'Таблица студентов';
comment on column student.user_id is 'Идентификатор пользователя';
comment on column student.diploma_topic_id is 'Идентификатор темы дипломной работы';
comment on column student.adviser_teacher_id is 'Идентификатор научного руководителя';
comment on column student.group_id is 'Идентификатор группы';
comment on column student.form is 'Форма обучения';
comment on column student.protection_day is 'День защиты';
comment on column student.protection_order is 'Порядок защиты';
comment on column student.magistracy is 'Магистратура';
comment on column student.digital_format_present is 'Предоставлен в электронном виде';
comment on column student.mark_comment is 'Комментарий к оценке';
comment on column student.mark_practice is 'Оценка практики';
comment on column student.predefence_comment is 'Комментарий к защите';
comment on column student.normal_control is 'Обычный контроль';
comment on column student.anti_plagiarism is 'Антиплагиат';
comment on column student.note is 'Примечание';
comment on column student.record_book_returned is 'Ведомость возвращена';
comment on column student.work is 'Работа';

Some files were not shown because too many files have changed in this diff Show More