src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
@@ -184,6 +184,10 @@
        if (approvalInstanceDto == null || approvalInstanceDto.getId() == null) {
            return false;
        }
        ApprovalInstance existing = this.getById(approvalInstanceDto.getId());
        if (existing == null) {
            return false;
        }
        // 判断是否有正在进行的审批任务,有则不允许修改
        long pendingTaskCount = approvalTaskService.count(
                Wrappers.<ApprovalTask>lambdaQuery()
@@ -194,9 +198,47 @@
        if (pendingTaskCount > 0) {
            throw new ServiceException("该审批单有正在进行的审批任务,不允许修改");
        }
        // 未通过且当前没有待处理节点,说明流程已终止(已驳回),
        // 修改保存即视为重新提交,流程从第一级重新开始
        boolean resubmit = !"APPROVED".equals(existing.getStatus())
                && approveProcessConfigNodeUtils.getCurrentNode(existing.getId()) == null;
        if (resubmit) {
            // 软删除旧的节点与任务(审批记录保留,时间线叠加)
            LambdaUpdateWrapper<ApprovalInstanceNode> nodeUpdateWrapper = Wrappers.lambdaUpdate();
            nodeUpdateWrapper.eq(ApprovalInstanceNode::getInstanceId, approvalInstanceDto.getId())
                    .eq(ApprovalInstanceNode::getDeleted, 0)
                    .set(ApprovalInstanceNode::getDeleted, (byte) 1);
            approvalInstanceNodeService.update(nodeUpdateWrapper);
            LambdaUpdateWrapper<ApprovalTask> taskUpdateWrapper = Wrappers.lambdaUpdate();
            taskUpdateWrapper.eq(ApprovalTask::getInstanceId, approvalInstanceDto.getId())
                    .eq(ApprovalTask::getDeleted, 0)
                    .set(ApprovalTask::getDeleted, (byte) 1);
            approvalTaskService.update(taskUpdateWrapper);
            approvalInstanceDto.setStatus("PENDING");
            approvalInstanceDto.setCurrentLevel(1);
            approvalInstanceDto.setFinishTime(null);
            approvalInstanceDto.setInstanceNo(existing.getInstanceNo());
            approvalInstanceDto.setApplicantId(existing.getApplicantId());
            approvalInstanceDto.setApplicantName(existing.getApplicantName());
        }
        boolean updated = this.updateById(approvalInstanceDto);
        if (!updated) {
            return false;
        }
        if (resubmit) {
            // updateById 忽略 null,finishTime 需用显式 set 才能清空
            this.update(
                    Wrappers.<ApprovalInstance>lambdaUpdate()
                            .eq(ApprovalInstance::getId, approvalInstanceDto.getId())
                            .set(ApprovalInstance::getFinishTime, null)
            );
            // 重建第一级节点与审批任务,并追加一条发起审批记录
            approveProcessConfigNodeUtils.createCurrentNodeAndTasks(approvalInstanceDto, true);
            sendApproveNotice(approvalInstanceDto, approveProcessConfigNodeUtils.getCurrentPendingTasks(approvalInstanceDto.getId()));
        }
        fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVAL_INSTANCE, approvalInstanceDto.getId(), approvalInstanceDto.getStorageBlobDTOs());
        return true;
@@ -745,10 +787,29 @@
        String title = StringUtils.hasText(instance.getTemplateName()) ? instance.getTemplateName() : "审批提醒";
        String message = "审批单号 " + instance.getInstanceNo() + " 需要您审批";
        String jumpPath = "/officeProcessAutomation/ApproveManage/approve-list?id=" + instance.getId();
        String jumpPath = getJumpPathByBusinessType(instance.getBusinessType(), instance.getId());
        sysNoticeService.simpleNoticeByUser(title, message, approverIds, jumpPath);
    }
    private String getJumpPathByBusinessType(Long businessType, Long instanceId) {
        if (businessType == null) {
            return "/collaborativeApproval/approve-list?id=" + instanceId;
        }
        if (TypeEnums.LEAVE_APPROVAL.getCode().equals(businessType)) {
            return "/collaborativeApproval/AttendManage/leave-apply?id=" + instanceId;
        }
        if (TypeEnums.OVERTIME_APPROVAL.getCode().equals(businessType)) {
            return "/collaborativeApproval/AttendManage/overtime-apply?id=" + instanceId;
        }
        if (TypeEnums.TRAVEL_REIMBURSEMENT_APPROVAL.getCode().equals(businessType)) {
            return "/collaborativeApproval/ReimburseManage/travel-reimburse?id=" + instanceId;
        }
        if (TypeEnums.EXPENSE_APPROVAL.getCode().equals(businessType)) {
            return "/collaborativeApproval/ReimburseManage/cost-reimburse?id=" + instanceId;
        }
        return "/collaborativeApproval/approve-list?id=" + instanceId;
    }
    private void sendEnterpriseNewsNotice(Long newsId) {
        EnterpriseNews enterpriseNews = enterpriseNewsMapper.selectById(newsId);
        if (enterpriseNews == null) {