package com.srsystem.service; import com.srsystem.dto.ChangePasswordRequest; import com.srsystem.dto.EmployeeSearchResult; import com.srsystem.dto.EmployeeSummary; import com.srsystem.entity.Employee; import com.srsystem.entity.EmployeeEmergencyContact; import com.srsystem.entity.enums.DispatchStatus; import com.srsystem.entity.enums.EmpStatus; import com.srsystem.entity.enums.RoleCode; import com.srsystem.exception.BusinessException; import com.srsystem.exception.ErrorCode; import com.srsystem.repository.DispatchHistoryRepository; import com.srsystem.repository.EmployeeEmergencyContactRepository; import com.srsystem.repository.EmployeeRepository; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDate; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class EmployeeService { private final EmployeeRepository employeeRepository; private final DispatchHistoryRepository dispatchHistoryRepository; private final EmployeeEmergencyContactRepository emergencyContactRepository; private final PasswordEncoder passwordEncoder; public Page findAll(EmpStatus status, Pageable pageable) { if (status != null) { return employeeRepository.findByEmpStatus(status, pageable); } return employeeRepository.findAll(pageable); } public Employee findById(Long empId) { return employeeRepository.findById(empId) .orElseThrow(() -> new BusinessException(ErrorCode.EMPLOYEE_NOT_FOUND)); } public Employee findByLoginId(String loginId) { return employeeRepository.findByLoginId(loginId) .orElseThrow(() -> new BusinessException(ErrorCode.EMPLOYEE_NOT_FOUND)); } public List searchByName(String keyword) { List byName = employeeRepository.findByNameContainingIgnoreCase(keyword); List results = new ArrayList<>(); Set seen = new HashSet<>(); for (Employee e : byName) { if (seen.add(e.getEmpId())) { results.add(toSearchResult(e)); } } return results; } public List getApprovers() { return employeeRepository.findByRoleCodeIn(List.of(RoleCode.MANAGER, RoleCode.ADMIN)) .stream().map(this::toSearchResult).toList(); } private EmployeeSearchResult toSearchResult(Employee e) { return new EmployeeSearchResult( e.getEmpId(), e.getName(), e.getEmail(), e.getRoleCode() != null ? e.getRoleCode().name() : null, e.getDispatchStatus() != null ? e.getDispatchStatus().name() : null ); } @Transactional public Employee create(Employee employee) { if (employee.getEmail() != null && employeeRepository.findByEmail(employee.getEmail()).isPresent()) { throw new BusinessException(ErrorCode.DUPLICATE_LOGIN_ID); } if (employee.getRoleCode() == null) employee.setRoleCode(RoleCode.EMPLOYEE); if (employee.getEmpStatus() == null) employee.setEmpStatus(EmpStatus.ACTIVE); if (employee.getDispatchStatus() == null) employee.setDispatchStatus(DispatchStatus.WAITING); employee.setPassword(passwordEncoder.encode(employee.getPassword())); return employeeRepository.save(employee); } @Transactional public Employee update(Long empId, Employee updateRequest) { Employee employee = findById(empId); if (employee.getEmpStatus() == EmpStatus.RESIGNED) { throw new BusinessException(ErrorCode.RESIGNED_EMPLOYEE); } employee.setName(updateRequest.getName()); employee.setEmail(updateRequest.getEmail()); employee.setPhone(updateRequest.getPhone()); employee.setJoinedDate(updateRequest.getJoinedDate()); employee.setProfileImage(updateRequest.getProfileImage()); employee.setPosition(updateRequest.getPosition()); employee.setManagerId(updateRequest.getManagerId()); employee.setBackupManagerId(updateRequest.getBackupManagerId()); return employee; } @Transactional public Employee updateStatus(Long empId, EmpStatus status, String leftDate) { Employee employee = findById(empId); employee.setEmpStatus(status); if (leftDate != null && !leftDate.isBlank()) { employee.setLeftDate(LocalDate.parse(leftDate)); } if (status == EmpStatus.RESIGNED && leftDate != null) { employee.setLeftDate(LocalDate.parse(leftDate)); } return employeeRepository.save(employee); } @Transactional public Employee updateRole(Long empId, RoleCode role) { Employee employee = findById(empId); employee.setRoleCode(role); return employeeRepository.save(employee); } @Transactional public Employee updateDispatchStatus(Long empId, DispatchStatus status) { Employee employee = findById(empId); employee.setDispatchStatus(status); return employeeRepository.save(employee); } @Transactional public void resetPassword(Long empId, String newPassword) { Employee employee = findById(empId); employee.setPassword(passwordEncoder.encode(newPassword)); employeeRepository.save(employee); } @Transactional public void delete(Long empId) { Employee employee = findById(empId); if (employee.getEmpStatus() == EmpStatus.RESIGNED) { throw new BusinessException(ErrorCode.RESIGNED_EMPLOYEE); } employeeRepository.delete(employee); } public EmployeeSummary toSummary(Employee employee) { return new EmployeeSummary( employee.getEmpId(), employee.getName(), employee.getEmail(), employee.getRoleCode(), employee.getEmpStatus(), employee.getDispatchStatus(), employee.getJoinedDate(), employee.getLeftDate(), employee.getProfileImage() ); } // ---- Emergency Contact CRUD ---- public List getEmergencyContacts(Long empId) { return emergencyContactRepository.findByEmployeeEmpIdOrderBySortOrderAsc(empId); } @Transactional public EmployeeEmergencyContact createEmergencyContact(Long empId, EmployeeEmergencyContact contact) { Employee employee = findById(empId); contact.setEmployee(employee); if (contact.getSortOrder() == null) contact.setSortOrder(0); return emergencyContactRepository.save(contact); } @Transactional public EmployeeEmergencyContact updateEmergencyContact(Long contactId, EmployeeEmergencyContact update) { EmployeeEmergencyContact contact = emergencyContactRepository.findById(contactId) .orElseThrow(() -> new BusinessException(ErrorCode.CODE_NOT_FOUND)); if (update.getName() != null) contact.setName(update.getName()); if (update.getRelation() != null) contact.setRelation(update.getRelation()); if (update.getPhone() != null) contact.setPhone(update.getPhone()); if (update.getSortOrder() != null) contact.setSortOrder(update.getSortOrder()); return emergencyContactRepository.save(contact); } @Transactional public void deleteEmergencyContact(Long contactId) { EmployeeEmergencyContact contact = emergencyContactRepository.findById(contactId) .orElseThrow(() -> new BusinessException(ErrorCode.CODE_NOT_FOUND)); contact.setIsDeleted('Y'); emergencyContactRepository.save(contact); } @Transactional public void changePassword(Long empId, ChangePasswordRequest request) { Employee employee = findById(empId); if (!passwordEncoder.matches(request.currentPassword(), employee.getPassword())) { throw new BusinessException(ErrorCode.AUTH_INVALID_CREDENTIALS); } employee.setPassword(passwordEncoder.encode(request.newPassword())); employeeRepository.save(employee); } }