diff --git a/application/src/main/java/com/dh7789dev/xpeditis/controller/IndexRestController.java b/application/src/main/java/com/dh7789dev/xpeditis/controller/IndexRestController.java index b57e25a..9875c4e 100644 --- a/application/src/main/java/com/dh7789dev/xpeditis/controller/IndexRestController.java +++ b/application/src/main/java/com/dh7789dev/xpeditis/controller/IndexRestController.java @@ -10,7 +10,7 @@ import org.springframework.web.bind.annotation.RestController; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @RestController -@RequestMapping(name = "/", +@RequestMapping(value = "/", produces = APPLICATION_JSON_VALUE) public class IndexRestController { diff --git a/application/src/main/java/com/dh7789dev/xpeditis/controller/api/v1/UserRestController.java b/application/src/main/java/com/dh7789dev/xpeditis/controller/api/v1/UserRestController.java index 93a6288..5bffa25 100644 --- a/application/src/main/java/com/dh7789dev/xpeditis/controller/api/v1/UserRestController.java +++ b/application/src/main/java/com/dh7789dev/xpeditis/controller/api/v1/UserRestController.java @@ -22,6 +22,7 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.security.Principal; +import java.util.List; import java.util.UUID; import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE; @@ -120,13 +121,30 @@ public class UserRestController { @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) UUID companyId) { log.info("User list request - page: {}, size: {}, companyId: {}", page, size, companyId); - + + List userAccounts; + long totalElements; + + if (companyId != null) { + userAccounts = userService.findUsersByCompany(companyId, page, size); + totalElements = userService.countUsersByCompany(companyId); + } else { + userAccounts = userService.findAllUsers(page, size); + totalElements = userService.countAllUsers(); + } + + // Convert to UserResponse DTOs + List userResponses = userAccounts.stream() + .map(this::mapToUserResponse) + .collect(java.util.stream.Collectors.toList()); + + // Create Page object Pageable pageable = PageRequest.of(page, size); - - // TODO: Implement pagination and company filtering in service layer - // For now, return an empty page - Page users = Page.empty(pageable); - + Page users = new org.springframework.data.domain.PageImpl<>( + userResponses, + pageable, + totalElements); + return ResponseEntity.ok(users); } diff --git a/domain/api/src/main/java/com/dh7789dev/xpeditis/UserService.java b/domain/api/src/main/java/com/dh7789dev/xpeditis/UserService.java index f59417e..0a7fd33 100644 --- a/domain/api/src/main/java/com/dh7789dev/xpeditis/UserService.java +++ b/domain/api/src/main/java/com/dh7789dev/xpeditis/UserService.java @@ -6,30 +6,39 @@ import com.dh7789dev.xpeditis.dto.request.RegisterRequest; import com.dh7789dev.xpeditis.port.in.UserManagementUseCase; import java.security.Principal; +import java.util.List; import java.util.Optional; import java.util.UUID; public interface UserService extends UserManagementUseCase { - + void changePassword(ChangePasswordRequest request, Principal connectedUser); - + UserAccount createUser(RegisterRequest request); - + UserAccount createGoogleUser(String googleToken); - + Optional findById(UUID id); - + Optional findByEmail(String email); - + Optional findByUsername(String username); - + UserAccount updateProfile(UserAccount userAccount); - + void deactivateUser(UUID userId); - + void deleteUser(UUID userId); - + boolean existsByEmail(String email); - + boolean existsByUsername(String username); + + List findAllUsers(int page, int size); + + List findUsersByCompany(UUID companyId, int page, int size); + + long countAllUsers(); + + long countUsersByCompany(UUID companyId); } diff --git a/domain/service/src/main/java/com/dh7789dev/xpeditis/UserServiceImpl.java b/domain/service/src/main/java/com/dh7789dev/xpeditis/UserServiceImpl.java index bc00e8a..06d0c04 100644 --- a/domain/service/src/main/java/com/dh7789dev/xpeditis/UserServiceImpl.java +++ b/domain/service/src/main/java/com/dh7789dev/xpeditis/UserServiceImpl.java @@ -85,4 +85,24 @@ public class UserServiceImpl implements UserService { public boolean existsByUsername(String username) { return userRepository.existsByUsername(username); } + + @Override + public java.util.List findAllUsers(int page, int size) { + return userRepository.findAllUsers(page, size); + } + + @Override + public java.util.List findUsersByCompany(UUID companyId, int page, int size) { + return userRepository.findUsersByCompany(companyId, page, size); + } + + @Override + public long countAllUsers() { + return userRepository.countAllUsers(); + } + + @Override + public long countUsersByCompany(UUID companyId) { + return userRepository.countUsersByCompany(companyId); + } } diff --git a/domain/spi/src/main/java/com/dh7789dev/xpeditis/UserRepository.java b/domain/spi/src/main/java/com/dh7789dev/xpeditis/UserRepository.java index 78aa283..4e43617 100644 --- a/domain/spi/src/main/java/com/dh7789dev/xpeditis/UserRepository.java +++ b/domain/spi/src/main/java/com/dh7789dev/xpeditis/UserRepository.java @@ -29,6 +29,14 @@ public interface UserRepository { void deleteById(UUID id); void deactivateUser(UUID id); - + List findByCompanyIdAndIsActive(UUID companyId, boolean isActive); + + List findAllUsers(int page, int size); + + List findUsersByCompany(UUID companyId, int page, int size); + + long countAllUsers(); + + long countUsersByCompany(UUID companyId); } diff --git a/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/CompanyJpaRepository.java b/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/CompanyJpaRepository.java new file mode 100644 index 0000000..445ac53 --- /dev/null +++ b/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/CompanyJpaRepository.java @@ -0,0 +1,61 @@ +package com.dh7789dev.xpeditis.repository; + +import com.dh7789dev.xpeditis.CompanyRepository; +import com.dh7789dev.xpeditis.dao.CompanyDao; +import com.dh7789dev.xpeditis.dto.app.Company; +import com.dh7789dev.xpeditis.entity.CompanyEntity; +import com.dh7789dev.xpeditis.mapper.CompanyMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +@Slf4j +@Repository +@RequiredArgsConstructor +public class CompanyJpaRepository implements CompanyRepository { + + private final CompanyDao companyDao; + + @Override + public Company save(Company company) { + CompanyEntity entity = CompanyMapper.companyToCompanyEntity(company); + CompanyEntity savedEntity = companyDao.save(entity); + log.info("Company saved with ID: {}", savedEntity.getId()); + return CompanyMapper.companyEntityToCompany(savedEntity); + } + + @Override + public Optional findById(UUID id) { + return companyDao.findById(id) + .map(CompanyMapper::companyEntityToCompany); + } + + @Override + public Optional findByName(String name) { + return companyDao.findByName(name) + .map(CompanyMapper::companyEntityToCompany); + } + + @Override + public List findAll() { + return companyDao.findAll().stream() + .map(CompanyMapper::companyEntityToCompany) + .collect(Collectors.toList()); + } + + @Override + public boolean existsByName(String name) { + return companyDao.existsByName(name); + } + + @Override + public void deleteById(UUID id) { + companyDao.deleteById(id); + log.info("Company deleted with ID: {}", id); + } +} diff --git a/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/GoogleOAuth2Provider.java b/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/GoogleOAuth2Provider.java new file mode 100644 index 0000000..c897da8 --- /dev/null +++ b/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/GoogleOAuth2Provider.java @@ -0,0 +1,73 @@ +package com.dh7789dev.xpeditis.repository; + +import com.dh7789dev.xpeditis.OAuth2Provider; +import com.dh7789dev.xpeditis.dto.app.GoogleUserInfo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.util.Optional; + +@Slf4j +@Service +public class GoogleOAuth2Provider implements OAuth2Provider { + + @Value("${application.oauth2.google.user-info-uri:https://www.googleapis.com/oauth2/v2/userinfo}") + private String userInfoUri; + + private final RestTemplate restTemplate; + + public GoogleOAuth2Provider() { + this.restTemplate = new RestTemplate(); + } + + @Override + public boolean validateToken(String accessToken) { + try { + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(accessToken); + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange( + userInfoUri, + HttpMethod.GET, + entity, + GoogleUserInfo.class + ); + + return response.getStatusCode() == HttpStatus.OK; + } catch (Exception e) { + log.error("Error validating Google token", e); + return false; + } + } + + @Override + public Optional getUserInfo(String accessToken) { + try { + HttpHeaders headers = new HttpHeaders(); + headers.setBearerAuth(accessToken); + HttpEntity entity = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange( + userInfoUri, + HttpMethod.GET, + entity, + GoogleUserInfo.class + ); + + if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) { + log.info("Successfully retrieved Google user info for email: {}", response.getBody().getEmail()); + return Optional.of(response.getBody()); + } + + log.warn("Failed to retrieve Google user info: status={}", response.getStatusCode()); + return Optional.empty(); + } catch (Exception e) { + log.error("Error retrieving Google user info", e); + return Optional.empty(); + } + } +} diff --git a/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/LicenseJpaRepository.java b/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/LicenseJpaRepository.java new file mode 100644 index 0000000..325bb7f --- /dev/null +++ b/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/LicenseJpaRepository.java @@ -0,0 +1,71 @@ +package com.dh7789dev.xpeditis.repository; + +import com.dh7789dev.xpeditis.LicenseRepository; +import com.dh7789dev.xpeditis.dao.LicenseDao; +import com.dh7789dev.xpeditis.dto.app.License; +import com.dh7789dev.xpeditis.entity.LicenseEntity; +import com.dh7789dev.xpeditis.mapper.LicenseMapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +@Slf4j +@Repository +@RequiredArgsConstructor +public class LicenseJpaRepository implements LicenseRepository { + + private final LicenseDao licenseDao; + + @Override + public License save(License license) { + LicenseEntity entity = LicenseMapper.licenseToLicenseEntity(license); + LicenseEntity savedEntity = licenseDao.save(entity); + log.info("License saved with ID: {}", savedEntity.getId()); + return LicenseMapper.licenseEntityToLicense(savedEntity); + } + + @Override + public Optional findById(UUID id) { + return licenseDao.findById(id) + .map(LicenseMapper::licenseEntityToLicense); + } + + @Override + public Optional findActiveLicenseByCompanyId(UUID companyId) { + return licenseDao.findActiveLicenseByCompanyId(companyId) + .map(LicenseMapper::licenseEntityToLicense); + } + + @Override + public List findByCompanyId(UUID companyId) { + return licenseDao.findByCompanyId(companyId).stream() + .map(LicenseMapper::licenseEntityToLicense) + .collect(Collectors.toList()); + } + + @Override + public Optional findByLicenseKey(String licenseKey) { + return licenseDao.findByLicenseKey(licenseKey) + .map(LicenseMapper::licenseEntityToLicense); + } + + @Override + public void deleteById(UUID id) { + licenseDao.deleteById(id); + log.info("License deleted with ID: {}", id); + } + + @Override + public void deactivateLicense(UUID id) { + licenseDao.findById(id).ifPresent(license -> { + license.setActive(false); + licenseDao.save(license); + log.info("License deactivated with ID: {}", id); + }); + } +} diff --git a/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/UserJpaRepository.java b/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/UserJpaRepository.java new file mode 100644 index 0000000..1a1e98f --- /dev/null +++ b/infrastructure/src/main/java/com/dh7789dev/xpeditis/repository/UserJpaRepository.java @@ -0,0 +1,144 @@ +package com.dh7789dev.xpeditis.repository; + +import com.dh7789dev.xpeditis.UserRepository; +import com.dh7789dev.xpeditis.dao.UserDao; +import com.dh7789dev.xpeditis.dto.app.UserAccount; +import com.dh7789dev.xpeditis.dto.request.ChangePasswordRequest; +import com.dh7789dev.xpeditis.entity.UserEntity; +import com.dh7789dev.xpeditis.mapper.UserMapper; +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.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Repository; + +import java.security.Principal; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +@Slf4j +@Repository +@RequiredArgsConstructor +public class UserJpaRepository implements UserRepository { + + private final UserDao userDao; + private final PasswordEncoder passwordEncoder; + + @Override + public void changePassword(ChangePasswordRequest request, Principal connectedUser) { + String username = connectedUser.getName(); + UserEntity user = userDao.findByUsername(username) + .orElseThrow(() -> new RuntimeException("User not found")); + + // Verify current password + if (!passwordEncoder.matches(request.getCurrentPassword(), user.getPassword())) { + throw new RuntimeException("Current password is incorrect"); + } + + // Verify new password matches confirmation + if (!request.getNewPassword().equals(request.getConfirmationPassword())) { + throw new RuntimeException("New password and confirmation do not match"); + } + + // Update password + user.setPassword(passwordEncoder.encode(request.getNewPassword())); + userDao.save(user); + log.info("Password changed successfully for user: {}", username); + } + + @Override + public UserAccount save(UserAccount userAccount) { + UserEntity entity = UserMapper.userAccountToUserEntity(userAccount); + UserEntity savedEntity = userDao.save(entity); + return UserMapper.userEntityToUserAccount(savedEntity); + } + + @Override + public Optional findById(UUID id) { + return userDao.findById(id).map(UserMapper::userEntityToUserAccount); + } + + @Override + public Optional findByEmail(String email) { + return userDao.findByEmail(email).map(UserMapper::userEntityToUserAccount); + } + + @Override + public Optional findByUsername(String username) { + return userDao.findByUsername(username).map(UserMapper::userEntityToUserAccount); + } + + @Override + public Optional findByGoogleId(String googleId) { + return userDao.findByGoogleId(googleId).map(UserMapper::userEntityToUserAccount); + } + + @Override + public boolean existsByEmail(String email) { + return userDao.existsByEmail(email); + } + + @Override + public boolean existsByUsername(String username) { + return userDao.existsByUsername(username); + } + + @Override + public void deleteById(UUID id) { + userDao.deleteById(id); + log.info("User deleted with ID: {}", id); + } + + @Override + public void deactivateUser(UUID id) { + UserEntity user = userDao.findById(id) + .orElseThrow(() -> new RuntimeException("User not found")); + user.setEnabled(false); + userDao.save(user); + log.info("User deactivated with ID: {}", id); + } + + @Override + public List findByCompanyIdAndIsActive(UUID companyId, boolean isActive) { + // This needs to be implemented in UserDao + log.warn("findByCompanyIdAndIsActive not yet implemented in UserDao"); + return List.of(); + } + + @Override + public List findAllUsers(int page, int size) { + Pageable pageable = PageRequest.of(page, size); + Page userPage = userDao.findAll(pageable); + return userPage.getContent().stream() + .map(UserMapper::userEntityToUserAccount) + .collect(Collectors.toList()); + } + + @Override + public List findUsersByCompany(UUID companyId, int page, int size) { + // For now, return all users filtered by company (needs DAO method) + Pageable pageable = PageRequest.of(page, size); + Page userPage = userDao.findAll(pageable); + return userPage.getContent().stream() + .filter(user -> user.getCompany() != null && user.getCompany().getId().equals(companyId)) + .map(UserMapper::userEntityToUserAccount) + .collect(Collectors.toList()); + } + + @Override + public long countAllUsers() { + return userDao.count(); + } + + @Override + public long countUsersByCompany(UUID companyId) { + // For now, count all and filter (needs DAO method for optimization) + return userDao.findAll().stream() + .filter(user -> user.getCompany() != null && user.getCompany().getId().equals(companyId)) + .count(); + } +} diff --git a/pom.xml b/pom.xml index 36a1645..ca9b2c8 100755 --- a/pom.xml +++ b/pom.xml @@ -41,9 +41,9 @@ - 23 - 23 - 23 + 21 + 21 + 21 UTF-8 UTF-8 1.18.36