commit
This commit is contained in:
@@ -4,12 +4,15 @@ import java.util.List;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import com.spring.domain.user.dto.ChangeUserRoleApproveRequest;
|
||||
import com.spring.domain.user.dto.UserFindRequest;
|
||||
import com.spring.domain.user.dto.UserManagementResponse;
|
||||
import com.spring.domain.user.service.UserManagementService;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -21,8 +24,13 @@ public class UserManagementApi {
|
||||
|
||||
private final UserManagementService userManagementService;
|
||||
|
||||
@PostMapping("/change-role-approve")
|
||||
public void signUp(@RequestBody @Valid List<ChangeUserRoleApproveRequest> requests) {
|
||||
@GetMapping
|
||||
public List<UserManagementResponse> getUsers(UserFindRequest request) {
|
||||
return userManagementService.getUsers(request);
|
||||
}
|
||||
|
||||
@PutMapping("/change-role-approve")
|
||||
public void changeRoleApprove(@RequestBody @Valid List<ChangeUserRoleApproveRequest> requests) {
|
||||
userManagementService.changeRoleApprove(requests);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ import lombok.RequiredArgsConstructor;
|
||||
@RequiredArgsConstructor
|
||||
public class ChangeUserRoleApproveRequest {
|
||||
|
||||
@NotBlank(message = "사용자ID는 필수값 입니다.")
|
||||
private final String userId;
|
||||
@NotBlank(message = "ID는 필수값 입니다.")
|
||||
private final String id;
|
||||
|
||||
@EnumValid(target = AgentUserRole.class, message = "권한은 필수값 입니다.")
|
||||
private final AgentUserRole userRole;
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.spring.domain.user.dto;
|
||||
|
||||
import com.spring.domain.user.entity.AgentUser;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public class UserFindRequest {
|
||||
|
||||
private final String userId;
|
||||
private final String userName;
|
||||
private final String email;
|
||||
private final String approved;
|
||||
|
||||
public static boolean matchesCriteria(AgentUser user, UserFindRequest request) {
|
||||
return matchesUserId(user, request.getUserId()) &&
|
||||
matchesUserName(user, request.getUserName()) &&
|
||||
matchesEmail(user, request.getEmail()) &&
|
||||
matchesApprovedCriteria(request.getApproved(), user.isApproved());
|
||||
}
|
||||
|
||||
public static boolean matchesUserId(AgentUser user, String userId) {
|
||||
return userId == null || userId.isEmpty() || user.getUserId().contains(userId);
|
||||
}
|
||||
|
||||
public static boolean matchesUserName(AgentUser user, String userName) {
|
||||
return userName == null || userName.isEmpty() || user.getMemberName().contains(userName);
|
||||
}
|
||||
|
||||
public static boolean matchesEmail(AgentUser user, String email) {
|
||||
return email == null || email.isEmpty() || user.getEmail().contains(email);
|
||||
}
|
||||
|
||||
public static boolean matchesApprovedCriteria(String approved, boolean userApproved) {
|
||||
return "all".equals(approved) ||
|
||||
("true".equals(approved) && userApproved) ||
|
||||
("false".equals(approved) && !userApproved);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.spring.domain.user.dto;
|
||||
|
||||
import com.spring.domain.user.entity.AgentUser;
|
||||
import com.spring.domain.user.entity.AgentUserRole;
|
||||
|
||||
import lombok.Getter;
|
||||
@@ -9,9 +10,22 @@ import lombok.RequiredArgsConstructor;
|
||||
@RequiredArgsConstructor
|
||||
public class UserManagementResponse {
|
||||
|
||||
private final String id;
|
||||
private final String userId;
|
||||
private final String userName;
|
||||
private final String email;
|
||||
private final boolean isApproved;
|
||||
private final AgentUserRole userRole;
|
||||
|
||||
public static UserManagementResponse valueOf(AgentUser user) {
|
||||
return new UserManagementResponse(
|
||||
user.getId().toString(),
|
||||
user.getUserId(),
|
||||
user.getMemberName(),
|
||||
user.getEmail(),
|
||||
user.isApproved(),
|
||||
user.getUserRole()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package com.spring.domain.user.service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.spring.domain.user.dto.ChangeUserRoleApproveRequest;
|
||||
import com.spring.domain.user.dto.UserFindRequest;
|
||||
import com.spring.domain.user.dto.UserManagementResponse;
|
||||
import com.spring.domain.user.entity.AgentUser;
|
||||
import com.spring.domain.user.error.UserNotFoundException;
|
||||
import com.spring.domain.user.repository.AgentUserRepository;
|
||||
@@ -18,10 +22,19 @@ public class UserManagementService {
|
||||
|
||||
private final AgentUserRepository agentUserRepository;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<UserManagementResponse> getUsers(UserFindRequest request) {
|
||||
return agentUserRepository.findAll().stream()
|
||||
.filter(user -> UserFindRequest.matchesCriteria(user, request))
|
||||
.map(UserManagementResponse::valueOf)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public void changeRoleApprove(List<ChangeUserRoleApproveRequest> requests) {
|
||||
for (ChangeUserRoleApproveRequest request : requests) {
|
||||
AgentUser user = agentUserRepository.findByUserId(request.getUserId()).orElseThrow(UserNotFoundException::new);
|
||||
AgentUser user = agentUserRepository.findById(UUID.fromString(request.getId()))
|
||||
.orElseThrow(UserNotFoundException::new);
|
||||
user.changeUserRole(request.getUserRole());
|
||||
user.changeApproved(request.isApproved());
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import apiClient from '../common/axios-instance.js';
|
||||
|
||||
const userService = {
|
||||
getUserList: async () => {
|
||||
const response = await apiClient.get('/api/user');
|
||||
return response.data;
|
||||
getUsers: async (searchParams) => {
|
||||
const response = await apiClient.get('/api/user', { params: searchParams });
|
||||
return response.data.data;
|
||||
},
|
||||
|
||||
changeRoleApprove: async (users) => {
|
||||
|
||||
@@ -1,59 +1,72 @@
|
||||
import userService from '../../apis/user-api.js';
|
||||
|
||||
let users = []; // 사용자 목록을 저장할 배열
|
||||
let users = [];
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
loadUserList();
|
||||
document.getElementById('saveChanges').addEventListener('click', saveChanges);
|
||||
fetchDataAndRender();
|
||||
setupEventListeners();
|
||||
});
|
||||
|
||||
const loadUserList = async () => {
|
||||
users = await userService.getUserList();
|
||||
const userTableBody = document.getElementById('userTableBody');
|
||||
userTableBody.innerHTML = ''; // 기존 내용 초기화
|
||||
const setupEventListeners = () => {
|
||||
document.getElementById('searchForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
fetchDataAndRender();
|
||||
});
|
||||
document.getElementById('saveChangesBtn').addEventListener('click', saveChanges);
|
||||
};
|
||||
|
||||
users.forEach(user => {
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td>${user.userId}</td>
|
||||
<td>${user.email}</td>
|
||||
<td>${user.userName}</td>
|
||||
<td>
|
||||
<select class="form-select" data-user-id="${user.id}" data-user-role="${user.userRole}">
|
||||
<option value="USER" ${user.userRole === 'USER' ? 'selected' : ''}>USER</option>
|
||||
<option value="ADMIN" ${user.userRole === 'ADMIN' ? 'selected' : ''}>ADMIN</option>
|
||||
const fetchDataAndRender = async () => {
|
||||
const searchParams = new URLSearchParams(new FormData(document.getElementById('searchForm')));
|
||||
users = await userService.getUsers(searchParams);
|
||||
updateTable(users);
|
||||
};
|
||||
|
||||
const updateTable = (users) => {
|
||||
const tableBody = document.querySelector('tbody');
|
||||
tableBody.innerHTML = users.map(user => `
|
||||
<tr>
|
||||
<td class="align-middle">${user.userId}</td>
|
||||
<td class="align-middle">${user.userName}</td>
|
||||
<td class="align-middle">${user.email}</td>
|
||||
<td class="align-middle">
|
||||
<select class="form-select form-select-sm" data-user-id="${user.id}" data-user-role="${user.userRole}">
|
||||
${ROLES.length > 0 ? ROLES.map(role => `
|
||||
<option value="${role}" ${user.userRole === role ? 'selected' : ''}>${role}</option>
|
||||
`).join('') : '<option value=""></option>'}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" ${user.isApproved ? 'checked' : ''} data-user-id="${user.id}" data-user-approved="${user.isApproved}">
|
||||
<td class="align-middle">
|
||||
<input type="checkbox" ${user.approved ? 'checked' : ''} data-user-id="${user.id}" data-user-approved="${user.approved}" class="form-check-input">
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-danger" onclick="deleteUser('${user.id}')">삭제</button>
|
||||
<td class="align-middle">
|
||||
<button id="deleteUserBtn" onclick="deleteUser('${user.id}')" class="btn btn-sm btn-outline-danger" data-bs-toggle="tooltip" data-bs-placement="left" title="사용자 삭제">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
userTableBody.appendChild(row);
|
||||
});
|
||||
</tr>
|
||||
`).join('');
|
||||
};
|
||||
|
||||
const saveChanges = async () => {
|
||||
const updatedUsers = users.map(user => {
|
||||
const selectElement = document.querySelector(`select[data-user-id="${user.id}"]`);
|
||||
const checkboxElement = document.querySelector(`input[data-user-id="${user.id}"]`);
|
||||
|
||||
const checkboxElement = document.querySelector(`input[data-user-approved="${user.approved}"]`);
|
||||
const userRole = selectElement ? selectElement.value : null;
|
||||
const isApproved = checkboxElement ? checkboxElement.checked : false;
|
||||
return {
|
||||
id: user.id,
|
||||
role: selectElement.value,
|
||||
isApproved: checkboxElement.checked
|
||||
userRole: userRole,
|
||||
isApproved: isApproved
|
||||
};
|
||||
});
|
||||
|
||||
await userService.updateUsers(updatedUsers); // 배열로 사용자 정보를 전송
|
||||
alert('모든 변경 사항이 저장되었습니다.');
|
||||
loadUserList(); // 사용자 목록 새로 고침
|
||||
await userService.changeRoleApprove(updatedUsers);
|
||||
alert('회원정보가 수정 되었습니다.');
|
||||
fetchDataAndRender();
|
||||
};
|
||||
|
||||
const deleteUser = async (userId) => {
|
||||
await userService.deleteUser(userId);
|
||||
alert('사용자가 삭제되었습니다.');
|
||||
loadUserList(); // 사용자 목록 새로 고침
|
||||
fetchDataAndRender();
|
||||
};
|
||||
@@ -8,6 +8,9 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/schedule"><i class="bi bi-menu-button-wide"></i><span>Schedule</span></a>
|
||||
</li>
|
||||
<li class="nav-item"></li>
|
||||
<a class="nav-link" href="/user/management"><i class="bi bi-person"></i><span>User Management</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
</html>
|
||||
@@ -4,30 +4,113 @@
|
||||
layout:decorate="~{layouts/layout}"
|
||||
layout:fragment="content" lang="ko" xml:lang="ko">
|
||||
<head>
|
||||
<title>User Management</title>
|
||||
<title>사용자 관리</title>
|
||||
</head>
|
||||
<body>
|
||||
<section>
|
||||
<div class="container">
|
||||
<h2 class="mt-4">회원 관리</h2>
|
||||
<table class="table table-striped mt-3">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>아이디</th>
|
||||
<th>이메일</th>
|
||||
<th>사용자명</th>
|
||||
<th>권한</th>
|
||||
<th>승인 여부</th>
|
||||
<th>작업</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="userTableBody">
|
||||
</tbody>
|
||||
</table>
|
||||
<button class="btn btn-primary" id="saveChanges">저장</button>
|
||||
<main id="main" class="main">
|
||||
<div class="pagetitle">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<h1><i class="bi bi-person"></i> 사용자 관리</h1>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<nav>
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item">홈</li>
|
||||
<li class="breadcrumb-item active">사용자 관리</li>
|
||||
</ol>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title fs-6 text-dark">
|
||||
<i class="bi bi-search"></i> 사용자 검색
|
||||
</h5>
|
||||
<form id="searchForm" class="row g-3">
|
||||
<div class="col-md-2">
|
||||
<div class="form-floating">
|
||||
<input type="text" class="form-control" id="userId" name="userId" placeholder="사용자ID">
|
||||
<label for="userId"><i class="bi bi-person"></i> 사용자 ID</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<div class="form-floating">
|
||||
<input type="text" class="form-control" id="userName" name="userName" placeholder="사용자명">
|
||||
<label for="userName"><i class="bi bi-person-vcard"></i> 사용자명</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="form-floating">
|
||||
<input type="text" class="form-control" id="email" name="email" placeholder="이메일">
|
||||
<label for="email"><i class="bi bi-envelope"></i> 이메일</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="approvedGroup"><i class="bi bi-check-circle"></i> 승인 여부</label>
|
||||
<div id="approvedGroup" class="d-flex">
|
||||
<div class="form-check me-2">
|
||||
<input type="radio" class="form-check-input" id="all" name="approved" value="all" checked>
|
||||
<label class="form-check-label" for="all">전체</label>
|
||||
</div>
|
||||
<div class="form-check me-2">
|
||||
<input type="radio" class="form-check-input" id="approved" name="approved" value="true">
|
||||
<label class="form-check-label" for="approved">승인</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="radio" class="form-check-input" id="notApproved" name="approved" value="false">
|
||||
<label class="form-check-label" for="notApproved">미승인</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<button type="submit" class="btn btn-primary h-100 w-100">
|
||||
<i class="bi bi-search"></i> 검색
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title d-flex justify-content-between align-items-center">
|
||||
<span class="fs-6 text-dark"><i class="bi bi-list-ul"></i> 사용자 목록</span>
|
||||
<button id="saveChangesBtn" class="btn btn-sm btn-outline-primary" data-bs-toggle="tooltip" data-bs-placement="left" title="사용자 수정">
|
||||
<i class="bi bi-pencil-fill"></i>
|
||||
</button>
|
||||
</h5>
|
||||
<div class="table-responsive" style="max-height: 450px; overflow-y: auto;">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-1 text-nowrap sticky-top bg-light"><i class="bi bi-person"></i> 사용자 ID</th>
|
||||
<th class="col-1 text-nowrap sticky-top bg-light"><i class="bi bi-person-vcard"></i> 사용자명</th>
|
||||
<th class="col-1 text-nowrap sticky-top bg-light"><i class="bi bi-envelope"></i> 이메일</th>
|
||||
<th class="col-1 text-nowrap sticky-top bg-light"><i class="bi bi-shield-lock"></i> 권한</th>
|
||||
<th class="col-1 text-nowrap sticky-top bg-light"><i class="bi bi-check-circle"></i> 승인 여부</th>
|
||||
<th class="col-1 text-nowrap sticky-top bg-light"><i class="bi bi-gear"></i> 액션</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script type="module" th:src="@{/js/pages/user/user-management.js}" defer></script>
|
||||
<script th:inline="javascript">
|
||||
/*<![CDATA[*/
|
||||
const ROLES = /*[[${roles}]]*/ [];
|
||||
/*]]>*/
|
||||
</script>
|
||||
<script type="module" th:src="@{/js/pages/user/user-management.js}" defer></script>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user