- Entity: LoginHistory, PageView, Issue, IssueComment 추가 - Repository: 각 엔티티별 JpaRepository 추가 - Service: UserService, RoleService, ActivityService, IssueService - Admin API: 사용자 관리 7개, 롤/권한 관리 7개, 통계 1개 엔드포인트 - Activity API: 페이지뷰 기록, 로그인 이력 조회 - Issue API: CRUD + 코멘트, 프로젝트/위치/Gitea 링크 지원 - Exception: GlobalExceptionHandler, ResourceNotFoundException, BusinessException - AuthController: 로그인 시 LoginHistory 기록 추가 - Dockerfile 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
121 lines
4.4 KiB
Java
121 lines
4.4 KiB
Java
package com.gcsc.guide.service;
|
|
|
|
import com.gcsc.guide.dto.StatsResponse;
|
|
import com.gcsc.guide.dto.UserResponse;
|
|
import com.gcsc.guide.entity.Role;
|
|
import com.gcsc.guide.entity.User;
|
|
import com.gcsc.guide.entity.UserStatus;
|
|
import com.gcsc.guide.exception.BusinessException;
|
|
import com.gcsc.guide.exception.ResourceNotFoundException;
|
|
import com.gcsc.guide.repository.LoginHistoryRepository;
|
|
import com.gcsc.guide.repository.RoleRepository;
|
|
import com.gcsc.guide.repository.UserRepository;
|
|
import lombok.RequiredArgsConstructor;
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
import java.time.LocalDate;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
@Service
|
|
@RequiredArgsConstructor
|
|
public class UserService {
|
|
|
|
private final UserRepository userRepository;
|
|
private final RoleRepository roleRepository;
|
|
private final LoginHistoryRepository loginHistoryRepository;
|
|
|
|
@Transactional(readOnly = true)
|
|
public List<UserResponse> getUsers(String status) {
|
|
List<User> users;
|
|
if (status != null && !status.isBlank()) {
|
|
UserStatus userStatus = UserStatus.valueOf(status.toUpperCase());
|
|
users = userRepository.findByStatus(userStatus);
|
|
} else {
|
|
users = userRepository.findAll();
|
|
}
|
|
return users.stream().map(UserResponse::from).toList();
|
|
}
|
|
|
|
@Transactional
|
|
public UserResponse approveUser(Long userId) {
|
|
User user = findUserById(userId);
|
|
if (user.getStatus() != UserStatus.PENDING) {
|
|
throw new BusinessException("PENDING 상태의 사용자만 승인할 수 있습니다 (현재: " + user.getStatus() + ")");
|
|
}
|
|
user.activate();
|
|
return UserResponse.from(userRepository.save(user));
|
|
}
|
|
|
|
@Transactional
|
|
public UserResponse rejectUser(Long userId) {
|
|
User user = findUserById(userId);
|
|
if (user.getStatus() != UserStatus.PENDING) {
|
|
throw new BusinessException("PENDING 상태의 사용자만 거절할 수 있습니다 (현재: " + user.getStatus() + ")");
|
|
}
|
|
user.reject();
|
|
return UserResponse.from(userRepository.save(user));
|
|
}
|
|
|
|
@Transactional
|
|
public UserResponse disableUser(Long userId) {
|
|
User user = findUserById(userId);
|
|
if (user.getStatus() != UserStatus.ACTIVE) {
|
|
throw new BusinessException("ACTIVE 상태의 사용자만 비활성화할 수 있습니다 (현재: " + user.getStatus() + ")");
|
|
}
|
|
user.disable();
|
|
return UserResponse.from(userRepository.save(user));
|
|
}
|
|
|
|
@Transactional
|
|
public UserResponse updateUserRoles(Long userId, List<Long> roleIds) {
|
|
User user = findUserById(userId);
|
|
Set<Role> roles = new HashSet<>(roleRepository.findAllById(roleIds));
|
|
if (roles.size() != roleIds.size()) {
|
|
throw new BusinessException("일부 롤을 찾을 수 없습니다");
|
|
}
|
|
user.updateRoles(roles);
|
|
userRepository.save(user);
|
|
|
|
return UserResponse.from(userRepository.findByIdWithRoles(userId).orElseThrow());
|
|
}
|
|
|
|
@Transactional
|
|
public UserResponse grantAdmin(Long userId) {
|
|
User user = findUserById(userId);
|
|
user.grantAdmin();
|
|
return UserResponse.from(userRepository.save(user));
|
|
}
|
|
|
|
@Transactional
|
|
public UserResponse revokeAdmin(Long userId) {
|
|
User user = findUserById(userId);
|
|
user.revokeAdmin();
|
|
return UserResponse.from(userRepository.save(user));
|
|
}
|
|
|
|
@Transactional(readOnly = true)
|
|
public StatsResponse getStats() {
|
|
long totalUsers = userRepository.count();
|
|
long activeUsers = userRepository.countByStatus(UserStatus.ACTIVE);
|
|
long pendingUsers = userRepository.countByStatus(UserStatus.PENDING);
|
|
long rejectedUsers = userRepository.countByStatus(UserStatus.REJECTED);
|
|
long disabledUsers = userRepository.countByStatus(UserStatus.DISABLED);
|
|
long todayLogins = loginHistoryRepository.countByLoginAtAfter(
|
|
LocalDate.now().atStartOfDay());
|
|
long totalRoles = roleRepository.count();
|
|
|
|
return new StatsResponse(
|
|
totalUsers, activeUsers, pendingUsers,
|
|
rejectedUsers, disabledUsers, todayLogins, totalRoles
|
|
);
|
|
}
|
|
|
|
private User findUserById(Long userId) {
|
|
return userRepository.findById(userId)
|
|
.orElseThrow(() -> new ResourceNotFoundException("사용자", userId));
|
|
}
|
|
}
|