diff --git a/src/main/java/de/avatic/lcc/controller/users/UserController.java b/src/main/java/de/avatic/lcc/controller/users/UserController.java index 21577bd..b6367b2 100644 --- a/src/main/java/de/avatic/lcc/controller/users/UserController.java +++ b/src/main/java/de/avatic/lcc/controller/users/UserController.java @@ -46,7 +46,9 @@ public class UserController { } /** - * Updates the details of an existing user. + * Updates the details of an existing user or creates a new one if it does not exist. + * Users are identified by its workday id. If a group from the group membership does not exist + * it is simply ignored. * * @param user A UserDTO object containing the updated user details. * @return A ResponseEntity indicating the operation was successful. diff --git a/src/main/java/de/avatic/lcc/repositories/users/GroupRepository.java b/src/main/java/de/avatic/lcc/repositories/users/GroupRepository.java index e38e0ab..f9b1a77 100644 --- a/src/main/java/de/avatic/lcc/repositories/users/GroupRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/users/GroupRepository.java @@ -1,13 +1,62 @@ package de.avatic.lcc.repositories.users; +import de.avatic.lcc.model.materials.Material; import de.avatic.lcc.model.users.Group; +import de.avatic.lcc.repositories.MaterialRepository; +import de.avatic.lcc.repositories.pagination.SearchQueryPagination; +import de.avatic.lcc.repositories.pagination.SearchQueryResult; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowCallbackHandler; +import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.List; @Repository public class GroupRepository { - public List listGroups() { + private final JdbcTemplate jdbcTemplate; + + public GroupRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + + @Transactional + public SearchQueryResult listGroups(SearchQueryPagination pagination) { + + String query = "SELECT * FROM sys_group ORDER BY group_name LIMIT ? OFFSET ?"; + + var groups = jdbcTemplate.query(query, new GroupMapper(), + pagination.getLimit(), pagination.getOffset()); + + Integer totalCount = jdbcTemplate.queryForObject( + "SELECT COUNT(*) FROM sys_group ORDER BY group_name LIMIT ? OFFSET ?", + Integer.class, pagination.getLimit(), pagination.getOffset() + ); + + return new SearchQueryResult<>(groups, pagination.getPage(), totalCount, pagination.getLimit()); } + + @Transactional + public void updateGroup(Group group) { + String query = "INSERT INTO sys_group (group_name, group_description) VALUES (?, ?) ON DUPLICATE KEY UPDATE group_description = ?"; + jdbcTemplate.update(query, group.getName(), group.getDescription(), group.getName(), group.getDescription()); + } + + private static class GroupMapper implements RowMapper { + + @Override + public Group mapRow(ResultSet rs, int rowNum) throws SQLException { + var group = new Group(); + + group.setId(rs.getInt("id")); + group.setName(rs.getString("group_name")); + group.setDescription(rs.getString("group_description")); + + return group; + } + } } diff --git a/src/main/java/de/avatic/lcc/repositories/users/UserRepository.java b/src/main/java/de/avatic/lcc/repositories/users/UserRepository.java index 7a7b17d..6b642ab 100644 --- a/src/main/java/de/avatic/lcc/repositories/users/UserRepository.java +++ b/src/main/java/de/avatic/lcc/repositories/users/UserRepository.java @@ -1,23 +1,167 @@ package de.avatic.lcc.repositories.users; +import de.avatic.lcc.model.users.Group; import de.avatic.lcc.model.users.User; import de.avatic.lcc.repositories.pagination.SearchQueryPagination; import de.avatic.lcc.repositories.pagination.SearchQueryResult; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.GeneratedKeyHolder; +import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; +import java.sql.PreparedStatement; +import java.sql.Statement; +import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; @Repository public class UserRepository { + private final JdbcTemplate jdbcTemplate; + + public UserRepository(JdbcTemplate jdbcTemplate) { + this.jdbcTemplate = jdbcTemplate; + } + @Transactional public SearchQueryResult listUsers(SearchQueryPagination pagination) { - String query = "SELECT * FROM users"; + String query = """ + SELECT * + FROM sys_user + ORDER BY sys_user.workday_id LIMIT ? OFFSET ?"""; + + return new SearchQueryResult<>(jdbcTemplate.query(query, (rs, rowNum) -> { + var user = new User(); + + int id = rs.getInt("id"); + + user.setId(id); + user.setActive(rs.getBoolean("is_active")); + user.setEmail(rs.getString("email")); + user.setFirstName(rs.getString("firstname")); + user.setLastName(rs.getString("lastname")); + user.setWorkdayId(rs.getString("workday_id")); + + user.setGroups(getGroupMemberships(id)); + + return user; + }, pagination.getLimit(), pagination.getOffset()), pagination.getPage(), getTotalUserCount(), pagination.getLimit()); + } + private Integer getTotalUserCount() { + String query = "SELECT COUNT(*) FROM sys_user"; + return jdbcTemplate.queryForObject(query, Integer.class); + } + + private List getGroupMemberships(int id) { + + String query = "SELECT * FROM sys_group WHERE id IN (SELECT group_id FROM sys_user_group_mapping WHERE user_id = ?)"; + + return jdbcTemplate.query(query, (rs, rowNum) -> { + var group = new Group(); + + group.setId(rs.getInt("id")); + group.setName(rs.getString("group_name")); + group.setDescription(rs.getString("group_description")); + + return group; + }, id); + } + + @Transactional public void update(User user) { + Integer userId = findUserId(user.getWorkdayId()); + + List groupIds = findGroupIds(user.getGroups().stream().map(Group::getName).toList()); + + if(userId == null) { + KeyHolder keyHolder = new GeneratedKeyHolder(); + jdbcTemplate.update(connection -> { + PreparedStatement ps = connection.prepareStatement( + "INSERT INTO sys_user (workday_id, email, firstname, lastname, is_active) " + + "VALUES (?, ?, ?, ?, ?)", + Statement.RETURN_GENERATED_KEYS + ); + ps.setString(1, user.getWorkdayId()); + ps.setString(2, user.getEmail()); + ps.setString(3, user.getFirstName()); + ps.setString(4, user.getLastName()); + ps.setBoolean(5, user.getActive()); + return ps; + }, keyHolder); + + userId = Objects.requireNonNull(keyHolder.getKey()).intValue(); + } + else { + String query = """ + UPDATE sys_user SET email = ?, firstname = ?, lastname = ?, workday_id = ?, is_active = ? WHERE id = ?"""; + + jdbcTemplate.update(query, user.getEmail(), user.getFirstName(), user.getLastName(), user.getWorkdayId(), user.getActive(), userId); + } + + updateUserGroupMappings(userId, groupIds); + + } + + private List findGroupIds(List groups) { + if (groups == null || groups.isEmpty()) { + return List.of(); + } + + // Create placeholders for parameterized query + String placeholders = String.join(",", Collections.nCopies(groups.size(), "?")); + String query = "SELECT id FROM sys_group WHERE group_name IN (" + placeholders + ")"; + + + return jdbcTemplate.query( + query, + ps -> { + for (int parameterIndex = 1; parameterIndex <= groups.size(); parameterIndex++) { + ps.setString(parameterIndex, groups.get(parameterIndex)); + } + }, + (rs, rowNum) -> rs.getInt("id") + ); + + } + + private void updateUserGroupMappings(Integer userId, List groups) { + + for (Integer groupId : groups) { + jdbcTemplate.update( + "INSERT IGNORE INTO sys_user_group_mapping (user_id, group_id) VALUES (?, ?)", + userId, groupId + ); + } + + String placeholders = String.join(",", Collections.nCopies(groups.size(), "?")); + String query = "DELETE FROM sys_user_group_mapping WHERE user_id = ? AND group_id NOT IN (" + placeholders + ")"; + + jdbcTemplate.query( + query, + ps -> { + for (int parameterIndex = 1; parameterIndex <= groups.size(); parameterIndex++) { + ps.setInt(parameterIndex, groups.get(parameterIndex)); + } + }, + (rs, rowNum) -> rs.getInt("id") + ); + } + + + private Integer findUserId(String workdayId) { + List results = jdbcTemplate.query("SELECT id FROM sys_user WHERE workday_id = ?", + (rs, rowNum) -> rs.getInt("id"), + workdayId); + + return results.isEmpty() ? null : results.getFirst(); + } + } diff --git a/src/main/java/de/avatic/lcc/service/users/GroupService.java b/src/main/java/de/avatic/lcc/service/users/GroupService.java index 23dc2e4..c468362 100644 --- a/src/main/java/de/avatic/lcc/service/users/GroupService.java +++ b/src/main/java/de/avatic/lcc/service/users/GroupService.java @@ -2,12 +2,18 @@ package de.avatic.lcc.service.users; import de.avatic.lcc.dto.users.GroupDTO; import de.avatic.lcc.dto.users.UserDTO; +import de.avatic.lcc.model.users.Group; +import de.avatic.lcc.repositories.pagination.SearchQueryPagination; import de.avatic.lcc.repositories.pagination.SearchQueryResult; import de.avatic.lcc.repositories.users.GroupRepository; import org.springframework.stereotype.Service; import java.util.List; +/** + * Service class for managing group-related operations such as updating groups + * and retrieving group data. + */ @Service public class GroupService { private final GroupRepository groupRepository; @@ -16,16 +22,54 @@ public class GroupService { this.groupRepository = groupRepository; } - - + /** + * Updates an existing group in the repository. + * + * @param group the {@link GroupDTO} object containing updated group information + */ public void updateGroup(GroupDTO group) { - + groupRepository.updateGroup(fromGroupDTO(group)); } + /** + * Retrieves a paginated list of groups from the repository. + * + * @param page the page number to retrieve + * @param limit the maximum number of groups returned per page + * @return a {@link SearchQueryResult} containing {@link GroupDTO} objects and pagination metadata + */ public SearchQueryResult listGroups(int page, int limit) { + return SearchQueryResult.map(groupRepository.listGroups(new SearchQueryPagination(page, limit)), this::toGroupDTO); + } + + /** + * Maps a {@link GroupDTO} object to a {@link Group} entity. + * + * @param dto the {@link GroupDTO} to be mapped + * @return a {@link Group} entity with data populated from the DTO + */ + private Group fromGroupDTO(GroupDTO dto) { + var group = new Group(); + + group.setDescription(dto.getDescription()); + group.setName(dto.getName()); + + return group; + } + + /** + * Maps a {@link Group} entity to a {@link GroupDTO}. + * + * @param group the {@link Group} entity to be mapped + * @return a {@link GroupDTO} containing data from the entity + */ + private GroupDTO toGroupDTO(Group group) { + var dto = new GroupDTO(); + + dto.setDescription( group.getDescription()); + dto.setName(group.getName()); + + return dto; - groupRepository.listGroups(); - //todo translate - return null; } } diff --git a/src/main/java/de/avatic/lcc/service/users/UserService.java b/src/main/java/de/avatic/lcc/service/users/UserService.java index 8f225ee..d01c255 100644 --- a/src/main/java/de/avatic/lcc/service/users/UserService.java +++ b/src/main/java/de/avatic/lcc/service/users/UserService.java @@ -1,37 +1,45 @@ package de.avatic.lcc.service.users; import de.avatic.lcc.dto.users.UserDTO; -import de.avatic.lcc.model.users.User; import de.avatic.lcc.repositories.pagination.SearchQueryPagination; import de.avatic.lcc.repositories.pagination.SearchQueryResult; import de.avatic.lcc.repositories.users.UserRepository; -import de.avatic.lcc.service.transformer.users.UserDTOTransformer; +import de.avatic.lcc.service.transformer.users.UserTransformer; import org.springframework.stereotype.Service; -import java.util.List; - +/** + * Service class responsible for handling business logic related to users. + * Provides methods to retrieve and update user information. + */ @Service public class UserService { private final UserRepository userRepository; - private final UserDTOTransformer userDTOTransformer; + private final UserTransformer userTransformer; - public UserService(UserRepository userRepository, UserDTOTransformer userDTOTransformer) { + public UserService(UserRepository userRepository, UserTransformer userTransformer) { this.userRepository = userRepository; - this.userDTOTransformer = userDTOTransformer; + this.userTransformer = userTransformer; } + /** + * Retrieves a paginated list of users. + * + * @param page the page number to retrieve + * @param limit the number of users per page + * @return a SearchQueryResult containing the paginated user data + */ public SearchQueryResult listUsers(int page, int limit) { - return SearchQueryResult.map(userRepository.listUsers(new SearchQueryPagination(page, limit)), userDTOTransformer::toUserDTO); + return SearchQueryResult.map(userRepository.listUsers(new SearchQueryPagination(page, limit)), userTransformer::toUserDTO); } + /** + * Updates an existing user's information. + * + * @param user the UserDTO containing updated user information + */ public void updateUser(UserDTO user) { - - groupRepository.update - userRepository.update(fromUserDTO(user)); + userRepository.update(userTransformer.fromUserDTO(user)); } - private User fromUserDTO(UserDTO dto) { - return new User(); - } }