二次开发规范
本文说明如何在流程引擎中进行二次开发,包括新建 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)类注解
@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 容器获取:
@Autowired
private IRestOperateCustomFormService restOperateCustomFormService;6)方法注解
方法必须为 public,且包含:
@ResponseBody
@RequestMapping(value = "/create${url.suffix}")
public String create() {
// ...
}完整示例
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)类注解
@Controller
@RequestMapping("${url.prefix}/app/业务地址")
public class DemoController extends SuperController {
// ...
}4)请求地址组成
| 片段 | 说明 |
|---|---|
${url.prefix} | 开头前缀,值为 /horizon/workflow |
/app | 固定串,必须保留 |
/业务地址 | 按业务自定义 |
完整示例:/horizon/workflow/app/demo/list.wf
完整示例
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 容器:
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 容器:
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 容器:
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
Access.getUUID(); // 生成 32 位 UUID数据源说明
Access 操作数据库方法的最后一个参数为数据源标识,可以是租户数据源标识,也可以是第三方数据源标识。
| 场景 | 写法 |
|---|---|
| 查询默认租户空间 | 不传数据源参数 |
| 查询默认租户空间 | DBConfigInfoHelper.getDefaultDatabaseIdentifier() |
| 当前登录租户数据源 | HorizonCore.TENANT_IDENTIFIER.value() |
查询数据
// 查询单条,返回 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());查询总数
// 返回 int,param 为 List
Access.getRsCount(sql, param, HorizonCore.TENANT_IDENTIFIER.value());修改数据
// 执行单条变更(新增 / 删除 / 修改),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.web | Rest*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 |
