Remove service interfaces — use concrete classes directly
Each domain had a single-implementation interface (e.g. AdminService interface + AdminServiceImpl). Merged implementation into the service class and deleted the redundant interfaces per KISS principle. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,21 +1,169 @@
|
||||
package com.recipeapp.admin;
|
||||
|
||||
import com.recipeapp.admin.dto.*;
|
||||
import com.recipeapp.admin.entity.AdminAuditLog;
|
||||
import com.recipeapp.auth.UserAccountRepository;
|
||||
import com.recipeapp.auth.entity.UserAccount;
|
||||
import com.recipeapp.common.ConflictException;
|
||||
import com.recipeapp.common.ResourceNotFoundException;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
public interface AdminService {
|
||||
@Service
|
||||
@Transactional
|
||||
public class AdminService {
|
||||
|
||||
ListUsersResult listUsers(String search, Boolean isActive, int limit, int offset);
|
||||
public record ListUsersResult(List<AdminUserResponse> users, long total) {}
|
||||
|
||||
AdminUserResponse createUser(CreateUserRequest request, String adminEmail);
|
||||
|
||||
AdminUserResponse updateUser(UUID userId, UpdateUserRequest request, String adminEmail);
|
||||
private final UserAccountRepository userAccountRepository;
|
||||
private final AdminAuditLogRepository auditLogRepository;
|
||||
private final AdminUserQueryRepository adminUserQueryRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
ResetPasswordResponse resetPassword(UUID userId, ResetPasswordRequest request, String adminEmail);
|
||||
public AdminService(UserAccountRepository userAccountRepository,
|
||||
AdminAuditLogRepository auditLogRepository,
|
||||
AdminUserQueryRepository adminUserQueryRepository,
|
||||
PasswordEncoder passwordEncoder) {
|
||||
this.userAccountRepository = userAccountRepository;
|
||||
this.auditLogRepository = auditLogRepository;
|
||||
this.adminUserQueryRepository = adminUserQueryRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
List<AuditLogResponse> listAuditLog(UUID targetUserId, int limit, int offset);
|
||||
|
||||
record ListUsersResult(List<AdminUserResponse> users, long total) {}
|
||||
@Transactional(readOnly = true)
|
||||
public ListUsersResult listUsers(String search, Boolean isActive, int limit, int offset) {
|
||||
Pageable pageable = PageRequest.of(offset / limit, limit);
|
||||
var users = adminUserQueryRepository.findUsersFiltered(search, isActive, pageable);
|
||||
long total = adminUserQueryRepository.countUsersFiltered(search, isActive);
|
||||
var responses = users.stream().map(this::toAdminUserResponse).toList();
|
||||
return new ListUsersResult(responses, total);
|
||||
}
|
||||
|
||||
|
||||
public AdminUserResponse createUser(CreateUserRequest request, String adminEmail) {
|
||||
if (userAccountRepository.existsByEmailIgnoreCase(request.email())) {
|
||||
throw new ConflictException("A user with this email already exists");
|
||||
}
|
||||
|
||||
var admin = resolveAdmin(adminEmail);
|
||||
String hashedPassword = passwordEncoder.encode(request.tempPassword());
|
||||
var user = new UserAccount(request.email(), request.displayName(), hashedPassword);
|
||||
if (request.systemRole() != null) {
|
||||
user.setSystemRole(request.systemRole());
|
||||
}
|
||||
user = userAccountRepository.save(user);
|
||||
|
||||
auditLogRepository.save(new AdminAuditLog(
|
||||
admin.getId(), user.getId(), "create_account",
|
||||
Map.of("email", request.email(), "displayName", request.displayName()), null));
|
||||
|
||||
return toAdminUserResponse(user);
|
||||
}
|
||||
|
||||
|
||||
public AdminUserResponse updateUser(UUID userId, UpdateUserRequest request, String adminEmail) {
|
||||
var admin = resolveAdmin(adminEmail);
|
||||
var user = userAccountRepository.findById(userId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
|
||||
|
||||
List<String> actions = new ArrayList<>();
|
||||
Map<String, Object> detail = new HashMap<>();
|
||||
|
||||
if (request.displayName() != null) {
|
||||
detail.put("displayName", request.displayName());
|
||||
user.setDisplayName(request.displayName());
|
||||
actions.add("update_account");
|
||||
}
|
||||
if (request.email() != null) {
|
||||
if (!request.email().equalsIgnoreCase(user.getEmail())
|
||||
&& userAccountRepository.existsByEmailIgnoreCase(request.email())) {
|
||||
throw new ConflictException("A user with this email already exists");
|
||||
}
|
||||
detail.put("email", request.email());
|
||||
user.setEmail(request.email());
|
||||
actions.add("update_account");
|
||||
}
|
||||
if (request.systemRole() != null && !request.systemRole().equals(user.getSystemRole())) {
|
||||
detail.put("systemRole", request.systemRole());
|
||||
detail.put("previousSystemRole", user.getSystemRole());
|
||||
user.setSystemRole(request.systemRole());
|
||||
actions.add("change_system_role");
|
||||
}
|
||||
if (request.isActive() != null && request.isActive() != user.isActive()) {
|
||||
detail.put("isActive", request.isActive());
|
||||
user.setActive(request.isActive());
|
||||
actions.add(request.isActive() ? "reactivate_account" : "deactivate_account");
|
||||
}
|
||||
|
||||
user = userAccountRepository.save(user);
|
||||
|
||||
String action = actions.isEmpty() ? "update_account" : actions.getLast();
|
||||
auditLogRepository.save(new AdminAuditLog(
|
||||
admin.getId(), user.getId(), action, detail, null));
|
||||
|
||||
return toAdminUserResponse(user);
|
||||
}
|
||||
|
||||
|
||||
public ResetPasswordResponse resetPassword(UUID userId, ResetPasswordRequest request, String adminEmail) {
|
||||
var admin = resolveAdmin(adminEmail);
|
||||
var user = userAccountRepository.findById(userId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
|
||||
|
||||
user.setPasswordHash(passwordEncoder.encode(request.tempPassword()));
|
||||
userAccountRepository.save(user);
|
||||
|
||||
Map<String, Object> detail = new HashMap<>();
|
||||
if (request.reason() != null) {
|
||||
detail.put("reason", request.reason());
|
||||
}
|
||||
auditLogRepository.save(new AdminAuditLog(
|
||||
admin.getId(), user.getId(), "reset_password", detail, null));
|
||||
|
||||
return new ResetPasswordResponse("Password reset successfully", true);
|
||||
}
|
||||
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public List<AuditLogResponse> listAuditLog(UUID targetUserId, int limit, int offset) {
|
||||
Pageable pageable = PageRequest.of(offset / limit, limit);
|
||||
List<AdminAuditLog> logs;
|
||||
if (targetUserId != null) {
|
||||
logs = auditLogRepository.findByTargetUserIdOrderByPerformedAtDesc(targetUserId, pageable);
|
||||
} else {
|
||||
logs = auditLogRepository.findAllByOrderByPerformedAtDesc(pageable);
|
||||
}
|
||||
|
||||
return logs.stream().map(log -> {
|
||||
String adminEmail = userAccountRepository.findById(log.getAdminId())
|
||||
.map(UserAccount::getEmail).orElse(null);
|
||||
String targetEmail = userAccountRepository.findById(log.getTargetUserId())
|
||||
.map(UserAccount::getEmail).orElse(null);
|
||||
return new AuditLogResponse(
|
||||
log.getId(), log.getAdminId(), adminEmail,
|
||||
log.getTargetUserId(), targetEmail,
|
||||
log.getAction(), log.getDetail(), log.getPerformedAt());
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private UserAccount resolveAdmin(String adminEmail) {
|
||||
return userAccountRepository.findByEmailIgnoreCase(adminEmail)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Admin user not found"));
|
||||
}
|
||||
|
||||
private AdminUserResponse toAdminUserResponse(UserAccount user) {
|
||||
return new AdminUserResponse(
|
||||
user.getId(), user.getEmail(), user.getDisplayName(),
|
||||
user.getSystemRole(), user.isActive(), user.getCreatedAt());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
package com.recipeapp.admin;
|
||||
|
||||
import com.recipeapp.admin.dto.*;
|
||||
import com.recipeapp.admin.entity.AdminAuditLog;
|
||||
import com.recipeapp.auth.UserAccountRepository;
|
||||
import com.recipeapp.auth.entity.UserAccount;
|
||||
import com.recipeapp.common.ConflictException;
|
||||
import com.recipeapp.common.ResourceNotFoundException;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class AdminServiceImpl implements AdminService {
|
||||
|
||||
private final UserAccountRepository userAccountRepository;
|
||||
private final AdminAuditLogRepository auditLogRepository;
|
||||
private final AdminUserQueryRepository adminUserQueryRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
|
||||
public AdminServiceImpl(UserAccountRepository userAccountRepository,
|
||||
AdminAuditLogRepository auditLogRepository,
|
||||
AdminUserQueryRepository adminUserQueryRepository,
|
||||
PasswordEncoder passwordEncoder) {
|
||||
this.userAccountRepository = userAccountRepository;
|
||||
this.auditLogRepository = auditLogRepository;
|
||||
this.adminUserQueryRepository = adminUserQueryRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public ListUsersResult listUsers(String search, Boolean isActive, int limit, int offset) {
|
||||
Pageable pageable = PageRequest.of(offset / limit, limit);
|
||||
var users = adminUserQueryRepository.findUsersFiltered(search, isActive, pageable);
|
||||
long total = adminUserQueryRepository.countUsersFiltered(search, isActive);
|
||||
var responses = users.stream().map(this::toAdminUserResponse).toList();
|
||||
return new ListUsersResult(responses, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminUserResponse createUser(CreateUserRequest request, String adminEmail) {
|
||||
if (userAccountRepository.existsByEmailIgnoreCase(request.email())) {
|
||||
throw new ConflictException("A user with this email already exists");
|
||||
}
|
||||
|
||||
var admin = resolveAdmin(adminEmail);
|
||||
String hashedPassword = passwordEncoder.encode(request.tempPassword());
|
||||
var user = new UserAccount(request.email(), request.displayName(), hashedPassword);
|
||||
if (request.systemRole() != null) {
|
||||
user.setSystemRole(request.systemRole());
|
||||
}
|
||||
user = userAccountRepository.save(user);
|
||||
|
||||
auditLogRepository.save(new AdminAuditLog(
|
||||
admin.getId(), user.getId(), "create_account",
|
||||
Map.of("email", request.email(), "displayName", request.displayName()), null));
|
||||
|
||||
return toAdminUserResponse(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdminUserResponse updateUser(UUID userId, UpdateUserRequest request, String adminEmail) {
|
||||
var admin = resolveAdmin(adminEmail);
|
||||
var user = userAccountRepository.findById(userId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
|
||||
|
||||
List<String> actions = new ArrayList<>();
|
||||
Map<String, Object> detail = new HashMap<>();
|
||||
|
||||
if (request.displayName() != null) {
|
||||
detail.put("displayName", request.displayName());
|
||||
user.setDisplayName(request.displayName());
|
||||
actions.add("update_account");
|
||||
}
|
||||
if (request.email() != null) {
|
||||
if (!request.email().equalsIgnoreCase(user.getEmail())
|
||||
&& userAccountRepository.existsByEmailIgnoreCase(request.email())) {
|
||||
throw new ConflictException("A user with this email already exists");
|
||||
}
|
||||
detail.put("email", request.email());
|
||||
user.setEmail(request.email());
|
||||
actions.add("update_account");
|
||||
}
|
||||
if (request.systemRole() != null && !request.systemRole().equals(user.getSystemRole())) {
|
||||
detail.put("systemRole", request.systemRole());
|
||||
detail.put("previousSystemRole", user.getSystemRole());
|
||||
user.setSystemRole(request.systemRole());
|
||||
actions.add("change_system_role");
|
||||
}
|
||||
if (request.isActive() != null && request.isActive() != user.isActive()) {
|
||||
detail.put("isActive", request.isActive());
|
||||
user.setActive(request.isActive());
|
||||
actions.add(request.isActive() ? "reactivate_account" : "deactivate_account");
|
||||
}
|
||||
|
||||
user = userAccountRepository.save(user);
|
||||
|
||||
String action = actions.isEmpty() ? "update_account" : actions.getLast();
|
||||
auditLogRepository.save(new AdminAuditLog(
|
||||
admin.getId(), user.getId(), action, detail, null));
|
||||
|
||||
return toAdminUserResponse(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResetPasswordResponse resetPassword(UUID userId, ResetPasswordRequest request, String adminEmail) {
|
||||
var admin = resolveAdmin(adminEmail);
|
||||
var user = userAccountRepository.findById(userId)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
|
||||
|
||||
user.setPasswordHash(passwordEncoder.encode(request.tempPassword()));
|
||||
userAccountRepository.save(user);
|
||||
|
||||
Map<String, Object> detail = new HashMap<>();
|
||||
if (request.reason() != null) {
|
||||
detail.put("reason", request.reason());
|
||||
}
|
||||
auditLogRepository.save(new AdminAuditLog(
|
||||
admin.getId(), user.getId(), "reset_password", detail, null));
|
||||
|
||||
return new ResetPasswordResponse("Password reset successfully", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<AuditLogResponse> listAuditLog(UUID targetUserId, int limit, int offset) {
|
||||
Pageable pageable = PageRequest.of(offset / limit, limit);
|
||||
List<AdminAuditLog> logs;
|
||||
if (targetUserId != null) {
|
||||
logs = auditLogRepository.findByTargetUserIdOrderByPerformedAtDesc(targetUserId, pageable);
|
||||
} else {
|
||||
logs = auditLogRepository.findAllByOrderByPerformedAtDesc(pageable);
|
||||
}
|
||||
|
||||
return logs.stream().map(log -> {
|
||||
String adminEmail = userAccountRepository.findById(log.getAdminId())
|
||||
.map(UserAccount::getEmail).orElse(null);
|
||||
String targetEmail = userAccountRepository.findById(log.getTargetUserId())
|
||||
.map(UserAccount::getEmail).orElse(null);
|
||||
return new AuditLogResponse(
|
||||
log.getId(), log.getAdminId(), adminEmail,
|
||||
log.getTargetUserId(), targetEmail,
|
||||
log.getAction(), log.getDetail(), log.getPerformedAt());
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private UserAccount resolveAdmin(String adminEmail) {
|
||||
return userAccountRepository.findByEmailIgnoreCase(adminEmail)
|
||||
.orElseThrow(() -> new ResourceNotFoundException("Admin user not found"));
|
||||
}
|
||||
|
||||
private AdminUserResponse toAdminUserResponse(UserAccount user) {
|
||||
return new AdminUserResponse(
|
||||
user.getId(), user.getEmail(), user.getDisplayName(),
|
||||
user.getSystemRole(), user.isActive(), user.getCreatedAt());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user