NCP-3 Add logic update user fields,change password

This commit is contained in:
vladp
2025-11-27 12:48:08 +07:00
parent ffb1d811a8
commit 5c8fdce323
10 changed files with 180 additions and 11 deletions
@@ -0,0 +1,41 @@
package ru.soune.no_copy.configuration;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Configuration
public class JacksonConfig {
@Bean
@Primary
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(
DateTimeFormatter.ofPattern("dd-MM-yyyy")));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(
DateTimeFormatter.ofPattern("dd-MM-yyyy")));
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(
DateTimeFormatter.ofPattern("dd-MM-yyyy HH:mm:ss")));
mapper.registerModule(javaTimeModule);
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
}
}
@@ -5,6 +5,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import ru.soune.no_copy.dto.ChangePasswordRequest; import ru.soune.no_copy.dto.ChangePasswordRequest;
import ru.soune.no_copy.dto.UserDTO; import ru.soune.no_copy.dto.UserDTO;
import ru.soune.no_copy.dto.UserRequest;
import ru.soune.no_copy.entity.AuthToken; import ru.soune.no_copy.entity.AuthToken;
import ru.soune.no_copy.entity.User; import ru.soune.no_copy.entity.User;
import ru.soune.no_copy.exception.InvalidUserEmail; import ru.soune.no_copy.exception.InvalidUserEmail;
@@ -35,7 +36,7 @@ public class UserController {
List<UserDTO> allUsers = userRepository.findAll().stream() List<UserDTO> allUsers = userRepository.findAll().stream()
.map(u -> new UserDTO(u.getFullName(), u.getCompany(), u.getEmail(), u.getIsActive(), .map(u -> new UserDTO(u.getFullName(), u.getCompany(), u.getEmail(), u.getIsActive(),
u.getPhone(), u.getGenderType(), u.getPhone(), u.getGenderType(),
u.getBirthday(), u.getCreatedAt())) u.getBirthday(), u.getCreatedAt(), u.getSubscriptionType()))
.toList(); .toList();
return ResponseEntity.ok(allUsers); return ResponseEntity.ok(allUsers);
@@ -61,4 +62,18 @@ public class UserController {
return ResponseEntity.ok(userMapper.toDTO(updateUser)); return ResponseEntity.ok(userMapper.toDTO(updateUser));
} }
@PatchMapping("/user-update")
public ResponseEntity<UserDTO> updateUser(@RequestBody UserRequest userRequest,
@RequestHeader("Authorization") String tokenHeader) {
String token = tokenHeader.replace("Bearer ", "");
Optional<AuthToken> tokenOptional = authTokenRepository.findByToken(token);
AuthToken authToken = tokenOptional.orElseThrow(() -> new NotFoundAuthToken("Token: " + token + "not found"));
User user = authToken.getUser();
return ResponseEntity.ok(userService.updateUser(userRequest, user));
}
} }
@@ -2,4 +2,9 @@ package ru.soune.no_copy.dto;
import java.time.LocalDateTime; import java.time.LocalDateTime;
public record AuthResponse (boolean success, String message, String token, LocalDateTime expiresAt) {} public record AuthResponse (
boolean success,
String message,
String token,
LocalDateTime expiresAt
) {}
@@ -3,6 +3,7 @@ package ru.soune.no_copy.dto;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Past;
import jakarta.validation.constraints.Size; import jakarta.validation.constraints.Size;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import ru.soune.no_copy.entity.GenderType; import ru.soune.no_copy.entity.GenderType;
@@ -27,6 +28,7 @@ public record RegisterRequest(
@DateTimeFormat(pattern = "dd-MM-yyyy") @DateTimeFormat(pattern = "dd-MM-yyyy")
@JsonFormat(pattern = "dd-MM-yyyy") @JsonFormat(pattern = "dd-MM-yyyy")
@Past
LocalDate birthday, LocalDate birthday,
@NotBlank(message = "error.not.blank") @NotBlank(message = "error.not.blank")
@@ -3,7 +3,13 @@ package ru.soune.no_copy.dto;
import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
public record UserContentRequest(@NotBlank @Email String userEmail, @NotBlank String fileName, public record UserContentRequest(
@NotBlank String originalFilename, @NotBlank String fileType, @NotBlank @Email String userEmail,
@NotBlank String fileExtension, @NotBlank Long fileSize, @NotBlank String fileName,
@NotBlank String filePath, Integer protectionLevel) {} @NotBlank String originalFilename,
@NotBlank String fileType,
@NotBlank String fileExtension,
@NotBlank Long fileSize,
@NotBlank String filePath,
Integer protectionLevel
) {}
@@ -3,8 +3,14 @@ package ru.soune.no_copy.dto;
import jakarta.validation.constraints.Email; import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
public record UserContentUpdateRequest(@NotBlank @Email String userEmail, @NotBlank String fileName, public record UserContentUpdateRequest(
@NotBlank String originalFilename, @NotBlank String fileType, @NotBlank @Email String userEmail,
@NotBlank String fileExtension, @NotBlank Long fileSize, @NotBlank String fileName,
@NotBlank String filePath, Integer protectionLevel, Long id) { @NotBlank String originalFilename,
} @NotBlank String fileType,
@NotBlank String fileExtension,
@NotBlank Long fileSize,
@NotBlank String filePath,
Integer protectionLevel,
Long id
) {}
@@ -1,9 +1,11 @@
package ru.soune.no_copy.dto; package ru.soune.no_copy.dto;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import ru.soune.no_copy.entity.GenderType; import ru.soune.no_copy.entity.GenderType;
import ru.soune.no_copy.entity.SubscriptionType;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@@ -11,6 +13,7 @@ import java.time.LocalDateTime;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@NoArgsConstructor @NoArgsConstructor
@Builder
public class UserDTO { public class UserDTO {
private String fullName; private String fullName;
private String company; private String company;
@@ -20,4 +23,5 @@ public class UserDTO {
private GenderType genderType; private GenderType genderType;
private LocalDate birthday; private LocalDate birthday;
private LocalDateTime createdAt; private LocalDateTime createdAt;
private SubscriptionType subscriptionType;
} }
@@ -0,0 +1,25 @@
package ru.soune.no_copy.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Past;
import jakarta.validation.constraints.Size;
import lombok.Data;
import ru.soune.no_copy.entity.GenderType;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
public class UserRequest {
@Size(min = 2)
private String fullName;
private String company;
@Email
private String email;
private String phone;
private GenderType genderType;
@Past
@JsonFormat(pattern = "dd-MM-yyyy")
private LocalDate birthday;
}
@@ -0,0 +1,7 @@
package ru.soune.no_copy.handler;
import org.springframework.stereotype.Component;
@Component
public class JwtTokenProvider {
}
@@ -3,9 +3,14 @@ package ru.soune.no_copy.service;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.soune.no_copy.dto.ChangePasswordRequest; import ru.soune.no_copy.dto.ChangePasswordRequest;
import ru.soune.no_copy.dto.UserDTO;
import ru.soune.no_copy.dto.UserRequest;
import ru.soune.no_copy.entity.User; import ru.soune.no_copy.entity.User;
import ru.soune.no_copy.exception.InvalidUserEmail;
import ru.soune.no_copy.exception.NotValidationPasswordException; import ru.soune.no_copy.exception.NotValidationPasswordException;
import ru.soune.no_copy.exception.UserNotFoundException;
import ru.soune.no_copy.repository.UserRepository; import ru.soune.no_copy.repository.UserRepository;
@Service @Service
@@ -16,6 +21,7 @@ public class UserService {
private final PasswordEncoder passwordEncoder; private final PasswordEncoder passwordEncoder;
@Transactional
public User changePassword(User user, ChangePasswordRequest request) { public User changePassword(User user, ChangePasswordRequest request) {
if (!passwordEncoder.matches(request.getCurrentPassword(), user.getPassword())) { if (!passwordEncoder.matches(request.getCurrentPassword(), user.getPassword())) {
throw new NotValidationPasswordException("Current password is incorrect"); throw new NotValidationPasswordException("Current password is incorrect");
@@ -25,4 +31,56 @@ public class UserService {
return userRepository.save(user); return userRepository.save(user);
} }
@Transactional
public UserDTO updateUser(UserRequest userRequest, User currentUser) {
User user = userRepository.findById(currentUser.getId())
.orElseThrow(() -> new UserNotFoundException("User not found with id: " + currentUser.getId()));
updateUserFromRequest(user, userRequest);
User updatedUser = userRepository.save(user);
return mapToDTO(updatedUser);
}
private void updateUserFromRequest(User user, UserRequest request) {
if (request.getFullName() != null) {
user.setFullName(request.getFullName());
}
if (request.getCompany() != null) {
user.setCompany(request.getCompany());
}
if (request.getEmail() != null && !request.getEmail().equals(user.getEmail())) {
if (userRepository.existsByEmail(request.getEmail())) {
throw new InvalidUserEmail("Email already exists: " + request.getEmail());
}
user.setEmail(request.getEmail());
}
if (request.getPhone() != null) {
user.setPhone(request.getPhone());
}
if (request.getGenderType() != null) {
user.setGenderType(request.getGenderType());
}
if (request.getBirthday() != null) {
user.setBirthday(request.getBirthday());
}
}
private UserDTO mapToDTO(User user) {
return UserDTO.builder()
.fullName(user.getFullName())
.email(user.getEmail())
.company(user.getCompany())
.phone(user.getPhone())
.genderType(user.getGenderType())
.birthday(user.getBirthday())
.subscriptionType(user.getSubscriptionType())
.createdAt(user.getCreatedAt())
.build();
}
} }