页面用调接口的方式,将图片流显示。效果图:
注意:布署到有些最小安装的linux服务器时,用户任务框里面的中文会显示不出来,这是因为缺少系统字体(宋体),需要在服务器安装字体才能正常显示。
接口定义:
//instanceId为业务数据绑定到前端列表接口
@RequestMapping("/img")
public void showProposess(String instanceId) throws Exception {
activitiTemplate.responseProcessImg(instanceId, "proposes_410000_v1");
}
activiti模板方法:
@Autowired
private ActivitiProcessImageHelper imageHelper;
public void responseProcessImg(String instanceId, String processDefinitionKey) {
imageHelper.responseProcessImg(instanceId, processDefinitionKey);
}
ActivitiProcessImageHelper 类:
public class ActivitiProcessImageHelper {
@Autowired
RuntimeService runtimeService;
@Autowired
RepositoryService repositoryService;
@Autowired
HistoryService historyService;
@Autowired
LakerProcessDiagramGenerator lakerProcessDiagramGenerator;
@Autowired(required = false)
HttpServletResponse response;
public void responseProcessImg(String instanceId, String processDefinitionKey) {
BpmnModel bpmnModel = getBpmnModel(processDefinitionKey);
List<String> highLightedActivities = null;
try {
highLightedActivities = runtimeService.getActiveActivityIds(instanceId);
} catch (Exception e) {
// 已完结的流程查不到活动节点
highLightedActivities = new ArrayList<String>();
}
List<HistoricActivityInstance> historyActivits = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(instanceId).orderByHistoricActivityInstanceId().asc().list();
List<String> highLightedFlows = getHighLightedFlows(bpmnModel, historyActivits);
try (InputStream ins = generateImgStream(bpmnModel, highLightedActivities, highLightedFlows);
ServletOutputStream outputStream = response.getOutputStream()) {
response.setContentType("image/jpg");
int len = 0;
byte[] b = new byte[1024];
while ((len = ins.read(b)) != -1) {
outputStream.write(b, 0, len);
outputStream.flush();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private InputStream generateImgStream(BpmnModel bpmnModel, List<String> highLightedActivities,
List<String> highLightedFlows) {
return lakerProcessDiagramGenerator.generateDiagram(bpmnModel, "jpg", highLightedActivities, highLightedFlows,
"宋体", "宋体", "宋体", null, 1);
}
private BpmnModel getBpmnModel(String definitionKey) {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().latestVersion()
.processDefinitionKey(definitionKey).singleResult();
return repositoryService.getBpmnModel(processDefinition.getId());// 模型
}
private List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historyActivitis) {
// 高亮流程已发生流转的线id集合
List<String> highLightedFlowIds = new ArrayList<>();
// 全部活动节点
List<FlowNode> historicActivityNodes = new ArrayList<>();
// 已完成的历史活动节点
List<HistoricActivityInstance> finishedActivityInstances = new ArrayList<>();
for (HistoricActivityInstance historicActivityInstance : historyActivitis) {
FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess()
.getFlowElement(historicActivityInstance.getActivityId(), true);
historicActivityNodes.add(flowNode);
if (historicActivityInstance.getEndTime() != null) {
finishedActivityInstances.add(historicActivityInstance);
}
}
FlowNode currentFlowNode = null;
FlowNode targetFlowNode = null;
// 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的
for (HistoricActivityInstance currentActivity : finishedActivityInstances) {
// 获得当前活动对应的节点信息及outgoingFlows信息
currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivity.getActivityId(),
true);
List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();
/**
* 遍历outgoingFlows 并找到已已流转的 满足如下条件认为已已流转:
* 1.当前节点是并行网关或兼容网关 ,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转
* 2.当前节点是以上两种类型之外的, 通过outgoingFlows查找到的时间最早的流转节点视为有效流转
*/
if ("parallelGateway".equals(currentActivity.getActivityType())
|| "inclusiveGateway".equals(currentActivity.getActivityType())) {
// 遍历历史活动节点,找到匹配流程目标节点的
for (SequenceFlow sequenceFlow : sequenceFlows) {
targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(),
true);
if (historicActivityNodes.contains(targetFlowNode)) {
highLightedFlowIds.add(targetFlowNode.getId());
}
}
} else {
for (int i = 0; i < historyActivitis.size(); i++) {
if (i != historyActivitis.size() - 1) {
HistoricActivityInstance historyActiviti = historyActivitis.get(i);
if (currentActivity.getActivityId().equals(historyActiviti.getActivityId())) {
// 找到当前节点的下一个节点
HistoricActivityInstance nexActivity = historyActivitis.get(i + 1);
for (SequenceFlow sequenceFlow : sequenceFlows) {
if (nexActivity.getActivityId().equals(sequenceFlow.getTargetRef())) {
highLightedFlowIds.add(sequenceFlow.getId());
}
}
continue;
}
}
}
}
}
return highLightedFlowIds;
}
}
LakerProcessDiagramCanvas类:
public class LakerProcessDiagramCanvas extends DefaultProcessDiagramCanvas {
public LakerProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType,
String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName,
customClassLoader);
THICK_TASK_BORDER_STROKE = new BasicStroke(6.0f);
Color wcolor = new Color(240, 240, 240);
//任务盒子的底色
TASK_BOX_COLOR = wcolor;
//子流程底色
SUBPROCESS_BOX_COLOR = wcolor;
//事件的底色
EVENT_COLOR = wcolor;
//连接箭头颜色
CONNECTION_COLOR = new Color(220, 220, 220);
CONDITIONAL_INDICATOR_COLOR = wcolor;
//高亮线条颜色
HIGHLIGHT_COLOR = Color.BLUE;
//任务里面那个字的颜色
LABEL_COLOR =Color.RED;
//任务边框颜色
TASK_BORDER_COLOR = new Color(220, 220, 220);
EVENT_BORDER_COLOR = new Color(220, 220, 220);
SUBPROCESS_BORDER_COLOR = new Color(100, 100, 100);
HIGHLIGHT_FLOW_STROKE = new BasicStroke(3f);
GATEWAY_TYPE_STROKE = new BasicStroke(1.0f);
try {
//用户任务左上角那个图标的图片类路径地址
USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("img/taskicon.jpg", customClassLoader));
} catch (IOException e) {
e.printStackTrace();
}
g.setColor(new Color(220, 220, 220));
}
public LakerProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType) {
super(width, height, minX, minY, imageType);
HIGHLIGHT_COLOR = Color.BLUE;
THICK_TASK_BORDER_STROKE = new BasicStroke(6.0f);
}
public void drawHighLightColor(int x, int y, int width, int height, Color color) {
Paint originalPaint = g.getPaint();
Stroke originalStroke = g.getStroke();
g.setPaint(color);
g.setStroke(THICK_TASK_BORDER_STROKE);
RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20);
g.draw(rect);
g.setPaint(originalPaint);
g.setStroke(originalStroke);
}
}
LakerProcessDiagramGenerator 类:
import java.awt.Color;
import java.util.List;
import org.activiti.bpmn.model.Activity;
import org.activiti.bpmn.model.Artifact;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.CallActivity;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.FlowElementsContainer;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.Gateway;
import org.activiti.bpmn.model.GraphicInfo;
import org.activiti.bpmn.model.Lane;
import org.activiti.bpmn.model.MultiInstanceLoopCharacteristics;
import org.activiti.bpmn.model.Pool;
import org.activiti.bpmn.model.Process;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.bpmn.model.SubProcess;
import org.activiti.image.impl.DefaultProcessDiagramCanvas;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
public class LakerProcessDiagramGenerator extends DefaultProcessDiagramGenerator {
// private BufferedImage processDiagram;
@Override
protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType,
List<String> highLightedActivities, List<String> highLightedFlows, String activityFontName,
String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor) {
{
prepareBpmnModel(bpmnModel);
DefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType,
activityFontName, labelFontName, annotationFontName, customClassLoader);
// Draw pool shape, if process is participant in collaboration
for (Pool pool : bpmnModel.getPools()) {
GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
processDiagramCanvas.drawPoolOrLane(pool.getName(), graphicInfo);
}
// Draw lanes
for (Process process : bpmnModel.getProcesses()) {
for (Lane lane : process.getLanes()) {
GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(lane.getId());
processDiagramCanvas.drawPoolOrLane(lane.getName(), graphicInfo);
}
}
// Draw activities and their sequence-flows
for (FlowNode flowNode : bpmnModel.getProcesses().get(0).findFlowElementsOfType(FlowNode.class)) {
drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows,
scaleFactor);
}
for (Process process : bpmnModel.getProcesses()) {
for (FlowNode flowNode : process.findFlowElementsOfType(FlowNode.class)) {
drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows,
scaleFactor);
}
}
// Draw artifacts
for (Process process : bpmnModel.getProcesses()) {
for (Artifact artifact : process.getArtifacts()) {
drawArtifact(processDiagramCanvas, bpmnModel, artifact);
}
List<SubProcess> subProcesses = process.findFlowElementsOfType(SubProcess.class, true);
if (subProcesses != null) {
for (SubProcess subProcess : subProcesses) {
for (Artifact subProcessArtifact : subProcess.getArtifacts()) {
drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact);
}
}
}
}
return processDiagramCanvas;
}
}
@Override
protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel,
FlowNode flowNode, List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor) {
{
ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass());
if (drawInstruction != null) {
drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode);
// Gather info on the multi instance marker
boolean multiInstanceSequential = false, multiInstanceParallel = false, collapsed = false;
if (flowNode instanceof Activity) {
Activity activity = (Activity) flowNode;
MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity
.getLoopCharacteristics();
if (multiInstanceLoopCharacteristics != null) {
multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential();
multiInstanceParallel = !multiInstanceSequential;
}
}
// Gather info on the collapsed marker
GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
if (flowNode instanceof SubProcess) {
collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded();
} else if (flowNode instanceof CallActivity) {
collapsed = true;
}
if (scaleFactor == 1.0) {
// Actually draw the markers
processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(),
(int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), multiInstanceSequential,
multiInstanceParallel, collapsed);
}
if (highLightedActivities.contains(flowNode.getId())) {
if (highLightedActivities.get(highLightedActivities.size() - 1)
.equalsIgnoreCase(flowNode.getId())) {
LakerProcessDiagramCanvas lakerProcessDiagramCanvas = ((LakerProcessDiagramCanvas) processDiagramCanvas);
lakerProcessDiagramCanvas.drawHighLightColor((int) graphicInfo.getX(), (int) graphicInfo.getY(),
(int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), Color.YELLOW);
} else {
processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(),
(int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
}
}
}
// Outgoing transitions of activity
for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId()));
String defaultFlow = null;
if (flowNode instanceof Activity) {
defaultFlow = ((Activity) flowNode).getDefaultFlow();
} else if (flowNode instanceof Gateway) {
defaultFlow = ((Gateway) flowNode).getDefaultFlow();
}
boolean isDefault = false;
if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) {
isDefault = true;
}
boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null
&& !(flowNode instanceof Gateway);
String sourceRef = sequenceFlow.getSourceRef();
String targetRef = sequenceFlow.getTargetRef();
FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef);
FlowElement targetElement = bpmnModel.getFlowElement(targetRef);
List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
if (graphicInfoList != null && graphicInfoList.size() > 0) {
graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement,
targetElement, graphicInfoList);
int xPoints[] = new int[graphicInfoList.size()];
int yPoints[] = new int[graphicInfoList.size()];
for (int i = 1; i < graphicInfoList.size(); i++) {
GraphicInfo graphicInfo = graphicInfoList.get(i);
GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);
if (i == 1) {
xPoints[0] = (int) previousGraphicInfo.getX();
yPoints[0] = (int) previousGraphicInfo.getY();
}
xPoints[i] = (int) graphicInfo.getX();
yPoints[i] = (int) graphicInfo.getY();
}
processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault,
highLighted, scaleFactor);
// Draw sequenceflow label
GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId());
if (labelGraphicInfo != null) {
processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false);
}
}
}
// Nested elements
if (flowNode instanceof FlowElementsContainer) {
for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) {
if (nestedFlowElement instanceof FlowNode) {
drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement,
highLightedActivities, highLightedFlows, scaleFactor);
}
}
}
}
}
protected static DefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType,
String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {
// We need to calculate maximum values to know how big the image will be in its
// entirety
double minX = Double.MAX_VALUE;
double maxX = 0;
double minY = Double.MAX_VALUE;
double maxY = 0;
for (Pool pool : bpmnModel.getPools()) {
GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
minX = graphicInfo.getX();
maxX = graphicInfo.getX() + graphicInfo.getWidth();
minY = graphicInfo.getY();
maxY = graphicInfo.getY() + graphicInfo.getHeight();
}
List<FlowNode> flowNodes = gatherAllFlowNodes(bpmnModel);
for (FlowNode flowNode : flowNodes) {
GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
// width
if (flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth() > maxX) {
maxX = flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth();
}
if (flowNodeGraphicInfo.getX() < minX) {
minX = flowNodeGraphicInfo.getX();
}
// height
if (flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight() > maxY) {
maxY = flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight();
}
if (flowNodeGraphicInfo.getY() < minY) {
minY = flowNodeGraphicInfo.getY();
}
for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
if (graphicInfoList != null) {
for (GraphicInfo graphicInfo : graphicInfoList) {
// width
if (graphicInfo.getX() > maxX) {
maxX = graphicInfo.getX();
}
if (graphicInfo.getX() < minX) {
minX = graphicInfo.getX();
}
// height
if (graphicInfo.getY() > maxY) {
maxY = graphicInfo.getY();
}
if (graphicInfo.getY() < minY) {
minY = graphicInfo.getY();
}
}
}
}
}
List<Artifact> artifacts = gatherAllArtifacts(bpmnModel);
for (Artifact artifact : artifacts) {
GraphicInfo artifactGraphicInfo = bpmnModel.getGraphicInfo(artifact.getId());
if (artifactGraphicInfo != null) {
// width
if (artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth() > maxX) {
maxX = artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth();
}
if (artifactGraphicInfo.getX() < minX) {
minX = artifactGraphicInfo.getX();
}
// height
if (artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight() > maxY) {
maxY = artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight();
}
if (artifactGraphicInfo.getY() < minY) {
minY = artifactGraphicInfo.getY();
}
}
List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());
if (graphicInfoList != null) {
for (GraphicInfo graphicInfo : graphicInfoList) {
// width
if (graphicInfo.getX() > maxX) {
maxX = graphicInfo.getX();
}
if (graphicInfo.getX() < minX) {
minX = graphicInfo.getX();
}
// height
if (graphicInfo.getY() > maxY) {
maxY = graphicInfo.getY();
}
if (graphicInfo.getY() < minY) {
minY = graphicInfo.getY();
}
}
}
}
int nrOfLanes = 0;
for (Process process : bpmnModel.getProcesses()) {
for (Lane l : process.getLanes()) {
nrOfLanes++;
GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());
// // width
if (graphicInfo.getX() + graphicInfo.getWidth() > maxX) {
maxX = graphicInfo.getX() + graphicInfo.getWidth();
}
if (graphicInfo.getX() < minX) {
minX = graphicInfo.getX();
}
// height
if (graphicInfo.getY() + graphicInfo.getHeight() > maxY) {
maxY = graphicInfo.getY() + graphicInfo.getHeight();
}
if (graphicInfo.getY() < minY) {
minY = graphicInfo.getY();
}
}
}
// Special case, see https://activiti.atlassian.net/browse/ACT-1431
if (flowNodes.isEmpty() && bpmnModel.getPools().isEmpty() && nrOfLanes == 0) {
// Nothing to show
minX = 0;
minY = 0;
}
return new LakerProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY, imageType,
activityFontName, labelFontName, annotationFontName, customClassLoader);
}
}