package com.njcn.rdms.module.project.service.overtime;

import com.njcn.rdms.framework.common.pojo.CommonResult;
import com.njcn.rdms.framework.common.pojo.PageParam;
import com.njcn.rdms.framework.common.pojo.PageResult;
import com.njcn.rdms.framework.common.util.json.JsonUtils;
import com.njcn.rdms.framework.common.util.object.BeanUtils;
import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
import com.njcn.rdms.module.project.constant.OvertimeApplicationConstants;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationExportVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationRespVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationSaveReqVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusActionReqVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusDictRespVO;
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusLogRespVO;
import com.njcn.rdms.module.project.dal.dataobject.audit.BizAuditLogDO;
import com.njcn.rdms.module.project.dal.dataobject.overtime.OvertimeApplicationDO;
import com.njcn.rdms.module.project.dal.dataobject.overtime.OvertimeApplicationStatusLogDO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusModelDO;
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusTransitionDO;
import com.njcn.rdms.module.project.dal.mysql.audit.BizAuditLogMapper;
import com.njcn.rdms.module.project.dal.mysql.overtime.OvertimeApplicationMapper;
import com.njcn.rdms.module.project.dal.mysql.overtime.OvertimeApplicationStatusLogMapper;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusModelMapper;
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusTransitionMapper;
import com.njcn.rdms.module.project.enums.ErrorCodeConstants;
import com.njcn.rdms.module.project.service.status.StatusActionTextResolver;
import com.njcn.rdms.module.system.api.user.AdminUserApi;
import com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;

@Service
public class OvertimeApplicationServiceImpl implements OvertimeApplicationService {

