hcm-rest-api/src/main/java/com/goi/erp/service/EmployeeService.java

416 lines
21 KiB
Java

package com.goi.erp.service;
import com.goi.erp.dto.EmployeeRequestDto;
import com.goi.erp.dto.EmployeeResponseDto;
import com.goi.erp.entity.Employee;
import com.goi.erp.entity.EntityChangeLog;
import com.goi.erp.repository.EmployeeRepository;
import com.goi.erp.repository.EntityChangeLogRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
@Transactional
public class EmployeeService {
private final EmployeeRepository employeeRepository;
private final PasswordEncoder passwordEncoder; // 비밀번호 암호화 (auth-service처럼)
private final EntityChangeLogRepository entityChangeLogRepository;
/**
* CREATE
*/
public EmployeeResponseDto createEmployee(EmployeeRequestDto dto) {
Employee employee = Employee.builder()
.empUuid(UUID.randomUUID())
.empNo(dto.getEmpNo())
.empLoginId(dto.getEmpLoginId())
.empLoginPassword(passwordEncoder.encode(dto.getEmpLoginPassword()))
.empFirstName(dto.getEmpFirstName())
.empMiddleName(dto.getEmpMiddleName())
.empLastName(dto.getEmpLastName())
.empPreferredFirstName(dto.getEmpPreferredFirstName())
.empPreferredLastName(dto.getEmpPreferredLastName())
.empStatus(dto.getEmpStatus())
.empFulltime(dto.getEmpFulltime())
.empEmploymentType(dto.getEmpEmploymentType())
.empLeaveStatus(dto.getEmpLeaveStatus())
.empOfferAcceptedDate(dto.getEmpOfferAcceptedDate())
.empNationality(dto.getEmpNationality())
.empCitizenshipStatus(dto.getEmpCitizenshipStatus())
.empPassportNo(dto.getEmpPassportNo())
.empPassportCountry(dto.getEmpPassportCountry())
.empPassportIssueDate(dto.getEmpPassportIssueDate())
.empPassportExpiryDate(dto.getEmpPassportExpiryDate())
.empVisaType(dto.getEmpVisaType())
.empVisaIssueDate(dto.getEmpVisaIssueDate())
.empVisaExpiryDate(dto.getEmpVisaExpiryDate())
.empVisaCountry(dto.getEmpVisaCountry())
.empDateOfBirth(dto.getEmpDateOfBirth())
.empSex(dto.getEmpSex())
.empSin(dto.getEmpSin())
.empAccountNo(dto.getEmpAccountNo())
.empDriverLicenseNo(dto.getEmpDriverLicenseNo())
.empPersonalEmail(dto.getEmpPersonalEmail())
.empPersonalPhone(dto.getEmpPersonalPhone())
.empAddress1(dto.getEmpAddress1())
.empAddress2(dto.getEmpAddress2())
.empPostalCode(dto.getEmpPostalCode())
.empCity(dto.getEmpCity())
.empProvince(dto.getEmpProvince())
.empContractStartDate(dto.getEmpContractStartDate())
.empProbationStartDate(dto.getEmpProbationStartDate())
.empProbationEndDate(dto.getEmpProbationEndDate())
.empProbationStatus(dto.getEmpProbationStatus())
.empJobStartDate(dto.getEmpJobStartDate())
.empJobEndDate(dto.getEmpJobEndDate())
.empTerminationDate(dto.getEmpTerminationDate())
.empTerminationReason(dto.getEmpTerminationReason())
.empDeptId(dto.getEmpDeptId())
.empRate(dto.getEmpRate())
.empRateOt(dto.getEmpRateOt())
.empRateDot(dto.getEmpRateDot())
.empOfficeEmail(dto.getEmpOfficeEmail())
.empOfficeMobile(dto.getEmpOfficeMobile())
.empOfficePhone(dto.getEmpOfficePhone())
.empExtensionNo(dto.getEmpExtensionNo())
.build();
employeeRepository.save(employee);
return mapToDto(employee);
}
/**
* READ - detail
*/
@Transactional(readOnly = true)
public EmployeeResponseDto getEmployeeByUuid(UUID empUuid) {
Employee employee = employeeRepository.findByEmpUuid(empUuid)
.orElseThrow(() -> new RuntimeException("Employee not found"));
return mapToDto(employee);
}
/**
* READ - list
*/
@Transactional(readOnly = true)
public List<EmployeeResponseDto> getAllEmployees() {
return employeeRepository.findAll().stream()
.map(this::mapToDto)
.collect(Collectors.toList());
}
/**
* READ - active
*/
@Transactional(readOnly = true)
public List<EmployeeResponseDto> getActiveEmployees() {
return employeeRepository.findAll().stream()
.filter(e -> "A".equals(e.getEmpStatus()))
.map(this::mapToDto)
.collect(Collectors.toList());
}
/**
* READ - inactive
*/
@Transactional(readOnly = true)
public List<EmployeeResponseDto> getInactiveEmployees() {
return employeeRepository.findAll().stream()
.filter(e -> "I".equals(e.getEmpStatus()))
.map(this::mapToDto)
.collect(Collectors.toList());
}
/**
* UPDATE
*/
public EmployeeResponseDto updateEmployee(UUID empUuid, EmployeeRequestDto newEmp) {
Employee oldEmployee = employeeRepository.findByEmpUuid(empUuid)
.orElseThrow(() -> new RuntimeException("Employee not found"));
// 기존 값 복사본 생성
Employee beforeUpdate = new Employee();
BeanUtils.copyProperties(oldEmployee, beforeUpdate);
// PATCH: 값이 있는 것만 적용
if (newEmp.getEmpNo() != null) oldEmployee.setEmpNo(newEmp.getEmpNo());
if (newEmp.getEmpLoginId() != null) oldEmployee.setEmpLoginId(newEmp.getEmpLoginId());
if (newEmp.getEmpLoginPassword() != null && !newEmp.getEmpLoginPassword().isEmpty()) oldEmployee.setEmpLoginPassword(passwordEncoder.encode(newEmp.getEmpLoginPassword()));
if (newEmp.getEmpFirstName() != null) oldEmployee.setEmpFirstName(newEmp.getEmpFirstName());
if (newEmp.getEmpMiddleName() != null) oldEmployee.setEmpMiddleName(newEmp.getEmpMiddleName());
if (newEmp.getEmpLastName() != null) oldEmployee.setEmpLastName(newEmp.getEmpLastName());
if (newEmp.getEmpPreferredFirstName() != null) oldEmployee.setEmpPreferredFirstName(newEmp.getEmpPreferredFirstName());
if (newEmp.getEmpPreferredLastName() != null) oldEmployee.setEmpPreferredLastName(newEmp.getEmpPreferredLastName());
if (newEmp.getEmpStatus() != null) oldEmployee.setEmpStatus(newEmp.getEmpStatus());
if (newEmp.getEmpFulltime() != null) oldEmployee.setEmpFulltime(newEmp.getEmpFulltime());
if (newEmp.getEmpEmploymentType() != null) oldEmployee.setEmpEmploymentType(newEmp.getEmpEmploymentType());
if (newEmp.getEmpLeaveStatus() != null) oldEmployee.setEmpLeaveStatus(newEmp.getEmpLeaveStatus());
if (newEmp.getEmpOfferAcceptedDate() != null) oldEmployee.setEmpOfferAcceptedDate(newEmp.getEmpOfferAcceptedDate());
if (newEmp.getEmpNationality() != null) oldEmployee.setEmpNationality(newEmp.getEmpNationality());
if (newEmp.getEmpCitizenshipStatus() != null) oldEmployee.setEmpCitizenshipStatus(newEmp.getEmpCitizenshipStatus());
if (newEmp.getEmpPassportNo() != null) oldEmployee.setEmpPassportNo(newEmp.getEmpPassportNo());
if (newEmp.getEmpPassportCountry() != null) oldEmployee.setEmpPassportCountry(newEmp.getEmpPassportCountry());
if (newEmp.getEmpPassportIssueDate() != null) oldEmployee.setEmpPassportIssueDate(newEmp.getEmpPassportIssueDate());
if (newEmp.getEmpPassportExpiryDate() != null) oldEmployee.setEmpPassportExpiryDate(newEmp.getEmpPassportExpiryDate());
if (newEmp.getEmpVisaType() != null) oldEmployee.setEmpVisaType(newEmp.getEmpVisaType());
if (newEmp.getEmpVisaIssueDate() != null) oldEmployee.setEmpVisaIssueDate(newEmp.getEmpVisaIssueDate());
if (newEmp.getEmpVisaExpiryDate() != null) oldEmployee.setEmpVisaExpiryDate(newEmp.getEmpVisaExpiryDate());
if (newEmp.getEmpVisaCountry() != null) oldEmployee.setEmpVisaCountry(newEmp.getEmpVisaCountry());
if (newEmp.getEmpDateOfBirth() != null) oldEmployee.setEmpDateOfBirth(newEmp.getEmpDateOfBirth());
if (newEmp.getEmpSex() != null) oldEmployee.setEmpSex(newEmp.getEmpSex());
if (newEmp.getEmpSin() != null) oldEmployee.setEmpSin(newEmp.getEmpSin());
if (newEmp.getEmpAccountNo() != null) oldEmployee.setEmpAccountNo(newEmp.getEmpAccountNo());
if (newEmp.getEmpDriverLicenseNo() != null) oldEmployee.setEmpDriverLicenseNo(newEmp.getEmpDriverLicenseNo());
if (newEmp.getEmpPersonalEmail() != null) oldEmployee.setEmpPersonalEmail(newEmp.getEmpPersonalEmail());
if (newEmp.getEmpPersonalPhone() != null) oldEmployee.setEmpPersonalPhone(newEmp.getEmpPersonalPhone());
if (newEmp.getEmpAddress1() != null) oldEmployee.setEmpAddress1(newEmp.getEmpAddress1());
if (newEmp.getEmpAddress2() != null) oldEmployee.setEmpAddress2(newEmp.getEmpAddress2());
if (newEmp.getEmpPostalCode() != null) oldEmployee.setEmpPostalCode(newEmp.getEmpPostalCode());
if (newEmp.getEmpCity() != null) oldEmployee.setEmpCity(newEmp.getEmpCity());
if (newEmp.getEmpProvince() != null) oldEmployee.setEmpProvince(newEmp.getEmpProvince());
if (newEmp.getEmpContractStartDate() != null) oldEmployee.setEmpContractStartDate(newEmp.getEmpContractStartDate());
if (newEmp.getEmpProbationStartDate() != null) oldEmployee.setEmpProbationStartDate(newEmp.getEmpProbationStartDate());
if (newEmp.getEmpProbationEndDate() != null) oldEmployee.setEmpProbationEndDate(newEmp.getEmpProbationEndDate());
if (newEmp.getEmpProbationStatus() != null) oldEmployee.setEmpProbationStatus(newEmp.getEmpProbationStatus());
if (newEmp.getEmpJobStartDate() != null) oldEmployee.setEmpJobStartDate(newEmp.getEmpJobStartDate());
if (newEmp.getEmpJobEndDate() != null) oldEmployee.setEmpJobEndDate(newEmp.getEmpJobEndDate());
if (newEmp.getEmpTerminationDate() != null) oldEmployee.setEmpTerminationDate(newEmp.getEmpTerminationDate());
if (newEmp.getEmpTerminationReason() != null) oldEmployee.setEmpTerminationReason(newEmp.getEmpTerminationReason());
if (newEmp.getEmpDeptId() != null) oldEmployee.setEmpDeptId(newEmp.getEmpDeptId());
if (newEmp.getEmpRate() != null) oldEmployee.setEmpRate(newEmp.getEmpRate());
if (newEmp.getEmpRateOt() != null) oldEmployee.setEmpRateOt(newEmp.getEmpRateOt());
if (newEmp.getEmpRateDot() != null) oldEmployee.setEmpRateDot(newEmp.getEmpRateDot());
if (newEmp.getEmpOfficeEmail() != null) oldEmployee.setEmpOfficeEmail(newEmp.getEmpOfficeEmail());
if (newEmp.getEmpOfficeMobile() != null) oldEmployee.setEmpOfficeMobile(newEmp.getEmpOfficeMobile());
if (newEmp.getEmpOfficePhone() != null) oldEmployee.setEmpOfficePhone(newEmp.getEmpOfficePhone());
if (newEmp.getEmpExtensionNo() != null) oldEmployee.setEmpExtensionNo(newEmp.getEmpExtensionNo());
// 변경 로그 기록
//compareAndLogChanges(oldData, oldEmployee, changedBy);
return mapToDto(oldEmployee);
}
/**
* INACTIVATE (Soft Delete)
* 직원 삭제하지 않고 emp_status = 'I' 로 비활성화
*/
public EmployeeResponseDto inactivateEmployee(UUID empUuid) {
Employee employee = employeeRepository.findByEmpUuid(empUuid)
.orElseThrow(() -> new RuntimeException("Employee not found"));
employee.setEmpStatus("I"); // Inactive
return mapToDto(employee);
}
/**
* ENTITY → DTO 변환
*/
private EmployeeResponseDto mapToDto(Employee e) {
EmployeeResponseDto dto = new EmployeeResponseDto();
dto.setEmpId(e.getEmpId());
dto.setEmpUuid(e.getEmpUuid());
dto.setEmpNo(e.getEmpNo());
dto.setEmpLoginId(e.getEmpLoginId());
dto.setEmpFirstName(e.getEmpFirstName());
dto.setEmpMiddleName(e.getEmpMiddleName());
dto.setEmpLastName(e.getEmpLastName());
dto.setEmpPreferredFirstName(e.getEmpPreferredFirstName());
dto.setEmpPreferredLastName(e.getEmpPreferredLastName());
dto.setEmpStatus(e.getEmpStatus());
dto.setEmpFulltime(e.getEmpFulltime());
dto.setEmpEmploymentType(e.getEmpEmploymentType());
dto.setEmpLeaveStatus(e.getEmpLeaveStatus());
dto.setEmpOfferAcceptedDate(e.getEmpOfferAcceptedDate());
dto.setEmpCreatedAt(e.getEmpCreatedAt());
dto.setEmpUpdatedAt(e.getEmpUpdatedAt());
dto.setEmpCreatedBy(e.getEmpCreatedBy());
dto.setEmpUpdatedBy(e.getEmpUpdatedBy());
dto.setEmpNationality(e.getEmpNationality());
dto.setEmpCitizenshipStatus(e.getEmpCitizenshipStatus());
dto.setEmpPassportNo(e.getEmpPassportNo());
dto.setEmpPassportCountry(e.getEmpPassportCountry());
dto.setEmpPassportIssueDate(e.getEmpPassportIssueDate());
dto.setEmpPassportExpiryDate(e.getEmpPassportExpiryDate());
dto.setEmpVisaType(e.getEmpVisaType());
dto.setEmpVisaIssueDate(e.getEmpVisaIssueDate());
dto.setEmpVisaExpiryDate(e.getEmpVisaExpiryDate());
dto.setEmpVisaCountry(e.getEmpVisaCountry());
dto.setEmpDateOfBirth(e.getEmpDateOfBirth());
dto.setEmpSex(e.getEmpSex());
dto.setEmpSin(e.getEmpSin());
dto.setEmpAccountNo(e.getEmpAccountNo());
dto.setEmpDriverLicenseNo(e.getEmpDriverLicenseNo());
dto.setEmpPersonalEmail(e.getEmpPersonalEmail());
dto.setEmpPersonalPhone(e.getEmpPersonalPhone());
dto.setEmpAddress1(e.getEmpAddress1());
dto.setEmpAddress2(e.getEmpAddress2());
dto.setEmpPostalCode(e.getEmpPostalCode());
dto.setEmpCity(e.getEmpCity());
dto.setEmpProvince(e.getEmpProvince());
dto.setEmpContractStartDate(e.getEmpContractStartDate());
dto.setEmpProbationStartDate(e.getEmpProbationStartDate());
dto.setEmpProbationEndDate(e.getEmpProbationEndDate());
dto.setEmpProbationStatus(e.getEmpProbationStatus());
dto.setEmpJobStartDate(e.getEmpJobStartDate());
dto.setEmpJobEndDate(e.getEmpJobEndDate());
dto.setEmpTerminationDate(e.getEmpTerminationDate());
dto.setEmpTerminationReason(e.getEmpTerminationReason());
dto.setEmpDeptId(e.getEmpDeptId());
dto.setEmpRate(e.getEmpRate());
dto.setEmpRateOt(e.getEmpRateOt());
dto.setEmpRateDot(e.getEmpRateDot());
dto.setEmpOfficeEmail(e.getEmpOfficeEmail());
dto.setEmpOfficeMobile(e.getEmpOfficeMobile());
dto.setEmpOfficePhone(e.getEmpOfficePhone());
dto.setEmpExtensionNo(e.getEmpExtensionNo());
return dto;
}
private void compareAndLogChanges(Employee oldData, Employee newData, String changedBy) {
Map<String, String> fieldToColumn = Map.ofEntries(
Map.entry("empNo", "emp_no"),
Map.entry("empLoginId", "emp_login_id"),
Map.entry("empFirstName", "emp_first_name"),
Map.entry("empMiddleName", "emp_middle_name"),
Map.entry("empLastName", "emp_last_name"),
Map.entry("empPreferredFirstName", "emp_preferred_first_name"),
Map.entry("empPreferredLastName", "emp_preferred_last_name"),
Map.entry("empStatus", "emp_status"),
Map.entry("empFulltime", "emp_fulltime"),
Map.entry("empEmploymentType", "emp_employment_type"),
Map.entry("empLeaveStatus", "emp_leave_status"),
Map.entry("empOfferAcceptedDate", "emp_offer_accepted_date"),
Map.entry("empNationality", "emp_nationality"),
Map.entry("empCitizenshipStatus", "emp_citizenship_status"),
Map.entry("empPassportNo", "emp_passport_no"),
Map.entry("empPassportCountry", "emp_passport_country"),
Map.entry("empPassportIssueDate", "emp_passport_issue_date"),
Map.entry("empPassportExpiryDate", "emp_passport_expiry_date"),
Map.entry("empVisaType", "emp_visa_type"),
Map.entry("empVisaIssueDate", "emp_visa_issue_date"),
Map.entry("empVisaExpiryDate", "emp_visa_expiry_date"),
Map.entry("empVisaCountry", "emp_visa_country"),
Map.entry("empDateOfBirth", "emp_date_of_birth"),
Map.entry("empSex", "emp_sex"),
Map.entry("empSin", "emp_sin"),
Map.entry("empAccountNo", "emp_account_no"),
Map.entry("empDriverLicenseNo", "emp_driver_license_no"),
Map.entry("empPersonalEmail", "emp_personal_email"),
Map.entry("empPersonalPhone", "emp_personal_phone"),
Map.entry("empAddress1", "emp_address1"),
Map.entry("empAddress2", "emp_address2"),
Map.entry("empPostalCode", "emp_postal_code"),
Map.entry("empCity", "emp_city"),
Map.entry("empProvince", "emp_province"),
Map.entry("empContractStartDate", "emp_contract_start_date"),
Map.entry("empProbationStartDate", "emp_probation_start_date"),
Map.entry("empProbationEndDate", "emp_probation_end_date"),
Map.entry("empProbationStatus", "emp_probation_status"),
Map.entry("empJobStartDate", "emp_job_start_date"),
Map.entry("empJobEndDate", "emp_job_end_date"),
Map.entry("empTerminationDate", "emp_termination_date"),
Map.entry("empTerminationReason", "emp_termination_reason"),
Map.entry("empDeptId", "emp_dept_id"),
Map.entry("empRate", "emp_rate"),
Map.entry("empRateOt", "emp_rate_ot"),
Map.entry("empRateDot", "emp_rate_dot"),
Map.entry("empOfficeEmail", "emp_office_email"),
Map.entry("empOfficeMobile", "emp_office_mobile"),
Map.entry("empOfficePhone", "emp_office_phone"),
Map.entry("empExtensionNo", "emp_extension_no")
);
Class<?> clazz = Employee.class;
for (var entry : fieldToColumn.entrySet()) {
String fieldName = entry.getKey();
String columnName = entry.getValue();
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
Object oldVal = field.get(oldData);
Object newVal = field.get(newData);
if (valuesAreDifferent(oldVal, newVal)) {
entityChangeLogRepository.save(
EntityChangeLog.builder()
.eclEntityType("Employee")
.eclEntityId(newData.getEmpId())
.eclFieldName(fieldName)
.eclColumnName(columnName)
.eclOldValue(oldVal == null ? null : oldVal.toString())
.eclNewValue(newVal == null ? null : newVal.toString())
.eclChangedBy(changedBy)
.eclChangedAt(LocalDateTime.now())
.eclEffectiveDate(LocalDate.now())
.build()
);
}
} catch (Exception e) {
throw new RuntimeException("Failed to compare field: " + fieldName, e);
}
}
}
private boolean valuesAreDifferent(Object oldVal, Object newVal) {
if (oldVal == null && newVal == null) return false;
if (oldVal == null || newVal == null) return true;
// BigDecimal (numeric 비교)
if (oldVal instanceof BigDecimal oldNum && newVal instanceof BigDecimal newNum) {
return oldNum.compareTo(newNum) != 0;
}
// LocalDate
if (oldVal instanceof LocalDate oldDate && newVal instanceof LocalDate newDate) {
return !oldDate.isEqual(newDate);
}
// LocalDateTime
if (oldVal instanceof LocalDateTime oldDt && newVal instanceof LocalDateTime newDt) {
return !oldDt.equals(newDt);
}
// Boolean
if (oldVal instanceof Boolean && newVal instanceof Boolean) {
return !oldVal.equals(newVal);
}
// 기본 equals
return !oldVal.equals(newVal);
}
}