Skip to content

二次开发规范

本文说明如何在流程引擎中进行二次开发,包括新建 REST 接口、普通内部接口,以及服务层、持久层的包路径与命名约定。

流程引擎默认源码位于 com.horizon 包下,二开代码请放在 com.app 包下(下文均以 com.app.demo 为示例)。

前提

开发前请先完成 V823 流程引擎接口扫描路径修改,确保引擎能扫描到 com.app 包下的接口类。

REST 接口(控制层)

接口地址中包含 /rest,可跳过工作流本身的登录拦截器。

示例路径:com.app.demo.rest.web.RestDemoController

1)包路径要求

com.app.**.rest.web

** 按业务自定义,其余层级固定。

2)类命名规则

  • Rest 开头、Controller 结尾(注意拼写)
  • 必须继承 RestSuperController

3)类注解

java
@Controller
@RequestMapping("${url.prefix}/rest/app/业务地址")
public class RestDemoController extends RestSuperController {
    // ...
}

4)请求地址组成

片段说明
${url.prefix}开头前缀,值为 /horizon/workflow
/rest/app固定串(也可按需改成其他)
/业务地址按业务自定义
${url.suffix}方法级后缀(如 .wf

完整示例:/horizon/workflow/rest/app/demo/create.wf

5)注入服务层 / 持久层

通过 @Autowired 从 Spring 容器获取:

java
@Autowired
private IRestOperateCustomFormService restOperateCustomFormService;

6)方法注解

方法必须为 public,且包含:

java
@ResponseBody
@RequestMapping(value = "/create${url.suffix}")
public String create() {
    // ...
}

完整示例

java
package com.app.demo.rest.web;

import com.horizon.workflow.rest.web.base.RestSuperController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("${url.prefix}/rest/app/demo")
public class RestDemoController extends RestSuperController {

    @Autowired
    private IDemoService demoService;

    @ResponseBody
    @RequestMapping(value = "/create${url.suffix}")
    public String create() {

        ResponseResult result = super.getParamWithToken();
        //验证token(所以请求的接口里与产品接口一样需要传递accessToken)
        if (!result.isSuccess()) {
            return renderJson(result);
        } else {
            //请求的参数,也可以获取到登录的userid
            RequestParams params = (RequestParams) result.getData();
            List<String> workIds = (List<String>) params.get("workIds");
            if(workIds==null || workIds.isEmpty()){
                return this.renderJson(ResponseResult.failure("请传入workIds"));
            }
            List<ResponseInstance> responseInstances = restInstanceStatusService.findInstancesList(workIds);
            //返回结果,格式与待办已办的一样,也可根据需求修改
            return this.renderJson(ResponseResult.success().data(responseInstances).recordsTotal(responseInstances.size() + ""));
        }
    }
}

内部接口(控制层)

不可跳过工作流登录拦截器,未登录访问会被拦截到登录页。

示例路径:com.app.demo.web.DemoController

1)包路径要求

com.app.**.web

** 按业务自定义,其余层级固定。

2)类命名规则

  • Controller 结尾(注意拼写)
  • 必须继承 SuperController

3)类注解

java
@Controller
@RequestMapping("${url.prefix}/app/业务地址")
public class DemoController extends SuperController {
    // ...
}

4)请求地址组成

片段说明
${url.prefix}开头前缀,值为 /horizon/workflow
/app固定串,必须保留
/业务地址按业务自定义

完整示例:/horizon/workflow/app/demo/list.wf

完整示例

java
package com.app.demo.web;

import com.horizon.core.common.controller.SuperController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("${url.prefix}/app/demo")
public class DemoController extends SuperController {

    @ResponseBody
    @RequestMapping(value = "/list${url.suffix}")
    public String list() {
        // ...
        return renderJson(/* ... */);
    }
}

服务层

示例:com.app.demo.service.impl.DemoServiceImpl

1)包路径要求

类型包路径
接口类com.app.**.service
实现类com.app.**.service.impl

** 按业务自定义,其余层级固定。需同时创建接口与实现类。

2)命名规则

  • 接口以 Service 结尾
  • 实现类以 ServiceImpl 结尾,并实现对应接口(注意拼写)

3)实现类注解

在实现类上增加 @Service,以便启动时实例化到 Spring 容器:

java
package com.app.demo.service.impl;

import com.app.demo.service.IDemoService;
import org.springframework.stereotype.Service;

@Service
public class DemoServiceImpl implements IDemoService {
    // ...
}

持久层(JDBC / Access)

示例:com.app.demo.dao.impl.DemoDaoImpl

1)包路径要求

