引言
最近因工作需求,需要写一个流程审批业务。
需求
当申请人在网页上完成申请单的填写并提交后,该申请单将自动流转至申请人所在部门的负责人进行审批。部门负责人将根据申请单所提供的信息,决定批准或者退回申请。
一旦部门负责人审批通过,申请单将继续流转至下一环节,届时将由其他同事根据审批结果和既定流程进行后续的处理或协作。
需求分解
想要实现这么一个功能。主要有俩大模块,分别是申请单和流程设计。
申请单:用户处理的就是申请单,申请单按照流程的设计来实现不同节点不同处理人之间的流转
流程:定义了申请单的审批链、各节点的处理人、各节点可以执行的操作(审批、退回)
表设计
申请单:申请单编号、申请单状态、创建时间、其他详细信息等,还需关联当前处理人、申请人、流程、当前节点
流程:流程名称、流程key等
流程节点:节点名称、节点顺序、节点处理人、审批类型(或签、会签)等,还需关联流程
流程流转:流转名称(即:提交、保存、退回、关闭)、源节点、目标节点等,还需关联流程
申请单流转日志:流转名称、审批意见、处理人等,还需关联申请单、当前节点
django model设计
class CellApplyForm(CoreModel,SoftDeleteModel):
app_number = models.CharField(max_length=64, null=True, blank=True, verbose_name="申请单编号", help_text="申请单编号")
test_number = models.CharField(max_length=64,null=True, blank=True, verbose_name="测试编号", help_text="测试编号")
handler = models.CharField(max_length=32,null=True, blank=True, verbose_name="当前处理人", help_text="当前处理人")
isSubmit = models.BooleanField(default=True, verbose_name="提交or保存", help_text="提交or保存")
important = models.CharField(max_length=16, null=True, blank=True, verbose_name="紧急程度", help_text="紧急程度")
status = models.CharField(max_length=16, null=True, blank=True, verbose_name="申请单状态(被驳回rejected,待提交created,待审核approve,测试中testing,已归档completed)", help_text="申请单状态")
test_type = models.CharField(max_length=16, null=True, blank=True, verbose_name="测试类型(电芯cell、材料material、电池pack)", help_text="测试类型")
flow_node = models.ForeignKey(FlowNode,null=True, blank=True,related_name="cell_flow_node", on_delete=models.CASCADE, verbose_name='当前节点')
flow = models.ForeignKey(Flow,null=True, blank=True, on_delete=models.CASCADE, related_name="cell_flow",verbose_name='流程名称')
apply_user = models.ForeignKey(Users, related_name="apply_user", on_delete=models.CASCADE, db_constraint=False,
verbose_name="关联申请人", help_text="关联申请人")
handle_user = models.ManyToManyField(to=Users, related_name="handle_user",blank=True, db_constraint=False,
verbose_name="当前处理人", help_text="当前处理人")
class Meta:
db_table = table_prefix + "cell_apply_form"
verbose_name = "电芯测试申请单"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
class Flow(CoreModel,SoftDeleteModel):
name = models.CharField(max_length=64, verbose_name="流程名称", help_text="流程名称")
key = models.CharField(max_length=64, default='xx',unique=True, verbose_name="流程key", help_text="流程key")
sort = models.IntegerField(default=1, verbose_name="流程顺序", help_text="流程顺序")
status = models.BooleanField(default=True, verbose_name="流程状态", help_text="流程状态")
class Meta:
db_table = table_prefix + "flow"
verbose_name = "流程表"
verbose_name_plural = verbose_name
ordering = ("sort",)
class FlowNode(CoreModel,SoftDeleteModel):
flow = models.ForeignKey(Flow, on_delete=models.CASCADE, related_name="flowNodes",verbose_name='流程名称') # 外键关联所属流程
name = models.CharField(max_length=64, verbose_name="节点名称", help_text="节点名称")
sort = models.IntegerField(default=0, verbose_name="节点顺序", help_text="节点顺序")
status = models.BooleanField(default=True, verbose_name="节点状态", help_text="节点状态")
NODE_TYPE_CHOICES = (
(0, '普通节点'),
(1, '初始节点'),
(2, '结束节点')
)
node_type = models.IntegerField(choices=NODE_TYPE_CHOICES,default=0,verbose_name='节点类型',help_text='节点类型')
PARTICIPANT_TYPE_CHOICES = (
(0, '无处理人'),
(1, '用户'),
# (2, '部门'),
# (3, '角色'),
)
participant_type = models.IntegerField(default=0, choices=PARTICIPANT_TYPE_CHOICES, verbose_name='参与者类型')
participant = models.ManyToManyField(to=Users, related_name='users', blank=True, db_constraint=False,
verbose_name="目标用户", help_text="目标用户")
APPROVAL_TYPE_CHOICES = (
(0, '或签'),
(1, '会签'),
)
approval_type = models.IntegerField(default=0, choices=PARTICIPANT_TYPE_CHOICES, verbose_name='审批类型')
class Meta:
db_table = table_prefix + "flow_node"
verbose_name = "流程节点表"
verbose_name_plural = verbose_name
ordering = ("sort",)
class FlowTransition(CoreModel,SoftDeleteModel):
flow = models.ForeignKey(Flow, on_delete=models.CASCADE, related_name="flowTransitions",verbose_name='流程名称') # 外键关联所属流程
NAME_CHOICES = (
(0, '保存'),
(1, '审批通过'),
(2, '驳回'),
(3, '撤销'),
)
name = models.IntegerField(default=1, choices=NAME_CHOICES, verbose_name='流转名称',help_text='流转名称')
source_node = models.ForeignKey(FlowNode, null=True, blank=True, on_delete=models.SET_NULL, related_name="source_node", verbose_name='源节点')
target_node = models.ForeignKey(FlowNode, null=True, blank=True, on_delete=models.SET_NULL, related_name="target_node", verbose_name='目标节点')
ATTRIBUTE_TYPE_CHOICES = (
(0, '同意'),
(1, '拒绝'),
(2, '其他'),
)
attribute_type = models.IntegerField(default=0,choices=ATTRIBUTE_TYPE_CHOICES, verbose_name='属性类型')
class Meta:
db_table = table_prefix + "flow_transition"
verbose_name = "流程流转表"
verbose_name_plural = verbose_name
ordering = ("flow",)
class FlowTransitionLog(CoreModel,SoftDeleteModel):
apply_form = models.ForeignKey(CellApplyForm,null=True, blank=True, related_name="transition_log", on_delete=models.CASCADE, db_constraint=False,
verbose_name="关联测试申请单", help_text="关联测试申请单")
flow_node = models.ForeignKey(FlowNode,null=True, blank=True, on_delete=models.CASCADE, db_constraint=False,
verbose_name="当前节点", help_text="当前节点")
flow_transition_name = models.CharField(max_length=8, null=True, blank=True,verbose_name='流程流转名称',help_text='流程流转名称')
handler = models.CharField(max_length=32,null=True, blank=True, verbose_name="当前处理人", help_text="当前处理人")
suggestion = models.TextField(null=True, blank=True, verbose_name="处理意见", help_text="处理意见")
start_datetime = models.DateTimeField(null=True, blank=True, help_text="开始时间",verbose_name="开始时间")
end_datetime = models.DateTimeField(null=True, blank=True, help_text="结束时间",verbose_name="结束时间")
class Meta:
db_table = table_prefix + "flow_transition_log"
verbose_name = "测试申请单-工单流转记录表"
verbose_name_plural = verbose_name
ordering = ("-create_datetime",)
vue3页面
流程页面如下:
流程节点配置如下:
流程流转配置如下:
申请单流程日志如下:
结束
在最开始拿到这个需求的时候我是比较懵逼的,不知道从哪下手。后来把这个需求一步一步分解,一步一步去完成,自然而然就写完了。最后想说的是,拿到困难需求不要畏惧,能让你痛苦的就能让你成长,能让你技术能力快速提高!