作者:于佳鑫
因为这个地方比较复杂而且涉及到的地方比较多,所以单独独立成一篇文章来介绍,下面先看一下这部分的流程图。
启动模型计算这一部分在后台主要做了两件事:1. 调用模型对用户上传的数据进行计算 2. 调用自动发服务模块将用户上传的 gdb 文件发布成服务。
此部分涉及到的表:
MapServerLog(存放发布服务时的日志)
注:只列出部分重要的字段。
字段名 | 描述 | 备注 |
---|---|---|
reviewTaskId | 审查任务 id | |
sourceFileId | 发布服务的数据源文件 id(ResourceFile 表) | 这个就是矢量数据下的那个 gdb 文件的 id |
publishStatus | 发布状态 | 0 -> 以发布,未检测,此时前端会提示“服务正在发布中,请等待!” |
1 -> 已检测,发布成功 | ||
2 -> 已检测,发布失败,此时前端会提示“是否重新发服务” | ||
serviceName | 服务名称 | 前端最终通过这个拼接出地址调用发布出来的服务 |
代码逻辑:
MapPublishServiceImpl.java
/**
* 发布地图服务
*/
@Async
public void publishMapService(Long reviewTaskId) {
// 如果之前已经发布过服务,则清楚之前发布的痕迹
this.cleanUpMapServiceData(reviewTaskId);
// 发布服务,此时会记录日志,publishStatus 的值为 0
this.publish(reviewTaskId);
// 检查服务发布状态(死循环),如果成功将 publishStatus 的值改为 1,失败将 publishStatus 的值改为 2
this.checkPublishStatus(reviewTaskId);
}
涉及到的表:
ModelCalcDatasourceLog(存放模型计算的数据源信息)
字段名 | 描述 | 备注 |
---|---|---|
reviewTaskId | 审查任务 id | 一个审查任务的成果包中可能有多个数据源 |
sysCode | 数据源 code | 调用模型上传数据源,模型返回的唯一标识 |
datasourceType | 数据源类型 | 标识着数据源在成果包的哪个位置 |
ModelCalcLog(模型计算任务日志)
字段名 | 描述 | 备注 |
---|---|---|
reviewTaskId | 审查任务 id | |
taskInstanceId | 任务实例 code | 模型计算任务的唯一标识 |
reviewPointId | 审查要点 id | |
calcStatus | 模型计算状态 | 0 -> 计算中 |
1 -> 计算成功 | ||
2 -> 计算失败 | ||
3 -> 未计算,出现的原因:在数据库中配置了这个模型参与计算,但是却没有给他配置计算任务(ModelCalcTask)![]() |
ModelCalculationResult(存放模型计算结果,纵表)
字段名 | 描述 | 备注 |
---|---|---|
reviewTaskId | 审查任务 id | |
reviewPointId | 审查要点 id | |
key | 属性名 | |
value | 属性值 | |
position | 代表是哪一行的数据 |
ModelConflictServer(模型计算的差异服务)
字段名 | 描述 | 备注 |
---|---|---|
reviewTaskId | 审查任务 id | |
reviewPointId | 审查要点 id | |
serverName | 服务名 | |
layerName | 图层名 |
类名的含义:
类名 | 含义 |
---|---|
DownloadDatasourceFile | 负责寻找到数据源并且下载 |
AcquireTaskInstanceFunctional | 负责封装不同模型的请求参数 |
DisposeCalcResultFunctional | 负责解析不同模型返回的参数 |
ModelCalcTask | 其实它应该叫“模型计算算法”,其中封装了模型系统中每种算法的请求-解析流程。 |
代码逻辑:
--------------- MmsCalcServiceImpl2
--------------- StartTaskFaced
--------------- StartTaskTemplateMethod(这个是模板方法,主要调用的是 ModelCalcTask 中的方法)
--------------- StartTaskFaced
有的空间性模型需要计算差异图斑,之前也是在 ModelCalculationResult 表中存储的,但是图斑数据量太大了,导致查询比较缓慢,所以新增了一个合并发服务的模型,将这些差异图斑发布成服务由前端调用。所以合并发服务需要在所有空间模型计算完成之后再计算。
前端获取差异图层的接口为:/v2/reviewPoint/conflictServer/{reviewTaskId}/{reviewPointId}
注:看不懂的话先看常见问题 2
一个模型计算规则由 AcquireTaskInstanceFunctional 和 DisposeCalcResultFunctional 组成,其中解析方式(DisposeCalcResultFunctional)相同的模型计算结果返回给前端时封装的格式也相同,例如:
这个计算规则配置的解析是 6,所以他对应的返回给前端的接口也就是 types6:
ReviewPoint 表
字段名 | 注释 | 作用 | 备注 |
---|---|---|---|
id | 节点 id | 系统自动生成 | |
reviewPointName | 审查要点名 | 与模型名一一对应 | |
doubleScreen | 是否双屏 | 控制前端页面双屏展示 | |
parentId | 父节点 id | 维持树形结构 | |
leafNode | 是否叶子节点 | 审查要点树叶子节点 | |
display | 是否显示 | 控制是否在前端显示该审查要点 | |
fromModelCalculation | 是否来自模型计算 | ||
reviewPointType | 审查要点类型编码 | 唯一标识 | 禁止非开发人员修改 |
position | 排序位置 | 在同一层级中的位置 | 可控制前端审查要点树显示顺序 |
subTitle | 副标题 | 前端会显示副标题 | |
modelVersionCode | 模型版本 code | 与模型匹配唯一标志 | 严格检查和模型系统中是否一致 |
belongAreaLevel | 适用的地区层级 | 当前审查要点适用于哪一级别 | 0:国家级,1:省级,2:市级,3:县级,4:乡镇级 |
belongPlanType | 适用的规划类型 | 当前审查要点适用于哪一规划类型 | 100000:总体规划,200000:专项规划,300000:详细规划 |
participateCalc | 控制是否参与计算 | 控制是否调用模型进行计算 | |
hasService | 是否具有差异图斑服务 | 控制是否参与合并发布服务 | |
needPublicService | 是否需要发布服务 | 空间性要点才需要发服务 |
新增审查要点的 sql:
insert into ARS_REVIEW_POINT(id, display, doublescreen, frommodelcalculation, leafnode, participatecalc,
modelversioncode, parentid, belongarealevel, belongplantype, position, reviewpointname,
reviewpointtype, subtitle, hasservice, algorithmname, needpublicservice)
VALUES (<随便写个id,不要重复就好>, 'Y', 'N', '<需要模型计算的审查要点为Y,不来自为 N>', '<叶子节点为Y,非叶子节点为N', 'N', '<模型code>', <父目录的id,没有父目录则为-1>,
<规划类型(100000:总体规划,200000:专项规划,300000:详细规划)>, <层级(0:国家级,1:省级,2:市级,3:县级,4:乡镇级)>, <同级最大的position+1>, '<审查要点的名字>',
<随便编一个不要重复就行>, '', 'N', '<算法名称>', '<需要发服务填Y,不需要为N>')
之后再去要点配置模块进行配置就好了。
对接新模型之前一定要确定这个新模型的算法是否已经开发过了,可以问实施这个模型和之前系统上的有没有相同的,确定没有再进行开发。
接下来,我们走一下开发新模型的流程:
从上面的介绍,大家应该知道对接模型的核心是 AcquireTaskInstanceFunctional(封装请求参数) 和 DisposeCalcResultFunctional(解析请求结果) 类,我们先看下 AcquireTaskInstanceFunctional 接口:
public interface AcquireTaskInstanceFunctional {
/**
* 获取任务实例code
*
* @param reviewTaskId 审查任务id
* @param reviewPointType 审查要点类型代码
* @return 模型内边的任务实例code
*/
String acquireTaskInstances(Long reviewTaskId, Integer reviewPointType);
/**
* 获取当前需要的数据源type
*
* @return 数据源types
*/
List<Integer> getDatasourceTypes();
}
里面有两个方法,我们先看第二个方法,这个方法是获取这个模型计算时需要的数据源类型,所以首先我们需要知道:我们要对接的新模型需要哪些数据源,这个信息可以通过实施来知道。
我们再看第一个方法,这个方法的作用是封装参数,发送请求获取模型任务实例 code,所以第二步,我们需要知道需要请求的参数是什么样的,这个信息可以通过模型的开发来知道,模型开发可能会给你这样格式的请求参数:
[
{
"modelVersionCode": "<模型code>",
"realtimeAttributes": [
{
"name": "accessLayer",
"value": "{\"name\":\"\",\"source\":\"<数据源的code>\"}"
},
{
"name": "checkedLayer",
"value": "{\"name\":\"\",\"source\":\"<数据源的code>\"}"
}
]
}
]
注:先看下配置文件中配置的这几个地址
![]()
写代码前可以先通过这几个接口先检查一下这个新模型是否可以正常的运行并返回结果。
swagger 地址分别为:
http://52.82.98.186:7321/swagger/index.html
http://52.82.98.186:7322/swagger/index.html
有了请求的参数我们就可以编写 AcquireTaskInstanceFunctional 类的实现,通过接口返回的结果我们也能编写 DisposeCalcResultFunctional 了。
之后我们需要新增一个 ModelCalcTask 类,使用 Spring 的依赖注入将刚刚写好的 AcquireTaskInstanceFunctional 和 DisposeCalcResultFunctional 注入进去,像这样:
AbstractModelCalcTask 类还有一个抽象方法 getTypes(),这个方法会返回这个类能够计算的审查要点类型,所以还需要在 ReviewPointTypeProperties 类中添加一个 getTypes10() 方法。
ReviewPointTypeProperties 类对应的是配置文件的这个地方:
最后,我们需要在下面这个地方配置上新写的计算规则:
META-INF/services/com.dist.ars.manager.remote.mms2.modelCalcTask.AbstractModelCalcTask
文件中。在我们配置的审查要点中有两项是辅助性模型里面的,计算这两项的目的就是为了对规划成果中 mdb 和 gdb 文件进行预览。
对应的 Command 为 TableAchievementCommand,这个 command 主要的作用就是根据 reviewTaskId 和 MDB 表格解析对应的 reviewPointId 获取计算结果返回前端。
所以 gdb 文件预览分为两个部分,一个是获取自动发服务发出来的地图服务,前端请求流程如下:
第二个是 GDB 图层解析模型计算出来的几个图层信息,对应的 Command 为 VectorDataCommand。
注意:因为自动发出来的服务也有图层信息,如果我们返回给前端图层信息数组不为空,那么将会取交集,如果返回的数组为空,则会直接使用发出来服务的图层信息。