From 42dfd277a6374b95f61d39d54e16c9781e9f8249 Mon Sep 17 00:00:00 2001 From: muhammadali Date: Mon, 14 Sep 2026 00:38:25 +0500 Subject: [PATCH] fixed login api and added select-organization api for selecting org --- .../example/crm/config/SecurityConfig.java | 1 + .../crm/controller/AuthController.java | 10 +++ .../org/example/crm/entity/dto/IdNameDto.java | 3 + .../crm/entity/login/LoginRequest.java | 3 - .../crm/entity/login/LoginResponse.java | 7 +- .../org/example/crm/entity/model/Student.java | 2 +- .../crm/initializer/DataInitializer.java | 15 ++-- .../crm/repository/StudentRepository.java | 18 +++++ .../org/example/crm/service/AuthService.java | 77 ++++++++++++++++--- 9 files changed, 114 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/example/crm/config/SecurityConfig.java b/src/main/java/org/example/crm/config/SecurityConfig.java index 64c33f0..3c126ea 100644 --- a/src/main/java/org/example/crm/config/SecurityConfig.java +++ b/src/main/java/org/example/crm/config/SecurityConfig.java @@ -36,6 +36,7 @@ public class SecurityConfig { "/api/v1/auth/login", "/api/v1/organization/name", "/api/v1/auth/refresh-token", + "/api/v1/auth/select-organization", "/v3/api-docs/**", "/v3/api-docs", "/swagger-ui/**", diff --git a/src/main/java/org/example/crm/controller/AuthController.java b/src/main/java/org/example/crm/controller/AuthController.java index 3f79a79..21798fc 100644 --- a/src/main/java/org/example/crm/controller/AuthController.java +++ b/src/main/java/org/example/crm/controller/AuthController.java @@ -31,6 +31,16 @@ public ResponseEntity login( return ResponseEntity.ok(loginResponseResponseEntity); } + @PostMapping("/select-organization") + public ResponseEntity selectOrganization( + @RequestParam String organizationId, + @RequestBody LoginRequest request, + HttpServletResponse response + ) { + LoginResponse loginResponseResponseEntity = authService.selectOrganization(organizationId, request,response); + return ResponseEntity.ok(loginResponseResponseEntity); + } + @PostMapping("/refresh-token") public ResponseEntity refreshToken(HttpServletRequest request, HttpServletResponse response) { diff --git a/src/main/java/org/example/crm/entity/dto/IdNameDto.java b/src/main/java/org/example/crm/entity/dto/IdNameDto.java index 294e357..dd4e8bc 100644 --- a/src/main/java/org/example/crm/entity/dto/IdNameDto.java +++ b/src/main/java/org/example/crm/entity/dto/IdNameDto.java @@ -1,5 +1,8 @@ package org.example.crm.entity.dto; +import lombok.Builder; + +@Builder public record IdNameDto( String id, String name diff --git a/src/main/java/org/example/crm/entity/login/LoginRequest.java b/src/main/java/org/example/crm/entity/login/LoginRequest.java index 412d5a7..6f0c61d 100644 --- a/src/main/java/org/example/crm/entity/login/LoginRequest.java +++ b/src/main/java/org/example/crm/entity/login/LoginRequest.java @@ -11,7 +11,4 @@ public class LoginRequest { @NotBlank(message = "Password is required") private String password; - - @NotBlank(message = "Organization ID is required") - private String organizationId; } \ No newline at end of file diff --git a/src/main/java/org/example/crm/entity/login/LoginResponse.java b/src/main/java/org/example/crm/entity/login/LoginResponse.java index a413dfb..a849c79 100644 --- a/src/main/java/org/example/crm/entity/login/LoginResponse.java +++ b/src/main/java/org/example/crm/entity/login/LoginResponse.java @@ -1,6 +1,9 @@ package org.example.crm.entity.login; import lombok.*; +import org.example.crm.entity.dto.IdNameDto; + +import java.util.List; @Getter @Setter @@ -9,5 +12,7 @@ @Builder public class LoginResponse { private String token; - private long expiry; + private Long expiry; + private boolean requiresOrganizationSelection; + private List organizations; } diff --git a/src/main/java/org/example/crm/entity/model/Student.java b/src/main/java/org/example/crm/entity/model/Student.java index 41617e0..8d24a56 100644 --- a/src/main/java/org/example/crm/entity/model/Student.java +++ b/src/main/java/org/example/crm/entity/model/Student.java @@ -24,7 +24,7 @@ public class Student extends BaseEntity { private BigDecimal balance; @ManyToOne(optional = false, cascade = CascadeType.PERSIST) - @JoinColumn(name = "user_id", referencedColumnName = "id", unique = true) + @JoinColumn(name = "user_id", referencedColumnName = "id") private User user; } \ No newline at end of file diff --git a/src/main/java/org/example/crm/initializer/DataInitializer.java b/src/main/java/org/example/crm/initializer/DataInitializer.java index c38aee5..7b82fb1 100644 --- a/src/main/java/org/example/crm/initializer/DataInitializer.java +++ b/src/main/java/org/example/crm/initializer/DataInitializer.java @@ -18,8 +18,8 @@ @Component @RequiredArgsConstructor -//implements CommandLineRunner -public class DataInitializer { +// +public class DataInitializer implements CommandLineRunner { final UserRepository userRepository; final TeacherRepository teacherRepository; @@ -34,15 +34,18 @@ public class DataInitializer { final BranchRepository branchRepository; final EntityManager entityManager; Organization organization = new Organization("org", "phone", "email", "website"); + Organization organization1 = new Organization("org1", "phone1", "email1", "website1"); -// @Override + + @Override @Transactional public void run(String... args) { // if (userRepository.count() > 0) return; organizationRepository.save(organization); + organizationRepository.save(organization1); String encodedPassword = passwordEncoder.encode("root1234"); @@ -187,9 +190,9 @@ public void run(String... args) { studentRepository.save(student1); Student student2 = new Student(); - student2.setOrganizationId(organization.getId()); - student2.setUser(studentUser2); - student2.setParentPhone("+998901234581"); + student2.setOrganizationId(organization1.getId()); + student2.setUser(studentUser1); + student2.setParentPhone("+998901234580"); studentRepository.save(student2); Student student3 = new Student(); diff --git a/src/main/java/org/example/crm/repository/StudentRepository.java b/src/main/java/org/example/crm/repository/StudentRepository.java index f8375df..4ff9c79 100644 --- a/src/main/java/org/example/crm/repository/StudentRepository.java +++ b/src/main/java/org/example/crm/repository/StudentRepository.java @@ -103,4 +103,22 @@ AnalyticStudentProjection getAnalyticStudent(String organizationId, @Query("select s.organizationId from Student s where s.user.id=:userId and s.organizationId=:orgId") Optional findOrgId(@Param("userId") String id, @Param("orgId") String organizationId); + + + @Query(""" + select count(s.id) + from Student s + where s.user.id=:userId +""") + Long countStudentsByUser_Id(String id); + + + @Query(""" + select s + from Student s + where s.user.id=:userId + """) + List findAllByUserId(String userId); + + Optional findStudentByOrganizationIdAndUserId(String organizationId, String userId); } diff --git a/src/main/java/org/example/crm/service/AuthService.java b/src/main/java/org/example/crm/service/AuthService.java index 12c3f46..d1e6a14 100644 --- a/src/main/java/org/example/crm/service/AuthService.java +++ b/src/main/java/org/example/crm/service/AuthService.java @@ -8,11 +8,13 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.example.crm.config.JwtUtils; +import org.example.crm.entity.dto.IdNameDto; import org.example.crm.entity.dto.user.UserDto; import org.example.crm.entity.enums.Role; import org.example.crm.entity.login.LoginRequest; import org.example.crm.entity.login.LoginResponse; import org.example.crm.entity.login.TokenDto; +import org.example.crm.entity.model.Student; import org.example.crm.entity.model.User; import org.example.crm.entity.request.ChangePasswordRequest; import org.example.crm.exceptions.ErrorCodes; @@ -20,6 +22,7 @@ import org.example.crm.exceptions.RestException; import org.example.crm.mapper.UserMapper; import org.example.crm.repository.OrganizationRepository; +import org.example.crm.repository.StudentRepository; import org.example.crm.repository.UserRepository; import org.example.crm.validator.UserValidator; import org.springframework.beans.factory.annotation.Value; @@ -30,6 +33,7 @@ import org.springframework.validation.annotation.Validated; import java.util.Arrays; +import java.util.List; import java.util.Map; @Slf4j @@ -44,6 +48,7 @@ public class AuthService { private final UserMapper userMapper; private final UserValidator userValidator; final OrganizationRepository organizationRepository; + final StudentRepository studentRepository; @Value("${jwt.refresh.token.expire.date:86400}") private Long refreshTokenExpiration; @@ -61,19 +66,35 @@ public LoginResponse getLoginResponseResponseEntity(@Valid LoginRequest request, throw new RestException(ErrorType.INVALID_PHONE_NUMBER_OR_PASSWORD, ErrorCodes.BadRequest); } - boolean b = organizationRepository.existsById(request.getOrganizationId()); - if (!b) { - throw new RestException(ErrorType.ORGANIZATION_NOT_FOUND, ErrorCodes.NotFound); + List studentList = studentRepository.findAllByUserId(user.getId()); + + if (user.getRole().equals(Role.STUDENT)&& !studentList.isEmpty()) { + if (studentList.size() > 1) { + List orgId = studentList.stream().map(Student::getOrganizationId).toList(); + List idNameDtos = organizationRepository.findAllById(orgId).stream().map( + organization -> IdNameDto.builder() + .id(organization.getId()) + .name(organization.getName()) + .build()).toList(); + + return LoginResponse.builder() + .token(null) + .expiry(null) + .requiresOrganizationSelection(true) + .organizations(idNameDtos) + .build(); + } + + Student student = studentList.get(0); + if (!student.getOrganizationId().equals(user.getOrganizationId())) { + throw new RestException(ErrorType.WRONG_ORGANIZATION, ErrorCodes.Forbidden); + } + } - Map claims = jwtUtils.prepareClaims(user, request.getOrganizationId()); - TokenDto accessToken = jwtUtils.generateToken(user.getPhone(), claims, "access"); - TokenDto refreshToken = jwtUtils.generateToken(user.getPhone(), claims, "refresh"); - setRefreshCookie(response, refreshToken.getToken()); - return LoginResponse.builder() - .token(accessToken.getToken()) - .expiry(accessToken.getExpiry()) - .build(); + return getLoginResponse(user.getOrganizationId(), response, user); + + } @@ -142,4 +163,38 @@ public UserDto getMe() { User user = userValidator.authenticateAndGetUser(); return userMapper.toDto(user); } + + public LoginResponse selectOrganization(String organizationId, LoginRequest request, HttpServletResponse response) { + User user = userRepository.findByPhoneAndDeletedFalse(request.getPhone()) + .orElseThrow(() -> new RestException(ErrorType.INVALID_PHONE_NUMBER_OR_PASSWORD, ErrorCodes.BadRequest)); + + if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) { + throw new RestException(ErrorType.INVALID_PHONE_NUMBER_OR_PASSWORD, ErrorCodes.BadRequest); + } + + boolean b = organizationRepository.existsById(organizationId); + if (!b) { + throw new RestException(ErrorType.ORGANIZATION_NOT_FOUND, ErrorCodes.NotFound); + } + studentRepository.findStudentByOrganizationIdAndUserId(organizationId, user.getId()) + .orElseThrow(() -> new RestException(ErrorType.STUDENT_NOT_FOUND, ErrorCodes.NotFound)); + + + return getLoginResponse(organizationId, response, user); + } + + private LoginResponse getLoginResponse(String organizationId, HttpServletResponse response, User user) { + Map claims = jwtUtils.prepareClaims(user, organizationId); + TokenDto accessToken = jwtUtils.generateToken(user.getPhone(), claims, "access"); + TokenDto refreshToken = jwtUtils.generateToken(user.getPhone(), claims, "refresh"); + setRefreshCookie(response, refreshToken.getToken()); + + + return LoginResponse.builder() + .token(accessToken.getToken()) + .expiry(accessToken.getExpiry()) + .requiresOrganizationSelection(false) + .organizations(null) + .build(); + } }