完善OA流程

This commit is contained in:
wanggeng 2022-03-30 17:17:46 +08:00
parent 3533ae4c32
commit 3f4a8b287a
27 changed files with 1127 additions and 146 deletions

View File

@ -4,10 +4,12 @@ import com.alibaba.fastjson.JSONObject;
import ink.wgink.common.base.DefaultBaseController;
import ink.wgink.exceptions.ParamsException;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.activiti.pojo.dtos.ActivitiFlowNodeDTO;
import ink.wgink.module.activiti.pojo.vos.ActivitiModelVO;
import ink.wgink.module.activiti.service.activiti.IActivitiModelService;
import ink.wgink.pojo.result.ErrorResult;
import ink.wgink.pojo.result.SuccessResult;
import ink.wgink.pojo.result.SuccessResultData;
import ink.wgink.util.RegexUtil;
import io.swagger.annotations.*;
import org.apache.commons.lang3.StringUtils;
@ -16,6 +18,7 @@ import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
;
@ -54,6 +57,16 @@ public class ActivitiModelController extends DefaultBaseController {
return new SuccessResult();
}
@ApiOperation(value = "通过部署ID获取节点列表", notes = "通过部署ID获取节点列表接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "deploymentId", value = "部署ID", paramType = "path"),
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("list-flow-node/deployment-id/{deploymentId}")
public List<ActivitiFlowNodeDTO> listFlowNodeByDefineId(@PathVariable("deploymentId") String deploymentId) {
return activitiModelService.listFlowNodeByDeploymentId(deploymentId);
}
@ApiOperation(value = "模型json数据", notes = "模型json数据接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "modelId", value = "模型ID", paramType = "path")
@ -71,4 +84,15 @@ public class ActivitiModelController extends DefaultBaseController {
return activitiModelService.getStencilset();
}
@ApiOperation(value = "流程XML数据", notes = "流程XML数据接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "processDefinitionId", value = "流程定义ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("get-process-xml/{processDefinitionId}")
public SuccessResultData<String> getProcessXml(@PathVariable("processDefinitionId") String processDefinitionId) {
String processXml = activitiModelService.getProcessXml(processDefinitionId);
return new SuccessResultData<>(processXml);
}
}

View File

@ -2,7 +2,6 @@ package ink.wgink.module.activiti.controller.api;
import ink.wgink.common.base.DefaultBaseController;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.activiti.pojo.dtos.ActivitiFlowNodeDTO;
import ink.wgink.module.activiti.pojo.dtos.ActivitiProcdefDTO;
import ink.wgink.module.activiti.service.activiti.IActivitiProcdefService;
import ink.wgink.pojo.ListPage;
@ -52,14 +51,4 @@ public class ActivitiProcdefController extends DefaultBaseController {
return activitiProcdefService.listPage(page.getPage(), page.getRows());
}
@ApiOperation(value = "通过部署ID获取任务列表", notes = "通过部署ID获取任务列表接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "deploymentId", value = "部署ID", paramType = "path"),
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("list-flow-node/deployment-id/{deploymentId}")
public List<ActivitiFlowNodeDTO> listFlowNodeByDefineId(@PathVariable("deploymentId") String deploymentId) {
return activitiProcdefService.listFlowNodeByDeploymentId(deploymentId);
}
}

View File

@ -0,0 +1,40 @@
package ink.wgink.module.activiti.controller.api;
import ink.wgink.common.base.DefaultBaseController;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.activiti.service.activiti.IActivitiUserTaskService;
import ink.wgink.pojo.result.SuccessResult;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
;
/**
* When you feel like quitting. Think about why you started
* 当你想要放弃的时候想想当初你为何开始
*
* @ClassName: ActivitiUserTaskController
* @Description: 流程用户任务
* @Author: WangGeng
* @Date: 2021/7/12 21:56
* @Version: 1.0
**/
@Api(tags = ISystemConstant.API_TAGS_SYSTEM_PREFIX + "流程用户任务接口")
@RestController
@RequestMapping(ISystemConstant.API_PREFIX + "/activiti/user-task")
public class ActivitiUserTaskController extends DefaultBaseController {
@Autowired
private IActivitiUserTaskService activitiUserTaskService;
@GetMapping("get-next-user-task/{userTaskId}")
public SuccessResult getNextUserTask(@PathVariable("userTaskId") String userTaskId) {
activitiUserTaskService.getNextUserTask(userTaskId);
return new SuccessResult();
}
}

View File

@ -0,0 +1,57 @@
package ink.wgink.module.activiti.controller.route;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.activiti.service.activiti.IActivitiModelService;
import ink.wgink.pojo.result.ErrorResult;
import io.swagger.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletResponse;
/**
* @ClassName: ActivitiModelRouteController
* @Description: 流程模型
* @Author: wanggeng
* @Date: 2022/3/28 15:57
* @Version: 1.0
*/
@Api(tags = ISystemConstant.ROUTE_TAGS_PREFIX + "activiti模型")
@Controller
@RequestMapping(ISystemConstant.ROUTE_PREFIX + "/activiti/model")
public class ActivitiModelRouteController {
@Autowired
private IActivitiModelService activitiModelService;
@GetMapping("get-process-xml")
public ModelAndView getProcessImage() {
ModelAndView mv = new ModelAndView("activiti/model/get-process-xml");
return mv;
}
@ApiOperation(value = "查看流程图", notes = "查看流程图接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "deploymentId", value = "流程部署ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("get-process-image/{deploymentId}")
public void getProcessImage(HttpServletResponse response, @PathVariable("deploymentId") String deploymentId) {
activitiModelService.getProcessImage(response, deploymentId);
}
@ApiOperation(value = "查看运行流程图", notes = "查看运行流程图接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "processInstanceId", value = "流程实例ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("get-runtime-process-image/{processInstanceId}")
public void getRuntimeProcessImage(HttpServletResponse response, @PathVariable("processInstanceId") String processInstanceId) {
activitiModelService.getRuntimeProcessImage(response, processInstanceId);
}
}

View File

@ -2,17 +2,13 @@ package ink.wgink.module.activiti.controller.route;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.activiti.service.activiti.IActivitiModelService;
import ink.wgink.pojo.result.ErrorResult;
import io.swagger.annotations.*;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletResponse;
/**
* @ClassName: ActivitiRouteController
* @Description: activiti路由
@ -25,10 +21,6 @@ import javax.servlet.http.HttpServletResponse;
@RequestMapping(ISystemConstant.ROUTE_PREFIX + "/activiti")
public class ActivitiRouteController {
@Autowired
private IActivitiModelService activitiModelService;
@GetMapping("list")
public ModelAndView list() {
ModelAndView mv = new ModelAndView("activiti/list");
@ -47,24 +39,4 @@ public class ActivitiRouteController {
return mv;
}
@ApiOperation(value = "查看流程图", notes = "查看流程图接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "deploymentId", value = "流程部署ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("get-process-image/{deploymentId}")
public void getProcessImage(HttpServletResponse response, @PathVariable("deploymentId") String deploymentId) {
activitiModelService.getProcessImage(response, deploymentId);
}
@ApiOperation(value = "查看运行流程图", notes = "查看运行流程图接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "processInstanceId", value = "流程实例ID", paramType = "path")
})
@ApiResponses({@ApiResponse(code = 400, message = "请求失败", response = ErrorResult.class)})
@GetMapping("get-runtime-process-image/{processInstanceId}")
public void getRuntimeProcessImage(HttpServletResponse response, @PathVariable("processInstanceId") String processInstanceId) {
activitiModelService.getRuntimeProcessImage(response, processInstanceId);
}
}

View File

@ -0,0 +1,32 @@
package ink.wgink.module.activiti.listener.task;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.JavaDelegate;
import org.activiti.engine.delegate.TaskListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @ClassName: OaUserTaskAssignmentListener
* @Description: OA用户任务节点分配处理人监听器
* @Author: wanggeng
* @Date: 2022/3/28 10:31
* @Version: 1.0
*/
@Component
public class OaUserTaskAssignmentListener implements TaskListener, JavaDelegate {
private static final Logger LOG = LoggerFactory.getLogger(OaUserTaskAssignmentListener.class);
@Override
public void notify(DelegateTask delegateTask) {
LOG.debug("userTask assignment");
}
@Override
public void execute(DelegateExecution execution) {
}
}

View File

@ -0,0 +1,56 @@
package ink.wgink.module.activiti.listener.task;
import ink.wgink.module.activiti.service.activiti.IActivitiModelService;
import ink.wgink.module.activiti.service.oa.IOaFormReportService;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.JavaDelegate;
import org.activiti.engine.delegate.TaskListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @ClassName: OaUserTaskCompleteListener
* @Description: OA用户任务节点完成监听器
* @Author: wanggeng
* @Date: 2022/3/28 10:31
* @Version: 1.0
*/
@Component
public class OaUserTaskCompleteListener implements TaskListener, JavaDelegate {
private static final Logger LOG = LoggerFactory.getLogger(OaUserTaskCompleteListener.class);
@Autowired
private RuntimeService runtimeService;
@Autowired
private IActivitiModelService activitiModelService;
@Autowired
private IOaFormReportService oaFormReportService;
@Override
public void notify(DelegateTask delegateTask) {
LOG.debug(">>>> userTask complete");
Map<String, Object> variables = runtimeService.getVariables(delegateTask.getProcessInstanceId());
String reportUid = variables.get(IOaFormReportService.KEY_REPORT_UID).toString();
String formCode = variables.get(IOaFormReportService.KEY_FORM_CODE).toString();
Integer formVersion = Integer.parseInt(variables.get(IOaFormReportService.KEY_FORM_VERSION).toString());
LOG.debug("查询表单数据formCode: {}, formVersion: {}, reportUid: {}", formCode, formVersion, reportUid);
Map<String, Object> reportForm = oaFormReportService.get(formCode, formVersion, reportUid);
UserTask nextUserTask = activitiModelService.getNextUserTaskByTaskDefinitionKeyAndProcessDefinitionIdAndReportForm(delegateTask.getTaskDefinitionKey(), delegateTask.getProcessDefinitionId(), reportForm);
if (nextUserTask != null) {
nextUserTask.setAssignee("1");
}
}
@Override
public void execute(DelegateExecution execution) {
}
}

View File

@ -0,0 +1,31 @@
package ink.wgink.module.activiti.listener.task;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.JavaDelegate;
import org.activiti.engine.delegate.TaskListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @ClassName: OaUserTaskCreateListener
* @Description: OA用户任务节点创建监听器
* @Author: wanggeng
* @Date: 2022/3/28 10:31
* @Version: 1.0
*/
@Component
public class OaUserTaskCreateListener implements TaskListener, JavaDelegate {
private static final Logger LOG = LoggerFactory.getLogger(OaUserTaskCreateListener.class);
@Override
public void notify(DelegateTask delegateTask) {
LOG.debug("userTask create");
}
@Override
public void execute(DelegateExecution execution) {
}
}

View File

@ -0,0 +1,32 @@
package ink.wgink.module.activiti.listener.task;
import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.JavaDelegate;
import org.activiti.engine.delegate.TaskListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* @ClassName: OaUserTaskDeleteListener
* @Description: OA用户任务节点删除监听器
* @Author: wanggeng
* @Date: 2022/3/28 10:31
* @Version: 1.0
*/
@Component
public class OaUserTaskDeleteListener implements TaskListener, JavaDelegate {
private static final Logger LOG = LoggerFactory.getLogger(OaUserTaskDeleteListener.class);
@Override
public void notify(DelegateTask delegateTask) {
LOG.debug("userTask delete");
}
@Override
public void execute(DelegateExecution execution) {
}
}

View File

@ -1,6 +1,7 @@
package ink.wgink.module.activiti.pojo.dtos.oa;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* @ClassName: OaTaskDTO
@ -12,14 +13,28 @@ import io.swagger.annotations.ApiModel;
@ApiModel
public class OaTaskDTO {
@ApiModelProperty(name = "taskId", value = "任务ID")
private String taskId;
@ApiModelProperty(name = "taskName", value = "任务名称")
private String taskName;
@ApiModelProperty(name = "taskDescription", value = "任务描述")
private String taskDescription;
@ApiModelProperty(name = "formCode", value = "表单编码")
private String formCode;
@ApiModelProperty(name = "formVersion", value = "表单版本")
private Integer formVersion;
@ApiModelProperty(name = "getFormKey", value = "动态表单ID")
private String getFormKey;
@ApiModelProperty(name = "reportUid", value = "上报表单UID")
private String reportUid;
@ApiModelProperty(name = "reportTitle", value = "上报表单标题")
private String reportTitle;
@ApiModelProperty(name = "owner", value = "所有者")
private String owner;
@ApiModelProperty(name = "taskDefinitionKey", value = "任务定义Key")
private String taskDefinitionKey;
@ApiModelProperty(name = "processDefinitionId", value = "流程定义ID")
private String processDefinitionId;
public String getTaskId() {
return taskId == null ? "" : taskId.trim();
@ -77,6 +92,14 @@ public class OaTaskDTO {
this.reportUid = reportUid;
}
public String getReportTitle() {
return reportTitle == null ? "" : reportTitle.trim();
}
public void setReportTitle(String reportTitle) {
this.reportTitle = reportTitle;
}
public String getOwner() {
return owner == null ? "" : owner.trim();
}
@ -84,4 +107,20 @@ public class OaTaskDTO {
public void setOwner(String owner) {
this.owner = owner;
}
public String getTaskDefinitionKey() {
return taskDefinitionKey == null ? "" : taskDefinitionKey.trim();
}
public void setTaskDefinitionKey(String taskDefinitionKey) {
this.taskDefinitionKey = taskDefinitionKey;
}
public String getProcessDefinitionId() {
return processDefinitionId == null ? "" : processDefinitionId.trim();
}
public void setProcessDefinitionId(String processDefinitionId) {
this.processDefinitionId = processDefinitionId;
}
}

View File

@ -1,10 +1,14 @@
package ink.wgink.module.activiti.service.activiti;
import com.alibaba.fastjson.JSONObject;
import ink.wgink.module.activiti.pojo.dtos.ActivitiFlowNodeDTO;
import org.activiti.bpmn.model.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Map;
/**
* When you feel like quitting. Think about why you started
@ -18,10 +22,111 @@ import java.io.UnsupportedEncodingException;
**/
public interface IActivitiModelService {
/**
* 排他网关条件关键字
*/
String EXCLUSIVE_GATEWAY_CONDITION_KEY = "exc";
/**
* 并行网关条件关键字
*/
String PARALLEL_GATEWAY_CONDITION_KEY = "par";
/**
* 保存模型
*
* @param modelId
* @param name
* @param description
* @param json_xml
* @param svg_xml
* @throws Exception
*/
void saveModel(String modelId, String name, String description, String json_xml, String svg_xml) throws Exception;
/**
* 获取原生流程节点列表
*
* @param bpmnModel
* @return
*/
List<FlowNode> listFlowNode(BpmnModel bpmnModel);
/**
* 获取当前流程节点
*
* @param currentTaskDefinitionKey
* @param processDefinitionId
* @return
*/
FlowNode getCurrentFlowNode(String currentTaskDefinitionKey, String processDefinitionId);
/**
* 获取下一个用户任务
*
* @param currentUserTask 当前用户任务
* @return
*/
UserTask getNextUserTaskByCurrentUserTask(UserTask currentUserTask);
/**
* 获取下一个用户任务
*
* @param exclusiveGateway 排他网关
* @param reportForm 上报表单
* @return
*/
UserTask getNextUserTaskByExclusiveGatewayAndReportForm(ExclusiveGateway exclusiveGateway, Map<String, Object> reportForm);
/**
* 获取下一个用户任务
*
* @param currentTaskDefinitionKey 当前任务定义Key
* @param processDefinitionId 流程定义ID
* @param reportForm 上报表单
* @return
*/
UserTask getNextUserTaskByTaskDefinitionKeyAndProcessDefinitionIdAndReportForm(String currentTaskDefinitionKey, String processDefinitionId, Map<String, Object> reportForm);
/**
* 获得开始节点
*
* @param flowNodes
* @return
*/
StartEvent getStartEvent(List<FlowNode> flowNodes);
/**
* 获取自定义的流程节点列表
*
* @param flowNodes
* @return
*/
List<ActivitiFlowNodeDTO> listActivitiFlowNodeByModel(List<FlowNode> flowNodes);
/**
* 获取流程节点列表
*
* @param deploymentId 部署ID
* @return
*/
List<ActivitiFlowNodeDTO> listFlowNodeByDeploymentId(String deploymentId);
/**
* 获取编辑的json数据
*
* @param modelId
* @return
* @throws UnsupportedEncodingException
*/
JSONObject getEditorJson(String modelId) throws UnsupportedEncodingException;
/**
* 获取模板套件
*
* @return
* @throws IOException
*/
JSONObject getStencilset() throws IOException;
/**
@ -39,4 +144,12 @@ public interface IActivitiModelService {
* @param processInstanceId
*/
void getRuntimeProcessImage(HttpServletResponse response, String processInstanceId);
/**
* 获取流程xml
*
* @param processDefinitionId 流程定义ID
*/
String getProcessXml(String processDefinitionId);
}

View File

@ -1,9 +1,7 @@
package ink.wgink.module.activiti.service.activiti;
import ink.wgink.module.activiti.pojo.dtos.ActivitiFlowNodeDTO;
import ink.wgink.module.activiti.pojo.dtos.ActivitiProcdefDTO;
import ink.wgink.pojo.result.SuccessResultList;
import org.activiti.bpmn.model.BpmnModel;
import java.util.List;
@ -20,7 +18,4 @@ public interface IActivitiProcdefService {
SuccessResultList<List<ActivitiProcdefDTO>> listPage(int page, int rows);
List<ActivitiFlowNodeDTO> listFlowNodeByDeploymentId(String deploymentId);
List<ActivitiFlowNodeDTO> listFlowNodeByModel(BpmnModel bpmnModel);
}

View File

@ -0,0 +1,14 @@
package ink.wgink.module.activiti.service.activiti;
/**
* @ClassName: IActivitiUserTaskService
* @Description: 流程用户任务
* @Author: wanggeng
* @Date: 2022/3/29 17:43
* @Version: 1.0
*/
public interface IActivitiUserTaskService {
void getNextUserTask(String userTaskId);
}

View File

@ -6,11 +6,11 @@ import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.SearchException;
import ink.wgink.exceptions.base.SystemException;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.module.activiti.pojo.dtos.ActivitiFlowNodeDTO;
import ink.wgink.module.activiti.service.activiti.IActivitiModelService;
import ink.wgink.util.ResourceUtil;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import ink.wgink.util.xml.XMLUtil;
import org.activiti.bpmn.model.*;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
@ -29,10 +29,7 @@ import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -87,6 +84,115 @@ public class ActivitiModelServiceImpl extends DefaultBaseService implements IAct
}
@Override
public List<FlowNode> listFlowNode(BpmnModel bpmnModel) {
return bpmnModel.getMainProcess().findFlowElementsOfType(FlowNode.class);
}
@Override
public FlowNode getCurrentFlowNode(String currentTaskDefinitionKey, String processDefinitionId) {
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
return (FlowNode) bpmnModel.getFlowElement(currentTaskDefinitionKey);
}
@Override
public UserTask getNextUserTaskByCurrentUserTask(UserTask currentUserTask) {
List<SequenceFlow> outgoingFlows = currentUserTask.getOutgoingFlows();
if (outgoingFlows.size() > 1 || outgoingFlows.size() == 0) {
throw new SystemException("流程错误下个用户任务节点数只能有1个");
}
FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement();
if (!(targetFlowElement instanceof UserTask)) {
throw new SystemException("下个节点不是用户任务");
}
return (UserTask) targetFlowElement;
}
@Override
public UserTask getNextUserTaskByExclusiveGatewayAndReportForm(ExclusiveGateway exclusiveGateway, Map<String, Object> reportForm) {
List<SequenceFlow> outgoingFlows = exclusiveGateway.getOutgoingFlows();
for (SequenceFlow sequenceFlow : outgoingFlows) {
FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
if (targetFlowElement instanceof UserTask) {
if (isExclusiveConditionPass(sequenceFlow.getConditionExpression(), reportForm)) {
return (UserTask) targetFlowElement;
}
} else if (targetFlowElement instanceof ExclusiveGateway) {
return getNextUserTaskByExclusiveGatewayAndReportForm(exclusiveGateway, reportForm);
}
}
throw new SystemException("未找到下一节点用户任务");
}
@Override
public UserTask getNextUserTaskByTaskDefinitionKeyAndProcessDefinitionIdAndReportForm(String currentTaskDefinitionKey, String processDefinitionId, Map<String, Object> reportForm) {
FlowNode currentFlowNode = getCurrentFlowNode(currentTaskDefinitionKey, processDefinitionId);
List<SequenceFlow> outgoingFlows = currentFlowNode.getOutgoingFlows();
for (SequenceFlow sequenceFlow : outgoingFlows) {
FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement();
if (targetFlowElement instanceof UserTask) {
return (UserTask) targetFlowElement;
} else if (targetFlowElement instanceof ExclusiveGateway) {
LOG.debug("排他网关节点,继续查找");
ExclusiveGateway exclusiveGateway = (ExclusiveGateway) targetFlowElement;
return getNextUserTaskByExclusiveGatewayAndReportForm(exclusiveGateway, reportForm);
} else if (targetFlowElement instanceof EndEvent) {
return null;
}
}
throw new SystemException("未找到下一节点用户任务");
}
@Override
public StartEvent getStartEvent(List<FlowNode> flowNodes) {
if (flowNodes == null || flowNodes.isEmpty()) {
return null;
}
for (FlowNode flowNode : flowNodes) {
if (flowNode instanceof StartEvent) {
return (StartEvent) flowNode;
}
}
return null;
}
@Override
public List<ActivitiFlowNodeDTO> listActivitiFlowNodeByModel(List<FlowNode> flowNodes) {
List<ActivitiFlowNodeDTO> activitiFlowNodeDTOs = new ArrayList<>();
for (FlowNode flowNode : flowNodes) {
ActivitiFlowNodeDTO activitiFlowNodeDTO = new ActivitiFlowNodeDTO();
activitiFlowNodeDTO.setId(flowNode.getId());
activitiFlowNodeDTO.setName(flowNode.getName());
activitiFlowNodeDTO.setSummary(flowNode.getDocumentation());
if (flowNode instanceof StartEvent) {
StartEvent startEvent = (StartEvent) flowNode;
activitiFlowNodeDTO.setFormKey(startEvent.getFormKey());
activitiFlowNodeDTO.setType(StartEvent.class.getSimpleName());
} else if (flowNode instanceof EndEvent) {
activitiFlowNodeDTO.setType(EndEvent.class.getSimpleName());
} else if (flowNode instanceof UserTask) {
UserTask userTask = (UserTask) flowNode;
activitiFlowNodeDTO.setFormKey(userTask.getFormKey());
activitiFlowNodeDTO.setType(UserTask.class.getSimpleName());
} else {
continue;
}
activitiFlowNodeDTOs.add(activitiFlowNodeDTO);
}
return activitiFlowNodeDTOs;
}
@Override
public List<ActivitiFlowNodeDTO> listFlowNodeByDeploymentId(String deploymentId) {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
if (processDefinition == null) {
throw new SearchException("流程定义不存在,请检查流程部署情况");
}
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
List<FlowNode> flowNodes = listFlowNode(bpmnModel);
return listActivitiFlowNodeByModel(flowNodes);
}
@Override
public JSONObject getEditorJson(String modelId) throws UnsupportedEncodingException {
Model model = repositoryService.getModel(modelId);
@ -166,6 +272,27 @@ public class ActivitiModelServiceImpl extends DefaultBaseService implements IAct
}
}
@Override
public String getProcessXml(String processDefinitionId) {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
if (processDefinition == null) {
throw new SearchException("流程定义不存在");
}
String result;
try (InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
) {
StringBuilder xmlSB = new StringBuilder();
for (String line; (line = bufferedReader.readLine()) != null; ) {
xmlSB.append(line);
}
result = XMLUtil.formatXmlString(xmlSB.toString());
} catch (IOException e) {
throw new SystemException(e);
}
return result;
}
/**
* 获取运行流程图输入流
*
@ -291,5 +418,48 @@ public class ActivitiModelServiceImpl extends DefaultBaseService implements IAct
return new ArrayList<>(flowSet);
}
/**
* 排他条件判断通过
*
* @param conditionExpression 条件表达式
* @param reportFormMap 表单变量
* @return
*/
private static boolean isExclusiveConditionPass(String conditionExpression, Map<String, Object> reportFormMap) {
if (StringUtils.isBlank(conditionExpression)) {
LOG.debug("排他网关条件表达式为空");
return false;
}
if (reportFormMap.isEmpty()) {
LOG.debug("上报表单为空");
return false;
}
//分割表达式
String[] expressionArray = conditionExpression.split("[{}$&]");
for (String expression : expressionArray) {
// 是否包含条件字段
if (!expression.contains(IActivitiModelService.EXCLUSIVE_GATEWAY_CONDITION_KEY)) {
continue;
}
// 表单中是否包含条件字段
if (!reportFormMap.containsKey(IActivitiModelService.EXCLUSIVE_GATEWAY_CONDITION_KEY)) {
continue;
}
if (expression.contains("==")) {
String[] primes = expression.split("==");
String valExpr = primes[1].trim();
if (valExpr.startsWith("'")) {
valExpr = valExpr.substring(1);
}
if (valExpr.endsWith("'")) {
valExpr = valExpr.substring(0, valExpr.length() - 1);
}
if (primes.length == 2 && valExpr.equals(reportFormMap.get(IActivitiModelService.EXCLUSIVE_GATEWAY_CONDITION_KEY))) {
return true;
}
}
}
return false;
}
}

View File

@ -1,14 +1,10 @@
package ink.wgink.module.activiti.service.activiti.impl;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.SearchException;
import ink.wgink.exceptions.base.SystemException;
import ink.wgink.module.activiti.pojo.dtos.ActivitiFlowNodeDTO;
import ink.wgink.module.activiti.pojo.dtos.ActivitiProcdefDTO;
import ink.wgink.module.activiti.service.activiti.IActivitiModelService;
import ink.wgink.module.activiti.service.activiti.IActivitiProcdefService;
import ink.wgink.pojo.result.SuccessResultList;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.*;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.ProcessDefinition;
@ -34,6 +30,8 @@ public class ActivitiProcdefServiceImpl extends DefaultBaseService implements IA
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private IActivitiModelService activitiModelService;
@Override
public void delete(String deploymentId) {
@ -55,46 +53,4 @@ public class ActivitiProcdefServiceImpl extends DefaultBaseService implements IA
return new SuccessResultList<>(activitiProcdefDTOs, page, processDefinitionQuery.count());
}
@Override
public List<ActivitiFlowNodeDTO> listFlowNodeByDeploymentId(String deploymentId) {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
if (processDefinition == null) {
throw new SearchException("流程定义不存在,请检查流程部署情况");
}
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
return listFlowNodeByModel(bpmnModel);
}
@Override
public List<ActivitiFlowNodeDTO> listFlowNodeByModel(BpmnModel bpmnModel) {
List<Process> processes = bpmnModel.getProcesses();
if (processes.size() > 1) {
throw new SystemException("流程不能有多条");
}
List<FlowNode> flowNodes = processes.get(0).findFlowElementsOfType(FlowNode.class);
List<ActivitiFlowNodeDTO> activitiFlowNodeDTOs = new ArrayList<>();
for (FlowNode flowNode : flowNodes) {
ActivitiFlowNodeDTO activitiFlowNodeDTO = new ActivitiFlowNodeDTO();
activitiFlowNodeDTO.setId(flowNode.getId());
activitiFlowNodeDTO.setName(flowNode.getName());
activitiFlowNodeDTO.setSummary(flowNode.getDocumentation());
if (flowNode instanceof StartEvent) {
StartEvent startEvent = (StartEvent) flowNode;
activitiFlowNodeDTO.setFormKey(startEvent.getFormKey());
activitiFlowNodeDTO.setType(StartEvent.class.getSimpleName());
} else if (flowNode instanceof EndEvent) {
activitiFlowNodeDTO.setType(EndEvent.class.getSimpleName());
} else if (flowNode instanceof UserTask) {
UserTask userTask = (UserTask) flowNode;
activitiFlowNodeDTO.setFormKey(userTask.getFormKey());
activitiFlowNodeDTO.setType(UserTask.class.getSimpleName());
} else {
continue;
}
activitiFlowNodeDTOs.add(activitiFlowNodeDTO);
}
return activitiFlowNodeDTOs;
}
}

View File

@ -12,6 +12,7 @@ import ink.wgink.module.activiti.pojo.dtos.ActivitiFlowNodeDTO;
import ink.wgink.module.activiti.pojo.vos.ActivitiVO;
import ink.wgink.module.activiti.pojo.vos.oa.NodeFieldUpdateVO;
import ink.wgink.module.activiti.pojo.vos.oa.NodeFieldVO;
import ink.wgink.module.activiti.service.activiti.IActivitiModelService;
import ink.wgink.module.activiti.service.activiti.IActivitiProcdefService;
import ink.wgink.module.activiti.service.activiti.IActivitiService;
import ink.wgink.module.activiti.service.oa.INodeFieldService;
@ -20,9 +21,7 @@ import ink.wgink.module.form.pojo.pos.design.FormPO;
import ink.wgink.module.form.service.design.IFormFieldService;
import ink.wgink.module.form.service.design.IFormService;
import ink.wgink.pojo.result.SuccessResultList;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.StartEvent;
import org.activiti.bpmn.model.*;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.HistoryService;
@ -60,6 +59,8 @@ public class ActivitiServiceImpl extends DefaultBaseService implements IActiviti
@Autowired
private RuntimeService runtimeService;
@Autowired
private IActivitiModelService activitiModelService;
@Autowired
private IActivitiProcdefService activitiProcdefService;
@Autowired
private INodeFieldService nodeFieldService;
@ -110,15 +111,46 @@ public class ActivitiServiceImpl extends DefaultBaseService implements IActiviti
LOG.debug("部署流程");
JsonNode modelNode = new ObjectMapper().readTree(modelEditorSource);
BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(modelNode);
LOG.debug("获取流程节点列表");
List<FlowNode> flowNodes = activitiModelService.listFlowNode(bpmnModel);
LOG.debug("获得开始节点");
StartEvent startEvent = activitiModelService.getStartEvent(flowNodes);
// 判断是否存在表单如果存在表单说明是OA流程进行初始化再部署如果不是OA流程则正常部署
String formKey = startEvent.getFormKey();
String deploymentId;
if (!StringUtils.isBlank(formKey)) {
LOG.debug("存在自定义表单是OA流程执行初始化操作");
LOG.debug("1.初始化");
LOG.debug("2.部署流程");
deploymentId = deployProcess(model, bpmnModel);
LOG.debug("3.绑定节点设置");
LOG.debug("4.绑定流程节点与表单关系");
saveNodeField(deploymentId, flowNodes);
} else {
LOG.debug("不存在自定义表单不是OA流程直接部署");
deploymentId = deployProcess(model, bpmnModel);
}
model.setDeploymentId(deploymentId);
LOG.debug("保存模型");
repositoryService.saveModel(model);
}
/**
* 部署流程
*
* @param model
* @param bpmnModel
* @return
*/
private String deployProcess(Model model, BpmnModel bpmnModel) {
Deployment deployment = repositoryService.createDeployment()
.name(model.getName())
.key(model.getKey())
.addBpmnModel(model.getKey() + "." + model.getVersion() + ".bpmn20.xml", bpmnModel)
.deploy();
model.setDeploymentId(deployment.getId());
repositoryService.saveModel(model);
LOG.debug("节点关联表单");
saveNodeField(deployment.getId(), bpmnModel);
return deployment.getId();
}
/**
@ -126,8 +158,8 @@ public class ActivitiServiceImpl extends DefaultBaseService implements IActiviti
*
* @param deploymentId
*/
private void saveNodeField(String deploymentId, BpmnModel bpmnModel) {
List<ActivitiFlowNodeDTO> activitiFlowNodeDTOs = activitiProcdefService.listFlowNodeByModel(bpmnModel);
private void saveNodeField(String deploymentId, List<FlowNode> flowNodes) {
List<ActivitiFlowNodeDTO> activitiFlowNodeDTOs = activitiModelService.listActivitiFlowNodeByModel(flowNodes);
if (activitiFlowNodeDTOs.isEmpty()) {
return;
}

View File

@ -0,0 +1,115 @@
package ink.wgink.module.activiti.service.activiti.impl;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.module.activiti.service.activiti.IActivitiUserTaskService;
import org.activiti.bpmn.model.*;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @ClassName: ActivitiUserTaskServiceImpl
* @Description: 流程用户任务
* @Author: wanggeng
* @Date: 2022/3/29 17:44
* @Version: 1.0
*/
@Service
public class ActivitiUserTaskServiceImpl extends DefaultBaseService implements IActivitiUserTaskService {
@Autowired
private TaskService taskService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private RepositoryService repositoryService;
@Override
public void getNextUserTask(String userTaskId) {
Task task = taskService.createTaskQuery().taskId(userTaskId).singleResult();
String taskDefinitionKey = task.getTaskDefinitionKey();
String processInstanceId = task.getProcessInstanceId();
String processDefinitionId = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult().getProcessDefinitionId();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey);
// 流入线
List<SequenceFlow> incomingFlows = flowNode.getIncomingFlows();
// 流出线
List<SequenceFlow> outgoingFlows = flowNode.getOutgoingFlows();
HashMap<String, Object> vars = new HashMap<>();
vars.put("message", "");
for (SequenceFlow outgoingFlow : outgoingFlows) {
//获取输出节点元素
FlowElement targetFlowElement = outgoingFlow.getTargetFlowElement();
//排除非用户任务接点
if (targetFlowElement instanceof UserTask) {
LOG.debug("UserTask");
UserTask userTask = (UserTask) targetFlowElement;
System.out.println(userTask);
//判断输出节点的el表达式
// if (isCondition(outgoingFlow.getConditionExpression(), vars)) {
// //true 获取输出节点名称
// // nameList.add(outgoingFlow.getTargetFlowElement().getName());
// }
} else if (targetFlowElement instanceof ExclusiveGateway) {
LOG.debug("排他网关");
} else if (targetFlowElement instanceof ParallelGateway) {
LOG.debug("并行网关");
}
}
System.out.println(flowNode);
}
/**
* el表达式判断
*
* @param expression
* @param vars
* @return
*/
private static boolean isCondition(String expression, Map<String, Object> vars) {
if (expression == null || expression == "") {
return false;
}
//分割表达式
String[] exprArr = expression.split("[{}$&]");
for (String expr : exprArr) {
//是否包含键message
if (expr.contains("message")) {
if (!vars.containsKey("message")) {
continue;
}
if (expr.contains("==")) {
String[] primes = expr.split("==");
String valExpr = primes[1].trim();
if (valExpr.startsWith("'")) {
valExpr = valExpr.substring(1);
}
if (valExpr.endsWith("'")) {
valExpr = valExpr.substring(0, valExpr.length() - 1);
}
if (primes.length == 2 && valExpr.equals(vars.get("message"))) {
return true;
}
}
}
}
return false;
}
}

View File

@ -27,6 +27,7 @@ public interface IOaFormReportService {
/**
* 更新表单
*
* @param taskId
* @param formCode
* @param formVersion
@ -35,4 +36,13 @@ public interface IOaFormReportService {
*/
void update(String taskId, String formCode, Integer formVersion, String uid, Map<String, Object> params);
/**
* 表单详情
*
* @param formCode
* @param formVersion
* @param uid
*/
Map<String, Object> get(String formCode, Integer formVersion, String uid);
}

View File

@ -47,4 +47,9 @@ public class OaFormReportServiceImpl extends DefaultBaseService implements IOaFo
taskService.complete(taskId, params);
}
@Override
public Map<String, Object> get(String formCode, Integer formVersion, String uid) {
return formReportService.get(formCode, formVersion, uid);
}
}

