Skip to content
广告❤️成为赞助商

穿越时空审批

指定某个日期发起审批,所有审批任务记录为该时间,使用场景请假事后补审。

测试用例

所在源码位置 test.mysql.config.TestCreateTime 测试用例。

java
public class TestCreateTime extends MysqlTest {
    public static final String setFlowCreateTime = "setFlowCreateTime";

    @BeforeEach
    public void before() {
        processId = this.deployByResource("test/simpleProcess.json", testCreator);
    }

    @Test
    public void testCreateTime() {
        // 发起流程设置指定日期
        FlowDataTransfer.put(setFlowCreateTime, "2025-01-10");

        // 发起流程实例
        flowLongEngine.startInstanceById(processId, testCreator).ifPresent(instance -> {

            // 默认主管审批
            this.executeTask(instance.getId(), testCreator);

            // 条件路由子审批
            this.executeTask(instance.getId(), testCreator);

        });
    }
}

创建时间处理器接口

  • 实现创建时间处理器接口 FlowCreateTimeHandler 在生产使用根据 instanceId 绑定动态设定该流程下的任务审批时间指定处理。
java
public interface FlowCreateTimeHandler {

    /**
     * 获取当前时间,用于设置创建时间或更新时间
     *
     * @param executeType 工作流执行类型 {@link ExecuteType}
     * @param instanceId 流程实例ID
     * @param taskId 审批任务ID(不存在为流程实例调用情况)
     */
    Date getCurrentTime(ExecuteType executeType, Long instanceId, Long taskId);

    /**
     * 获取完成时间,用于设置流程实例结束时间或任务完成时间
     *
     * @param instanceId 流程实例ID
     * @param taskId 审批任务ID(不存在为流程实例调用情况)
     */
    Date getFinishTime(Long instanceId, Long taskId);
}

创建时间处理器接口实现

需要注意的是缓存需要清理,这里使用 ConcurrentHashMap 缓存,使用 instanceId 绑定动态设定该流程下的任务审批时间指定处理,分布式可以使用 redis 等缓存。

java

public class TestFlowCreateTimeHandler implements FlowCreateTimeHandler {
    private final Map<Long, String> instanceIdMap = new ConcurrentHashMap<>();

    @Override
    public Date getCurrentTime(ExecuteType executeType, Long instanceId, Long taskId) {
        String setTime = null;
        if (ExecuteType.process != executeType) {
            // 创建实例后续逻辑
            if (null != instanceId) {
                setTime = instanceIdMap.get(instanceId);
            }
            if (null == setTime) {
                setTime = FlowDataTransfer.get(TestCreateTime.setFlowCreateTime);
                if (null != setTime && null != instanceId) {
                    // 缓存实例指定时间
                    instanceIdMap.put(instanceId, setTime);
                    // 移除传递参数
                    FlowDataTransfer.removeByKey(TestCreateTime.setFlowCreateTime);
                }
            }
        }
        return getSetTime(setTime);
    }

    @Override
    public Date getFinishTime(Long instanceId, Long taskId) {
        if (null == instanceId) {
            return DateUtils.getCurrentDate();
        }
        String setTime = instanceIdMap.get(instanceId);
        if (null != setTime && null == taskId) {
            // 实例完成,删除缓存记录
            instanceIdMap.remove(instanceId);
        }
        return getSetTime(setTime);
    }

    public Date getSetTime(String setTime) {
        if (null != setTime) {
            try {
                // 获取当前时间的时分秒
                SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
                String currentTimeStr = timeFormat.format(new Date());

                // 组合日期和时间
                String combinedDateTimeStr = setTime + " " + currentTimeStr;
                SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                return dateTimeFormat.parse(combinedDateTimeStr);
            } catch (ParseException e) {
                e.printStackTrace();
                return null;
            }
        }
        return DateUtils.getCurrentDate();
    }
}

典型应用场景

1. 财务费用报销与成本核算

这是最经典的应用场景。

  • 场景描述: 员工在12月29日发生了一笔业务招待费,但直到1月4日才回到公司提交报销申请。根据会计准则,这笔费用必须计入上一财年(12月) 的成本中。
  • 工作流如何工作:
    1. 员工在1月4日提交报销单,但在表单中手动选择或填写“费用发生日期”为 12月29日
    2. 系统将此日期作为整个审批流程的“基准日期”。
    3. 无论部门经理、财务人员在1月的哪一天审批,所有审批记录和最终的系统凭证日期都显示为 12月29日
    4. 财务系统在生成报表时,这笔费用会自动归集到12月的账目中,确保了财务报表的准确性。

2. 合同与协议审批

  • 场景描述: 一份销售合同的实际签署日和生效日是12月25日,但内部法务和管理层因为假期原因,在1月初才完成线上审批流程。
  • 工作流如何工作:
    1. 销售人员在1月5日发起合同审批,但指定“合同生效日期”为 12月25日
    2. 所有审批节点的操作时间在系统记录上都关联到12月25日。
    3. 最终生成的合同编号、系统记录的合同生效日期都是12月25日,这与纸质合同保持一致,避免了法律和业务上的混淆。

3. 补打卡、补请假等考勤流程

  • 场景描述: 员工忘记在12月30日打卡或申请事假,需要在1月3日进行补申请。
  • 工作流如何工作:
    1. 员工在1月3日发起“补打卡申请”或“事假申请”,并选择申请日期为 12月30日
    2. 审批流程记录为该日期。
    3. 最终,HR系统和考勤计算会认为该申请是在12月30日生效的,从而正确计算12月的出勤、薪资和扣款。

4. 采购与资产入库

  • 场景描述: 一批货物在12月31日已经送达仓库并验收完毕(实物入库),但相关的采购发票和审批流程在1月份才完成。
  • 工作流如何工作:
    1. 采购人员在1月发起采购付款审批,但指定“货物接收日期”或“资产确认日期”为 12月31日
    2. 审批流程以此日期为准。
    3. 这批货物在资产管理系统和财务账上会被记为上一财年的资产,符合权责发生制原则。

5. 项目里程碑确认

  • 场景描述: 一个项目在12月28日达到了某个关键里程碑,但项目报告和确认流程在1月初才走完。
  • 工作流如何工作:
    1. 项目经理在1月提交里程碑达成审批,指定“里程碑达成日期”为 12月28日
    2. 所有审批记录与此日期绑定。
    3. 项目管理系统和绩效评估会基于12月28日这个日期来记录进度和计算奖金。

更适合中国人的工作流引擎