    @Resource
    private OvertimeApplicationMapper overtimeApplicationMapper;
    @Resource
    private OvertimeApplicationStatusLogMapper overtimeApplicationStatusLogMapper;
    @Resource
    private BizAuditLogMapper bizAuditLogMapper;
    @Resource
    private ObjectStatusModelMapper objectStatusModelMapper;
    @Resource
    private ObjectStatusTransitionMapper objectStatusTransitionMapper;
    @Resource
    private StatusActionTextResolver statusActionTextResolver;
    @Resource
    private AdminUserApi adminUserApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createApplication(OvertimeApplicationSaveReqVO reqVO) {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        AdminUserRespDTO approver = validateApprover(reqVO.getApproverId());
        String initialStatus = getInitialStatusCode();

        OvertimeApplicationDO application = new OvertimeApplicationDO();
        application.setApplicantId(loginUserId);
        application.setApplicantName(defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
        applySaveFields(application, reqVO, approver);
        application.setStatusCode(initialStatus);
        application.setApprovalComment(null);
        application.setSubmitTime(LocalDateTime.now());
        application.setApprovalTime(null);
        overtimeApplicationMapper.insert(application);

        // 创建即进入初始状态 pending，这里保留 submit 作为业务留痕动作，不依赖状态机流转表存在 submit 配置。
        writeStatusLog(application, OvertimeApplicationConstants.ACTION_SUBMIT, null, initialStatus, null);
        writeAuditLog(application, OvertimeApplicationConstants.ACTION_SUBMIT, null, initialStatus, null, null, null);
        return application.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void resubmitApplication(Long id, OvertimeApplicationSaveReqVO reqVO) {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        OvertimeApplicationDO current = validateApplicationExists(id);
        if (!Objects.equals(current.getApplicantId(), loginUserId)) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPLICANT_ONLY);
        }
        String fromStatus = current.getStatusCode();
        ObjectStatusTransitionDO transition = validateTransition(fromStatus, OvertimeApplicationConstants.ACTION_RESUBMIT,
                null);
        AdminUserRespDTO approver = validateApprover(reqVO.getApproverId());

        OvertimeApplicationDO before = cloneApplication(current);
        OvertimeApplicationDO update = new OvertimeApplicationDO();
        applySaveFields(update, reqVO, approver);
        update.setStatusCode(transition.getToStatusCode());
        update.setApprovalComment(null);
        update.setSubmitTime(LocalDateTime.now());
        update.setApprovalTime(null);

        int updateCount = overtimeApplicationMapper.updateByIdAndStatusesAndApplicantId(update, id,
                List.of(OvertimeApplicationConstants.STATUS_REJECTED, OvertimeApplicationConstants.STATUS_CANCELLED),
                loginUserId);
        if (updateCount != 1) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_CONCURRENT_MODIFIED);
        }

        OvertimeApplicationDO after = mergeUpdated(current, update);
        writeStatusLog(after, OvertimeApplicationConstants.ACTION_RESUBMIT, fromStatus, transition.getToStatusCode(),
                null);
        writeAuditLog(after, OvertimeApplicationConstants.ACTION_RESUBMIT, fromStatus, transition.getToStatusCode(),
                buildFieldChanges(before, after), null, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approve(Long id, OvertimeApplicationStatusActionReqVO reqVO) {
        processApprovalAction(id, OvertimeApplicationConstants.ACTION_APPROVE, reqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reject(Long id, OvertimeApplicationStatusActionReqVO reqVO) {
        processApprovalAction(id, OvertimeApplicationConstants.ACTION_REJECT, reqVO);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancel(Long id, OvertimeApplicationStatusActionReqVO reqVO) {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        OvertimeApplicationDO current = validateApplicationExists(id);
        if (!Objects.equals(current.getApplicantId(), loginUserId)) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPLICANT_ONLY);
        }
        String reason = normalizeNullableText(reqVO == null ? null : reqVO.getReason());
        String fromStatus = current.getStatusCode();
        ObjectStatusTransitionDO transition = validateTransition(fromStatus, OvertimeApplicationConstants.ACTION_CANCEL,
                reason);

        OvertimeApplicationDO update = new OvertimeApplicationDO();
        update.setStatusCode(transition.getToStatusCode());
        update.setApprovalComment(reason);
        update.setApprovalTime(LocalDateTime.now());
        int updateCount = overtimeApplicationMapper.updateByIdAndStatusAndApplicantId(update, id, fromStatus,
                loginUserId);
        if (updateCount != 1) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_CONCURRENT_MODIFIED);
        }

        OvertimeApplicationDO after = mergeUpdated(current, update);
        writeStatusLog(after, OvertimeApplicationConstants.ACTION_CANCEL, fromStatus, transition.getToStatusCode(),
                reason);
        writeAuditLog(after, OvertimeApplicationConstants.ACTION_CANCEL, fromStatus, transition.getToStatusCode(),
                null, reason, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteApplication(Long id) {
        OvertimeApplicationDO current = validateApplicationExists(id);
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        if (!Objects.equals(current.getApplicantId(), loginUserId)) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPLICANT_ONLY);
        }
        if (!OvertimeApplicationConstants.STATUS_CANCELLED.equals(current.getStatusCode())) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_DELETE_ONLY_CANCELLED);
        }
        overtimeApplicationMapper.deleteById(id);
        writeAuditLog(current, OvertimeApplicationConstants.ACTION_DELETE, current.getStatusCode(), null, null, null,
                null);
    }

    @Override
    public OvertimeApplicationRespVO getApplication(Long id) {
        OvertimeApplicationDO application = validateReadableApplication(id);
        return toRespVO(application);
    }

    @Override
    public List<OvertimeApplicationStatusDictRespVO> getStatusDict() {
        List<ObjectStatusModelDO> statusModels = objectStatusModelMapper
                .selectListByObjectTypeEnabled(OvertimeApplicationConstants.STATUS_OBJECT_TYPE);
        return statusModels.stream()
                .map(this::buildStatusDictRespVO)
                .collect(Collectors.toList());
    }

    @Override
    public PageResult<OvertimeApplicationRespVO> getMyPage(OvertimeApplicationPageReqVO reqVO) {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        PageResult<OvertimeApplicationDO> page = overtimeApplicationMapper.selectMyPage(loginUserId, reqVO);
        return BeanUtils.toBean(page, OvertimeApplicationRespVO.class, this::applyStatusView);
    }

    @Override
    public PageResult<OvertimeApplicationRespVO> getApprovalPage(OvertimeApplicationPageReqVO reqVO) {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        PageResult<OvertimeApplicationDO> page = overtimeApplicationMapper.selectApprovalPage(loginUserId, reqVO);
        return BeanUtils.toBean(page, OvertimeApplicationRespVO.class, this::applyStatusView);
    }

    @Override
    public List<OvertimeApplicationStatusLogRespVO> getStatusLogs(Long id) {
        validateReadableApplication(id);
        return BeanUtils.toBean(overtimeApplicationStatusLogMapper.selectListByApplicationId(id),
                OvertimeApplicationStatusLogRespVO.class);
    }

    @Override
    public List<OvertimeApplicationExportVO> getExportList(OvertimeApplicationPageReqVO reqVO) {
        reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        PageResult<OvertimeApplicationRespVO> page = getMyPage(reqVO);
        return BeanUtils.toBean(page.getList(), OvertimeApplicationExportVO.class);
    }

    private void processApprovalAction(Long id, String actionCode, OvertimeApplicationStatusActionReqVO reqVO) {
        OvertimeApplicationDO current = validateApplicationExists(id);
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        if (!Objects.equals(current.getApproverId(), loginUserId)) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPROVER_ONLY);
        }
        String reason = normalizeNullableText(reqVO == null ? null : reqVO.getReason());
        String fromStatus = current.getStatusCode();
        ObjectStatusTransitionDO transition = validateTransition(fromStatus, actionCode, reason);

        OvertimeApplicationDO update = new OvertimeApplicationDO();
        update.setStatusCode(transition.getToStatusCode());
        update.setApprovalComment(reason);
        update.setApprovalTime(LocalDateTime.now());
        int updateCount = overtimeApplicationMapper.updateByIdAndStatus(update, id, fromStatus);
        if (updateCount != 1) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_CONCURRENT_MODIFIED);
        }

        OvertimeApplicationDO after = mergeUpdated(current, update);
        writeStatusLog(after, actionCode, fromStatus, transition.getToStatusCode(), reason);
        writeAuditLog(after, actionCode, fromStatus, transition.getToStatusCode(), null, reason, null);
    }

    private OvertimeApplicationDO validateApplicationExists(Long id) {
        OvertimeApplicationDO application = overtimeApplicationMapper.selectById(id);
        if (application == null) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_NOT_EXISTS);
        }
        return application;
    }

    private OvertimeApplicationDO validateReadableApplication(Long id) {
        OvertimeApplicationDO application = validateApplicationExists(id);
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        if (!Objects.equals(application.getApplicantId(), loginUserId)
                && !Objects.equals(application.getApproverId(), loginUserId)) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_READ_FORBIDDEN);
        }
        return application;
    }

    private ObjectStatusTransitionDO validateTransition(String fromStatus, String actionCode, String reason) {
        ObjectStatusTransitionDO transition = objectStatusTransitionMapper
                .selectByObjectTypeAndFromStatusAndAction(OvertimeApplicationConstants.STATUS_OBJECT_TYPE, fromStatus,
                        actionCode);
        if (transition == null) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_ACTION_NOT_ALLOWED,
                    statusActionTextResolver.statusName(OvertimeApplicationConstants.STATUS_OBJECT_TYPE, fromStatus),
                    statusActionTextResolver.actionName(OvertimeApplicationConstants.STATUS_OBJECT_TYPE, actionCode));
        }
        if (Boolean.TRUE.equals(transition.getNeedReason()) && !StringUtils.hasText(reason)) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_ACTION_REASON_REQUIRED,
                    statusActionTextResolver.actionName(OvertimeApplicationConstants.STATUS_OBJECT_TYPE, actionCode));
        }
        ObjectStatusModelDO toModel = objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled(
                OvertimeApplicationConstants.STATUS_OBJECT_TYPE, transition.getToStatusCode());
        if (toModel == null) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_MODEL_NOT_EXISTS_OR_DISABLED);
        }
        return transition;
    }

    private String getInitialStatusCode() {
        ObjectStatusModelDO statusModel = objectStatusModelMapper
                .selectInitialByObjectTypeEnabled(OvertimeApplicationConstants.STATUS_OBJECT_TYPE);
        if (statusModel == null || !StringUtils.hasText(statusModel.getStatusCode())) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_MODEL_NOT_EXISTS_OR_DISABLED);
        }
        return statusModel.getStatusCode();
    }

    private AdminUserRespDTO validateApprover(Long approverId) {
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        if (Objects.equals(approverId, loginUserId)) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPROVER_SELF_FORBIDDEN);
        }
        return loadUser(approverId);
    }

    private AdminUserRespDTO loadUser(Long userId) {
        if (userId == null) {
            throw invalidParamException("用户编号不能为空");
        }
        adminUserApi.validateUserList(Collections.singleton(userId)).getCheckedData();
        CommonResult<AdminUserRespDTO> result = adminUserApi.getUser(userId);
        AdminUserRespDTO user = result.getCheckedData();
        if (user == null) {
            throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPROVER_INVALID);
        }
        return user;
    }

    private void applySaveFields(OvertimeApplicationDO target, OvertimeApplicationSaveReqVO reqVO,
                                 AdminUserRespDTO approver) {
        target.setOvertimeDate(reqVO.getOvertimeDate());
        target.setOvertimeDuration(normalizeRequiredText(reqVO.getOvertimeDuration(), "加班时长不能为空"));
        target.setOvertimeReason(normalizeRequiredText(reqVO.getOvertimeReason(), "加班原因不能为空"));
        target.setOvertimeContent(normalizeRequiredText(reqVO.getOvertimeContent(), "加班内容不能为空"));
        target.setApproverId(approver.getId());
        target.setApproverName(defaultText(approver.getNickname()));
    }

    private OvertimeApplicationRespVO toRespVO(OvertimeApplicationDO application) {
        return BeanUtils.toBean(application, OvertimeApplicationRespVO.class, this::applyStatusView);
    }

    private void applyStatusView(OvertimeApplicationRespVO respVO) {
        ObjectStatusModelDO statusModel = objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled(
                OvertimeApplicationConstants.STATUS_OBJECT_TYPE, respVO.getStatusCode());
        if (statusModel == null) {
            respVO.setStatusName(respVO.getStatusCode());
            respVO.setAllowEdit(false);
            respVO.setTerminal(false);
            return;
        }
        respVO.setStatusName(statusModel.getStatusName());
        respVO.setAllowEdit(Boolean.TRUE.equals(statusModel.getAllowEdit()));
        respVO.setTerminal(Boolean.TRUE.equals(statusModel.getTerminalFlag()));
    }

    private OvertimeApplicationStatusDictRespVO buildStatusDictRespVO(ObjectStatusModelDO statusModel) {
        OvertimeApplicationStatusDictRespVO respVO = new OvertimeApplicationStatusDictRespVO();
        respVO.setStatusCode(statusModel.getStatusCode());
        respVO.setStatusName(statusModel.getStatusName());
        respVO.setSort(statusModel.getSort());
        respVO.setInitialFlag(statusModel.getInitialFlag());
        respVO.setTerminalFlag(statusModel.getTerminalFlag());
        respVO.setAllowEdit(statusModel.getAllowEdit());
        return respVO;
    }

    private void writeStatusLog(OvertimeApplicationDO application, String actionType, String fromStatus,
                                String toStatus, String reason) {
        OvertimeApplicationStatusLogDO log = new OvertimeApplicationStatusLogDO();
        log.setApplicationId(application.getId());
        log.setActionType(actionType);
        log.setFromStatus(fromStatus);
        log.setToStatus(toStatus);
        log.setReason(reason);
        log.setOperatorUserId(SecurityFrameworkUtils.getLoginUserId());
        log.setOperatorName(defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
        log.setApplicantNameSnapshot(application.getApplicantName());
        log.setOvertimeDateSnapshot(application.getOvertimeDate());
        log.setOvertimeDurationSnapshot(application.getOvertimeDuration());
        log.setRemark(buildSnapshotRemark(application));
        overtimeApplicationStatusLogMapper.insert(log);
    }

    private void writeAuditLog(OvertimeApplicationDO application, String actionType, String fromStatus,
                               String toStatus, String fieldChanges, String reason, String remark) {
        BizAuditLogDO auditLog = new BizAuditLogDO();
        auditLog.setBizType(OvertimeApplicationConstants.BIZ_TYPE);
        auditLog.setBizId(application.getId());
        auditLog.setActionType(actionType);
        auditLog.setFromStatus(fromStatus);
        auditLog.setToStatus(toStatus);
        auditLog.setFieldChanges(fieldChanges);
        auditLog.setReason(reason);
        auditLog.setOperatorUserId(SecurityFrameworkUtils.getLoginUserId());
        auditLog.setOperatorName(defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
        auditLog.setRemark(StringUtils.hasText(remark) ? remark : buildSnapshotRemark(application));
        bizAuditLogMapper.insert(auditLog);
    }

    private String buildSnapshotRemark(OvertimeApplicationDO application) {
        return "申请人：" + defaultText(application.getApplicantName())
                + "，加班日期：" + application.getOvertimeDate()
                + "，加班时长：" + defaultText(application.getOvertimeDuration());
    }

    private String buildFieldChanges(OvertimeApplicationDO before, OvertimeApplicationDO after) {
        Map<String, Object> fieldChanges = new LinkedHashMap<>();
        appendFieldChange(fieldChanges, "overtimeDate", valueOf(before, OvertimeApplicationDO::getOvertimeDate),
                valueOf(after, OvertimeApplicationDO::getOvertimeDate));
        appendFieldChange(fieldChanges, "overtimeDuration",
                valueOf(before, OvertimeApplicationDO::getOvertimeDuration),
                valueOf(after, OvertimeApplicationDO::getOvertimeDuration));
        appendFieldChange(fieldChanges, "overtimeReason", valueOf(before, OvertimeApplicationDO::getOvertimeReason),
                valueOf(after, OvertimeApplicationDO::getOvertimeReason));
        appendFieldChange(fieldChanges, "overtimeContent", valueOf(before, OvertimeApplicationDO::getOvertimeContent),
                valueOf(after, OvertimeApplicationDO::getOvertimeContent));
        appendFieldChange(fieldChanges, "approverId", valueOf(before, OvertimeApplicationDO::getApproverId),
                valueOf(after, OvertimeApplicationDO::getApproverId));
        appendFieldChange(fieldChanges, "approverName", valueOf(before, OvertimeApplicationDO::getApproverName),
                valueOf(after, OvertimeApplicationDO::getApproverName));
        appendFieldChange(fieldChanges, "statusCode", valueOf(before, OvertimeApplicationDO::getStatusCode),
                valueOf(after, OvertimeApplicationDO::getStatusCode));
        return fieldChanges.isEmpty() ? null : JsonUtils.toJsonString(fieldChanges);
    }

    private void appendFieldChange(Map<String, Object> fieldChanges, String fieldName, Object before, Object after) {
        if (Objects.equals(before, after)) {
            return;
        }
        Map<String, Object> value = new LinkedHashMap<>();
        value.put("before", before);
        value.put("after", after);
        fieldChanges.put(fieldName, value);
    }

    private <T> T valueOf(OvertimeApplicationDO application, Function<OvertimeApplicationDO, T> getter) {
        return application == null ? null : getter.apply(application);
    }

    private OvertimeApplicationDO cloneApplication(OvertimeApplicationDO source) {
        OvertimeApplicationDO target = new OvertimeApplicationDO();
        target.setId(source.getId());
        target.setApplicantId(source.getApplicantId());
        target.setApplicantName(source.getApplicantName());
        target.setOvertimeDate(source.getOvertimeDate());
        target.setOvertimeDuration(source.getOvertimeDuration());
        target.setOvertimeReason(source.getOvertimeReason());
        target.setOvertimeContent(source.getOvertimeContent());
        target.setApproverId(source.getApproverId());
        target.setApproverName(source.getApproverName());
        target.setStatusCode(source.getStatusCode());
        target.setApprovalComment(source.getApprovalComment());
        target.setSubmitTime(source.getSubmitTime());
        target.setApprovalTime(source.getApprovalTime());
        return target;
    }

    private OvertimeApplicationDO mergeUpdated(OvertimeApplicationDO current, OvertimeApplicationDO update) {
        OvertimeApplicationDO after = cloneApplication(current);
        if (update.getOvertimeDate() != null) {
            after.setOvertimeDate(update.getOvertimeDate());
        }
        if (update.getOvertimeDuration() != null) {
            after.setOvertimeDuration(update.getOvertimeDuration());
        }
        if (update.getOvertimeReason() != null) {
            after.setOvertimeReason(update.getOvertimeReason());
        }
        if (update.getOvertimeContent() != null) {
            after.setOvertimeContent(update.getOvertimeContent());
        }
        if (update.getApproverId() != null) {
            after.setApproverId(update.getApproverId());
        }
        if (update.getApproverName() != null) {
            after.setApproverName(update.getApproverName());
        }
        if (update.getStatusCode() != null) {
            after.setStatusCode(update.getStatusCode());
        }
        after.setApprovalComment(update.getApprovalComment());
        if (update.getSubmitTime() != null) {
            after.setSubmitTime(update.getSubmitTime());
        }
        after.setApprovalTime(update.getApprovalTime());
        return after;
    }

    private String normalizeRequiredText(String value, String message) {
        if (!StringUtils.hasText(value)) {
            throw invalidParamException(message);
        }
        return value.trim();
    }

    private String normalizeNullableText(String value) {
        if (!StringUtils.hasText(value)) {
            return null;
        }
        return value.trim();
    }

    private String defaultText(String value) {
        return StringUtils.hasText(value) ? value : "";
    }
}
