dev add additional info user
Test Workflow / test (push) Successful in 11s

This commit is contained in:
vladp
2026-03-24 11:37:25 +07:00
parent 69c3d876ae
commit 40f8effb31
13 changed files with 429 additions and 2 deletions
@@ -21,6 +21,7 @@ import ru.soune.nocopy.repository.ReferralJpaRepository;
import ru.soune.nocopy.repository.UserRepository;
import ru.soune.nocopy.service.register.AuthService;
import ru.soune.nocopy.service.mail.EmailService;
import ru.soune.nocopy.service.user.AdditionalInfoService;
import java.util.Arrays;
import java.util.Map;
@@ -43,6 +44,8 @@ public class RegRequestHandler implements RequestHandler {
private final ReferralJpaRepository referralJpaRepository;
private final AdditionalInfoService additionalInfoService;
@Override
public BaseResponse handle(BaseRequest request) throws ValidationException {
RegRequest regRequest = objectMapper.convertValue(request.getMessageBody(), RegRequest.class);
@@ -124,9 +127,10 @@ public class RegRequestHandler implements RequestHandler {
referralService.onRegister(authToken.getUser().getId(), regRequest.getReferralLink());
}
additionalInfoService.additionalInfo(regRequest.getIp(), regRequest.getUserAgent());
return new BaseResponse(request.getMsgId(), MessageCode.SUCCESS.getCode(),
MessageCode.SUCCESS.getDescription(), regAnswer);
} catch (CompanyAlreadyExist e) {
return new BaseResponse(request.getMsgId(), MessageCode.COMPANY_ALREADY_EXISTS.getCode(),
MessageCode.COMPANY_ALREADY_EXISTS.getDescription(), Map.of(
@@ -0,0 +1,214 @@
package ru.soune.nocopy.handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import ru.soune.nocopy.dto.BaseRequest;
import ru.soune.nocopy.dto.BaseResponse;
import ru.soune.nocopy.dto.MessageCode;
import ru.soune.nocopy.dto.user.UserAdditionalInfoBody;
import ru.soune.nocopy.entity.user.UserAdditionalInfo;
import ru.soune.nocopy.entity.user.UserAdditionalInfoAnswer;
import ru.soune.nocopy.entity.user.UserAdditionalInfoListAnswer;
import ru.soune.nocopy.exception.UserAdditionalInfoNotFound;
import ru.soune.nocopy.service.user.AdditionalInfoService;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@Component
@Slf4j
@RequiredArgsConstructor
public class UserInfoHandler implements RequestHandler {
private final AdditionalInfoService additionalInfoService;
private final ObjectMapper objectMapper;
@Override
public BaseResponse handle(BaseRequest request) throws Exception {
try {
UserAdditionalInfoBody body = objectMapper.convertValue(request.getMessageBody(),
UserAdditionalInfoBody.class);
log.info("Processing UserInfoHandler with action: {}", body.getAction());
return switch (body.getAction()) {
case "getAll" -> getAllAdditionalInfos(request);
case "getById" -> getAdditionalInfoById(request, body.getUserId());
case "update" -> updateAdditionalInfo(request, body);
case "delete" -> deleteAdditionalInfo(request, body.getUserId());
default -> new BaseResponse(
request.getMsgId(),
MessageCode.INVALID_ACTION.getCode(),
"Invalid action: " + body.getAction(),
null
);
};
} catch (IllegalArgumentException e) {
log.error("Validation error in UserInfoHandler", e);
return new BaseResponse(
request.getMsgId(),
MessageCode.VALIDATION_ERROR.getCode(),
e.getMessage(),
null);
} catch (UserAdditionalInfoNotFound e) {
log.error("User additional info not found", e);
return new BaseResponse(
request.getMsgId(),
MessageCode.NOT_FOUND.getCode(),
e.getMessage(),
null);
} catch (Exception e) {
log.error("Unexpected error in UserInfoHandler", e);
return new BaseResponse(
request.getMsgId(),
MessageCode.INTERNAL_ERROR.getCode(),
"Internal server error: " + e.getMessage(),
null);
}
}
private BaseResponse getAllAdditionalInfos(BaseRequest request) {
log.info("Getting all additional infos");
List<UserAdditionalInfo> infos = additionalInfoService.getAllAdditionalInfos();
List<UserAdditionalInfoAnswer> answerItems = infos.stream()
.map(this::convertToAnswer)
.collect(Collectors.toList());
UserAdditionalInfoListAnswer listAnswer = UserAdditionalInfoListAnswer.builder()
.items(answerItems)
.count(answerItems.size())
.build();
return new BaseResponse(
request.getMsgId(),
MessageCode.SUCCESS.getCode(),
MessageCode.SUCCESS.getDescription(),
listAnswer
);
}
private BaseResponse getAdditionalInfoById(BaseRequest request, Long userId) {
log.info("Getting additional info for user: {}", userId);
if (userId == null) {
throw new IllegalArgumentException("User ID is required for getById action");
}
UserAdditionalInfo info = additionalInfoService.getAdditionalInfoByUser(userId);
if (info == null) {
throw new UserAdditionalInfoNotFound(
String.format("User additional info not found for userId: %d", userId)
);
}
UserAdditionalInfoAnswer answer = convertToAnswer(info);
return new BaseResponse(
request.getMsgId(),
MessageCode.SUCCESS.getCode(),
MessageCode.SUCCESS.getDescription(),
answer
);
}
private BaseResponse updateAdditionalInfo(BaseRequest request, UserAdditionalInfoBody body) {
log.info("Updating additional info for user: {}", body.getUserId());
if (body.getUserId() == null) {
throw new IllegalArgumentException("User ID is required for update action");
}
validateIpAddress(body.getIpAddress());
validateUserAgent(body.getUserAgent());
Optional<UserAdditionalInfo> updatedInfo = additionalInfoService.updateAdditionalInfo(
body.getUserId(),
body.getIpAddress(),
body.getUserAgent()
);
if (updatedInfo.isEmpty()) {
throw new UserAdditionalInfoNotFound(
String.format("User additional info not found for userId: %d", body.getUserId())
);
}
UserAdditionalInfoAnswer answer = convertToAnswer(updatedInfo.get());
return new BaseResponse(
request.getMsgId(),
MessageCode.SUCCESS.getCode(),
MessageCode.SUCCESS.getDescription(),
answer
);
}
private BaseResponse deleteAdditionalInfo(BaseRequest request, Long userId) {
log.info("Deleting additional info for user: {}", userId);
if (userId == null) {
throw new IllegalArgumentException("User ID is required for delete action");
}
UserAdditionalInfo existingInfo = additionalInfoService.getAdditionalInfoByUser(userId);
if (existingInfo == null) {
throw new UserAdditionalInfoNotFound(
String.format("User additional info not found for userId: %d", userId)
);
}
additionalInfoService.deleteAdditionalInfo(userId);
return new BaseResponse(
request.getMsgId(),
MessageCode.SUCCESS.getCode(),
MessageCode.SUCCESS.getDescription(),
Map.of("deleted", true, "user_id", userId)
);
}
private UserAdditionalInfoAnswer convertToAnswer(UserAdditionalInfo info) {
return UserAdditionalInfoAnswer.builder()
.id(info.getId())
.userId(info.getUser() != null ? info.getUser().getId() : null)
.ipAddress(info.getIpAddress())
.userAgent(info.getUserAgent())
.registrationDate(info.getRegistrationDate())
.build();
}
private void validateIpAddress(String ip) {
if (ip == null || ip.trim().isEmpty()) {
throw new IllegalArgumentException("IP address is required for update action");
}
String ipv4Pattern =
"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
if (!ip.trim().matches(ipv4Pattern)) {
log.warn("Invalid IP address format: {}", ip);
throw new IllegalArgumentException("Invalid IP address format");
}
}
private void validateUserAgent(String userAgent) {
if (userAgent == null || userAgent.trim().isEmpty()) {
throw new IllegalArgumentException("User agent is required for update action");
}
if (userAgent.length() > 500) {
throw new IllegalArgumentException("User agent must not exceed 500 characters");
}
}
}
@@ -1,6 +1,7 @@
package ru.soune.nocopy.handler.validator;
import org.apache.commons.validator.routines.EmailValidator;
import org.apache.commons.validator.routines.InetAddressValidator;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
@@ -28,6 +29,7 @@ public class RegRequestValidator implements Validator {
validateEmail(request.getEmail(), errors);
validateCompanyName(request.getCompanyName(), errors);
validateFullName(request.getFullName(), errors);
validateAdditionalInfo(request.getIp(), request.getUserAgent(), errors);
}
private void validateFullName(String fullName, Errors errors) {
@@ -73,6 +75,21 @@ public class RegRequestValidator implements Validator {
}
}
public void validateAdditionalInfo(String ip, String userAgent, Errors errors) {
if (ip == null || ip.trim().isEmpty()) {
errors.rejectValue("ip", "ip.required", "IP address is required");
} else if (!InetAddressValidator.getInstance().isValid(ip.trim())) {
errors.rejectValue("ip", "ip.invalid", "Invalid IP address format");
}
if (userAgent == null || userAgent.trim().isEmpty()) {
errors.rejectValue("userAgent", "userAgent.required", "User agent is required");
} else if (userAgent.length() > 500) {
errors.rejectValue("userAgent", "userAgent.tooLong", "User agent must not exceed " +
"500 characters");
}
}
public void validateEmail(String email, Errors errors) {
if (email == null || email.trim().isEmpty()) {
errors.rejectValue("email", "email.is.empty", "Email must not be empty");