diff --git a/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java b/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java index 69bbe52..f4e6704 100644 --- a/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java +++ b/src/main/java/ru/soune/nocopy/configuration/HandlerConfig.java @@ -19,7 +19,8 @@ public class HandlerConfig { LogoutRequestHandler logoutHandler, ImageFoundRequestHandler imageFoundRequestHandler, VerifyRegisterUserHandler verifyRegisterUser, - AuthRequestHandler authRequestHandler + AuthRequestHandler authRequestHandler, + CompanyHandler companyHandler ) { Map map = new HashMap<>(); map.put(20001, login); @@ -30,6 +31,7 @@ public class HandlerConfig { map.put(20007, imageFoundRequestHandler); map.put(20008, authRequestHandler); map.put(20009, verifyRegisterUser); + map.put(30000, companyHandler); return map; } diff --git a/src/main/java/ru/soune/nocopy/controller/UserController.java b/src/main/java/ru/soune/nocopy/controller/UserController.java index f4f0ffd..58107c7 100644 --- a/src/main/java/ru/soune/nocopy/controller/UserController.java +++ b/src/main/java/ru/soune/nocopy/controller/UserController.java @@ -47,7 +47,7 @@ public class UserController { @GetMapping("/all") public ResponseEntity> getAllUsers() { List allUsers = userRepository.findAll().stream() - .map(u -> new UserDTO(u.getFullName(), u.getCompany(), u.getEmail(), u.isActive(), + .map(u -> new UserDTO(u.getFullName(), u.getCompany().getCompanyName(), u.getEmail(), u.isActive(), u.getPhone(), u.getGenderType(), u.getBirthday(), u.getCreatedAt(), u.getSubscriptionType())) .toList(); @@ -71,7 +71,7 @@ public class UserController { UserDTO userDTO = userMapper.toDTO(user); userDTO.setEmail(email); userDTO.setFullName(user.getFullName()); - userDTO.setCompany(user.getCompany()); + userDTO.setCompany(user.getCompany().getCompanyName()); userDTO.setPhone(user.getPhone()); userDTO.setGenderType(user.getGenderType()); userDTO.setBirthday(user.getBirthday()); @@ -144,10 +144,6 @@ public class UserController { user.setActive(true); user.setEmailVerified(true); - if (registerRequest.getCompanyName() != null) { - user.setCompany(registerRequest.getCompanyName()); - } - if (registerRequest.getPhone() != null) { user.setPhone(registerRequest.getPhone()); } diff --git a/src/main/java/ru/soune/nocopy/dto/MessageCode.java b/src/main/java/ru/soune/nocopy/dto/MessageCode.java index 1027f5a..c712e7b 100644 --- a/src/main/java/ru/soune/nocopy/dto/MessageCode.java +++ b/src/main/java/ru/soune/nocopy/dto/MessageCode.java @@ -29,7 +29,12 @@ public enum MessageCode { SEND_EMAIL_EXCEPTION(2, "Send email exception"), SIMILAR_FILES_FOUND(0, "Similar files found"), FILE_IS_PROTECTED(0, "File is protected"), - FILE_IS_NOT_PROTECTED(0, "File is not protected"); + FILE_IS_NOT_PROTECTED(0, "File is not protected"), + VALIDATION_ERROR(2, "Validation error"), + RESOURCE_NOT_FOUND(4, "Resource not found"), + INTERNAL_ERROR(4, "Internal server error"), + COMPANY_NOT_FOUND(4, "Company not found"), + COMPANY_ALREADY_EXISTS(2, "Company already exists"); private final Integer code; diff --git a/src/main/java/ru/soune/nocopy/dto/company/CompanyActionRequestDto.java b/src/main/java/ru/soune/nocopy/dto/company/CompanyActionRequestDto.java new file mode 100644 index 0000000..1326f41 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/company/CompanyActionRequestDto.java @@ -0,0 +1,23 @@ +package ru.soune.nocopy.dto.company; + +import lombok.Data; +import java.util.Map; + +@Data +public class CompanyActionRequestDto { + private String action; + private String companyId; + private String companyName; + private Map companyData; + private Long userId; + private Map searchParams; + private PageableParams pageable; + + @Data + public static class PageableParams { + private int page; + private int size; + private String sort; + private String direction; + } +} diff --git a/src/main/java/ru/soune/nocopy/dto/company/CompanyResponseDto.java b/src/main/java/ru/soune/nocopy/dto/company/CompanyResponseDto.java new file mode 100644 index 0000000..52756e2 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/company/CompanyResponseDto.java @@ -0,0 +1,16 @@ +package ru.soune.nocopy.dto.company; + +import lombok.Data; +import java.time.LocalDateTime; + +@Data +public class CompanyResponseDto { + private String id; + private String companyName; + private String phone; + private String address; + private String email; + private LocalDateTime registerDate; + private LocalDateTime updateDate; + private long userCount; +} diff --git a/src/main/java/ru/soune/nocopy/dto/register/AccountType.java b/src/main/java/ru/soune/nocopy/dto/register/AccountType.java new file mode 100644 index 0000000..afcf13a --- /dev/null +++ b/src/main/java/ru/soune/nocopy/dto/register/AccountType.java @@ -0,0 +1,5 @@ +package ru.soune.nocopy.dto.register; + +public enum AccountType { + B2B, B2C +} diff --git a/src/main/java/ru/soune/nocopy/dto/register/RegRequest.java b/src/main/java/ru/soune/nocopy/dto/register/RegRequest.java index 4ae695f..28e042d 100644 --- a/src/main/java/ru/soune/nocopy/dto/register/RegRequest.java +++ b/src/main/java/ru/soune/nocopy/dto/register/RegRequest.java @@ -13,6 +13,9 @@ public class RegRequest { private String companyName; + @NotEmpty(message = "Need account type") + private String accountType; + @Size(min = 11, max = 14, message = "Phone must be 11-14 digits") private String phone; diff --git a/src/main/java/ru/soune/nocopy/entity/company/Company.java b/src/main/java/ru/soune/nocopy/entity/company/Company.java new file mode 100644 index 0000000..a7cc176 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/entity/company/Company.java @@ -0,0 +1,49 @@ +package ru.soune.nocopy.entity.company; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.*; +import jakarta.validation.constraints.Size; +import lombok.*; +import org.springframework.data.annotation.CreatedDate; +import ru.soune.nocopy.entity.user.User; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Entity +@AllArgsConstructor +@NoArgsConstructor +@Getter @Setter +@Table(name = "company") +public class Company { + + @Id + @GeneratedValue(strategy = GenerationType.UUID) + private String id; + + @Column(name = "company_name") + private String companyName; + + @Column(name = "address") + private String address; + + @Column(name = "phone") + private String phone; + + @CreatedDate + @Column(name = "register_date", updatable = false, nullable = false) + private LocalDateTime registerDate = LocalDateTime.now(); + + @Column(name = "update_date") + private LocalDateTime updateDate; + + @Size(max = 1024) + @Column(name = "email", length = 1024) + private String email; + + @OneToMany(mappedBy = "company") + @JsonIgnore + @ToString.Exclude + private List users = new ArrayList<>(); +} diff --git a/src/main/java/ru/soune/nocopy/entity/user/User.java b/src/main/java/ru/soune/nocopy/entity/user/User.java index 335f5f6..aad5cf9 100644 --- a/src/main/java/ru/soune/nocopy/entity/user/User.java +++ b/src/main/java/ru/soune/nocopy/entity/user/User.java @@ -6,6 +6,7 @@ import jakarta.validation.constraints.Size; import lombok.*; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.jpa.domain.support.AuditingEntityListener; +import ru.soune.nocopy.entity.company.Company; import ru.soune.nocopy.entity.file.Violation; import java.time.LocalDate; @@ -37,9 +38,6 @@ public class User { @Column(name = "email_verified") private boolean emailVerified = false; - @Column(name = "company") - private String company; - @Column(nullable = false) @Size(min = 6) @JsonIgnore @@ -70,6 +68,9 @@ public class User { @Column(name = "is_active") private boolean isActive = false; + @Column(name = "permissions") + private Long userPermissions; + @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) @JsonIgnore @ToString.Exclude @@ -88,4 +89,9 @@ public class User { @OneToOne(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true) @JsonIgnore private ProtectedFileCheck protectedFileCheck; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "company_id") + @ToString.Exclude + private Company company; } diff --git a/src/main/java/ru/soune/nocopy/exception/ResourceNotFoundException.java b/src/main/java/ru/soune/nocopy/exception/ResourceNotFoundException.java new file mode 100644 index 0000000..4d768c8 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/exception/ResourceNotFoundException.java @@ -0,0 +1,7 @@ +package ru.soune.nocopy.exception; + +public class ResourceNotFoundException extends RuntimeException { + public ResourceNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/soune/nocopy/handler/CompanyHandler.java b/src/main/java/ru/soune/nocopy/handler/CompanyHandler.java new file mode 100644 index 0000000..f47e5c8 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/handler/CompanyHandler.java @@ -0,0 +1,411 @@ +package ru.soune.nocopy.handler; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Component; +import org.springframework.validation.BeanPropertyBindingResult; +import org.springframework.validation.BindingResult; +import ru.soune.nocopy.dto.BaseRequest; +import ru.soune.nocopy.dto.BaseResponse; +import ru.soune.nocopy.dto.MessageCode; +import ru.soune.nocopy.dto.company.CompanyActionRequestDto; +import ru.soune.nocopy.dto.company.CompanyResponseDto; +import ru.soune.nocopy.entity.company.Company; +import ru.soune.nocopy.entity.user.User; +import ru.soune.nocopy.exception.ResourceNotFoundException; +import ru.soune.nocopy.exception.ValidationException; +import ru.soune.nocopy.handler.validator.RegRequestValidator; +import ru.soune.nocopy.service.CompanyService; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RequiredArgsConstructor +public class CompanyHandler implements RequestHandler { + + private final CompanyService companyService; + + private final RegRequestValidator regRequestValidator; + + private final ObjectMapper objectMapper; + + @Override + public BaseResponse handle(BaseRequest request) { + try { + CompanyActionRequestDto actionRequest = objectMapper.convertValue( + request.getMessageBody(), + CompanyActionRequestDto.class + ); + + String action = actionRequest.getAction(); + + return switch (action) { + case "create" -> handleCreate(actionRequest, request.getMsgId()); + case "update" -> handleUpdate(actionRequest, request.getMsgId()); + case "delete" -> handleDelete(actionRequest, request.getMsgId()); + case "getCompany" -> handleGet(actionRequest, request.getMsgId()); + case "getAll" -> handleGetAll(actionRequest, request.getMsgId()); + case "addUser" -> handleAddUser(actionRequest, request.getMsgId()); + case "removeUser" -> handleRemoveUser(actionRequest, request.getMsgId()); + case "getUsers" -> handleGetUsers(actionRequest, request.getMsgId()); + case "search" -> handleSearch(actionRequest, request.getMsgId()); + case "countUser" -> handleCountUsers(actionRequest, request.getMsgId()); + default -> new BaseResponse( + request.getMsgId(), + MessageCode.INVALID_ACTION.getCode(), + "Invalid action: " + action, + null + ); + }; + + } catch (ValidationException e) { + Map errors = e.getBindingResult().getFieldErrors().stream() + .collect(Collectors.toMap( + error -> error.getField(), + error -> error.getDefaultMessage() != null ? error.getDefaultMessage() : "Validation error" + )); + + return new BaseResponse( + request.getMsgId(), + MessageCode.VALIDATION_ERROR.getCode(), + "Validation failed", + Map.of("errors", errors) + ); + + } catch (ResourceNotFoundException e) { + return new BaseResponse( + request.getMsgId(), + MessageCode.COMPANY_NOT_FOUND.getCode(), + e.getMessage(), + null + ); + + } catch (IllegalArgumentException e) { + return new BaseResponse( + request.getMsgId(), + MessageCode.VALIDATION_ERROR.getCode(), + e.getMessage(), + null + ); + + } catch (Exception e) { + log.error("Error handling company action", e); + return new BaseResponse( + request.getMsgId(), + MessageCode.INTERNAL_ERROR.getCode(), + "Error processing request: " + e.getMessage(), + null + ); + } + } + + private BaseResponse handleCreate(CompanyActionRequestDto request, Integer msgId) { + if (companyService.companyExistsByName(request.getCompanyName())) { + return new BaseResponse( + msgId, + MessageCode.COMPANY_ALREADY_EXISTS.getCode(), + "Company with this name already exists", + null + ); + } + + validateCompanyCreateData(request); + + Company company = companyService.addCompany( + request.getCompanyName(), + getStringParam(request, "phone"), + getStringParam(request, "address"), + getStringParam(request, "email") + ); + + return new BaseResponse( + msgId, + MessageCode.SUCCESS.getCode(), + "Company created successfully", + mapToResponseDto(company) + ); + } + + private BaseResponse handleUpdate(CompanyActionRequestDto request, Integer msgId) { + if (request.getCompanyId() == null) { + throw new IllegalArgumentException("Company ID is required for update"); + } + + Map companyData = request.getCompanyData(); + if (companyData == null) { + companyData = new HashMap<>(); + } + + Company company = companyService.updateCompany( + request.getCompanyId(), + (String) companyData.get("companyName"), + (String) companyData.get("phone"), + (String) companyData.get("address"), + (String) companyData.get("email") + ); + + return new BaseResponse( + msgId, + MessageCode.SUCCESS.getCode(), + "Company updated successfully", + mapToResponseDto(company) + ); + } + + private BaseResponse handleDelete(CompanyActionRequestDto request, Integer msgId) { + if (request.getCompanyId() == null) { + throw new IllegalArgumentException("Company ID is required for deletion"); + } + + companyService.deleteCompany(request.getCompanyId()); + + return new BaseResponse( + msgId, + MessageCode.SUCCESS.getCode(), + "Company deleted successfully", + Map.of("deletedCompanyId", request.getCompanyId()) + ); + } + + private BaseResponse handleGet(CompanyActionRequestDto request, Integer msgId) { + Company company; + + if (request.getCompanyId() != null) { + company = companyService.getCompanyById(request.getCompanyId()); + } else if (request.getCompanyName() != null) { + company = companyService.getCompanyByName(request.getCompanyName()); + } else { + throw new IllegalArgumentException("Either companyId or companyName is required"); + } + + return new BaseResponse( + msgId, + MessageCode.SUCCESS.getCode(), + "Company retrieved successfully", + mapToResponseDto(company) + ); + } + + private BaseResponse handleGetAll(CompanyActionRequestDto request, Integer msgId) { + List companies; + + if (request.getPageable() != null) { + Pageable pageable = createPageable(request.getPageable()); + Page companyPage = companyService.getAllCompanies(pageable); + + List companyDtos = companyPage.getContent().stream() + .map(this::mapToResponseDto) + .collect(Collectors.toList()); + + return new BaseResponse( + msgId, + MessageCode.SUCCESS.getCode(), + "Companies retrieved successfully", + Map.of( + "companies", companyDtos, + "totalElements", companyPage.getTotalElements(), + "totalPages", companyPage.getTotalPages(), + "currentPage", companyPage.getNumber(), + "pageSize", companyPage.getSize() + ) + ); + } else { + companies = companyService.getAllCompanies(); + List companyDtos = companies.stream() + .map(this::mapToResponseDto) + .collect(Collectors.toList()); + + return new BaseResponse( + msgId, + MessageCode.SUCCESS.getCode(), + "Companies retrieved successfully", + Map.of("companies", companyDtos) + ); + } + } + + private BaseResponse handleAddUser(CompanyActionRequestDto request, Integer msgId) { + if (request.getCompanyId() == null || request.getUserId() == null) { + throw new IllegalArgumentException("Company ID and User ID are required"); + } + + companyService.addUserToCompany(request.getCompanyId(), request.getUserId()); + + return new BaseResponse( + msgId, + MessageCode.SUCCESS.getCode(), + "User added to company successfully", + Map.of( + "companyId", request.getCompanyId(), + "userId", request.getUserId() + ) + ); + } + + private BaseResponse handleRemoveUser(CompanyActionRequestDto request, Integer msgId) { + if (request.getUserId() == null) { + throw new IllegalArgumentException("User ID is required"); + } + + companyService.deleteUserFromCompany(request.getUserId()); + + return new BaseResponse( + msgId, + MessageCode.SUCCESS.getCode(), + "User removed from company successfully", + Map.of("userId", request.getUserId()) + ); + } + + private BaseResponse handleGetUsers(CompanyActionRequestDto request, Integer msgId) { + if (request.getCompanyId() == null) { + throw new IllegalArgumentException("Company ID is required"); + } + + List users = companyService.getCompanyUsers(request.getCompanyId()); + + List> userDtos = users.stream() + .map(user -> { + Map map = new HashMap<>(); + map.put("id", user.getId()); + map.put("email", user.getEmail()); + map.put("phone", user.getPhone()); + map.put("fullName", user.getFullName()); + return map; + }).toList(); + + return new BaseResponse( + msgId, + MessageCode.SUCCESS.getCode(), + "Company users retrieved successfully", + Map.of( + "companyId", request.getCompanyId(), + "users", userDtos, + "count", userDtos.size() + ) + ); + } + + private BaseResponse handleSearch(CompanyActionRequestDto request, Integer msgId) { + String searchTerm = getStringParam(request, "name"); + if (searchTerm == null) { + throw new IllegalArgumentException("Search term is required"); + } + + List companies = companyService.searchCompaniesByName(searchTerm); + List companyDtos = companies.stream() + .map(this::mapToResponseDto) + .collect(Collectors.toList()); + + return new BaseResponse( + msgId, + MessageCode.SUCCESS.getCode(), + "Companies search completed", + Map.of( + "searchTerm", searchTerm, + "companies", companyDtos, + "count", companyDtos.size() + ) + ); + } + + private BaseResponse handleCountUsers(CompanyActionRequestDto request, Integer msgId) { + if (request.getCompanyId() == null) { + throw new IllegalArgumentException("Company ID is required"); + } + + long count = companyService.countCompanyUsers(request.getCompanyId()); + + return new BaseResponse( + msgId, + MessageCode.SUCCESS.getCode(), + "User count retrieved", + Map.of( + "companyId", request.getCompanyId(), + "userCount", count + ) + ); + } + + private void validateCompanyCreateData(CompanyActionRequestDto request) throws ValidationException { + BindingResult bindingResult = new BeanPropertyBindingResult(request, "companyRequest"); + + if (request.getCompanyName() != null) { + regRequestValidator.validateCompanyName(request.getCompanyName(), bindingResult); + } + + String email = getStringParam(request, "email"); + if (email != null) { + regRequestValidator.validateEmail(email, bindingResult); + } + + String phone = getStringParam(request, "phone"); + if (phone != null) { + regRequestValidator.validatePhone(phone, bindingResult); + } + + if (bindingResult.hasErrors()) { + throw new ValidationException(bindingResult, null); + } + } + + private String getStringParam(CompanyActionRequestDto request, String paramName) { + if (request.getCompanyData() != null) { + Object value = request.getCompanyData().get(paramName); + return value != null ? value.toString() : null; + } + + Map searchParams = request.getSearchParams(); + if (searchParams != null) { + Object value = searchParams.get(paramName); + return value != null ? value.toString() : null; + } + + return null; + } + + private CompanyResponseDto mapToResponseDto(Company company) { + CompanyResponseDto dto = new CompanyResponseDto(); + dto.setId(company.getId()); + dto.setCompanyName(company.getCompanyName()); + dto.setPhone(company.getPhone()); + dto.setAddress(company.getAddress()); + dto.setEmail(company.getEmail()); + dto.setRegisterDate(company.getRegisterDate()); + dto.setUpdateDate(company.getUpdateDate()); + + try { + dto.setUserCount(companyService.countCompanyUsers(company.getId())); + } catch (Exception e) { + dto.setUserCount(0); + } + + return dto; + } + + private Pageable createPageable(CompanyActionRequestDto.PageableParams params) { + Sort sort = Sort.unsorted(); + + if (params.getSort() != null) { + Sort.Direction direction = params.getDirection() != null && + "desc".equalsIgnoreCase(params.getDirection()) ? + Sort.Direction.DESC : Sort.Direction.ASC; + sort = Sort.by(direction, params.getSort()); + } + + return PageRequest.of( + params.getPage(), + params.getSize(), + sort + ); + } +} diff --git a/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java b/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java index 4cf3914..6718fca 100644 --- a/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java +++ b/src/main/java/ru/soune/nocopy/handler/RegRequestHandler.java @@ -10,7 +10,6 @@ import ru.soune.nocopy.dto.*; import ru.soune.nocopy.dto.register.RegAnswer; import ru.soune.nocopy.dto.register.RegRequest; import ru.soune.nocopy.entity.user.AuthToken; -import ru.soune.nocopy.entity.user.EmailVerificationToken; import ru.soune.nocopy.exception.NotValidFieldException; import ru.soune.nocopy.exception.ValidationException; import ru.soune.nocopy.handler.validator.RegRequestValidator; diff --git a/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java b/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java index c2b2496..b57c7fb 100644 --- a/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java +++ b/src/main/java/ru/soune/nocopy/handler/validator/RegRequestValidator.java @@ -55,7 +55,7 @@ public class RegRequestValidator implements Validator { } } - private void validateCompanyName(String companyName, Errors errors) { + public void validateCompanyName(String companyName, Errors errors) { if (companyName == null || companyName.trim().isEmpty()) { return; } @@ -73,7 +73,7 @@ public class RegRequestValidator implements Validator { } } - private void validateEmail(String email, Errors errors) { + public void validateEmail(String email, Errors errors) { if (email == null || email.trim().isEmpty()) { errors.rejectValue("email", "email.is.empty", "Email must not be empty"); return; @@ -94,7 +94,7 @@ public class RegRequestValidator implements Validator { } - private void validatePhone(String phone, Errors errors) { + public void validatePhone(String phone, Errors errors) { if (phone == null || phone.trim().isEmpty()) { errors.rejectValue("phone", "phone.empty", "Test phone numbers are not allowed"); diff --git a/src/main/java/ru/soune/nocopy/repository/CompanyRepository.java b/src/main/java/ru/soune/nocopy/repository/CompanyRepository.java new file mode 100644 index 0000000..b075868 --- /dev/null +++ b/src/main/java/ru/soune/nocopy/repository/CompanyRepository.java @@ -0,0 +1,23 @@ +package ru.soune.nocopy.repository; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.soune.nocopy.entity.company.Company; + +import java.util.List; +import java.util.Optional; + +@Repository +public interface CompanyRepository extends JpaRepository { + Optional findByCompanyName(String companyName); + + boolean existsByCompanyName(String companyName); + + List findByCompanyNameContainingIgnoreCase(String name); + + Page findAll(Pageable pageable); + + Page findByCompanyNameContainingIgnoreCase(String name, Pageable pageable); +} diff --git a/src/main/java/ru/soune/nocopy/repository/UserRepository.java b/src/main/java/ru/soune/nocopy/repository/UserRepository.java index b0e8032..88c3bce 100644 --- a/src/main/java/ru/soune/nocopy/repository/UserRepository.java +++ b/src/main/java/ru/soune/nocopy/repository/UserRepository.java @@ -4,10 +4,12 @@ import jakarta.validation.constraints.Size; import org.springframework.data.jpa.repository.JpaRepository; import ru.soune.nocopy.entity.user.User; -import java.util.Optional; +import java.util.List; public interface UserRepository extends JpaRepository { User findByEmail(String email); boolean existsByEmail(String email); boolean existsByPhone(@Size(min = 11, max = 14) String phone); + List findByCompanyId(String companyId); + long countByCompanyId(String companyId); } diff --git a/src/main/java/ru/soune/nocopy/service/CompanyService.java b/src/main/java/ru/soune/nocopy/service/CompanyService.java new file mode 100644 index 0000000..c3bfb2e --- /dev/null +++ b/src/main/java/ru/soune/nocopy/service/CompanyService.java @@ -0,0 +1,198 @@ +package ru.soune.nocopy.service; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.BeanPropertyBindingResult; +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import ru.soune.nocopy.entity.company.Company; +import ru.soune.nocopy.entity.user.User; +import ru.soune.nocopy.exception.ValidationException; +import ru.soune.nocopy.exception.ResourceNotFoundException; +import ru.soune.nocopy.handler.validator.RegRequestValidator; +import ru.soune.nocopy.repository.CompanyRepository; +import ru.soune.nocopy.repository.UserRepository; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +@AllArgsConstructor +@Slf4j +public class CompanyService { + + private final CompanyRepository companyRepository; + private final UserRepository userRepository; + private final RegRequestValidator regRequestValidator; + + @Transactional(readOnly = true) + public Company getCompanyById(String id) { + return companyRepository.findById(id) + .orElseThrow(() -> new ResourceNotFoundException("Company not found with id: " + id)); + } + + @Transactional(readOnly = true) + public Company getCompanyByName(String companyName) { + return companyRepository.findByCompanyName(companyName) + .orElseThrow(() -> new ResourceNotFoundException("Company not found with name: " + companyName)); + } + + @Transactional(readOnly = true) + public List getAllCompanies() { + return companyRepository.findAll(); + } + + @Transactional(readOnly = true) + public Page getAllCompanies(Pageable pageable) { + return companyRepository.findAll(pageable); + } + + @Transactional(readOnly = true) + public List searchCompaniesByName(String name) { + return companyRepository.findByCompanyNameContainingIgnoreCase(name); + } + + @Transactional + public Company updateCompany(String id, String companyName, String phone, String address, String email) { + Company company = getCompanyById(id); + + if (companyName != null) { + company.setCompanyName(companyName); + } + if (phone != null) { + company.setPhone(phone); + } + if (address != null) { + company.setAddress(address); + } + if (email != null) { + company.setEmail(email); + } + + company.setUpdateDate(LocalDateTime.now()); + + validateCompanyData(company); + + return companyRepository.save(company); + } + + @Transactional + public Company addCompany(String companyName, String phone, String address, String email) { + Company company = new Company(); + company.setCompanyName(companyName); + company.setPhone(phone); + company.setAddress(address); + company.setEmail(email); + company.setRegisterDate(LocalDateTime.now()); + + validateCompanyData(company); + + return companyRepository.save(company); + } + + @Transactional + public void deleteCompany(String id) { + List users = userRepository.findByCompanyId(id); + users.forEach(user -> { + user.setCompany(null); + userRepository.save(user); + }); + + companyRepository.deleteById(id); + log.info("Company deleted: {}", id); + } + + @Transactional(readOnly = true) + public List getCompanyUsers(String companyId) { + return userRepository.findByCompanyId(companyId); + } + + @Transactional(readOnly = true) + public long countCompanyUsers(String companyId) { + return userRepository.countByCompanyId(companyId); + } + + @Transactional + public void addUserToCompany(String companyId, Long userId) { + Company company = getCompanyById(companyId); + User user = userRepository.findById(userId) + .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId)); + + if (user.getCompany() != null) { + throw new IllegalArgumentException("User already belongs to another company"); + } + + user.setCompany(company); + userRepository.save(user); + + log.info("User {} added to company {}", userId, companyId); + } + + @Transactional + public void addUserToCompany(Company company, User user) { + if (user.getCompany() != null) { + throw new IllegalArgumentException("User already belongs to another company"); + } + + user.setCompany(company); + userRepository.save(user); + + log.info("User {} added to company {}", user.getId(), company.getId()); + } + + @Transactional + public void deleteUserFromCompany(User user) { + user.setCompany(null); + userRepository.save(user); + + log.info("User {} removed from company", user.getId()); + } + + @Transactional + public void deleteUserFromCompany(Long userId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new ResourceNotFoundException("User not found with id: " + userId)); + + user.setCompany(null); + userRepository.save(user); + + log.info("User {} removed from company", userId); + } + + @Transactional(readOnly = true) + public boolean companyExistsByName(String companyName) { + return companyRepository.existsByCompanyName(companyName); + } + + private void validateCompanyData(Company company) { + BindingResult bindingResult = new BeanPropertyBindingResult(company, "company"); + + if (company.getCompanyName() != null) { + regRequestValidator.validateCompanyName(company.getCompanyName(), bindingResult); + } + if (company.getEmail() != null) { + regRequestValidator.validateEmail(company.getEmail(), bindingResult); + } + if (company.getPhone() != null) { + regRequestValidator.validatePhone(company.getPhone(), bindingResult); + } + + if (bindingResult.hasErrors()) { + Map fieldErrors = bindingResult.getFieldErrors() + .stream() + .collect(Collectors.toMap( + FieldError::getField, + fieldError -> fieldError.getDefaultMessage() != null + ? fieldError.getDefaultMessage() + : "Validation error")); + + throw new ValidationException(bindingResult, null); + } + } +} \ No newline at end of file diff --git a/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java b/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java index e92337a..3d7e1dc 100644 --- a/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java +++ b/src/main/java/ru/soune/nocopy/service/file/FileEntityService.java @@ -372,7 +372,7 @@ public class FileEntityService { .fileName(fileEntity.getOriginalFileName()) .ownerName(user.getFullName()) .ownerEmail(user.getEmail()) - .ownerCompany(user.getCompany()) + .ownerCompany(user.getCompany().getCompanyName()) //TODO fix after add logic for check for protect file .checksCount(0) .fileUploadDate(fileEntity.getCreatedAt()) diff --git a/src/main/java/ru/soune/nocopy/service/register/AuthService.java b/src/main/java/ru/soune/nocopy/service/register/AuthService.java index c5272c8..ee8cf9e 100644 --- a/src/main/java/ru/soune/nocopy/service/register/AuthService.java +++ b/src/main/java/ru/soune/nocopy/service/register/AuthService.java @@ -5,14 +5,17 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.soune.nocopy.dto.*; +import ru.soune.nocopy.dto.register.AccountType; import ru.soune.nocopy.dto.register.LoginAnswer; import ru.soune.nocopy.dto.register.LoginRequest; import ru.soune.nocopy.dto.register.RegRequest; +import ru.soune.nocopy.entity.company.Company; import ru.soune.nocopy.entity.user.AuthToken; import ru.soune.nocopy.entity.user.User; import ru.soune.nocopy.exception.NotFoundAuthToken; import ru.soune.nocopy.exception.NotValidFieldException; import ru.soune.nocopy.repository.AuthTokenRepository; +import ru.soune.nocopy.repository.CompanyRepository; import ru.soune.nocopy.repository.UserRepository; import ru.soune.nocopy.service.file.CheckCounterService; @@ -30,6 +33,8 @@ public class AuthService { private final PasswordEncoder passwordEncoder; + private final CompanyRepository companyRepository; + private final CheckCounterService checkCounterService; private final SecureRandom secureRandom = new SecureRandom(); @@ -43,8 +48,14 @@ public class AuthService { user.setActive(true); user.setEmailVerified(true); - if (registerRequest.getCompanyName() != null) { - user.setCompany(registerRequest.getCompanyName()); + if (registerRequest.getCompanyName() != null && + AccountType.valueOf(registerRequest.getAccountType().toUpperCase()).equals(AccountType.B2B)) { + Company company = new Company(); + company.setCompanyName(registerRequest.getCompanyName()); + + companyRepository.save(company); + + user.setCompany(company); } if (registerRequest.getPhone() != null) { diff --git a/src/main/java/ru/soune/nocopy/service/user/UserService.java b/src/main/java/ru/soune/nocopy/service/user/UserService.java index efce0e4..7778f25 100644 --- a/src/main/java/ru/soune/nocopy/service/user/UserService.java +++ b/src/main/java/ru/soune/nocopy/service/user/UserService.java @@ -61,10 +61,6 @@ public class UserService { 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()); @@ -87,7 +83,7 @@ public class UserService { return UserDTO.builder() .fullName(user.getFullName()) .email(user.getEmail()) - .company(user.getCompany()) + .company(user.getCompany().getCompanyName()) .phone(user.getPhone()) .genderType(user.getGenderType()) .birthday(user.getBirthday())