1. 结束/终止 正在运行的流程实例
思路:跟回退一样的思路一样,直接从当前节点跳到结束节点(EndEvent)

/**
* 结束任务
* @param taskId 当前任务ID
*/
public void endTask(String taskId) {
// 当前任务
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
List endEventList = bpmnModel.getMainProcess().findFlowElementsOfType(EndEvent.class);
FlowNode endFlowNode = endEventList.get(0);
FlowNode currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(task.getTaskDefinitionKey());
// 临时保存当前活动的原始方向
List originalSequenceFlowList = new ArrayList<>();
originalSequenceFlowList.addAll(currentFlowNode.getOutgoingFlows());
// 清理活动方向
currentFlowNode.getOutgoingFlows().clear();
// 建立新方向
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newSequenceFlowId");
newSequenceFlow.setSourceFlowElement(currentFlowNode);
newSequenceFlow.setTargetFlowElement(endFlowNode);
List newSequenceFlowList = new ArrayList<>();
newSequenceFlowList.add(newSequenceFlow);
// 当前节点指向新的方向
currentFlowNode.setOutgoingFlows(newSequenceFlowList);
// 完成当前任务
taskService.complete(task.getId());
// 可以不用恢复原始方向,不影响其它的流程
// currentFlowNode.setOutgoingFlows(originalSequenceFlowList);
}
补充1:关于BUSINESS_KEY_
BUSINESS_KEY_ 字段是用于将业务系统与Actititi工作流关联的关键字段,通常我们用它来存放业务表的ID,比如:请假ID、报销ID等等。
但是,通常咱们系统不可能只有一个流程,假设我们做的是一个OA系统,那么公司的流程有请假、采购、报销等等流程,那这一个字段如何区分到底是哪个业务流程的ID呢,换言之,假设BUSINESS_KEY_这个字段现在是2,那么我怎么知道这个2是请假表的ID,还是采购表的ID呢?因此,要想通过这个一个字段区分不同的类型就要求这个字段是唯一的,比如我们可以加上业务标识,比如:holiday:2,purchase:3等等。还有一种方式,利用另外一个空闲字段TENANT_ID_,我们可以把业务类型存到TENANT_ID_字段中,这样TENANT_ID_和BUSINESS_KEY_两个字段就能唯一确定是哪个业务的那个ID。
/**
* 7.1.0.M6的act_ru_task表中有BUSINESS_KEY_字段,因此可以直接task.getBusinessKey()
* 而7.1.0.M5中没有这个字段,因此要想获取BUSINESS_KEY_必须从act_ru_execution表中取
*/
@Test
public void testBusinessKey() {
List<Task> taskList = taskService.createTaskQuery().taskCandidateOrAssigned("lisi").taskTenantId("11").list();
Set<String> processInstanceIds = taskList.stream().map(Task::getProcessInstanceId).collect(Collectors.toSet());
List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processInstanceIds(processInstanceIds).list();
List<String> businessKeyList = processInstances.stream().map(ProcessInstance::getBusinessKey).collect(Collectors.toList());
System.out.println(businessKeyList);
Pattern pattern = Pattern.compile("^(\\w+):(\\d+)$");
List<Integer> businessIds = new ArrayList<>();
businessKeyList.forEach(businessKey->{
Matcher matcher = pattern.matcher(businessKey);
if (matcher.find()) {
String id = matcher.group(2);
businessIds.add(Integer.valueOf(id));
}
});
}
补充2:Activiti不同版本的Bug
首先,我发现不同版本的表结构不一样,当用 7.1.0.M5 版本时,启动就报错,缺少字段
org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: java.sql.SQLSyntaxErrorException: Unknown column 'VERSION_' in 'field list'
### The error may exist in org/activiti/db/mapping/entity/Deployment.xml
### The error may involve org.activiti.engine.impl.persistence.entity.DeploymentEntityImpl.insertDeployment-Inline
### The error occurred while setting parameters
### SQL: insert into ACT_RE_DEPLOYMENT(ID_, NAME_, CATEGORY_, KEY_, TENANT_ID_, DEPLOY_TIME_, ENGINE_VERSION_, VERSION_, PROJECT_RELEASE_VERSION_) values(?, ?, ?, ?, ?, ?, ?, ?, ?)
### Cause: java.sql.SQLSyntaxErrorException: Unknown column 'VERSION_' in 'field list'
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) ~[mybatis-3.5.0.jar:3.5.0]
at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:200) ~[mybatis-3.5.0.jar:3.5.0]
at org.apache.ibatis.session.defaults.DefaultSqlSession.insert(DefaultSqlSession.java:185) ~[mybatis-3.5.0.jar:3.5.0]
at org.activiti.engine.impl.db.DbSqlSession.flushRegularInsert(DbSqlSession.java:787) ~[activiti-engine-7.1.0.M5.jar:na]
at org.activiti.engine.impl.db.DbSqlSession.flushInsertEntities(DbSqlSession.java:662) ~[activiti-engine-7.1.0.M5.jar:na]
at org.activiti.engine.impl.db.DbSqlSession.flushInserts(DbSqlSession.java:642) ~[activiti-engine-7.1.0.M5.jar:na]
at org.activiti.engine.impl.db.DbSqlSession.flush(DbSqlSession.java:525) ~[activiti-engine-7.1.0.M5.jar:na]
解决办法也很简单,将缺失的字段加上即可:
ALTER TABLE `act_re_deployment`
ADD COLUMN `VERSION_` int(11) NULL DEFAULT NULL,
ADD COLUMN `PROJECT_RELEASE_VERSION_` varchar(255) NULL DEFAULT NULL;
其次,我发现在启动流程实例查询流程定义时,7.1.0.M5是按版本升序取第一个,而7.1.0.M6是降序


