后端

# 1. 如何修改上传文件的位置?

打开后端代码文件FileController就可以看到有两个配置项


    @Value("${file.dir}")
    private String fileDir;
    @Value("${file.showUrl}")
    private String fileShowUrl;

file.dir:上传文件存储的目录 file.showUrl:文件显示的url配置

默认配置如下:

file:
  ##静态文件在磁盘位置,此处与nginx配置要一直
  dir: /tmp/cxygzl
  ##静态文件访问路径
  showUrl: http://127.0.0.1:${server.port}/file/show

# 2. 流程通知事件默认字段有哪些?

# 前置事件

  1. flowId:流程id
  2. processInstanceId:流程实例id
  3. rootUser:发起人的id

# 后置事件

  1. flowId:流程id
  2. processInstanceId:流程实例id
  3. cancel:是否是撤销结束
  4. approveResult:审批结果(1同意2拒绝)

# 3. 触发器和通知事件的返回值数据是什么样的格式?

具体参考各个表单:点击前往

# 4. 记录日志排除某些字段

controller的方法上添加NotWriteLogAnno注解,具体查看:


/**
 * 日志打印注解
 * 添加到controller方法上
 */
@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NotWriteLogAnno {

    boolean exclude() default false;


    //所有的字段都不记录日志
     boolean all() default true;
     //排除记录日志的字段
     String[] paramsExclude() default {};

    /**
     * 是否打印返回值
     * @return
     */
     boolean printResultLog() default  true;
}

默认打印所有参数和返回值;针对有些参数,比如base64文件、HttpServletRequest等,无法打印,可以添加该注解排除日志打印

# 5. controller的返回值格式

本项目使用了AOP拦截所有controller,针对一些错误情况,统一try住返回对象R,所以返回值格式都是对象 R

# 6. 内部节点连线和外部节点连线

假如存在一个流程 有两个审批节点A1,A2,那这两个节点之间的联系就是外部节点连线

但是在工作流内部,每一个审批节点都是包含了用户任务(UserTask)服务任务(ServiceTask)两个节点,那这两个节点之间的连线就是内部节点连线

也就是说一个审批节点是由用户任务和服务任务组成

具体连线和节点构造可以看ModeUtil类中的方法buildInnerSequenceFlow,buildApproveNode

# 7. 网关之后为啥要有聚合网关?

聚合网关的作用就是拦截等待一起执行

假如有一个并行网关有两个分支,网关之后跟着一个审批节点A

如果没有聚合网关,则每一个分支执行完成之后都会立即到达审批节点A,则就会执行两次A节点

添加聚合网关之后,优先结束的则等待其他分支执行完成之后一起到达A节点

# 8. 审批人处理方式怎么实现的

具体查看接口MultiInstanceHandlerAssignUserStrategy的实现,使用策略模式实现

# 9. 如何查看流程的 bpmn20.xml文件?

可以查看FlowServiceImpl.create方法,默认在创建流程的时候自动创建了文件,存在目录/tmp/flowable-deployment/

 {
            byte[] bpmnBytess = new BpmnXMLConverter().convertToXML(bpmnModel);
            String filename = "/tmp/flowable-deployment/" + flowId + ".bpmn20.xml";
            log.debug("部署时的模型文件:{}", filename);
            FileUtil.writeBytes(bpmnBytess, filename);
        }

如果不需要或者保存到其他介质(比如数据库),可以自行修改

# 10. 如何查看流程实例的flowable执行图

查看FlowController.showImg方法,参数是流程实例id

从这个图片就可以看到每个业务节点对应的实际节点构成,比如审批节点是由用户任务服务任务组成

# 11. 为啥在节点执行结束的时候再去更新业务表的任务执行状态?

多实例或签节点中,一个任务执行完成之后,整个节点就会执行结束。其他未执行的任务要在节点执行结束事件中更新为取消状态

# 12. 接口文档如何访问?

接口文档通过swagger实现,访问地址是根路径+/doc.html

# 13. 如何升级支持jdk17?

打开biz-apppom.xml文件,引入:


<dependency>
    <groupId>org.openjdk.nashorn</groupId>
    <artifactId>nashorn-core</artifactId>
    <version>15.4</version>
</dependency>

# 14. 获取当前登录用户

本项目使用SaToken做用户认证授权,具体文档请查看SaToken文档

    String userId = StpUtil.getLoginIdAsString();

# 15. 登录页面,关闭验证码

打开biz的配置文件,修改如下配置

##账号密码登录页面:是否开启验证码
login:
  captcha: false

# 16. 流程页面的执行人等信息如何渲染出来的?

在发起流程或者处理待办任务等页面会显示流程各个节点以及节点下的执行人信息,该信息分为两部分:

  1. 若该节点未执行,则根据配置信息实时计算
  2. 若该节点已执行,则从数据库获取相应的数据显示

渲染显示的代码位置在:NodeFormatUtil.java;计算执行是在接口:NodeStrategy.javaApprovalNodeAssignedTypeStrategy.java下的各个实现类里实现

流程在具体执行到用户任务节点、抄送节点等计算执行人可以看接口:AssignUserStrategy.java下的各个实现类

# 17. 抄送任务是异步执行的吗?在哪里配置的?

是同步执行的;是在创建流程时设置的,具体查看ModelUtil类下的方法buildCCNode中的setAsynchronous

# java:程序包sun.font不存在

改成jdk8或者如果用jdk17,从springboot3分支找到对应位置修改即可

# 如何将报表数据存储到ES中?

本项目是使用bboss来对接es的,想对接方式请跳转到bboss官网查看

  1. 打开bboss配置
spring:
  elasticsearch:
#    bboss:
#      elasticsearch:
#        showTemplate: true
#        rest:
#          hostNames: 127.0.0.1:9200
  1. 修改配置
##报表配置
report:
  enable: true
#  存储介质
  store: es

此处要注意,项目运行过程中不要修改存储介质,否则会出现不同流程存储不同介质中,导致报表数据查询失败

# 如何修改审批节点完成条件?

现有的或签审批是指:一个审批通过算完成或者全部拒绝算完成。若需要修改为一个人拒绝也算完成,则需要修改完成条件判断。

打开代码:cc.flyflow.core.node.MultiInstanceHandler#completionCondition,找到如下位置:

     if (
                multipleMode.intValue() == FlyFlowConstant.MULTIPLE_MODE_ONE
        ) {
            //或签
            if (okNum > 0) {
                entity.setVariable(StrUtil.format("{}_{}", node.getId(), APPROVE_NODE_RESULT),
                        ApproveResultEnum.PASS.getValue());

                return true;
            }
            if (nrOfCompletedInstances == nrOfInstances) {
                entity.setVariable(StrUtil.format("{}_{}", node.getId(), APPROVE_NODE_RESULT),
                        ApproveResultEnum.REFUSE.getValue());

                return true;
            }

            return false;
        }

新增一个判断即可:



if (
    multipleMode.intValue() == FlyFlowConstant.MULTIPLE_MODE_ONE
) {
    //或签
    if (okNum > 0) {
        entity.setVariable(StrUtil.format("{}_{}", node.getId(), APPROVE_NODE_RESULT),
            ApproveResultEnum.PASS.getValue());

        return true;
    }
    if (nrOfCompletedInstances == nrOfInstances||failNum>0) {
        entity.setVariable(StrUtil.format("{}_{}", node.getId(), APPROVE_NODE_RESULT),
            ApproveResultEnum.REFUSE.getValue());

        return true;
    }

    return false;
}

只要return true即表示当前节点已完成