From 5c8fdce32364f994939819269835ecdfef02f829 Mon Sep 17 00:00:00 2001 From: vladp Date: Thu, 27 Nov 2025 12:48:08 +0700 Subject: [PATCH] NCP-3 Add logic update user fields,change password --- .../no_copy/configuration/JacksonConfig.java | 41 +++++++++++++ .../no_copy/controller/UserController.java | 17 +++++- .../ru/soune/no_copy/dto/AuthResponse.java | 7 ++- .../ru/soune/no_copy/dto/RegisterRequest.java | 2 + .../soune/no_copy/dto/UserContentRequest.java | 14 +++-- .../no_copy/dto/UserContentUpdateRequest.java | 16 +++-- .../java/ru/soune/no_copy/dto/UserDTO.java | 4 ++ .../ru/soune/no_copy/dto/UserRequest.java | 25 ++++++++ .../no_copy/handler/JwtTokenProvider.java | 7 +++ .../ru/soune/no_copy/service/UserService.java | 58 +++++++++++++++++++ 10 files changed, 180 insertions(+), 11 deletions(-) create mode 100644 src/main/java/ru/soune/no_copy/configuration/JacksonConfig.java create mode 100644 src/main/java/ru/soune/no_copy/dto/UserRequest.java create mode 100644 src/main/java/ru/soune/no_copy/handler/JwtTokenProvider.java diff --git a/src/main/java/ru/soune/no_copy/configuration/JacksonConfig.java b/src/main/java/ru/soune/no_copy/configuration/JacksonConfig.java new file mode 100644 index 0000000..0f726fb --- /dev/null +++ b/src/main/java/ru/soune/no_copy/configuration/JacksonConfig.java @@ -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; + } +} diff --git a/src/main/java/ru/soune/no_copy/controller/UserController.java b/src/main/java/ru/soune/no_copy/controller/UserController.java index 167502b..f4db208 100644 --- a/src/main/java/ru/soune/no_copy/controller/UserController.java +++ b/src/main/java/ru/soune/no_copy/controller/UserController.java @@ -5,6 +5,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; 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.AuthToken; import ru.soune.no_copy.entity.User; import ru.soune.no_copy.exception.InvalidUserEmail; @@ -35,7 +36,7 @@ public class UserController { List allUsers = userRepository.findAll().stream() .map(u -> new UserDTO(u.getFullName(), u.getCompany(), u.getEmail(), u.getIsActive(), u.getPhone(), u.getGenderType(), - u.getBirthday(), u.getCreatedAt())) + u.getBirthday(), u.getCreatedAt(), u.getSubscriptionType())) .toList(); return ResponseEntity.ok(allUsers); @@ -61,4 +62,18 @@ public class UserController { return ResponseEntity.ok(userMapper.toDTO(updateUser)); } + + @PatchMapping("/user-update") + public ResponseEntity updateUser(@RequestBody UserRequest userRequest, + @RequestHeader("Authorization") String tokenHeader) { + String token = tokenHeader.replace("Bearer ", ""); + + Optional 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)); + } } diff --git a/src/main/java/ru/soune/no_copy/dto/AuthResponse.java b/src/main/java/ru/soune/no_copy/dto/AuthResponse.java index 8e76c1a..c39301a 100644 --- a/src/main/java/ru/soune/no_copy/dto/AuthResponse.java +++ b/src/main/java/ru/soune/no_copy/dto/AuthResponse.java @@ -2,4 +2,9 @@ package ru.soune.no_copy.dto; 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 +) {} diff --git a/src/main/java/ru/soune/no_copy/dto/RegisterRequest.java b/src/main/java/ru/soune/no_copy/dto/RegisterRequest.java index 997d27e..20cef35 100644 --- a/src/main/java/ru/soune/no_copy/dto/RegisterRequest.java +++ b/src/main/java/ru/soune/no_copy/dto/RegisterRequest.java @@ -3,6 +3,7 @@ package ru.soune.no_copy.dto; import com.fasterxml.jackson.annotation.JsonFormat; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Past; import jakarta.validation.constraints.Size; import org.springframework.format.annotation.DateTimeFormat; import ru.soune.no_copy.entity.GenderType; @@ -27,6 +28,7 @@ public record RegisterRequest( @DateTimeFormat(pattern = "dd-MM-yyyy") @JsonFormat(pattern = "dd-MM-yyyy") + @Past LocalDate birthday, @NotBlank(message = "error.not.blank") diff --git a/src/main/java/ru/soune/no_copy/dto/UserContentRequest.java b/src/main/java/ru/soune/no_copy/dto/UserContentRequest.java index de64e7f..06ef01e 100644 --- a/src/main/java/ru/soune/no_copy/dto/UserContentRequest.java +++ b/src/main/java/ru/soune/no_copy/dto/UserContentRequest.java @@ -3,7 +3,13 @@ package ru.soune.no_copy.dto; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; -public record UserContentRequest(@NotBlank @Email String userEmail, @NotBlank String fileName, - @NotBlank String originalFilename, @NotBlank String fileType, - @NotBlank String fileExtension, @NotBlank Long fileSize, - @NotBlank String filePath, Integer protectionLevel) {} +public record UserContentRequest( + @NotBlank @Email String userEmail, + @NotBlank String fileName, + @NotBlank String originalFilename, + @NotBlank String fileType, + @NotBlank String fileExtension, + @NotBlank Long fileSize, + @NotBlank String filePath, + Integer protectionLevel +) {} diff --git a/src/main/java/ru/soune/no_copy/dto/UserContentUpdateRequest.java b/src/main/java/ru/soune/no_copy/dto/UserContentUpdateRequest.java index ba8ab5f..8955969 100644 --- a/src/main/java/ru/soune/no_copy/dto/UserContentUpdateRequest.java +++ b/src/main/java/ru/soune/no_copy/dto/UserContentUpdateRequest.java @@ -3,8 +3,14 @@ package ru.soune.no_copy.dto; import jakarta.validation.constraints.Email; import jakarta.validation.constraints.NotBlank; -public record UserContentUpdateRequest(@NotBlank @Email String userEmail, @NotBlank String fileName, - @NotBlank String originalFilename, @NotBlank String fileType, - @NotBlank String fileExtension, @NotBlank Long fileSize, - @NotBlank String filePath, Integer protectionLevel, Long id) { -} +public record UserContentUpdateRequest( + @NotBlank @Email String userEmail, + @NotBlank String fileName, + @NotBlank String originalFilename, + @NotBlank String fileType, + @NotBlank String fileExtension, + @NotBlank Long fileSize, + @NotBlank String filePath, + Integer protectionLevel, + Long id +) {} diff --git a/src/main/java/ru/soune/no_copy/dto/UserDTO.java b/src/main/java/ru/soune/no_copy/dto/UserDTO.java index a5fa251..ba5b278 100644 --- a/src/main/java/ru/soune/no_copy/dto/UserDTO.java +++ b/src/main/java/ru/soune/no_copy/dto/UserDTO.java @@ -1,9 +1,11 @@ package ru.soune.no_copy.dto; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import ru.soune.no_copy.entity.GenderType; +import ru.soune.no_copy.entity.SubscriptionType; import java.time.LocalDate; import java.time.LocalDateTime; @@ -11,6 +13,7 @@ import java.time.LocalDateTime; @Data @AllArgsConstructor @NoArgsConstructor +@Builder public class UserDTO { private String fullName; private String company; @@ -20,4 +23,5 @@ public class UserDTO { private GenderType genderType; private LocalDate birthday; private LocalDateTime createdAt; + private SubscriptionType subscriptionType; } diff --git a/src/main/java/ru/soune/no_copy/dto/UserRequest.java b/src/main/java/ru/soune/no_copy/dto/UserRequest.java new file mode 100644 index 0000000..db8c8fe --- /dev/null +++ b/src/main/java/ru/soune/no_copy/dto/UserRequest.java @@ -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; +} diff --git a/src/main/java/ru/soune/no_copy/handler/JwtTokenProvider.java b/src/main/java/ru/soune/no_copy/handler/JwtTokenProvider.java new file mode 100644 index 0000000..721869c --- /dev/null +++ b/src/main/java/ru/soune/no_copy/handler/JwtTokenProvider.java @@ -0,0 +1,7 @@ +package ru.soune.no_copy.handler; + +import org.springframework.stereotype.Component; + +@Component +public class JwtTokenProvider { +} diff --git a/src/main/java/ru/soune/no_copy/service/UserService.java b/src/main/java/ru/soune/no_copy/service/UserService.java index 6197cfc..4cb0a51 100644 --- a/src/main/java/ru/soune/no_copy/service/UserService.java +++ b/src/main/java/ru/soune/no_copy/service/UserService.java @@ -3,9 +3,14 @@ package ru.soune.no_copy.service; import lombok.AllArgsConstructor; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; 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.exception.InvalidUserEmail; import ru.soune.no_copy.exception.NotValidationPasswordException; +import ru.soune.no_copy.exception.UserNotFoundException; import ru.soune.no_copy.repository.UserRepository; @Service @@ -16,6 +21,7 @@ public class UserService { private final PasswordEncoder passwordEncoder; + @Transactional public User changePassword(User user, ChangePasswordRequest request) { if (!passwordEncoder.matches(request.getCurrentPassword(), user.getPassword())) { throw new NotValidationPasswordException("Current password is incorrect"); @@ -25,4 +31,56 @@ public class UserService { 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(); + } }