# 工作流开发指南

### 工作流开发

#### 工作流开发整体思路
工作流的开发大致分为以下几步：  
* 流程设计  
* 流程部署  
* 流程启动  
下面以付款申请流程为例。  

#### 一、流程设计
1. 开始设计流程之前，先在工作流配置界面进行新建模型操作，输入完整界面上各个字段信息后进行保存。  
**唯一标识**及**分类**两个字段需要特别注意，这两个字段用于后台业务代码区分具体是哪一个工作流信息。  
界面如下图所示：  
![新建模型界面](/assets/wfl_payment_req_create.png)  
  
2. 模型新建之后，需要设计具体的业务流程  
![进入模型界面](/assets/wfl_payment_req_modify_btn.png)  
从左边选择相应的节点(一般情况下只会用到开始事件、结束事件、人工任务、单一网关等节点)，描述出整个业务的流程  
    1. 放置一个开始事件，并在右侧的**名称**属性上设置为 “开始”，见下图：  
    ![放置开始节点](/assets/wfl_payment_req_start.png)  
    2. 再添加一个人工任务且名称设置为“部门经理审批”，并通过选中开始事件弹出菜单中的顺序跳转线将该节点与开始事件连接起来。  
    ![连接节点](/assets/wfl_payment_req_connect.png)   
    3. 编写该工作流的启动代码：  
    ```java
    @Service
    @Transactional
    public class PaymentActivitStartServiceImpl implements IActivitiCommonService {
    // 必须实现IActivitiCommonService接口
    // workFlowType字段为启动工作流时的唯一识别字段，不可与其他工作流代码重复
        private static final String workFlowType = "PAY_PAYMENT";
        @Override
        public String getWorkFlowType() {
            return workFlowType;
        }

        // 启动工作流时，会自动调用该process方法
        @Override
        public void process(IRequest iRequest, List list,Map params) {
            ProcessInstanceCreateRequest createRequest = getProcessInstanceCreateRequest(list,iRequest,params);
            // 启动流程
            activitiService.startProcess(iRequest, createRequest);
        }

        public ProcessInstanceCreateRequest getProcessInstanceCreateRequest(List<HlsCusConContractCashflow> list, IRequest iRequest, Map params) {
            ProcessInstanceCreateRequest createRequest = new ProcessInstanceCreateRequest();
            // 此处的两个参数，分别对应了新建模型时输入的**唯一标识**以及**分类**
            ReProcdef reProcdefs = reProcdefService.queryReProcdef("CSH_PAYMENT_REQ_WORK_FLOW","CSH_PAYMENT");
            SysUser sysUser = sysUserService.selectUserById(iRequest.getUserId());
            String id = reProcdefs.getId_();
            String name = reProcdefs.getName_();
            createRequest.setProcessDefinitionId(id);
            createRequest.setBusinessKey(list.get(0).getContract_id().toString());

            List<RestVariable> variables = new ArrayList<RestVariable>();
            List<RestVariable> transientVariables = new ArrayList<RestVariable>();

            HlsCusCshPaymentReqLn cshPaymentReqLn =  (HlsCusCshPaymentReqLn)params.get("cshPaymentReqLn");

            // 注意此处添加的这些参数，将会在流程设计的过程中被使用到
            variables.add(newVariable("paymentRedId",cshPaymentReqLn.getPayment_req_id()));
            variables.add(newVariable("contractId",cshPaymentReqLn.getSource_doc_id()));
            variables.add(newVariable("processDefinitionId",id));
            variables.add(newVariable("contractCashflows",list));
            variables.add(newVariable("iRequest",iRequest));

            double sumAmount = 0L;
            for(HlsCusConContractCashflow contractCashflow:list){
                sumAmount = add(sumAmount,contractCashflow.getAmount());
            }
            
            variables.add(newVariable("sumAmount",new BigDecimal(sumAmount).toPlainString()));
            variables.add(newVariable("startUserName",iRequest.getEmployeeCode()));
            variables.add(newVariable("documentCategory","CSH_PAYMENT_REQ"));
            variables.add(newVariable("documentType","PAYMENT_REQ"));
            variables.add(newVariable("documentId",cshPaymentReqLn.getPayment_req_id()));
            variables.add(newVariable("pName",name));
            variables.add(newVariable("cshPaymentReqLn",cshPaymentReqLn));
            variables.add(newVariable("ContractId",cshPaymentReqLn.getContract_id()));
            variables.add(newVariable("startUserDescription",sysUser.getDescription()));

            createRequest.setVariables(variables);
            createRequest.setTransientVariables(transientVariables);

            return createRequest;
        }

        private  RestVariable newVariable(String name, Object value){
            RestVariable restVariable = new RestVariable();
            restVariable.setName("ContractId");
            restVariable.setValue
            return restVariable;
        }
    }
    ```  
    **在该业务实现代码中，所有通过variables.add进行添加的参数，都可以在工作流设计时直接通过${ PARAM_NAME }使用，详细的用法在后面会进行介绍**  
    4. 设置“部门经理审批”节点的文档为“${startUserDescription}申请金额${sumAmount}”，其中用到了在代码中设置的两个参数startUserDescription和sumAmount.  
    5. 根据业务需要对“部门经理审批”设置审批方式，在这里，根据业务，在右侧的审批方式属性上选择“全部通过”，选择之后，可以看到表单属性自动添加了一条数据。见下图：  
    ![设置审批方式](/assets/wfl_payment_req_way.png)  
    6. 设置完审批方式后，需要设置改节点的审批规则,在选中”部门经理审批“节点后，点击右侧的审批规则选择按钮进入审批规则的配置界面,新建一条记录，审批规则选择”指定人“，审批者可以通过Lov选择对应的部门经理，在这里选择“马超”.审批权限留空然后保存。保存后，表单属性中的属性会自动增加变为2，设置完的界面见下图  
    ![设置审批规则](/assets/wfl_payment_req_rule.png)  
    7. 审批规则设置完成后，“部门经理审批”节点的设置基本完成，接下来添加一个单一网关并将“部门经理审批”连接到单一网关，然后添加一个结束事件以及一个名为“总裁审批”的人工任务，并且将单一网关与这两个节点连接，如图：  
    ![添加网关节点](/assets/wfl_payment_req_gateway.png)  
    8. 将单一网关与“总裁审批”之间的连线右侧属性的**默认跳线**勾选，以便让流程在默认状态时会走向“总裁审批”。  
    ![添加设置默认跳转](/assets/wfl_payment_req_default.png)  
    9. 将单一网关与结束事件之间的连线右侧的属性中t跳转条件设置为“${approveResult=='REJECTED'}”，其中approveResult为上一节点（部门经理审批）的审批结果，‘REJECTED’为预设的值属性代表审批拒绝，常用的还有APPROVED代表审批同意。如图：  
    ![设置跳转条件](/assets/wfl_payment_req_condi.png)  
    10. 将“总裁审批”节点按照5-6的步骤设置审批方式为全部通过，审批规则设置为指定人-李林。两个属性设置完成后，对该节点设置**任务监听**为“${approveActionEventTask}”（该监听类(ApproveActionEventTask)用于给指定审批人发送消息,approveActionEventTask为监听类在spring容器中的名称，一般为首字母小写后的类名）,添加任务监听是为了在流程到达指定节点时触发一部分预先设置好的业务逻辑。设置好的   
    ![设置跳转条件](/assets/wfl_payment_req_listener.png)   
    11. 任务监听设置需要先编写一段监听代码，如下  
    ```java
    @Component
    public class ApproveActionEventTask implements TaskListener,IActivitiBean {
        @Autowired
        private SysEventService sysEventService;

        @Override
        public void notify(DelegateTask delegateTask) {
            // 触发消息推送事件将消息发给审批人
            sysEventService.createNotice(delegateTask.getExecution(),"APPROVE");
        }
    }
    ```
    12. 按照上面的流程再添加人工任务“董事长审批”以及响应的网关，最终完成付款申请流程的设计，最终设计完成的流程图如下：  
    ![描述模型界面](/assets/wfl_payment_req_draw.png)  
#### 二、流程部署
流程设计好以后，点击流程设计页面操作栏的最后一个按钮(勾)即可发布流程  
`未发布过的流程或者改动过的流程，勾会以绿色展示，反之灰色`  
发布以后可以在流程部署页面查看部署情况  
`如果有旧的流程启动了，发布的新版不会影响旧流程的运行`  
部署按钮见下图  
![部署模型界面](/assets/wfl_payment_req_deploy.png)  

#### 三、流程启动
流程启动一般是在前端发送请求到后端，后端进行处理后，触发工作流启动。  
请求处理类：    
```java
 @Override
    public void paymentApproval(IRequest iRequest, HttpSession session, List<HlsCusConContractCashflow> list) {
        // ...
        Map<String, Object> params = new HashMap<String, Object>();
        // 注意此处的workFlowType，该参数将和业务类中的Code对应
        params.put("workFlowType", "PAY_PAYMENT");
        params.put("cshPaymentReqLn", ss.get(0));
        // 启动工作流流程
        activitiStartService.start(iRequest, list, params);
        // ...
    }
```  