同时,还发现7.1.0.M6有一个明显的Bug,这个Bug会导致当有多个部署的时候,即当act_re_deployment表中的记录多于1条时,就无法使用processRuntime.start()启动流程实例

本着用新不用旧的原则,还是建议用7.1.0.M6
还有一个bug,这个bug发生在当使用原生的taskService完成任务时,多次设置同一个流程变量的值后者不会覆盖前者。即,ACT_RU_VARIABLE表中变量的值没有更新。
为了说明这个问题,看下面这个图:

节点“1”完成任务时设置result==3,于是后面的流程永远是1->3->1->3->... ,即使下一次执行时result==1,流程也不会走到2。经过分析,我猜测对同一个变量多次赋值,后者没有覆盖前者,也就是说第2次赋值没有生效,查看ACT_RU_VARIABLE表果然是这样,变量的值没有更新。经过反复试验,我发现用taskRuntime.complete()完成任务时就不会出现这个问题,流程变量的值会正常更新。
Map<String, Object> variables = new HashMap<>();
variables.put("result", 3);
// 这样写的话,多次对同一个流程变量赋值时,流程变量的值不会更新,即后面的赋值永远是不生效的
Task task = taskService.createTaskQuery().taskId("1234").singleResult();
taskService.complete(task.getId(), variables);
// 这样写的话,就很完美,ACT_RU_VARIABLE表的流程变量的值正常更新
taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId("1234").withVariables(variables).build());
补充3:任务监听器
@Slf4j
@Component
public class TaskCreateListener implements TaskRuntimeEventListener<TaskCreatedEvent> {
@Override
public void onEvent(TaskCreatedEvent event) {
System.out.println("任务被创建");
}
}
@Slf4j
@Component
public class TaskAssignedListener implements TaskRuntimeEventListener<TaskAssignedEvent> {
@Override
public void onEvent(TaskAssignedEvent event) {
System.out.println("任务被指派");
}
}
@Slf4j
@Component
public class TaskCompletedListener implements TaskRuntimeEventListener<TaskCompletedEvent> {
@Override
public void onEvent(TaskCompletedEvent event) {
System.out.println("任务被完成");
}
}

我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion在首页我有:汽车:VolvoSaabMercedesAudistatic_pages_spec.rb中的测试代码:it"shouldhavetherightselect"dovisithome_pathit{shouldhave_select('cars',:options=>['volvo','saab','mercedes','audi'])}end响应是rspec./spec/request
在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo
使用Ruby1.9.2运行IDE提示说需要gemruby-debug-base19x并提供安装它。但是,在尝试安装它时会显示消息Failedtoinstallgems.Followinggemswerenotinstalled:C:/ProgramFiles(x86)/JetBrains/RubyMine3.2.4/rb/gems/ruby-debug-base19x-0.11.30.pre2.gem:Errorinstallingruby-debug-base19x-0.11.30.pre2.gem:The'linecache19'nativegemrequiresinstall
我知道全局变量$!包含最新的异常对象,但我对下面的语法感到困惑。谁能帮助我理解以下语法?rescue$! 最佳答案 此构造可防止异常停止您的程序并使堆栈跟踪冒泡。它还会将该异常作为值返回,这很有用。a=get_me_datarescue$!在此行之后,a将保存请求的数据或异常。然后您可以分析该异常并采取相应措施。defget_me_dataraise'Nodataforyou'enda=get_me_datarescue$!puts"Executioncarrieson"pa#>>Executioncarrieson#>>#更现实的
我在我正在处理的一些代码中发现了这一点。它旨在解决从磁盘读取key文件的要求。在生产环境中,key文件的内容位于环境变量中。旧代码:key=File.read('path/to/key.pem')新代码:key=File.read('|echo$KEY_VARIABLE')这是如何工作的? 最佳答案 来自IOdocs:Astringstartingwith“|”indicatesasubprocess.Theremainderofthestringfollowingthe“|”isinvokedasaprocesswithappro
我今天看到了一个ruby代码片段。[1,2,3,4,5,6,7].inject(:+)=>28[1,2,3,4,5,6,7].inject(:*)=>5040这里的注入(inject)和之前看到的完全不一样,比如[1,2,3,4,5,6,7].inject{|sum,x|sum+x}请解释一下它是如何工作的? 最佳答案 没有魔法,符号(方法)只是可能的参数之一。这是来自文档:#enum.inject(initial,sym)=>obj#enum.inject(sym)=>obj#enum.inject(initial){|mem
我刚刚有一个关于RubyonRails和模型(Rails3)中的attr_accessible属性的一般性问题。有人可以解释应该在那里定义哪些模型属性吗?我记得一些关于批量分配风险的事情,虽然我在这方面不太了解......谢谢:) 最佳答案 想象一个带有一些字段的订单类:Order.new({:type=>'Corn',:quantity=>6})现在假设订单也有折扣代码,比如:price_off。您不想将:price_off标记为attr_accessible。这会阻止恶意代码制作最终会执行如下操作的帖子:Order.new({: