diff --git a/src/main/java/com/itn/admin/cmn/config/SecurityConfig.java b/src/main/java/com/itn/admin/cmn/config/SecurityConfig.java index 0cd8669..9bf902c 100644 --- a/src/main/java/com/itn/admin/cmn/config/SecurityConfig.java +++ b/src/main/java/com/itn/admin/cmn/config/SecurityConfig.java @@ -133,6 +133,7 @@ public class SecurityConfig { UserVO loginVO = new UserVO(); loginVO.setUserName( userDetails.getUser().getUserName()); loginVO.setBiostarId( userDetails.getUser().getBiostarId()); + loginVO.setUniqId( userDetails.getUser().getUniqId()); request.getSession().setAttribute("loginVO", loginVO); // 요청 URL의 호스트를 추출 (디버깅용) diff --git a/src/main/java/com/itn/admin/itn/code/server/impl/CodeDetailServiceImpl.java b/src/main/java/com/itn/admin/itn/code/server/impl/CodeDetailServiceImpl.java index 25b4d83..5ebbafd 100644 --- a/src/main/java/com/itn/admin/itn/code/server/impl/CodeDetailServiceImpl.java +++ b/src/main/java/com/itn/admin/itn/code/server/impl/CodeDetailServiceImpl.java @@ -9,6 +9,7 @@ import com.itn.admin.itn.code.mapper.domain.CodeDetailVO; import com.itn.admin.itn.code.mapper.domain.CodeVO; import com.itn.admin.itn.code.server.CodeDetailService; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; @@ -35,7 +36,7 @@ public class CodeDetailServiceImpl implements CodeDetailService { public String getCodeName(String codeGroupId, String codeId) { CodeDetailVO codeDetailVO = codeDetailMapper.findById(codeGroupId, codeId); log.info("codeDetailVO: {}", codeDetailVO); - return codeDetailVO.getCodeName(); + return StringUtils.isEmpty(codeDetailVO.getCodeName()) ? null : codeDetailVO.getCodeName() ; } diff --git a/src/main/java/com/itn/admin/itn/leave/mapper/ItnLeaveMapper.java b/src/main/java/com/itn/admin/itn/leave/mapper/ItnLeaveMapper.java new file mode 100644 index 0000000..d5028b5 --- /dev/null +++ b/src/main/java/com/itn/admin/itn/leave/mapper/ItnLeaveMapper.java @@ -0,0 +1,37 @@ +package com.itn.admin.itn.leave.mapper; + +import com.itn.admin.itn.leave.mapper.domain.LeaveChangeRequestVO; // 추가 +import com.itn.admin.itn.leave.mapper.domain.LeavePlanVO; +import com.itn.admin.itn.leave.mapper.domain.LeaveStatusVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface ItnLeaveMapper { + // LeaveStatus 관련 + LeaveStatusVO selectLeaveStatusByUserIdAndYear(String uniqId, String year); + int insertLeaveStatus(LeaveStatusVO leaveStatusVO); + int updateLeaveStatus(LeaveStatusVO leaveStatusVO); + + // LeavePlan 관련 + List selectLeavePlansByUserIdAndYear(String uniqId, String year); + LeavePlanVO selectLeavePlanById(Long planId); + int insertLeavePlan(LeavePlanVO leavePlanVO); + int updateLeavePlan(LeavePlanVO leavePlanVO); + int deleteLeavePlan(Long planId); + + // 관리자용 + List selectAllPendingLeavePlans(); + int updateLeavePlanStatusAndReason(LeavePlanVO leavePlanVO); // 추가: 연차 계획 상태 및 반려 사유 업데이트 + + // LeaveChangeRequest 관련 + int insertLeaveChangeRequest(LeaveChangeRequestVO leaveChangeRequestVO); // 추가: 연차 변경 요청 삽입 + LeaveChangeRequestVO selectLeaveChangeRequestById(Long changeRequestId); // 추가: 연차 변경 요청 상세 조회 + int updateLeaveChangeRequestStatus(LeaveChangeRequestVO leaveChangeRequestVO); // 추가: 연차 변경 요청 상태 업데이트 + List selectAllPendingLeaveChangeRequests(); // 추가: 모든 승인 대기 중인 연차 변경 요청 조회 + + LeaveChangeRequestVO selectItnLeaveChangeRequestById(Long changeRequestId); + + List getChangeHistoryByPlanId(Long planId); +} diff --git a/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeaveChangeRequestVO.java b/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeaveChangeRequestVO.java new file mode 100644 index 0000000..84b5942 --- /dev/null +++ b/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeaveChangeRequestVO.java @@ -0,0 +1,31 @@ +package com.itn.admin.itn.leave.mapper.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Getter +@Setter +@ToString +public class LeaveChangeRequestVO { + private Long changeRequestId; + private Long planId; + private String uniqId; + private String userName; + private LocalDate originalLeaveDate; + private LocalDate requestedLeaveDate; + private String requestReason; + private String requestStatus; + private String requestStatusName; // 추가 + private String approverId; + private String rejectionReason; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Seoul") + private LocalDateTime requestDt; + private LocalDateTime approvalDt; + +} \ No newline at end of file diff --git a/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeavePlanVO.java b/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeavePlanVO.java new file mode 100644 index 0000000..8402ffa --- /dev/null +++ b/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeavePlanVO.java @@ -0,0 +1,35 @@ +package com.itn.admin.itn.leave.mapper.domain; + +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Getter +@Setter +@ToString +public class LeavePlanVO { + private Long planId; + private String uniqId; + private String userName; + private String year; + private LocalDate leaveDate; + private BigDecimal leaveDays; + private String status; + private String statusName; // 추가 + private LocalDateTime requestDt; + private String approverId; + private LocalDateTime approvalDt; + private String rejectReason; + private String changeRequestStatus; + private String changeRequestStatusName; + private String rejectionReason; + private String requestedLeaveDate; + + private String requestStatus; // 변경 상태 + private String requestStatusName; // 변경 상태 코드 name + private String changeCount; // 변경 카운트 +} diff --git a/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeaveStatusVO.java b/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeaveStatusVO.java new file mode 100644 index 0000000..d446327 --- /dev/null +++ b/src/main/java/com/itn/admin/itn/leave/mapper/domain/LeaveStatusVO.java @@ -0,0 +1,14 @@ +package com.itn.admin.itn.leave.mapper.domain; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class LeaveStatusVO extends LeaveChangeRequestVO{ + private String year; + private BigDecimal totalLeave; + private BigDecimal usedLeave; + private BigDecimal promotedLeave; + private BigDecimal remainingLeave; +} diff --git a/src/main/java/com/itn/admin/itn/leave/service/ItnLeaveService.java b/src/main/java/com/itn/admin/itn/leave/service/ItnLeaveService.java new file mode 100644 index 0000000..aa4ff3a --- /dev/null +++ b/src/main/java/com/itn/admin/itn/leave/service/ItnLeaveService.java @@ -0,0 +1,39 @@ +package com.itn.admin.itn.leave.service; + +import com.itn.admin.cmn.msg.RestResponse; +import com.itn.admin.itn.leave.mapper.domain.LeaveChangeRequestVO; +import com.itn.admin.itn.leave.mapper.domain.LeavePlanVO; +import com.itn.admin.itn.leave.mapper.domain.LeaveStatusVO; + +import java.util.List; + +public interface ItnLeaveService { + + LeaveStatusVO getLeaveStatus(String uniqId, String year); + + List getLeavePlans(String userId, String year); + + LeavePlanVO getLeavePlanDetail(Long changeRequestId); + + boolean saveLeavePlan(LeavePlanVO leavePlanVO); + + boolean deleteLeavePlan(Long planId); + + List getAllPendingLeavePlans(); + + List getAllPendingLeaveChangeRequests(); + + boolean approveLeavePlan(Long planId, String approverId); + + boolean rejectLeavePlan(Long planId, String approverId, String rejectReason); + + boolean requestLeaveChange(LeaveChangeRequestVO leaveChangeRequestVO); + + LeaveChangeRequestVO getLeaveChangeRequestDetail(Long changeRequestId); + + boolean approveLeaveChangeRequest(Long changeRequestId, String approverId); + + boolean rejectLeaveChangeRequest(Long changeRequestId, String approverId, String rejectionReason); + + RestResponse getChangeHistoryByPlanId(Long planId); +} diff --git a/src/main/java/com/itn/admin/itn/leave/service/impl/ItnLeaveServiceImpl.java b/src/main/java/com/itn/admin/itn/leave/service/impl/ItnLeaveServiceImpl.java new file mode 100644 index 0000000..c47484d --- /dev/null +++ b/src/main/java/com/itn/admin/itn/leave/service/impl/ItnLeaveServiceImpl.java @@ -0,0 +1,248 @@ +package com.itn.admin.itn.leave.service.impl; + +import com.itn.admin.cmn.msg.RestResponse; +import com.itn.admin.itn.leave.mapper.ItnLeaveMapper; +import com.itn.admin.itn.leave.service.ItnLeaveService; +import com.itn.admin.itn.leave.mapper.domain.LeaveChangeRequestVO; +import com.itn.admin.itn.leave.mapper.domain.LeavePlanVO; +import com.itn.admin.itn.leave.mapper.domain.LeaveStatusVO; +import com.itn.admin.cmn.util.thymeleafUtils.TCodeUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.List; + +import static com.itn.admin.cmn.code.common.CommonCodeConstants.*; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ItnLeaveServiceImpl implements ItnLeaveService { + + private final ItnLeaveMapper itnLeaveMapper; + private final TCodeUtils tCodeUtils; + + // 연차 현황 조회 + @Override + public LeaveStatusVO getLeaveStatus(String uniqId, String year) { + LeaveStatusVO leaveStatus = itnLeaveMapper.selectLeaveStatusByUserIdAndYear(uniqId, year); + if (leaveStatus == null) { + leaveStatus = new LeaveStatusVO(); + leaveStatus.setUniqId(uniqId); + leaveStatus.setYear(year); + leaveStatus.setTotalLeave(BigDecimal.ZERO); + leaveStatus.setUsedLeave(BigDecimal.ZERO); + leaveStatus.setPromotedLeave(BigDecimal.ZERO); + leaveStatus.setRemainingLeave(BigDecimal.ZERO); // 초기값 설정 + } + return leaveStatus; + } + + // 연차 계획 조회 (사용자용) + @Override + public List getLeavePlans(String uniqId, String year) { + List leavePlans = itnLeaveMapper.selectLeavePlansByUserIdAndYear(uniqId, year); + leavePlans.forEach(plan -> { + plan.setStatusName(tCodeUtils.getCodeName("LEAVE_STATUS", plan.getStatus())); + plan.setRequestStatusName(tCodeUtils.getCodeName("CHANGE_REQUEST_STATUS", plan.getRequestStatus())); + }); + return leavePlans; + } + + // 연차 계획 상세 조회 + @Override + public LeavePlanVO getLeavePlanDetail(Long planId) { + LeavePlanVO leavePlan = itnLeaveMapper.selectLeavePlanById(planId); + if (leavePlan != null) { + leavePlan.setChangeRequestStatusName(tCodeUtils.getCodeName("CHANGE_REQUEST_STATUS", leavePlan.getChangeRequestStatus())); + leavePlan.setUserName(tCodeUtils.getUserName(leavePlan.getUniqId())); + } + return leavePlan; + } + + + @Override + public LeaveChangeRequestVO getLeaveChangeRequestDetail(Long changeRequestId) { + LeaveChangeRequestVO leaveChangeRequestVO = itnLeaveMapper.selectItnLeaveChangeRequestById(changeRequestId); + if (leaveChangeRequestVO != null) { + leaveChangeRequestVO.setUserName(tCodeUtils.getUserName(leaveChangeRequestVO.getUniqId())); + } + return leaveChangeRequestVO; + } + + // 연차 계획 신청/수정 + @Override + @Transactional + public boolean saveLeavePlan(LeavePlanVO leavePlanVO) { + if (leavePlanVO.getPlanId() == null) { + // 신규 신청 + leavePlanVO.setStatus(LEAVE_STATUS_PENDING); // 초기 상태는 PENDING + return itnLeaveMapper.insertLeavePlan(leavePlanVO) > 0; + } else { + // 수정 + // TODO: 기존 상태 확인 로직 추가 (예: 승인된 계획은 수정 불가 등) + return itnLeaveMapper.updateLeavePlan(leavePlanVO) > 0; + } + } + + // 연차 계획 삭제 + @Override + @Transactional + public boolean deleteLeavePlan(Long planId) { + // TODO: 삭제 권한 및 상태 확인 로직 추가 + return itnLeaveMapper.deleteLeavePlan(planId) > 0; + } + + // 관리자: 승인 대기 연차 계획 목록 조회 + @Override + public List getAllPendingLeavePlans() { + List leavePlans = itnLeaveMapper.selectAllPendingLeavePlans(); + leavePlans.forEach(plan -> { +// plan.setStatusName(tCodeUtils.getCodeName("LEAVE_STATUS", plan.getStatus())); +// plan.setChangeRequestStatusName(tCodeUtils.getCodeName("CHANGE_REQUEST_STATUS", plan.getChangeRequestStatus())); + log.info("leavePlans :: [{}]", plan.toString()); + + }); + return leavePlans; + } + + // 관리자: 승인 대기 연차 변경 요청 목록 조회 + @Override + public List getAllPendingLeaveChangeRequests() { + List changeRequests = itnLeaveMapper.selectAllPendingLeaveChangeRequests(); + changeRequests.forEach(request -> { + request.setRequestStatusName(tCodeUtils.getCodeName("CHANGE_REQUEST_STATUS", request.getRequestStatus())); + }); + return changeRequests; + } + + // 관리자: 연차 계획 승인 + @Override + @Transactional + public boolean approveLeavePlan(Long planId, String approverId) { + LeavePlanVO leavePlan = itnLeaveMapper.selectLeavePlanById(planId); + if (leavePlan == null || !LEAVE_STATUS_PENDING.equals(leavePlan.getStatus())) { + return false; // 계획이 없거나 이미 처리됨 + } + + leavePlan.setStatus(LEAVE_STATUS_APPROVED); + leavePlan.setApproverId(approverId); + leavePlan.setApprovalDt(LocalDateTime.now()); + + // 사용된 연차 업데이트 + LeaveStatusVO leaveStatus = itnLeaveMapper.selectLeaveStatusByUserIdAndYear(leavePlan.getUniqId(), leavePlan.getYear()); + if (leaveStatus == null) { + // 해당 연도 연차 현황이 없으면 새로 생성 + leaveStatus = new LeaveStatusVO(); + leaveStatus.setUniqId(leavePlan.getUniqId()); + leaveStatus.setYear(leavePlan.getYear()); + leaveStatus.setTotalLeave(new BigDecimal("0.0")); // 초기값 + leaveStatus.setUsedLeave(leavePlan.getLeaveDays()); + leaveStatus.setPromotedLeave(new BigDecimal("0.0")); // 초기값 + itnLeaveMapper.insertLeaveStatus(leaveStatus); + } else { + leaveStatus.setUsedLeave(leaveStatus.getUsedLeave().add(leavePlan.getLeaveDays())); + itnLeaveMapper.updateLeaveStatus(leaveStatus); + } + + return itnLeaveMapper.updateLeavePlan(leavePlan) > 0; + } + + // 관리자: 연차 계획 반려 + @Override + @Transactional + public boolean rejectLeavePlan(Long planId, String approverId, String rejectReason) { + LeavePlanVO leavePlan = itnLeaveMapper.selectLeavePlanById(planId); + if (leavePlan == null || !LEAVE_STATUS_PENDING.equals(leavePlan.getStatus())) { + return false; // 계획이 없거나 이미 처리됨 + } + + leavePlan.setStatus(LEAVE_STATUS_REJECTED); + leavePlan.setApproverId(approverId); + leavePlan.setApprovalDt(LocalDateTime.now()); + leavePlan.setRejectReason(rejectReason); + + return itnLeaveMapper.updateLeavePlan(leavePlan) > 0; + } + + // 연차 변경 요청 + @Override + @Transactional + public boolean requestLeaveChange(LeaveChangeRequestVO leaveChangeRequestVO) { + // 기존 연차 계획의 변경 요청 상태를 10으로 업데이트 +// LeavePlanVO originalLeavePlan = itnLeaveMapper.selectLeavePlanById(leaveChangeRequestVO.getPlanId()); +// if (originalLeavePlan == null) { +// return false; // 원본 연차 계획이 없음 +// } +// leaveChangeRequestVO.setUniqId(originalLeavePlan.getUniqId()); + + + // 연차 변경 요청 이력 저장 + leaveChangeRequestVO.setRequestStatus(LEAVE_CHANGE_REQUEST_STATUS_PENDING); + return itnLeaveMapper.insertLeaveChangeRequest(leaveChangeRequestVO) > 0; + } + + + // 관리자: 연차 변경 요청 승인 + @Override + @Transactional + public boolean approveLeaveChangeRequest(Long changeRequestId, String approverId) { + LeaveChangeRequestVO changeRequest = itnLeaveMapper.selectLeaveChangeRequestById(changeRequestId); + if (changeRequest == null || !LEAVE_CHANGE_REQUEST_STATUS_PENDING.equals(changeRequest.getRequestStatus())) { + return false; // 변경 요청이 없거나 이미 처리됨 + } + + // 변경 요청 상태 업데이트 + changeRequest.setRequestStatus(LEAVE_CHANGE_REQUEST_STATUS_APPROVED); + changeRequest.setApproverId(approverId); + itnLeaveMapper.updateLeaveChangeRequestStatus(changeRequest); + + // 원본 연차 계획 업데이트 (날짜 변경) + LeavePlanVO originalLeavePlan = itnLeaveMapper.selectLeavePlanById(changeRequest.getPlanId()); + if (originalLeavePlan != null) { + originalLeavePlan.setLeaveDate(changeRequest.getRequestedLeaveDate()); + itnLeaveMapper.updateLeavePlan(originalLeavePlan); // 기존 updateLeavePlan 사용 + } + + return true; + } + + // 관리자: 연차 변경 요청 반려 + @Override + @Transactional + public boolean rejectLeaveChangeRequest(Long changeRequestId, String approverId, String rejectionReason) { + LeaveChangeRequestVO changeRequest = itnLeaveMapper.selectLeaveChangeRequestById(changeRequestId); + if (changeRequest == null || !LEAVE_CHANGE_REQUEST_STATUS_PENDING.equals(changeRequest.getRequestStatus())) { + return false; // 변경 요청이 없거나 이미 처리됨 + } + + // 변경 요청 상태 업데이트 + changeRequest.setRequestStatus(LEAVE_CHANGE_REQUEST_STATUS_REJECTED); + changeRequest.setApproverId(approverId); + changeRequest.setRejectionReason(rejectionReason); + changeRequest.setApprovalDt(LocalDateTime.now()); + itnLeaveMapper.updateLeaveChangeRequestStatus(changeRequest); + + // 원본 연차 계획의 변경 요청 상태 업데이트 (반려 사유 포함) + /* LeavePlanVO originalLeavePlan = itnLeaveMapper.selectLeavePlanById(changeRequest.getPlanId()); + if (originalLeavePlan != null) { + originalLeavePlan.setChangeRequestStatus(LEAVE_CHANGE_REQUEST_STATUS_REJECTED); + originalLeavePlan.setRejectionReason(rejectionReason); + itnLeaveMapper.updateLeavePlanStatusAndReason(originalLeavePlan); + }*/ + + return true; + } + + @Override + public RestResponse getChangeHistoryByPlanId(Long planId) { + + List leaveChangeRequestVOList = itnLeaveMapper.getChangeHistoryByPlanId(planId); + return new RestResponse(HttpStatus.OK, "연차 계획 상세 조회 성공", leaveChangeRequestVOList); + } +} diff --git a/src/main/java/com/itn/admin/itn/leave/web/ItnLeaveController.java b/src/main/java/com/itn/admin/itn/leave/web/ItnLeaveController.java new file mode 100644 index 0000000..2099040 --- /dev/null +++ b/src/main/java/com/itn/admin/itn/leave/web/ItnLeaveController.java @@ -0,0 +1,71 @@ +package com.itn.admin.itn.leave.web; + +import com.itn.admin.cmn.config.CustomUserDetails; +import com.itn.admin.itn.leave.mapper.domain.LeaveChangeRequestVO; +import com.itn.admin.itn.leave.service.ItnLeaveService; +import com.itn.admin.itn.leave.mapper.domain.LeavePlanVO; +import com.itn.admin.itn.leave.mapper.domain.LeaveStatusVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.time.Year; +import java.util.List; + +@Controller +@Slf4j +@RequestMapping("/itn/leave") +@RequiredArgsConstructor +public class ItnLeaveController { + + private final ItnLeaveService itnLeaveService; + + // 내 연차 현황 화면 + @GetMapping("/status") + public String getLeaveStatus(Model model + , @AuthenticationPrincipal CustomUserDetails loginUser) { + String uniqId = loginUser.getUser().getUniqId(); // 로그인한 사용자의 uniq_id + + Integer year = Year.now().getValue(); // 현재 연도 + + LeaveStatusVO leaveStatus = itnLeaveService.getLeaveStatus(uniqId, String.valueOf(year)); + model.addAttribute("leaveStatus", leaveStatus); + model.addAttribute("leavePlans", itnLeaveService.getLeavePlans(uniqId, String.valueOf(year))); + return "itn/leave/leave_status"; + } + + // 연차 사용 계획 신청/변경 화면 + @GetMapping("/request") + public String showLeaveRequestForm(@RequestParam(required = false) Long planId, Model model) { + LeavePlanVO leavePlan = new LeavePlanVO(); + if (planId != null) { + leavePlan = itnLeaveService.getLeavePlanDetail(planId); + } + model.addAttribute("leavePlan", leavePlan); + return "itn/leave/leave_request"; + } + + // 연차 변경 요청 화면 + @GetMapping("/change-request-form") + public String showLeaveChangeRequestForm(@RequestParam Long planId, Model model) { + LeavePlanVO leavePlan = itnLeaveService.getLeavePlanDetail(planId); + model.addAttribute("leavePlan", leavePlan); + return "itn/leave/leave_change_request"; + } + + // [관리자] 승인 대기 목록 화면 + @GetMapping("/admin/approvals") + public String getApprovalList(Model model) { + List pendingChangeRequests = itnLeaveService.getAllPendingLeaveChangeRequests(); + pendingChangeRequests.forEach(t->{ + System.out.println("t :: "+ t.toString()); + }); + model.addAttribute("pendingChangeRequests", pendingChangeRequests); + return "itn/leave/leave_approval_list"; + } +} \ No newline at end of file diff --git a/src/main/java/com/itn/admin/itn/leave/web/ItnLeaveRestController.java b/src/main/java/com/itn/admin/itn/leave/web/ItnLeaveRestController.java new file mode 100644 index 0000000..4c60c85 --- /dev/null +++ b/src/main/java/com/itn/admin/itn/leave/web/ItnLeaveRestController.java @@ -0,0 +1,165 @@ +package com.itn.admin.itn.leave.web; + +import com.itn.admin.cmn.config.CustomUserDetails; +import com.itn.admin.cmn.msg.RestResponse; +import com.itn.admin.itn.leave.service.ItnLeaveService; +import com.itn.admin.itn.leave.mapper.domain.LeaveChangeRequestVO; +import com.itn.admin.itn.leave.mapper.domain.LeavePlanVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; + +import java.time.Year; +import java.util.List; + +@RestController +@Slf4j +@RequestMapping("/itn/leave") +@RequiredArgsConstructor +public class ItnLeaveRestController { + + private final ItnLeaveService itnLeaveService; + + + // 연차 사용 계획 제출 (신규/변경) + @PostMapping("/request") + public ResponseEntity submitLeavePlan(@RequestBody LeavePlanVO leavePlanVO + , @AuthenticationPrincipal CustomUserDetails loginUser) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String uniqId = loginUser.getUser().getUniqId(); + leavePlanVO.setUniqId(uniqId); + leavePlanVO.setYear(String.valueOf(Year.now().getValue())); + + boolean result = itnLeaveService.saveLeavePlan(leavePlanVO); + if (result) { + return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 계획이 성공적으로 저장되었습니다.")); + } else { + return ResponseEntity.badRequest().body(new RestResponse(HttpStatus.BAD_REQUEST, "연차 계획 저장에 실패했습니다.")); + } + } + + // 연차 계획 삭제 + @DeleteMapping("/request/{planId}") + public ResponseEntity deleteLeavePlan(@PathVariable Long planId) { + boolean result = itnLeaveService.deleteLeavePlan(planId); + if (result) { + return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 계획이 성공적으로 삭제되었습니다.")); + } else { + return ResponseEntity.badRequest().body(new RestResponse(HttpStatus.BAD_REQUEST, "연차 계획 삭제에 실패했습니다.")); + } + } + + + + // [관리자] 연차 계획 승인 + @PostMapping("/admin/approve") + public ResponseEntity approveLeavePlan(@RequestParam Long planId) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String approverId = authentication.getName(); // 로그인한 관리자 ID + boolean result = itnLeaveService.approveLeavePlan(planId, approverId); + if (result) { + return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 계획이 승인되었습니다.")); + } else { + return ResponseEntity.badRequest().body(new RestResponse(HttpStatus.BAD_REQUEST, "연차 계획 승인에 실패했습니다.")); + } + } + + // [관리자] 연차 계획 반려 + @PostMapping("/admin/reject") + public ResponseEntity rejectLeavePlan(@RequestParam Long planId, @RequestParam String rejectReason) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String approverId = authentication.getName(); // 로그인한 관리자 ID + boolean result = itnLeaveService.rejectLeavePlan(planId, approverId, rejectReason); + if (result) { + return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 계획이 반려되었습니다.")); + } else { + return ResponseEntity.badRequest().body(new RestResponse(HttpStatus.BAD_REQUEST, "연차 계획 반려에 실패했습니다.")); + } + } + + // 연차 변경 요청 제출 + @PostMapping("/change-request") + public ResponseEntity submitLeaveChangeRequest(@RequestBody LeaveChangeRequestVO leaveChangeRequestVO + , @AuthenticationPrincipal CustomUserDetails loginUser) { + leaveChangeRequestVO.setUniqId(loginUser.getUser().getUniqId()); + boolean result = itnLeaveService.requestLeaveChange(leaveChangeRequestVO); + if (result) { + return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 변경 요청이 성공적으로 제출되었습니다.")); + } else { + return ResponseEntity.badRequest().body(new RestResponse(HttpStatus.BAD_REQUEST, "연차 변경 요청 제출에 실패했습니다.")); + } + } + + // 연차 변경 요청 상세 조회 + @GetMapping("/change-request/{changeRequestId}") + public ResponseEntity getLeaveChangeRequestDetail(@PathVariable Long changeRequestId) { + LeaveChangeRequestVO detail = itnLeaveService.getLeaveChangeRequestDetail(changeRequestId); + if (detail != null) { + return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 변경 요청 상세 조회 성공", detail)); + } else { + return ResponseEntity.notFound().build(); + } + } + + // [관리자] 연차 변경 요청 승인 + @PostMapping("/admin/change-request/approve") + public ResponseEntity approveLeaveChangeRequest(@RequestParam Long changeRequestId + ,@AuthenticationPrincipal CustomUserDetails loginUser) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + String approverId = loginUser.getUser().getUniqId(); // 로그인한 관리자 ID + boolean result = itnLeaveService.approveLeaveChangeRequest(changeRequestId, approverId); + if (result) { + return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 변경 요청이 승인되었습니다.")); + } else { + return ResponseEntity.badRequest().body(new RestResponse(HttpStatus.BAD_REQUEST, "연차 변경 요청 승인에 실패했습니다.")); + } + } + + // [관리자] 연차 변경 요청 반려 + @PostMapping("/admin/change-request/reject") + public ResponseEntity rejectLeaveChangeRequest(@RequestParam Long changeRequestId, @RequestParam String rejectionReason + ,@AuthenticationPrincipal CustomUserDetails loginUser) { + String approverId = loginUser.getUser().getUniqId(); // 로그인한 관리자 ID + boolean result = itnLeaveService.rejectLeaveChangeRequest(changeRequestId, approverId, rejectionReason); + if (result) { + return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 변경 요청이 반려되었습니다.")); + } else { + return ResponseEntity.badRequest().body(new RestResponse(HttpStatus.BAD_REQUEST, "연차 변경 요청 반려에 실패했습니다.")); + } + } + // [관리자] 연차 계획 상세 조회 (Ajax용) + @GetMapping("/admin/change-request-detail/{changeRequestId}") + public ResponseEntity getLeavePlanDetail(@PathVariable Long changeRequestId) { + LeaveChangeRequestVO detail = itnLeaveService.getLeaveChangeRequestDetail(changeRequestId); + log.info(" + detail :: [{}]", detail.toString()); + if (detail != null) { + return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 계획 상세 조회 성공", detail)); + } else { + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body(new RestResponse(HttpStatus.NOT_FOUND, "연차 계획을 찾을 수 없습니다.")); + } + } + + + // [관리자] 연차 계획 상세 조회 (Ajax용) + @GetMapping("/change-history/{planId}") + public ResponseEntity getChangeHistoryByPlanId(@PathVariable Long planId) { + log.info(" + planId : [{}]", planId); +// log.info(" + detail :: [{}]", detail.toString()); +// if (detail != null) { +// return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 계획 상세 조회 성공", detail)); +// } else { +// return ResponseEntity.status(HttpStatus.NOT_FOUND) +// .body(new RestResponse(HttpStatus.NOT_FOUND, "연차 계획을 찾을 수 없습니다.")); +// } +// return ResponseEntity.ok().body(new RestResponse(HttpStatus.OK, "연차 계획 상세 조회 성공")); + return ResponseEntity.ok().body(itnLeaveService.getChangeHistoryByPlanId(planId)); + } +} \ No newline at end of file diff --git a/src/main/resources/log4jdbc.log4j2.properties b/src/main/resources/log4jdbc.log4j2.properties index a48b3e9..97db892 100644 --- a/src/main/resources/log4jdbc.log4j2.properties +++ b/src/main/resources/log4jdbc.log4j2.properties @@ -1,2 +1,3 @@ log4jdbc.spylogdelegator.name=net.sf.log4jdbc.log.slf4j.Slf4jSpyLogDelegator -log4jdbc.dump.sql.maxlinelength=0 \ No newline at end of file +log4jdbc.dump.sql.maxlinelength=0 +log4jdbc.dump.sql.addsemicolon=true \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index 58c5d7c..66e0137 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -4,7 +4,8 @@ - + + @@ -41,20 +42,18 @@ - - + + + + + + + + - - - - - - - - - - - + + + diff --git a/src/main/resources/mapper/itn/leave/ItnLeaveMapper.xml b/src/main/resources/mapper/itn/leave/ItnLeaveMapper.xml new file mode 100644 index 0000000..87e8cb6 --- /dev/null +++ b/src/main/resources/mapper/itn/leave/ItnLeaveMapper.xml @@ -0,0 +1,255 @@ + + + + + + + + + INSERT INTO ITN_LEAVE_STATUS ( + UNIQ_ID, + YEAR, + TOTAL_LEAVE, + USED_LEAVE, + PROMOTED_LEAVE + ) VALUES ( + #{userId}, + #{year}, + #{totalLeave}, + #{usedLeave}, + #{promotedLeave} + ) + + + + UPDATE ITN_LEAVE_STATUS + SET + TOTAL_LEAVE = #{totalLeave}, + USED_LEAVE = #{usedLeave}, + PROMOTED_LEAVE = #{promotedLeave} + WHERE UNIQ_ID = #{userId} AND YEAR = #{year} + + + + + + + + + INSERT INTO ITN_LEAVE_PLAN ( + UNIQ_ID, + YEAR, + LEAVE_DATE, + LEAVE_DAYS, + STATUS + ) VALUES ( + #{userId}, + #{year}, + #{leaveDate}, + #{leaveDays}, + #{status} + ) + + SELECT LAST_INSERT_ID() + + + + + UPDATE ITN_LEAVE_PLAN + SET + LEAVE_DATE = #{leaveDate}, + LEAVE_DAYS = #{leaveDays}, + STATUS = #{status} + WHERE PLAN_ID = #{planId} + + + + + DELETE FROM ITN_LEAVE_PLAN + WHERE PLAN_ID = #{planId} + + + + + + + + INSERT INTO ITN_LEAVE_CHANGE_REQUEST ( + PLAN_ID, + UNIQ_ID, + ORIGINAL_LEAVE_DATE, + REQUESTED_LEAVE_DATE, + REQUEST_REASON, + REQUEST_STATUS, + REQUEST_DT + ) VALUES ( + #{planId}, + #{uniqId}, + #{originalLeaveDate}, + #{requestedLeaveDate}, + #{requestReason}, + #{requestStatus}, + NOW() + ) + + SELECT LAST_INSERT_ID() + + + + + + + UPDATE ITN_LEAVE_CHANGE_REQUEST + SET + REQUEST_STATUS = #{requestStatus}, + APPROVER_ID = #{approverId}, + REJECTION_REASON = #{rejectionReason}, + APPROVAL_DT = NOW() + WHERE CHANGE_REQUEST_ID = #{changeRequestId} + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/header.html b/src/main/resources/templates/fragments/header.html index c1754ea..113aa28 100644 --- a/src/main/resources/templates/fragments/header.html +++ b/src/main/resources/templates/fragments/header.html @@ -112,7 +112,7 @@ }); - function fn_successAlert(title, msg){ + function fn_successAlert(title="성공", msg){ $(document).Toasts('create', { class: 'bg-info', title: title, @@ -123,7 +123,7 @@ }) } - function fn_failedAlert(title, msg){ + function fn_failedAlert(title="실패", msg){ $(document).Toasts('create', { class: 'bg-danger', title: title, diff --git a/src/main/resources/templates/fragments/mainsidebar.html b/src/main/resources/templates/fragments/mainsidebar.html index f96eb01..e95646c 100644 --- a/src/main/resources/templates/fragments/mainsidebar.html +++ b/src/main/resources/templates/fragments/mainsidebar.html @@ -66,7 +66,7 @@ --> + +