除了前端的操作插件,表单插件的扩展能力外,DWF还支持后端扩展新的restful api服务,这种扩展能力一般会用于通过系统集成将新的功能添加到DWF中来,并且将新的功能开放为服务供前端调用。
1 代码包后端的组织结构
后端扩展使用的语言是java,在DWF的组织结构中,后端统一放到part-svc文件夹之中,包括:modeler/common/app三部分,其中
- dwf-part-all\part01\part-svc\app:包含了所有用于扩展应用服务的代码,对应在核心代码中表示dwf-app工程。
- dwf-part-all\part01\part-svc\modeler:包含了用于扩展模型服务的代码,对应调试启动dwf-modeler工程。
- dwf-part-all\part01\part-svc\common:两边都需要包含的后端服务代码,无论启动dwf-app或者dwf-modeler都会包含其中的业务逻辑。
为了更好的分解不同层次的代码,在modeler/common/app中分别安置了controler/service/entity三个文件夹
- controler:用于存放spring中的controler的代码文件,主要是标注对外发布的restful api。
- service:用于存放是后端数据访问和业务逻辑的代码文件。
- entity:则表示普通的中间层或者restful api需要发布的实体类代码对应的文件。
图-后端代码组织结构示意图
2 扩展Hello World!RESTful api服务
下面,先开发一个简单的hello world!的RESTful api服务,在common\controler文件夹下增加一个HelloWorldCommonController.java文件,写入如下代码:
package edu.thss.platform.controller; import io.swagger.annotations.Api; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Api(tags = {"part01插件"}) @RestController @RequestMapping("dwf/v1/part01/common-ext/") public class HelloWorldCommonController { @GetMapping(path = "helloworld-service") public ResponseMsg<String> helloWorldService() { return new ResponseMsg<>("Hello World!"); } }
在上面的代码中扩展了一个非常简单的RESTFul服务,由于这个文件是在common\controler中编写,所以在模型服务和应用服务都会出现,在程序中使用了一些Spring的标注,具体解释如下:
@Api(tags = {"part01插件"}):表示服务在Swagger界面的中文描述;
@RestControler:表示当前的HelloWorldCommonController需要发布为Restful API服务;
@RequestMapping("dwf/v1/part01/common-ext/"):表示访问这个服务URL的相对路径为,dwf/v1/part01/common-ext/;
@GetMapping(path = "helloworld-service"):ResponseMsg<String> helloWorldService()在收到前端浏览器发来Get请求的时候被激活;
ResponseMsg是DWF平台定义的响应结构体,包含了以下字段:
|
如使用 ResponseMsg 默认构造函数,success=true,code=200, message=“OK”。data字段将取构造函数传入的值,可以是任意类型的对象。如果是自定义的对象,将会自动被序列化为JSON格式。
关于这些标注的含义和作用,建议通过下面的链接进一步了解:https://www.baeldung.com/spring-mvc-annotations
在VS.Code开发环境中,点击SPRING-BOOT DASHBOART,找到dwf-app,右键点击可以开始调试:
服务如果正确启动,在浏览器中输入:http://localhost:9090/swagger-ui.html
可以看到发布出来的restful api服务,界面如下所示:
3 与数据库进行交互的RESTful API
RESTful API主要用来与数据库交互。在DWF,RESTful API与数据库交互有三种方式:
1.使用DWF内置的OMF(对象管理框架)函数访问实体类、关联类的数据。
2.使用DWF内置的Hibernate EntityManager直接访问数据库。
3.使用JDBC或其它方式自行创建连接并维护连接池。
除非有特殊需求,尽量使用前两种数据库访问方式。
3.1 使用DWF内置的OMF(对象管理框架)函数访问实体类、关联类的数据
可以在controler的代码中注入ObjectAccessService,实现对实体类的查询操作
import.... @RestController @RequestMapping("dwf/v1/app-ext") public class AppExtController { @Autowired ObjectAccessService objectAccessService; @GetMapping(path = "getAllObjects") public ResponseMsg<?> listAllClasses(@RequestParam String name){ List objs = objectAccessService.getByCondition(name, ""); return new ResponseMsg<>(objs); } }
ObjectAccessService
objectAccessService还有一些其他的功能如下所示:
getByCondition public List<Map<String,Object>> getByCondition(String className, String condition) 查询对象(不分页) 参数: className - 实体类名 condition - 查询条件,以and开头。eg: "and plt_oid='43ab314ba35c1345b32a2'" |
getByCondition public List<Map<String,Object>> getByCondition(String className, String condition, Integer pageSize, Integer startIndex, String sampleMethod) 查询对象(分页) 参数: className - 实体类名 condition - 查询条件,以and开头。eg:"and plt_oid = '123'" pageSize - 分页大小,如果不分页可设置为null startIndex - 分页的起始偏移,如果不分页可设置为null sampleMethod - 采样方式,如果使用默认顺序采样方式可设置为null |
ObjectAccessService 是DWF的一个由Spring管理的Bean,包含了对DWF管理对象的查询接口:
另外,对对象的增删改操作,可由另外两个Bean完成:ItemClassAccessService 和 RelationAccessService。
ItemClassAccessService:
addEntityObjects public List<Map<String,Object>> addEntityObjects(String className, List<Map<String,Object>> objs)批量添加实体类对象 参数: className - 实体类名 objs - 要添加的对象列表。每个对象是一个Map:key是属性名,value是属性值返回:已添加的对象列表 |
deleteByOids public void deleteByOids(String className, List<String> oids)根据oid删除实体类 参数: className - 实体类名 oids - oid列表 |
updateEntityObjs public List<Map<String,Object>> updateEntityObjs(String className, List<Map<String,Object>> objs)批量更新实体类对象。根据对象中的oid确定更新的目标对象 参数: className - 实体类名 objs - 实体类对象列表 返回: 修改后的对象 |
cudBatch public List<Object> cudBatch(List<CudEvent> events)在一个事务中批量执行多个增、删、改操作 参数: events - 指定的操作返回:相应的操作结果 public class CudEvent { } |
addEntityObjectsFast public List<String> addEntityObjectsFast(String className, List<Map<String,Object>> objs)批量快速插入实体类对象。不执行插件,不执行脚本和存储过程,可用于大数据量的导入 参数: className - 实体类名 objs - 对象列表 返回: 插入对象的oid列表 |
RelationAccessService
对关联类的增删改操作,可由RelationAccessService完成:
addRelations public List<Map<String,Object>> addRelations(String relName, List<Map<String,Object>> relObjs) 添加关联对象 参数: relName - 关联类名 relObjs - 关联对象列表 返回: 添加的关联对象列表 |
deleteByOids public void deleteByOids(String relClassName, List<String> oids) 根据Oid删除关联对象 参数: relClassName - 关联类名 oids - 关联对象OID列表 |
updateRelations public List<Map<String,Object>> updateRelations(String relName, List<Map<String,Object>> relObjs) 更新关联对象。 不允许更新关联的左右连接类(leftClass, rightClass), 左右oid(leftOid, rightOid), rightRev)。如果传入的对象中包含这些属性,这些属性会被自动忽略。 参数: relName - 关联类名 relObjs - 要更新的关联对象。根据其中的oid决定要更新的对象返回:更新后的对象 |
getLeftEntityObj public Map<String,Object> getLeftEntityObj(String relName, String oid) 获取关联对象的左实体对象 参数: relName - 关联类名 oid - 关联对象名 返回: 关联对象的左实体对象 |
getRightEntityObj public Map<String,Object> getRightEntityObj(String relName, String oid) 获取关联对象的右实体对象 参数: relName - 关联类名 oid - 关联对象名 返回: 获取关联对象的右实体对象 |
3.2 使用DWF内置的Hibernate EntityManager直接访问数据库
可以在service文件夹中新建java文件,注入Hibernate框架的EntityManager对象实现增删改查,程序实例:
@Service public class AppInfoDao { @PersistenceContext EntityManager em; public List<AppInfoDO> getAll(){ return em.createQuery("select obj from AppInfoDO obj order by modifyTime desc, appName").getResultList(); } public AppInfoDO getById(String id){ return em.find(AppInfoDO.class, id); } public AppInfoDO getByAppName(String appName){ List<AppInfoDO> list = em.createQuery("select obj from AppInfoDO obj where obj.appName='" + appName + "'", AppInfoDO.class) .getResultList(); if(list.isEmpty()) return null; else return list.get(0); } public void update(AppInfoDO appInfoDO){ em.merge(appInfoDO); } @Transactional public String add(AppInfoDO appInfoDO){ appInfoDO.setId(UUIDGenerator.getUUID()); em.persist(appInfoDO); return appInfoDO.getId(); } public void delete(String id){ AppInfoDO oldAppInfoDO = getById(id); if(oldAppInfoDO != null) em.remove(oldAppInfoDO); } public void deleteByName(String appName){ em.remove(getByAppName(appName)); } }
注:Hibernate框架默认的查询语句是HQL。如果需要使用原生SQL,可以使用entityManager.createNativeQuery(..)。
对Hibernate的介绍,可参考:https://www.baeldung.com/hibernate-entitymanager
4 引用外部依赖识别图片的车型
如果在开发的过程中需要引用第三方发布的Jar包,可以在part-svc的modeler/common/app下均有一个pom.xml文件,开发人员可以根据自己的需要修改dependency标记添加需要的后端引用。
例如:如果希望同时在模型服务或者应用服务中使用百度人工智能的SDK,可以在common/porn.xml下添加如下的依赖:
<dependency> <groupId>com.baidu.aip</groupId> <artifactId>java-sdk</artifactId> <version>4.12.0</version> </dependency>
关于百度人工智能SDK的具体使用方法可以参阅官网
添加完成后,只需在dwf-app或者dwf-modeler文件中输入mvn install即可完成第三方库的更新,如下图所示
图-后端添加第三方引用的pom.xml文件以及添加外部引用实例
添加完成之后,即可编码引用,本案例中我们将示意如何识别一个设备的图片中出现的车型,并更新到设备的备注属性上。
package edu.thss.platform.controller; import com.baidu.aip.imageclassify.AipImageClassify; import java.util.HashMap; import org.json.JSONObject; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import io.swagger.annotations.Api; @Api(tags = { "part01插件" }) @RestController @RequestMapping("dwf/v1/part01/common-ext/") public class HelloWorldCommonController { public static final String APP_ID = "你的 App ID"; public static final String API_KEY = "你的 Api Key"; public static final String SECRET_KEY = "你的 Secret Key"; @GetMapping(path = "helloworld-service") public ResponseMsg<String> helloWorldService() { return new ResponseMsg<>("Hello World!"); } @GetMapping(path = "indentify-car") public ResponseMsg<String> identifyCar() { // 初始化一个AipImageClassifyClient AipImageClassify client = new AipImageClassify(APP_ID, API_KEY, SECRET_KEY); HashMap<String, String> options = new HashMap<String, String>(); options.put("top_num", "3"); options.put("baike_num", "5"); // 调用接口识别本地图片 String image = "D:\20100911090.jpg"; JSONObject res = client.carDetect(image, options); return new ResponseMsg<>(res.toString(2)); } }
启动调试之后,可以按到part01插件中新出现了一个identifyCar的调用:
图-新增车辆识别服务示意图
将此调用执行以后,会识别本地文件夹中D:\20100911090.jpg的图片,图片内容如下所示:
识别的结果如下,可以看到通过调用第三方车辆识别的接口将图片自动识别为丰田凯美瑞。
5 小结
本章介绍了如何利用DWF SDK扩展后台的restful api的方法,并且介绍了一些DWF后端调用的方法。