# 案例
# 背景
本文将以企业实际业务中的“合同管理”为例,介绍如何基于Alfresco-Vue来实现。
系统的扩展配置,需要使用系统管理员身份登录。
合同管理需求点:
- 合同起草人基于合同模板在线创建合同,此时合同为“草稿”状态,仅创建人本人可见
- 在线编辑合同,并可分配权限给其他人员,进行共通编辑
- 合同定稿后,发起审批,自动分配权限给审批人员,此时合同为“审批中”状态
- 合同审批流程和审批表单可在线灵活设计
- 合同审批通过后,状态变更为“已审批”
- 在合同一览中需要直观显示“合同状态”、“合同编号”、“有效期限”
审批流程:
- 起草合同
- 发起合同审批,选择合同确认人员(单人),提交审批
- 合同确认人员审批通过,下一步流转到合同复核,合同复核是由合同复核组内所有人员进行审批,只要有一人审批通过即可
- 下一步流转到合同审核,合同审核是由合同审核组内所有人员进行审批,需要所有人都审批通过
- 审批过程中,只要被人驳回,均流转到合同审批的发起人员进行修改,之后再次发起审批
前期准备:
- 打开 系统管理 > 账号管理 页面
- 新增公司组织:厦门艾魅尔科技有限公司
- 新增组 合同复核、合同审核
- 新增合同起草人员:林毅(加入到公司组)
- 新增合同确认人员:许志宏(加入到公司组)
- 新增合同复核人员:杨先壮、吕竹风(加入到公司组、合同复核组)
- 新增合同审核人员:张伟华、蔡春辉(加入到公司组、合同审核组)
# 1、创建数据模型
说明
首先,数据模型的定义并不存在唯一解。也就是说,属性可以定义在Types中,也可以定义在Aspect中,需要根据具体情况具体分析。
关于数据模型定义的一些建议,可以参考:内容建模最佳实践。
在ADF-VUE中,为了能够灵活地往文档中添加属性,需要将属性都设置在Aspect中。
# 打开 系统管理 > 数据模型 页面
# 创建新的数据模型:cusModel.xml
<?xml version="1.0" encoding="UTF-8"?>
<model name="cus:model" xmlns="http://www.alfresco.org/model/dictionary/1.0">
<!-- Optional meta-data about the model -->
<description>Custom Model</description>
<author>leencloud</author>
<version>1.0</version>
<imports>
<!-- Import Alfresco Dictionary Definitions -->
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
<import uri="http://www.alfresco.org/model/system/1.0" prefix="sys"/>
<!-- Import Alfresco Content Domain Model Definitions -->
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
</imports>
<!-- Introduction of new namespaces defined by this model -->
<!-- NOTE: The following namespace custom.model should be changed to reflect your own namespace -->
<namespaces>
<namespace uri="http://www.alfresco.org/model/custom/1.0" prefix="cus"/>
</namespaces>
<aspects>
<aspect name="cus:contract">
<title>合同</title>
<properties>
<property name="cus:status">
<title>合同状态</title>
<type>d:text</type>
<index enabled="true">
<atomic>true</atomic>
<stored>false</stored>
<tokenised>false</tokenised>
</index>
</property>
<property name="cus:number">
<title>合同编号</title>
<type>d:text</type>
<index enabled="true">
<atomic>true</atomic>
<stored>false</stored>
<tokenised>both</tokenised>
</index>
</property>
<property name="cus:valid">
<title>有效期限</title>
<type>d:date</type>
<index enabled="true">
<atomic>true</atomic>
<stored>false</stored>
<tokenised>false</tokenised>
</index>
</property>
<property name="cus:confirm">
<title>合同确认者</title>
<type>d:text</type>
<index enabled="false">
</index>
</property>
</properties>
</aspect>
</aspects>
</model>
property中的index说明
- enabled
- true 生成索引(可使用alfresco search搜索该属性)
- false 不生成索引(下方的三个配置也将不生效)
- atomic
- true 同步生成索引
- false 后台异步生成索引
- stored
- true 属性值将存储在索引中,方便在开发环境中查看准确的索引值
- false 在生产环境中,请设为false
- tokenised
- true 索引前进行分词处理,索引分词结果
- false 索引前不进行分词处理,索引这个值
- both 索引前进行分词梳理,索引整个值和分词结果
# 点击页面右上方的“激活”按钮,激活数据模型
# 2、创建站点
# 打开 系统管理 > 站点管理 页面
# 点击 创建站点 按钮,创建一个站点用于管理合同
关于站点类型
站点类型属于预置属性,目前没有特别的业务和功能含义。
# 3、站点配置
# 设置权限
# 进入 合同管理 站点,切换到 站点成员与权限 标签页,将 公司组 设置为站点的阅览者,合同起草人员 林毅 设置为站点的编辑者,
# 设置列
# 回到 文档库 标签页,高级列表上方的 高级列设置 按钮,删除 版本、大小 列,添加 合同编号、合同状态、有效期限 列
# 4、文件夹规则
# 新建文件夹规则
# 打开 系统管理 > 数据模型 页面
创建新的规则脚本:
- 文件名:contract_upload
- 规则名:合同文件上传后处理
脚本内容:
if (document.typeShort == "cm:content") {
// 切断继承,仅限上传者访问
document.setInheritsPermissions(false);
// 更改合同状态
document.properties["cus:status"] = "草稿";
document.save();
}
# 为文件夹绑定规则
# 返回 合同管理 站点,创建新文件夹 ”2023年“ 用于存储2023年的合同
# 点击文件夹右侧的操作列中的 “...” 图标,展开操作菜单,选择 规则设置
# 点击 新建规则,为文件夹绑定刚才创建的规则
# 5、设计审批表单
说明
如果根据流程状态的不同,表单的表现形式也不同,那么需要为每个流程节点设计不同的表单
一般的情况是:发起审批时表单各项目可填写,审批阶段表单各项目不可编辑
因此我们需要设计两个表单,分别对应不同情况
# 设计发起审批节点的表单
# 打开 系统管理 > 流程表单 页面,点击 创建表单
各表单项目的字段标识
- 合同编号:cus_number(cusModel.xml中新定义属性)
- 有效期限:cus_valid(cusModel.xml中新定义属性)
- 申请说明:idoc_initiatdesc(既有属性)
- 合同确认者:cus_confirm(cusModel.xml中新定义属性)
- 待审批文件:wf_doc(默认属性)
# 设计审批中、审批完成的表单
# 回到流程表单页面,点击表单数据右侧的 复制 按钮,将刚创建的表单复制一份
# 点击编辑,将复制出的表单各项改为“禁用”
# 激活表单
回到 流程表单 页面,将两份表单的状态改为:“激活”
# 6、设计审批流程
# 打开 系统管理 > 工作流程 页面,点击 创建流程
流程设计器知识点
关于流程设计器的使用,可参考:后端开发 > Workflow
设计的流程文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.activiti.org/processdef">
<process id="contract_approve_test" name="合同审批测试" isExecutable="true">
<extensionElements>
<activiti:executionListener class="org.alfresco.repo.workflow.activiti.listener.ScriptExecutionListener" event="end">
<activiti:field name="script">
<activiti:string>var completed = execution.getVariable('wf_completed');
// 如果是取消流程,则还原文档状态为“草稿”
if (!completed) {
authUtil.runas(function() {
var nodes = bpm_package.childAssocs["bpm:packageContains"];
if (nodes) {
for (var i=0; i<nodes.length; i++) {
var node = nodes[i];
node.properties["cus:status"] = "草稿";
node.save();
}
}
});
}</activiti:string>
</activiti:field>
</activiti:executionListener>
</extensionElements>
<startEvent id="StartEvent_1" name="开始" activiti:formKey="form_75351084311678346338531">
<extensionElements>
<activiti:executionListener class="org.alfresco.repo.workflow.activiti.listener.ScriptExecutionListener" event="end">
<activiti:field name="script">
<activiti:string>// 记录流程历史
var hisDate = utils.toISO8601(new Date()).substr(0, 19).replace("T", " ");
var hisUser = person.properties["cm:firstName"] + "(" + person.properties["cm:userName"] + ")";
var hisAct = "wfActionStart";
bpm_package.properties["idoc:wfhis"] = hisDate + "|" + hisUser + "|" + hisAct + "|";
bpm_package.save();</activiti:string>
</activiti:field>
</activiti:executionListener>
</extensionElements>
<outgoing>Flow_1hn4w3o</outgoing>
</startEvent>
<userTask id="Activity_0q8spra" name="合同确认" activiti:formKey="form_75351084311678346338531-1" activiti:assignee="${cus_confirm}">
<extensionElements>
<activiti:executionListener class="org.alfresco.repo.workflow.activiti.listener.ScriptExecutionListener" event="start">
<activiti:field name="script">
<activiti:string>// 将表单中的合同确认者设置到流程变量中
var cus_confirm = bpm_package.properties["cus:confirm"];
execution.setVariable('cus_confirm', cus_confirm);
// 更改审批文档
var nodes = bpm_package.childAssocs["bpm:packageContains"];
if (nodes) {
for (var i=0; i<nodes.length; i++) {
var node = nodes[i];
// 将文档状态更改为:审批中
node.properties["cus:status"] = "审批中";
node.save();
}
}</activiti:string>
</activiti:field>
</activiti:executionListener>
<activiti:taskListener class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener" event="complete">
<activiti:field name="script">
<activiti:string>// 将通过/否决的信息设置到流程变量,以便决定分支走向
var wf_reviewOutcome = task.getVariable('wf_reviewOutcome');
execution.setVariable('wf_reviewOutcome', wf_reviewOutcome);
// 记录流程历史
var hisDate = utils.toISO8601(new Date()).substr(0, 19).replace("T", " ");
var hisUser = person.properties["cm:firstName"] + "(" + person.properties["cm:userName"] + ")";
var hisAct = "wfAction" + wf_reviewOutcome;
var wfhis = bpm_package.properties["idoc:wfhis"];
if (!wfhis) {
wfhis = new Array();
}
var wf_comment = task.getVariable('wf_comment');
wfhis.push(hisDate + "|" + hisUser + "|" + hisAct + "|" + wf_comment);
bpm_package.properties["idoc:wfhis"] = wfhis;
bpm_package.save();</activiti:string>
</activiti:field>
</activiti:taskListener>
<activiti:taskListener class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener" event="assignment">
<activiti:field name="script">
<activiti:string>// 给审批者赋予权限
authUtil.runas(function() {
var nodes = bpm_package.childAssocs["bpm:packageContains"];
if (nodes) {
for (var i=0; i<nodes.length; i++) {
var node = nodes[i];
// 给审批者赋予权限
node.setPermission("Collaborator", task.assignee);
}
}
});</activiti:string>
</activiti:field>
</activiti:taskListener>
</extensionElements>
<incoming>Flow_1hn4w3o</incoming>
<incoming>Flow_0dlq2m8</incoming>
<outgoing>Flow_02z2rrq</outgoing>
</userTask>
<sequenceFlow id="Flow_1hn4w3o" sourceRef="StartEvent_1" targetRef="Activity_0q8spra" />
<userTask id="Activity_0hxiyt6" name="合同复核" activiti:formKey="form_75351084311678346338531-1" activiti:assignee="${reviewer}">
<extensionElements>
<activiti:executionListener class="org.alfresco.repo.workflow.activiti.listener.ScriptExecutionListener" event="start">
<activiti:field name="script">
<activiti:string>// 取出合同复核组中的成员
var aGroup = people.getGroup("GROUP_contract_review");
var members = people.getMembers(aGroup);
var memberNames = new java.util.ArrayList();
for (var i in members) {
memberNames.add(members[i].properties.userName);
}
// 将合同复核组中的成员设置到流程变量中
execution.setVariable('review_assign', memberNames);
// 初始化合同复核节点的变量
execution.setVariable('review_complete', false);</activiti:string>
</activiti:field>
</activiti:executionListener>
<activiti:taskListener class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener" event="complete">
<activiti:field name="script">
<activiti:string>// 只要有一人审批即可(无论通过还是否决)
execution.setVariable('review_complete', true);
// 将通过/否决的信息设置到流程变量,以便决定分支走向
var wf_reviewOutcome = task.getVariable('wf_reviewOutcome');
execution.setVariable('wf_reviewOutcome', wf_reviewOutcome);
// 记录流程历史
var hisDate = utils.toISO8601(new Date()).substr(0, 19).replace("T", " ");
var hisUser = person.properties["cm:firstName"] + "(" + person.properties["cm:userName"] + ")";
var hisAct = "wfAction" + wf_reviewOutcome;
var wfhis = bpm_package.properties["idoc:wfhis"];
if (!wfhis) {
wfhis = new Array();
}
var wf_comment = task.getVariable('wf_comment');
wfhis.push(hisDate + "|" + hisUser + "|" + hisAct + "|" + wf_comment);
bpm_package.properties["idoc:wfhis"] = wfhis;
bpm_package.save();</activiti:string>
</activiti:field>
</activiti:taskListener>
<activiti:taskListener class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener" event="assignment">
<activiti:field name="script">
<activiti:string>// 给审批者赋予权限
authUtil.runas(function() {
var nodes = bpm_package.childAssocs["bpm:packageContains"];
if (nodes) {
for (var i=0; i<nodes.length; i++) {
var node = nodes[i];
// 给审批者赋予权限
node.setPermission("Collaborator", task.assignee);
}
}
});</activiti:string>
</activiti:field>
</activiti:taskListener>
</extensionElements>
<incoming>Flow_0cf866a</incoming>
<outgoing>Flow_0qnbcun</outgoing>
<multiInstanceLoopCharacteristics activiti:collection="review_assign" activiti:elementVariable="reviewer">
<completionCondition xsi:type="tFormalExpression">${review_complete==true}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
<userTask id="Activity_0v1fbjk" name="合同审核" activiti:formKey="form_75351084311678346338531-1" activiti:assignee="${approver}">
<extensionElements>
<activiti:executionListener class="org.alfresco.repo.workflow.activiti.listener.ScriptExecutionListener" event="start">
<activiti:field name="script">
<activiti:string>// 取出合同审核组中的成员
var aGroup = people.getGroup("GROUP_contract_approve");
var members = people.getMembers(aGroup);
var memberNames = new java.util.ArrayList();
for (var i in members) {
memberNames.add(members[i].properties.userName);
}
// 将合同审核组中的成员设置到流程变量中
execution.setVariable('approve_assign', memberNames);
// 初始化合同审核节点的变量
// 是否被否决
execution.setVariable('wf_reviewOutcome', '');
// 需要处理总数
execution.setVariable('review_complete_need', members.length);
// 当前处理总数
execution.setVariable('review_complete', 0);</activiti:string>
</activiti:field>
</activiti:executionListener>
<activiti:taskListener class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener" event="complete">
<activiti:field name="script">
<activiti:string>// 将通过/否决的信息设置到流程变量,以便决定分支走向
var wf_reviewOutcome = task.getVariable('wf_reviewOutcome');
execution.setVariable('wf_reviewOutcome', wf_reviewOutcome);
// 记录流程历史
var hisDate = utils.toISO8601(new Date()).substr(0, 19).replace("T", " ");
var hisUser = person.properties["cm:firstName"] + "(" + person.properties["cm:userName"] + ")";
var hisAct = "wfAction" + wf_reviewOutcome;
var wfhis = bpm_package.properties["idoc:wfhis"];
if (!wfhis) {
wfhis = new Array();
}
var wf_comment = task.getVariable('wf_comment');
wfhis.push(hisDate + "|" + hisUser + "|" + hisAct + "|" + wf_comment);
bpm_package.properties["idoc:wfhis"] = wfhis;
bpm_package.save();</activiti:string>
</activiti:field>
</activiti:taskListener>
<activiti:taskListener class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener" event="assignment">
<activiti:field name="script">
<activiti:string>// 给审批者赋予权限
authUtil.runas(function() {
var nodes = bpm_package.childAssocs["bpm:packageContains"];
if (nodes) {
for (var i=0; i<nodes.length; i++) {
var node = nodes[i];
// 给审批者赋予权限
node.setPermission("Collaborator", task.assignee);
}
}
});</activiti:string>
</activiti:field>
</activiti:taskListener>
</extensionElements>
<incoming>Flow_0am3486</incoming>
<outgoing>Flow_09v32lj</outgoing>
<multiInstanceLoopCharacteristics activiti:collection="approve_assign" activiti:elementVariable="approver">
<completionCondition xsi:type="tFormalExpression">${wf_reviewOutcome=="Reject"|| review_complete >= review_complete_need}</completionCondition>
</multiInstanceLoopCharacteristics>
</userTask>
<endEvent id="Event_1hgiorz" name="结束">
<extensionElements>
<activiti:executionListener class="org.alfresco.repo.workflow.activiti.listener.ScriptExecutionListener" event="end">
<activiti:field name="script">
<activiti:string>// 将文档状态更改为:已审批
var nodes = bpm_package.childAssocs["bpm:packageContains"];
if (nodes) {
for (var i=0; i<nodes.length; i++) {
var node = nodes[i];
node.properties["cus:status"] = "已审批";
node.properties["cus:number"] = bpm_package.properties["cus:number"];
node.properties["cus:valid"] = bpm_package.properties["cus:valid"];
node.save();
}
}
// 流程标识为已完成(流程的end监听里根据这个变量判断是已完成还是流程取消)
execution.setVariable('wf_completed', true);</activiti:string>
</activiti:field>
</activiti:executionListener>
</extensionElements>
<incoming>Flow_0ow0dx1</incoming>
</endEvent>
<userTask id="Activity_1sb5w1v" name="申请者修改" activiti:formKey="form_75351084311678346338531" activiti:assignee="${initiator.properties.userName}">
<extensionElements>
<activiti:taskListener class="org.alfresco.repo.workflow.activiti.tasklistener.ScriptTaskListener" event="complete">
<activiti:field name="script">
<activiti:string>// 记录流程历史
var hisDate = utils.toISO8601(new Date()).substr(0, 19).replace("T", " ");
var hisUser = person.properties["cm:firstName"] + "(" + person.properties["cm:userName"] + ")";
var hisAct = "wfActionResubmit";
var wfhis = bpm_package.properties["idoc:wfhis"];
if (!wfhis) {
wfhis = new Array();
}
wfhis.push(hisDate + "|" + hisUser + "|" + hisAct + "|");
bpm_package.properties["idoc:wfhis"] = wfhis;
bpm_package.save();</activiti:string>
</activiti:field>
</activiti:taskListener>
</extensionElements>
<incoming>Flow_0v04kgi</incoming>
<incoming>Flow_0x8aqhn</incoming>
<incoming>Flow_16jqt75</incoming>
<outgoing>Flow_0dlq2m8</outgoing>
</userTask>
<exclusiveGateway id="Gateway_0yuqj8r" default="Flow_0cf866a">
<incoming>Flow_02z2rrq</incoming>
<outgoing>Flow_0v04kgi</outgoing>
<outgoing>Flow_0cf866a</outgoing>
</exclusiveGateway>
<sequenceFlow id="Flow_02z2rrq" sourceRef="Activity_0q8spra" targetRef="Gateway_0yuqj8r" />
<sequenceFlow id="Flow_0v04kgi" name="否决" sourceRef="Gateway_0yuqj8r" targetRef="Activity_1sb5w1v">
<conditionExpression xsi:type="tFormalExpression">${wf_reviewOutcome == 'Reject'}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="Flow_0dlq2m8" sourceRef="Activity_1sb5w1v" targetRef="Activity_0q8spra" />
<sequenceFlow id="Flow_0cf866a" name="通过" sourceRef="Gateway_0yuqj8r" targetRef="Activity_0hxiyt6" />
<exclusiveGateway id="Gateway_15wjx01" default="Flow_0am3486">
<incoming>Flow_0qnbcun</incoming>
<outgoing>Flow_0x8aqhn</outgoing>
<outgoing>Flow_0am3486</outgoing>
</exclusiveGateway>
<sequenceFlow id="Flow_0qnbcun" sourceRef="Activity_0hxiyt6" targetRef="Gateway_15wjx01" />
<sequenceFlow id="Flow_0x8aqhn" name="否决" sourceRef="Gateway_15wjx01" targetRef="Activity_1sb5w1v">
<conditionExpression xsi:type="tFormalExpression">${wf_reviewOutcome == 'Reject'}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="Flow_0am3486" name="通过" sourceRef="Gateway_15wjx01" targetRef="Activity_0v1fbjk" />
<exclusiveGateway id="Gateway_1xqztsb" default="Flow_0ow0dx1">
<incoming>Flow_09v32lj</incoming>
<outgoing>Flow_16jqt75</outgoing>
<outgoing>Flow_0ow0dx1</outgoing>
</exclusiveGateway>
<sequenceFlow id="Flow_09v32lj" sourceRef="Activity_0v1fbjk" targetRef="Gateway_1xqztsb" />
<sequenceFlow id="Flow_16jqt75" name="否决" sourceRef="Gateway_1xqztsb" targetRef="Activity_1sb5w1v">
<conditionExpression xsi:type="tFormalExpression">${wf_reviewOutcome == 'Reject'}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="Flow_0ow0dx1" name="通过" sourceRef="Gateway_1xqztsb" targetRef="Event_1hgiorz" />
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="contract_approve_test_di" bpmnElement="contract_approve_test">
<bpmndi:BPMNShape id="_BPMNShape_StartEvent_1" bpmnElement="StartEvent_1">
<omgdc:Bounds x="-18" y="322" width="36" height="36" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="-11" y="365" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0q8spra_di" bpmnElement="Activity_0q8spra">
<omgdc:Bounds x="70" y="300" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0hxiyt6_di" bpmnElement="Activity_0hxiyt6">
<omgdc:Bounds x="310" y="300" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0v1fbjk_di" bpmnElement="Activity_0v1fbjk">
<omgdc:Bounds x="560" y="300" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_1hgiorz_di" bpmnElement="Event_1hgiorz">
<omgdc:Bounds x="802" y="322" width="36" height="36" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="809" y="365" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1sb5w1v_di" bpmnElement="Activity_1sb5w1v">
<omgdc:Bounds x="70" y="430" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_0yuqj8r_di" bpmnElement="Gateway_0yuqj8r" isMarkerVisible="true">
<omgdc:Bounds x="205" y="315" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_15wjx01_di" bpmnElement="Gateway_15wjx01" isMarkerVisible="true">
<omgdc:Bounds x="445" y="315" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_1xqztsb_di" bpmnElement="Gateway_1xqztsb" isMarkerVisible="true">
<omgdc:Bounds x="695" y="315" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="Flow_1hn4w3o_di" bpmnElement="Flow_1hn4w3o">
<omgdi:waypoint x="18" y="340" />
<omgdi:waypoint x="70" y="340" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_02z2rrq_di" bpmnElement="Flow_02z2rrq">
<omgdi:waypoint x="170" y="340" />
<omgdi:waypoint x="205" y="340" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0v04kgi_di" bpmnElement="Flow_0v04kgi">
<omgdi:waypoint x="230" y="365" />
<omgdi:waypoint x="230" y="470" />
<omgdi:waypoint x="170" y="470" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="234" y="415" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0dlq2m8_di" bpmnElement="Flow_0dlq2m8">
<omgdi:waypoint x="120" y="430" />
<omgdi:waypoint x="120" y="380" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0cf866a_di" bpmnElement="Flow_0cf866a">
<omgdi:waypoint x="255" y="340" />
<omgdi:waypoint x="310" y="340" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="272" y="322" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0qnbcun_di" bpmnElement="Flow_0qnbcun">
<omgdi:waypoint x="410" y="340" />
<omgdi:waypoint x="445" y="340" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0x8aqhn_di" bpmnElement="Flow_0x8aqhn">
<omgdi:waypoint x="470" y="365" />
<omgdi:waypoint x="470" y="470" />
<omgdi:waypoint x="170" y="470" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="474" y="415" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0am3486_di" bpmnElement="Flow_0am3486">
<omgdi:waypoint x="495" y="340" />
<omgdi:waypoint x="560" y="340" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="517" y="322" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_09v32lj_di" bpmnElement="Flow_09v32lj">
<omgdi:waypoint x="660" y="340" />
<omgdi:waypoint x="695" y="340" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_16jqt75_di" bpmnElement="Flow_16jqt75">
<omgdi:waypoint x="720" y="365" />
<omgdi:waypoint x="720" y="470" />
<omgdi:waypoint x="170" y="470" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="724" y="415" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0ow0dx1_di" bpmnElement="Flow_0ow0dx1">
<omgdi:waypoint x="745" y="340" />
<omgdi:waypoint x="802" y="340" />
<bpmndi:BPMNLabel>
<omgdc:Bounds x="763" y="322" width="22" height="14" />
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
# 激活流程
回到 工作流程 页面,将流程的状态改为:“激活”
# 7、为站点配置流程
回到 合同管理 站点,点击 站点设置,将流程绑定到站点上
# 8、验证
# 上传
切换用户:林毅
进入 合同站点 > 文档库 > 2023年,上传一笔合同文件
验证:此时合同文件状态为“草稿”
# 发起申请
选中待审批的合同文件,点击 发起流程 > 合同审批
填写表单,合同确认人员选择:许志宏,点击 提交审批
# 审批
切换用户:许志宏
进入 我的任务 > 我的待办,点击任务进入审批表单,对审批表单进行处理
依次切换用户:杨先壮、张伟华、蔡春辉,验证审批流程
# 结束
审批通过后,回到 合同审批 站点,此时合同状态变更为:已审批。