类型包路径
接口类com.app.**.dao
实现类com.app.**.dao.impl

** 按业务自定义,其余层级固定。需同时创建接口与实现类。

2)命名规则

  • 接口以 Dao 结尾
  • 实现类以 DaoImpl 结尾,并实现对应接口(注意拼写)

3)实现类注解

在实现类上增加 @Service,以便启动时实例化到 Spring 容器:

java
package com.app.demo.dao.impl;

import com.app.demo.dao.IDemoDao;
import com.horizon.db.Access;
import org.springframework.stereotype.Service;

@Service
public class DemoDaoImpl implements IDemoDao {
    // 使用 Access 工具类操作数据库
}

持久层(MyBatis)

1)包路径要求

com.app.**.repository

** 按业务自定义,其余层级固定。

2)命名规则

接口以 Repository 结尾(注意拼写)。

3)注解

在接口上增加 @HRepository,以便启动时实例化到 Spring 容器:

java
package com.app.demo.repository;

import com.horizon.core.common.annotation.HRepository;

@HRepository
public interface DemoRepository {
    // ...
}

可参考:

lib/horizon/horizon-workflow-extend-8.2.3.jar!/com/horizon/workflow/monitor/repository/FlowAreaInstanceRepository.class

4)对应 Mapper XML

需创建对应 XML 编写 SQL,一般放在本业务的 com.app.** 包下。

可参考:

lib/horizon/horizon-workflow-extend-8.2.3.jar!/com/horizon/monitor/FlowAreaInstanceMapper.xml

注意

  • Spring Boot 环境:XML 需放到 resources 目录下
  • Spring MVC 环境:按实际工程结构放置

Access 数据库工具类

生成 UUID

java
Access.getUUID(); // 生成 32 位 UUID

数据源说明

Access 操作数据库方法的最后一个参数为数据源标识,可以是租户数据源标识,也可以是第三方数据源标识。

场景写法
查询默认租户空间不传数据源参数
查询默认租户空间DBConfigInfoHelper.getDefaultDatabaseIdentifier()
当前登录租户数据源HorizonCore.TENANT_IDENTIFIER.value()

查询数据

java
// 查询单条,返回 List,param 为 List
Access.getSingleList(sql, param, HorizonCore.TENANT_IDENTIFIER.value());

// 查询单条,返回 Map,param 为 List
Access.getSingleMap(sql, param, HorizonCore.TENANT_IDENTIFIER.value());

// 查询单条,返回对象,param 为 List
Access.getSingleObject(sql, param, XxxEntity.class, HorizonCore.TENANT_IDENTIFIER.value());

// 查询多条,返回 List<List>,param 为 List
Access.getMultiList(sql, param, HorizonCore.TENANT_IDENTIFIER.value());

// 查询多条,返回 List<Map>,param 为 List
Access.getMultiMap(sql, param, HorizonCore.TENANT_IDENTIFIER.value());

// 查询多条,返回 List<对象>,param 为 List
Access.getMultiObject(sql, param, HorizonCore.TENANT_IDENTIFIER.value());

查询总数

java
// 返回 int,param 为 List
Access.getRsCount(sql, param, HorizonCore.TENANT_IDENTIFIER.value());

修改数据

java
// 执行单条变更(新增 / 删除 / 修改),param 为 List
Access.executeUpdate(sql, param, HorizonCore.TENANT_IDENTIFIER.value());

// 执行多个 SQL,同一事务;一个 SQL 对应一条变更
// saveMap 类型:Map<String, List>
Access.executeUpdate(saveMap, HorizonCore.TENANT_IDENTIFIER.value());

// 执行多个 SQL,同一事务;一个 SQL 对应多条变更
// saveMap 类型:Map<String, List<List>>
Access.executeMultiUpdate(saveMap, HorizonCore.TENANT_IDENTIFIER.value());

// 按对象入库
// saveMap:Map<数据表名称(全部大写), 对象>
// 支持批量:Map<数据表名称(全部大写), List<对象>>
// 主键相同则更新,否则新增;可同时处理多张表
Access.saveObjectData(saveMap, HorizonCore.TENANT_IDENTIFIER.value());

分层约定速查

层级包路径命名基类 / 注解
REST 控制层com.app.**.rest.webRest*Controller继承 RestSuperController
内部控制层com.app.**.web*Controller继承 SuperController
服务接口com.app.**.service*Service
服务实现com.app.**.service.impl*ServiceImpl@Service
JDBC 持久接口com.app.**.dao*Dao
JDBC 持久实现com.app.**.dao.impl*DaoImpl@Service
MyBatis 持久com.app.**.repository*Repository@HRepository