View File

@ -134,6 +134,8 @@ public class OaServiceImpl extends DefaultBaseService implements IOaService {
oaTaskDTO.setTaskName(task.getName());
oaTaskDTO.setTaskDescription(task.getDescription());
oaTaskDTO.setOwner(task.getOwner());
oaTaskDTO.setTaskDefinitionKey(task.getTaskDefinitionKey());
oaTaskDTO.setProcessDefinitionId(task.getProcessDefinitionId());
Map<String, Object> variables = runtimeService.getVariables(task.getProcessInstanceId());
if (variables.isEmpty()) {

View File

@ -33,7 +33,7 @@ var KisBpmTaskListenersCtrl = [ '$scope', '$modal', '$timeout', '$translate', fu
$modal(opts);
}];
var KisBpmTaskListenersPopupCtrl = [ '$scope', '$q', '$translate', function($scope, $q, $translate) {
var KisBpmTaskListenersPopupCtrl = [ '$scope', '$timeout', '$q', '$translate', function($scope, $timeout, $q, $translate) {
// Put json representing form properties on scope
if ($scope.property.value !== undefined && $scope.property.value !== null
@ -158,15 +158,89 @@ var KisBpmTaskListenersPopupCtrl = [ '$scope', '$q', '$translate', function($sco
}
};
function addListener(event, className, expression, delegateExpression) {
var implementation = '';
if(className) {
implementation = className;
} else if(expression) {
implementation = expression;
} else if(delegateExpression) {
implementation = delegateExpression;
}
$scope.taskListeners.push({
event : event,
implementation : implementation,
className : className ? className : '',
expression: expression ? expression : '',
delegateExpression: delegateExpression ? delegateExpression : ''
});
}
// Click handler for add button
$scope.addNewListener = function() {
$scope.taskListeners.push({ event : 'create',
implementation : '',
className : '',
expression: '',
delegateExpression: ''});
addListener('create');
};
/**
* 判断监听器是否存在
* @param event
* @param className
*/
function isListenerExist(event, className) {
for(var i = 0, item; item = $scope.taskListeners[i++]; ) {
if(item.event == event && item.className == className) {
return true;
}
}
return false;
}
// quick add listener
$scope.addOaUserTaskCreateListener = function() {
var event = 'create',
className = '',
expression = '',
delegateExpression = '${oaUserTaskCreateListener}';
if(isListenerExist(event, className)) {
top.dialog.msg('监听器已经存在');
return;
}
addListener(event, className, expression, delegateExpression);
}
$scope.addOaUserTaskCompleteListener = function() {
var event = 'complete',
className = '',
expression = '',
delegateExpression = '${oaUserTaskCompleteListener}';
if(isListenerExist(event, className)) {
top.dialog.msg('监听器已经存在');
return;
}
addListener(event, className, expression, delegateExpression);
}
$scope.addOaUserTaskAssignmentListener = function() {
var event = 'assignment',
className = '',
expression = '',
delegateExpression = '${oaUserTaskAssignmentListener}';
if(isListenerExist(event, className)) {
top.dialog.msg('监听器已经存在');
return;
}
addListener(event, className, expression, delegateExpression);
}
$scope.addOaUserTaskDeleteListener = function() {
var event = 'delete',
className = '',
expression = '',
delegateExpression = '${oaUserTaskDeleteListener}';
if(isListenerExist(event, className)) {
top.dialog.msg('监听器已经存在');
return;
}
addListener(event, className, expression, delegateExpression);
}
// Click handler for remove button
$scope.removeListener = function() {
if ($scope.selectedListeners.length > 0) {
@ -189,12 +263,12 @@ var KisBpmTaskListenersPopupCtrl = [ '$scope', '$q', '$translate', function($sco
var index = $scope.taskListeners.indexOf($scope.selectedListeners[0]);
if (index != 0) { // If it's the first, no moving up of course
// Reason for funny way of swapping, see https://github.com/angular-ui/ng-grid/issues/272
var prevIndex = index - 1;
var temp = $scope.taskListeners[index];
$scope.taskListeners.splice(index, 1);
$timeout(function(){
$scope.taskListeners.splice(index + -1, 0, temp);
$timeout(function() {
$scope.taskListeners.splice(prevIndex, 0, temp);
}, 100);
}
}
};
@ -205,10 +279,11 @@ var KisBpmTaskListenersPopupCtrl = [ '$scope', '$q', '$translate', function($sco
var index = $scope.taskListeners.indexOf($scope.selectedListeners[0]);
if (index != $scope.taskListeners.length - 1) { // If it's the last element, no moving down of course
// Reason for funny way of swapping, see https://github.com/angular-ui/ng-grid/issues/272
var nextIndex = index + 1;
var temp = $scope.taskListeners[index];
$scope.taskListeners.splice(index, 1);
$timeout(function(){
$scope.taskListeners.splice(index + 1, 0, temp);
$scope.taskListeners.splice(nextIndex, 0, temp);
}, 100);
}

View File

@ -0,0 +1,102 @@
<div class="modal" ng-controller="KisBpmTaskListenersPopupCtrl">
<div class="modal-dialog modal-wide">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" ng-click="close()">&times;</button>
<h2>{{'PROPERTY.PROPERTY.EDIT.TITLE' | translate:property}}</h2>
</div>
<div class="modal-body">
<div class="row row-no-gutter">
<div class="col-xs-6">
<div ng-if="translationsRetrieved" class="kis-listener-grid" ng-grid="gridOptions"></div>
<div class="pull-right">
<div class="btn-group">
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.MOVE.UP | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveListenerUp()"><i class="glyphicon glyphicon-arrow-up"></i></a>
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.MOVE.DOWN | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveListenerDown()"><i class="glyphicon glyphicon-arrow-down"></i></a>
</div>
<div class="btn-group">
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.ADD | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="addNewListener()"><i class="glyphicon glyphicon-plus"></i></a>
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.REMOVE | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="removeListener()"><i class="glyphicon glyphicon-minus"></i></a>
</div>
</div>
</div>
<div class="col-xs-6">
<div ng-show="selectedListeners.length > 0">
<div class="form-group">
<label for="eventField">{{'PROPERTY.TASKLISTENERS.EVENT' | translate}}</label>
<select id="eventField" class="form-control" ng-model="selectedListeners[0].event">
<option>create</option>
<option>assignment</option>
<option>complete</option>
<option>delete</option>
</select>
</div>
<div class="form-group">
<label for="classField">{{'PROPERTY.TASKLISTENERS.CLASS' | translate}}</label>
<input type="text" id="classField" class="form-control" ng-model="selectedListeners[0].className" ng-change="listenerDetailsChanged()" placeholder="{{'PROPERTY.TASKLISTENERS.CLASS.PLACEHOLDER' | translate}}" />
</div>
<div class="form-group">
<label for="expressionField">{{'PROPERTY.TASKLISTENERS.EXPRESSION' | translate}}</label>
<input type="text" id="expressionField" class="form-control" ng-model="selectedListeners[0].expression" ng-change="listenerDetailsChanged()" placeholder="{{'PROPERTY.TASKLISTENERS.EXPRESSION.PLACEHOLDER' | translate}}" />
</div>
<div class="form-group">
<label for="delegateExpressionField">{{'PROPERTY.TASKLISTENERS.DELEGATEEXPRESSION' | translate}}</label>
<input type="text" id="delegateExpressionField" class="form-control" ng-model="selectedListeners[0].delegateExpression" ng-change="listenerDetailsChanged()" placeholder="{{'PROPERTY.TASKLISTENERS.DELEGATEEXPRESSION.PLACEHOLDER' | translate}}" />
</div>
</div>
<div ng-show="selectedListeners.length == 0" class="muted no-property-selected" translate>PROPERTY.TASKLISTENERS.UNSELECTED</div>
</div>
</div>
<div class="row row-no-gutter">
<div class="col-xs-6">
<div ng-if="translationsRetrieved" class="kis-field-grid" ng-grid="gridFieldOptions"></div>
<div class="pull-right">
<div class="btn-group">
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.MOVE.UP | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveFieldUp()"><i class="glyphicon glyphicon-arrow-up"></i></a>
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.MOVE.DOWN | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveFieldDown()"><i class="glyphicon glyphicon-arrow-down"></i></a>
</div>
<div class="btn-group">
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.ADD | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="addNewField()"><i class="glyphicon glyphicon-plus"></i></a>
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.REMOVE | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="removeField()"><i class="glyphicon glyphicon-minus"></i></a>
</div>
</div>
</div>
<div class="col-xs-6">
<div ng-show="selectedFields.length > 0">
<div class="form-group">
<label for="nameField">{{'PROPERTY.TASKLISTENERS.FIELDS.NAME' | translate}}</label>
<input type="text" id="nameField" class="form-control" ng-model="selectedFields[0].name" placeholder="{{'PROPERTY.TASKLISTENERS.FIELDS.NAME.PLACEHOLDER' | translate}}" />
</div>
<div class="form-group">
<label for="stringValueField">{{'PROPERTY.TASKLISTENERS.FIELDS.STRINGVALUE' | translate}}</label>
<input type="text" id="stringValueField" class="form-control" ng-model="selectedFields[0].stringValue" ng-change="fieldDetailsChanged()" placeholder="{{'PROPERTY.TASKLISTENERS.FIELDS.STRINGVALUE.PLACEHOLDER' | translate}}" />
</div>
<div class="form-group">
<label for="expressionField">{{'PROPERTY.TASKLISTENERS.FIELDS.EXPRESSION' | translate}}</label>
<input type="text" id="expressionField" class="form-control" ng-model="selectedFields[0].expression" ng-change="fieldDetailsChanged()" placeholder="{{'PROPERTY.TASKLISTENERS.FIELDS.EXPRESSION.PLACEHOLDER' | translate}}" />
</div>
<div class="form-group">
<label for="stringField">{{'PROPERTY.TASKLISTENERS.FIELDS.STRING' | translate}}</label>
<textarea id="stringField" class="form-control" ng-model="selectedFields[0].string" ng-change="fieldDetailsChanged()" placeholder="{{'PROPERTY.TASKLISTENERS.FIELDS.STRING.PLACEHOLDER' | translate}}"></textarea>
</div>
</div>
<div ng-show="selectedFields.length == 0" class="muted no-property-selected"translate>PROPERTY.TASKLISTENERS.FIELDS.EMPTY</div>
</div>
</div>
</div>
<div class="modal-footer">
<button ng-click="cancel()" class="btn btn-primary" translate>ACTION.CANCEL</button>
<button ng-click="save()" class="btn btn-primary" translate>ACTION.SAVE</button>
</div>
</div>
</div>
</div>

View File

@ -11,18 +11,26 @@
<div class="row row-no-gutter">
<div class="col-xs-6">
<div ng-if="translationsRetrieved" class="kis-listener-grid" ng-grid="gridOptions"></div>
<div class="pull-right">
<div class="btn-group">
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.MOVE.UP | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveListenerUp()"><i class="glyphicon glyphicon-arrow-up"></i></a>
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.MOVE.DOWN | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveListenerDown()"><i class="glyphicon glyphicon-arrow-down"></i></a>
</div>
<div class="btn-group">
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.ADD | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="addNewListener()"><i class="glyphicon glyphicon-plus"></i></a>
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.REMOVE | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="removeListener()"><i class="glyphicon glyphicon-minus"></i></a>
</div>
</div>
<div style="margin: 10px 0;">
快速设置
<div class="btn-group">
<button type="button" ng-click="addOaUserTaskCreateListener()">任务创建(OA)</button>
<button type="button" ng-click="addOaUserTaskAssignmentListener()">任务代理人设置(OA)</button>
<button type="button" ng-click="addOaUserTaskCompleteListener()">任务完成(OA)</button>
<button type="button" ng-click="addOaUserTaskDeleteListener()">任务删除(OA)</button>
</div>
</div>
<div class="pull-right" style="margin-bottom: 15px;">
<div class="btn-group">
<a class="btn btn-icon btn-xs" rel="tooltip" data-title="{{ACTION.MOVE.UP | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveListenerUp()"><i class="glyphicon glyphicon-arrow-up"></i></a>
<a class="btn btn-icon btn-xs" rel="tooltip" data-title="{{ACTION.MOVE.DOWN | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveListenerDown()"><i class="glyphicon glyphicon-arrow-down"></i></a>
</div>
<div class="btn-group">
<a class="btn btn-icon btn-xs" rel="tooltip" data-title="{{ACTION.ADD | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="addNewListener()"><i class="glyphicon glyphicon-plus"></i></a>
<a class="btn btn-icon btn-xs" rel="tooltip" data-title="{{ACTION.REMOVE | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="removeListener()"><i class="glyphicon glyphicon-minus"></i></a>
</div>
</div>
</div>
<div class="col-xs-6">
<div ng-show="selectedListeners.length > 0">
@ -57,12 +65,12 @@
<div ng-if="translationsRetrieved" class="kis-field-grid" ng-grid="gridFieldOptions"></div>
<div class="pull-right">
<div class="btn-group">
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.MOVE.UP | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveFieldUp()"><i class="glyphicon glyphicon-arrow-up"></i></a>
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.MOVE.DOWN | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveFieldDown()"><i class="glyphicon glyphicon-arrow-down"></i></a>
<a class="btn btn-icon btn-xs" rel="tooltip" data-title="{{ACTION.MOVE.UP | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveFieldUp()"><i class="glyphicon glyphicon-arrow-up"></i></a>
<a class="btn btn-icon btn-xs" rel="tooltip" data-title="{{ACTION.MOVE.DOWN | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="moveFieldDown()"><i class="glyphicon glyphicon-arrow-down"></i></a>
</div>
<div class="btn-group">
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.ADD | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="addNewField()"><i class="glyphicon glyphicon-plus"></i></a>
<a class="btn btn-icon btn-lg" rel="tooltip" data-title="{{ACTION.REMOVE | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="removeField()"><i class="glyphicon glyphicon-minus"></i></a>
<a class="btn btn-icon btn-xs" rel="tooltip" data-title="{{ACTION.ADD | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="addNewField()"><i class="glyphicon glyphicon-plus"></i></a>
<a class="btn btn-icon btn-xs" rel="tooltip" data-title="{{ACTION.REMOVE | translate}}" data-placement="bottom" data-original-title="" title="" ng-click="removeField()"><i class="glyphicon glyphicon-minus"></i></a>
</div>
</div>
</div>

View File

@ -116,7 +116,7 @@
if(!row.deploymentId) {
return '-';
}
var flowChatImg = '<img id="flowChat'+ row.id +'" src="route/activiti/get-process-image/'+ row.deploymentId +'" style="width: 30px; height: 30px;"/>'
var flowChatImg = '<img id="flowChat'+ row.id +'" src="route/activiti/model/get-process-image/'+ row.deploymentId +'" style="width: 30px; height: 30px;"/>'
setTimeout(function() {
new Viewer(document.getElementById('flowChat'+ row.id));
}, 50);

View File

@ -0,0 +1,104 @@
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<base th:href="${#request.getContextPath() + '/'}">
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
<link rel="stylesheet" href="assets/fonts/font-awesome/css/font-awesome.css"/>
<link rel="stylesheet" href="assets/layuiadmin/layui/css/layui.css" media="all">
<link rel="stylesheet" href="assets/layuiadmin/style/admin.css" media="all">
<link rel="stylesheet" href="assets/js/vendor/codemirror/codemirror.css">
<link rel="stylesheet" href="assets/js/vendor/codemirror/theme/dracula.css">
<style>
.resource-img, .resource-img-path {text-align: center}
.layui-btn-container .layui-btn {margin-bottom: 0px}
.layui-table {width: auto;}
</style>
</head>
<body>
<div class="layui-anim layui-anim-fadein">
<div class="layui-card">
<div class="layui-card-body" style="padding: 15px;">
<form class="layui-form layui-form-pane" lay-filter="dataForm">
<textarea id="formXml" name="formXml" style="display: none;"></textarea>
<div class="layui-form-item layui-layout-admin">
<div class="layui-input-block">
<div class="layui-footer" style="left: 0;">
<button type="button" class="layui-btn layui-btn-primary close">返回上级</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
<script src="assets/js/vendor/viewer/viewer.min.js"></script>
<script src="assets/js/vendor/codemirror/codemirror.js"></script>
<script src="assets/js/vendor/codemirror/mode/xml/xml.js"></script>
<script src="assets/layuiadmin/layui/layui.js"></script>
<script>
layui.config({
base: 'assets/layuiadmin/' //静态资源所在路径
}).extend({
index: 'lib/index' //主入口模块
}).use(['index', 'form', 'laydate'], function(){
var $ = layui.$;
var win = $(window)
var laytpl = layui.laytpl;
var processDefinitionId = top.restAjax.params(window.location.href).processDefinitionId;
var codeMirrorConfig = {
lineNumbers: true,
theme: 'dracula',
matchBrackets: true,
indentUnit : 4,
tabSize : 4,
extraKeys: {
'Shift-Tab': function (cm) {
if (cm.somethingSelected()) {
cm.indentSelection('subtract');
} else {
cm.indentLine(cm.getCursor().line, "subtract");
}
return;
},
}
};
function closeBox() {
parent.layer.close(parent.layer.getFrameIndex(window.name));
}
function initFormXml(value) {
codeMirrorConfig.mode = 'application/xml';
var editor = CodeMirror.fromTextArea(document.getElementById('formXml'), codeMirrorConfig);
editor.setValue(value);
editor.setSize('100%', win.height() - 90);
editor.on('change', function(self, changeValue) {
$('#formXml').val(self.getValue());
});
}
// 初始化
function initData() {
var loadLayerIndex;
top.restAjax.get(top.restAjax.path('api/activiti/model/get-process-xml/{processDefinitionId}', [processDefinitionId]), {}, null, function(code, data) {
initFormXml(data.data);
}, function(code, data) {
top.dialog.msg(data.msg);
}, function() {
loadLayerIndex = top.dialog.msg(top.dataMessage.loading, {icon: 16, time: 0, shade: 0.3});
}, function() {
top.dialog.close(loadLayerIndex);
});
}
initData();
$('.close').on('click', function() {
closeBox();
});
});
</script>
</body>
</html>

View File

@ -57,7 +57,7 @@
cols: [
[
{field:'rowNum', width:80, title: '序号', fixed: 'left', align:'center', templet: '<span>{{d.LAY_INDEX}}</span>'},
{field:'id', width:150, title: '主键', align:'center',
{field:'id', width:220, title: '流程定义ID', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
@ -66,7 +66,7 @@
return rowData;
}
},
{field:'key', width:150, title: '流程定义ID', align:'center',
{field:'key', width:150, title: '流程key', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
@ -84,7 +84,7 @@
return rowData;
}
},
{field:'name', width:150, title: '名称', align:'center',
{field:'name', width:200, title: '名称', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
@ -93,7 +93,7 @@
return rowData;
}
},
{field:'description', width:150, title: '描述', align:'center',
{field:'description', width:200, title: '描述', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
@ -111,13 +111,13 @@
return rowData;
}
},
{field:'resourceName', width:200, title: '流程资源名称', align:'center',
{field:'resourceName', width:300, title: '流程资源名称', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
return '-';
}
return rowData;
return '<a href="javascript:void(0);" lay-event="showXml">'+ rowData +'</a>';
}
},
{field: 'diagramResourceName', width: 80, title: '流程图', align:'center',
@ -125,7 +125,7 @@
if(!row.deploymentId) {
return '-';
}
var flowChatImg = '<img id="flowChat'+ row.id +'" src="route/activiti/get-process-image/'+ row.deploymentId +'" style="width: 30px; height: 30px;"/>'
var flowChatImg = '<img id="flowChat'+ row.id +'" src="route/activiti/model/get-process-image/'+ row.deploymentId +'" style="width: 30px; height: 30px;"/>'
setTimeout(function() {
new Viewer(document.getElementById('flowChat'+ row.id));
}, 50);
@ -181,7 +181,15 @@
table.on('tool(dataTable)', function(obj) {
var data = obj.data;
var layEvent = obj.event;
if(layEvent === 'nodeEvent') {
if(layEvent === 'showXml') {
top.dialog.open({
url: top.restAjax.path('route/activiti/model/get-process-xml?processDefinitionId={processDefinitionId}', [data.id]),
title: '流程XML',
width: '80%',
height: '95%',
onClose: function() {}
});
} else if(layEvent === 'nodeEvent') {
top.dialog.open({
url: top.restAjax.path('route/oa/node-field/list-node?deploymentId={deploymentId}', [data.deploymentId]),
title: '节点字段管理',

View File

@ -57,7 +57,7 @@
cols: [
[
{field:'rowNum', width:80, title: '序号', fixed: 'left', align:'center', templet: '<span>{{d.LAY_INDEX}}</span>'},
{field:'id', width:150, title: '主键', align:'center',
{field:'id', width:300, title: '流程定义ID', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
@ -75,7 +75,7 @@
return rowData;
}
},
{field:'name', width:150, title: '名称', align:'center',
{field:'name', width:200, title: '名称', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
@ -84,7 +84,7 @@
return rowData;
}
},
{field:'description', width:150, title: '描述', align:'center',
{field:'description', width:200, title: '描述', align:'center',
templet: function(row) {
var rowData = row[this.field];
if(typeof(rowData) === 'undefined' || rowData == null || rowData == '') {
@ -98,7 +98,7 @@
if(!row.deploymentId) {
return '-';
}
var flowChatImg = '<img id="flowChat'+ row.id +'" src="route/activiti/get-process-image/'+ row.deploymentId +'" style="width: 30px; height: 30px;"/>'
var flowChatImg = '<img id="flowChat'+ row.id +'" src="route/activiti/model/get-process-image/'+ row.deploymentId +'" style="width: 30px; height: 30px;"/>'
setTimeout(function() {
new Viewer(document.getElementById('flowChat'+ row.id));
}, 50);