Browse Source

系统图服务端代码

zhaoyk 3 years ago
parent
commit
8030553f09
58 changed files with 4952 additions and 0 deletions
  1. 32 0
      adm-business/adm-diagram/pom.xml
  2. 35 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/client/DicClient.java
  3. 77 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/client/RwdClient.java
  4. 65 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/controller/CommonController.java
  5. 47 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/controller/DemoDiagramController.java
  6. 40 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/controller/LegendController.java
  7. 77 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/controller/TemplateController.java
  8. 34 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/ContentParser.java
  9. 64 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/DataStrategy.java
  10. 232 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/DiagramDataLoader.java
  11. 55 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/DiagramView.java
  12. 69 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/AbstractLine.java
  13. 42 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/ConnectPoint.java
  14. 17 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/Const.java
  15. 20 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/DataPanelNode.java
  16. 252 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/Diagram.java
  17. 53 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/DiagramNode.java
  18. 118 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/EquipmentNode.java
  19. 44 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/Label.java
  20. 165 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/Line.java
  21. 143 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/AbstractComponent.java
  22. 200 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/Container.java
  23. 43 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/IComponent.java
  24. 26 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/IContainer.java
  25. 85 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/Layout.java
  26. 68 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/PositionData.java
  27. 27 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/XY.java
  28. 149 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/legend/Anchor.java
  29. 158 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/legend/Legend.java
  30. 75 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/style/BaseStyle.java
  31. 11 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/style/EquipNodeStyle.java
  32. 64 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/style/LineStyle.java
  33. 88 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/template/ContainerRefPoint.java
  34. 240 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/template/DiagramTemplate.java
  35. 139 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/template/MainPipe.java
  36. 45 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/template/MainPipePoint.java
  37. 57 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/util/GsonUtil.java
  38. 53 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/util/MyObjectMapper.java
  39. 374 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/demo/DemoDataStrategy.java
  40. 58 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/DiagramEntity.java
  41. 36 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/DiagramType.java
  42. 32 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/LegendEntity.java
  43. 91 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/ModelAdapter.java
  44. 41 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/TemplateEntity.java
  45. 21 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/TypeObject.java
  46. 26 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/TypeRelation.java
  47. 27 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/frame/BdtpRequest.java
  48. 8 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/frame/DiagramAppConfig.java
  49. 64 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/frame/EditRequest.java
  50. 33 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/frame/ExceptionAspect.java
  51. 171 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/manage/DemoDiagramManager.java
  52. 70 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/manage/Folder.java
  53. 101 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/manage/LegendManager.java
  54. 329 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/manage/TemplateManager.java
  55. 144 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/service/BdtpDataService.java
  56. 20 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/util/CallException.java
  57. 31 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/util/Pagination.java
  58. 66 0
      adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/util/ServiceUtil.java

+ 32 - 0
adm-business/adm-diagram/pom.xml

@@ -12,6 +12,20 @@
     <groupId>com.persagy</groupId>
     <artifactId>adm-diagram</artifactId>
 
+    <repositories>
+        <repository>
+            <id>Persagy</id>
+            <name>persagy</name>
+            <url>http://47.93.132.139:8081/nexus/content/groups/public/</url>
+            <snapshots>
+                <enabled>true</enabled>
+            </snapshots>
+            <releases>
+                <enabled>true</enabled>
+            </releases>
+        </repository>
+    </repositories>
+
     <dependencies>
         <!-- 配置中心(包含注册中心) -->
         <dependency>
@@ -35,5 +49,23 @@
             <groupId>com.persagy</groupId>
             <artifactId>dmp-file-starter</artifactId>
         </dependency>
+
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.locationtech.jts</groupId>
+            <artifactId>jts-core</artifactId>
+            <version>1.16.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+
     </dependencies>
 </project>

+ 35 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/client/DicClient.java

@@ -0,0 +1,35 @@
+package com.persagy.adm.diagram.client;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.dmp.basic.config.DigitalFeignConfig;
+import com.persagy.dmp.basic.model.QueryCriteria;
+import com.persagy.dmp.common.model.response.CommonResult;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.List;
+
+@FeignClient(name = "dmp-dic", configuration = DigitalFeignConfig.class,
+		url = "${persagy.bdtp.dic.service:}")
+public interface DicClient {
+
+	/**
+	 * 查询类型定义
+	 * @param groupCode 集团编码
+	 * @param projectId 项目ID
+	 * @param appId 应用ID
+	 * @param userId 用户ID
+	 * @param criteria 查询条件
+	 * @return 类型定义
+	 */
+	@PostMapping("/dic/dt/data/query")
+	CommonResult<List<ObjectNode>> dataQuery(
+			@RequestParam("groupCode") String groupCode,
+			@RequestParam("projectId") String projectId,
+			@RequestParam("appId") String appId,
+			@RequestParam("userId") String userId,
+			@RequestBody QueryCriteria criteria);
+
+}

+ 77 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/client/RwdClient.java

@@ -0,0 +1,77 @@
+package com.persagy.adm.diagram.client;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.dmp.basic.config.DigitalFeignConfig;
+import com.persagy.dmp.basic.model.QueryCriteria;
+import com.persagy.dmp.common.model.response.CommonResult;
+import com.persagy.dmp.define.entity.GraphDefine;
+import com.persagy.dmp.define.entity.RelationDefine;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.List;
+
+@FeignClient(name = "dmp-rwd", configuration = DigitalFeignConfig.class,
+		url = "${persagy.bdtp.rwd.service:}")
+public interface RwdClient {
+
+	/**
+	 * 查询类型定义
+	 * @param groupCode 集团编码
+	 * @param projectId 项目ID
+	 * @param appId 应用ID
+	 * @param userId 用户ID
+	 * @param criteria 查询条件
+	 * @return 类型定义
+	 */
+	@PostMapping("/rwd/def/class")
+	CommonResult<List<ObjectNode>> queryObjectType(
+			@RequestParam("groupCode") String groupCode,
+			@RequestParam("projectId") String projectId,
+			@RequestParam("appId") String appId,
+			@RequestParam("userId") String userId,
+			@RequestBody QueryCriteria criteria);
+
+	@PostMapping("/rwd/def/funcid")
+	CommonResult<List<ObjectNode>> queryFunc(
+			@RequestParam("groupCode") String groupCode,
+			@RequestParam("projectId") String projectId,
+			@RequestParam("appId") String appId,
+			@RequestParam("userId") String userId,
+			@RequestBody QueryCriteria criteria);
+
+	/**
+	 * 查询边类型
+	 * @param groupCode: 集团编码
+	 * @param projectId: 项目ID
+	 * @param appId: 应用ID
+	 * @param userId: 用户ID
+	 * @param criteria: 查询条件
+	 * @return 边类型数组
+	 */
+	@PostMapping("/rwd/def/rel/query")
+	CommonResult<List<RelationDefine>> queryRelDef(@RequestParam("groupCode") String groupCode,
+											 @RequestParam("projectId") String projectId,
+											 @RequestParam("appId") String appId,
+											 @RequestParam("userId") String userId,
+											 @RequestBody QueryCriteria criteria);
+
+	@PostMapping("/rwd/def/graph/query")
+	CommonResult<List<GraphDefine>> queryGraphDef(@RequestParam("groupCode") String groupCode,
+														 @RequestParam("projectId") String projectId,
+														 @RequestParam("appId") String appId,
+														 @RequestParam("userId") String userId,
+														 @RequestBody QueryCriteria criteria);
+
+	/**
+	 * 查询关系
+	 */
+	@PostMapping({"/rwd/instance/relation/query"})
+	CommonResult<List<ObjectNode>> queryRelation(@RequestParam("groupCode") String groupCode,
+										 @RequestParam("projectId") String projectId,
+										 @RequestParam("appId") String appId,
+										 @RequestParam("userId") String userId,
+										 @RequestBody QueryCriteria criteria);
+}

+ 65 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/controller/CommonController.java

@@ -0,0 +1,65 @@
+package com.persagy.adm.diagram.controller;
+
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.adm.diagram.core.DataStrategy;
+import com.persagy.adm.diagram.entity.DiagramType;
+import com.persagy.adm.diagram.frame.BdtpRequest;
+import com.persagy.adm.diagram.service.BdtpDataService;
+import com.persagy.dmp.common.model.response.CommonResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.CrossOrigin;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@CrossOrigin
+public class CommonController {
+
+	@Autowired
+	private BdtpDataService bdtpDataService;
+
+	@Autowired
+	private DataStrategy dataStrategy;
+
+	@RequestMapping("equipTypes")
+	public CommonResult<List<ObjectNode>> equipTypes(){
+		return CommonResult.success(bdtpDataService.getEquipTypeList(new BdtpRequest(), null));
+	}
+
+	@RequestMapping("relTypes")
+	public CommonResult<List<Map<String, Object>>> relTypes(){
+		return CommonResult.success(bdtpDataService.getRelTypeTree(new BdtpRequest()));
+	}
+
+	@RequestMapping("majSys")
+	public CommonResult<List<ObjectNode>> majSys(){
+		BdtpRequest req = new BdtpRequest();
+		List<ObjectNode> majList = bdtpDataService.getMajorList(req);
+		List<ObjectNode> sysList = bdtpDataService.getSystemList(req);
+
+		for(ObjectNode maj : majList){
+			ArrayNode childrenSys = maj.putArray("children");
+			String majorCode = maj.get("code").asText();
+			maj.put("name", maj.get("label").asText());
+			for(Iterator<ObjectNode> sysIter = sysList.iterator(); sysIter.hasNext();){
+				ObjectNode sys = sysIter.next();
+				if(sys.get("majorCode") != null && sys.get("majorCode").asText().equals(majorCode)){
+					childrenSys.add(sys);
+					sysIter.remove();
+				}
+			}
+		}
+		return CommonResult.success(majList);
+	}
+
+	@RequestMapping("diagramTypes")
+	public CommonResult<List<DiagramType>> diagramTypes(){
+		return CommonResult.success(dataStrategy.getDiagramTypes());
+	}
+
+}

+ 47 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/controller/DemoDiagramController.java

@@ -0,0 +1,47 @@
+package com.persagy.adm.diagram.controller;
+
+import com.persagy.adm.diagram.frame.EditRequest;
+import com.persagy.adm.diagram.manage.DemoDiagramManager;
+import com.persagy.adm.diagram.manage.Folder;
+import com.persagy.adm.diagram.core.model.Diagram;
+import com.persagy.dmp.common.model.response.CommonResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@CrossOrigin
+@RequestMapping("diagram")
+public class DemoDiagramController {
+
+	@Autowired
+	private DemoDiagramManager diagramManager;
+
+	@RequestMapping("getDiagrams")
+	public CommonResult<List<Folder>> getDiagrams(@RequestParam String projectId, @RequestParam(required = false) String buildingId){
+		return CommonResult.success(diagramManager.getDiagrams(projectId, buildingId));
+	}
+
+	@RequestMapping("newDiagram")
+	public CommonResult<Diagram> newDiagram(@RequestBody Map<String, String> params, @RequestParam String projectId, @RequestParam(required = false) String buildingId, @RequestParam(required = false) String floorId){
+		return CommonResult.success(diagramManager.createDiagram(params.get("name"), params.get("type"), projectId, buildingId, floorId));
+	}
+
+	@RequestMapping("delDiagram")
+	public CommonResult<Boolean> delDiagram(@RequestBody Map<String, String> params){
+		return CommonResult.success(diagramManager.deleteDiagram(params.get("id")));
+	}
+
+	@RequestMapping("setTemplate")
+	public CommonResult<Diagram> setTemplate(@RequestBody EditRequest req){
+		return CommonResult.success(diagramManager.setTemplate(req.getDiagramId(), req.getTemplateId()));
+	}
+
+	@RequestMapping("loadData")
+	public CommonResult<Diagram> loadData(@RequestBody EditRequest req) {
+		return CommonResult.success(diagramManager.loadData(req.getDiagramId(), true));
+	}
+
+}

+ 40 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/controller/LegendController.java

@@ -0,0 +1,40 @@
+package com.persagy.adm.diagram.controller;
+
+import com.persagy.adm.diagram.frame.EditRequest;
+import com.persagy.adm.diagram.core.model.legend.Legend;
+import com.persagy.adm.diagram.manage.LegendManager;
+import com.persagy.dmp.common.model.response.CommonResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@CrossOrigin
+@RequestMapping("legend")
+public class LegendController {
+
+	@Autowired
+	private LegendManager legendManager;
+
+	@RequestMapping("getLegends")
+	public CommonResult<List<Legend>> getLegends(@RequestParam String sys){
+		return CommonResult.success(legendManager.getLegends(sys));
+	}
+
+	@RequestMapping("saveLegend")
+	public CommonResult<Legend> saveLegend(@RequestBody EditRequest req){
+		return CommonResult.success(legendManager.saveLegend(req.getLegend(), req.getSys()));
+	}
+
+	@RequestMapping("delLegend")
+	public CommonResult<Boolean> delLegend(@RequestBody EditRequest req){
+		return CommonResult.success(legendManager.deleteLegend(req.getCurrentCompId(), req.getSys()));
+	}
+
+//	@RequestMapping("fixRel")
+//	public CommonResult<Boolean> fixRel(){
+//		return CommonResult.success(legendManager.fixRel());
+//	}
+
+}

+ 77 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/controller/TemplateController.java

@@ -0,0 +1,77 @@
+package com.persagy.adm.diagram.controller;
+
+import com.persagy.adm.diagram.frame.EditRequest;
+import com.persagy.adm.diagram.manage.Folder;
+import com.persagy.adm.diagram.manage.TemplateManager;
+import com.persagy.adm.diagram.core.model.template.DiagramTemplate;
+import com.persagy.dmp.common.model.response.CommonResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@CrossOrigin
+@RequestMapping("template")
+public class TemplateController {
+
+    @Autowired
+    private TemplateManager templateManager;
+
+    @RequestMapping("getTemplates")
+    public CommonResult<List<Folder>> getTemplates(){
+        return CommonResult.success(templateManager.getTemplates());
+    }
+
+    @RequestMapping("getTemplatesByType")
+    public CommonResult<List<DiagramTemplate>> getTemplatesByType(@RequestParam String diagramType){
+        return CommonResult.success(templateManager.getTemplates(diagramType));
+    }
+
+    @RequestMapping("newTemplate")
+    public CommonResult<DiagramTemplate> newTemplate(@RequestBody Map<String, String> params){
+        return CommonResult.success(templateManager.createTemplate(params.get("name"), params.get("type")));
+    }
+
+    @RequestMapping("delTemplate")
+    public CommonResult<Boolean> delTemplate(@RequestBody Map<String, String> params){
+        return CommonResult.success(templateManager.deleteTemplate(params.get("id")));
+    }
+
+    @RequestMapping("addCon")
+    public CommonResult<DiagramTemplate> addCon(@RequestBody EditRequest params){
+        return CommonResult.success(templateManager.addCon(params.getNewCompId(), params.getCurrentCompId(), params.getTemplateId()));
+    }
+
+    @RequestMapping("delCon")
+    public CommonResult<DiagramTemplate> delCon(@RequestBody EditRequest params){
+        return CommonResult.success(templateManager.delCon(params.getCurrentCompId(), params.getTemplateId()));
+    }
+
+    @RequestMapping("delLine")
+    public CommonResult<DiagramTemplate> delLine(@RequestBody EditRequest params){
+        return CommonResult.success(templateManager.delMainPipe(params.getCurrentCompId(), params.getTemplateId()));
+    }
+
+    @RequestMapping("moveCon")
+    public CommonResult<DiagramTemplate> moveCon(@RequestBody EditRequest params){
+        return CommonResult.success(templateManager.moveCon(params.getCurrentCompId(), params.getTemplateId(), params.getMoveOffset()));
+    }
+
+    @RequestMapping("addMainPipe")
+    public CommonResult<DiagramTemplate> addMainPipe(@RequestBody EditRequest params){
+        return CommonResult.success(templateManager.addMainPipe(params.getLines(), params.getTemplateId()));
+    }
+
+    @RequestMapping("modifyCon")
+    public CommonResult<DiagramTemplate> modifyCon(@RequestBody EditRequest params){
+        return CommonResult.success(templateManager.modifyCon(params.getTemplatePropsData(), params.getCurrentCompId(), params.getTemplateId()));
+    }
+
+    @RequestMapping("modifyMainPipe")
+    public CommonResult<DiagramTemplate> modifyMainPipe(@RequestBody EditRequest params){
+        return CommonResult.success(templateManager.modifyMainPipe(params.getTemplatePropsData(), params.getCurrentCompId(), params.getTemplateId()));
+    }
+
+}

+ 34 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/ContentParser.java

@@ -0,0 +1,34 @@
+package com.persagy.adm.diagram.core;
+
+import com.persagy.adm.diagram.core.util.GsonUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * json对象解析工具
+ * @author zhaoyk
+ */
+@Service
+public class ContentParser {
+
+	/**
+	 * 暂时使用gson做模型的存储和解析
+	 */
+	@Autowired
+	private GsonUtil gsonUtil;
+
+	/**
+	 * 解析实体的content字段,创建模型对象
+	 */
+	public <T> T parseContent(String jsonContent, Class<T> targetType) {
+		return gsonUtil.gson().fromJson(jsonContent, targetType);
+	}
+
+	/**
+	 * 把模型对象序列化为json
+	 */
+	public String toJson(Object model) {
+		return gsonUtil.gson().toJson(model);
+	}
+
+}

+ 64 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/DataStrategy.java

@@ -0,0 +1,64 @@
+package com.persagy.adm.diagram.core;
+
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.adm.diagram.core.model.template.DiagramTemplate;
+import com.persagy.adm.diagram.entity.DiagramType;
+import com.persagy.adm.diagram.core.model.Diagram;
+import com.persagy.adm.diagram.core.model.legend.Legend;
+
+import java.util.List;
+
+/**
+ * IO策略接口
+ * @author zhaoyk
+ */
+public interface DataStrategy {
+
+	List<DiagramType> getDiagramTypes();
+
+	//模板
+	List<DiagramTemplate> getTemplates();
+
+	List<DiagramTemplate> getTemplates(String diagramType);
+
+	DiagramTemplate saveTemplate(DiagramTemplate template);
+
+	boolean deleteTemplate(String templateId);
+
+	DiagramTemplate getTemplate(String templateId);
+
+
+	//图例
+	List<Legend> getLegends(String systemCode);
+
+	Legend saveLegend(Legend legend, String systemCode);
+
+	boolean deleteLegend(String legendId, String systemCode);
+
+	Legend getLegend(String legendId, String systemCode);
+
+	List<Legend> getLegendsForEquipment(String equipmentType);
+
+
+	//系统图
+	List<Diagram> getDiagrams(String projectId, String buildingId, String floorId); //根据具体情况,buildingId和floorId可以为空
+
+	List<Diagram> getDiagrams(String diagramType, String projectId, String buildingId, String floorId); //按系统图类型查询
+
+	Diagram saveDiagram(Diagram diagram);
+
+	boolean deleteDiagram(String diagramId);
+
+	Diagram getDiagram(String diagramId);
+
+
+	//系统图数据
+	List<ObjectNode> loadObjectsByType(List<String> equipmentTypes, String projectId, String buildingId, String floorId);
+
+	List<ObjectNode> loadRelationsByType(List<String[]> relationTypes, List<String> objectIds, String projectId);
+
+	List<ObjectNode> loadObjectsById(List<String> objectIds, String projectId);
+
+	List<ObjectNode> loadRelationsById(List<String> relationIds, String projectId);
+
+}

+ 232 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/DiagramDataLoader.java

@@ -0,0 +1,232 @@
+package com.persagy.adm.diagram.core;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.adm.diagram.core.model.EquipmentNode;
+import com.persagy.adm.diagram.core.model.base.Container;
+import com.persagy.adm.diagram.core.model.Diagram;
+import com.persagy.adm.diagram.core.model.DiagramNode;
+import com.persagy.adm.diagram.core.model.Label;
+import com.persagy.adm.diagram.core.model.legend.Legend;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * 系统图数据加载
+ * @author zhaoyk
+ */
+public class DiagramDataLoader {
+
+	private Diagram diagram;
+
+	private DataStrategy dataStrategy;
+
+	private List<Container> containers;
+
+	private List<ObjectNode> objs;
+
+	private List<ObjectNode> rels;
+
+	private List<ObjectNode> optionalObjs;
+
+	private List<ObjectNode> optionalRels;
+
+	public DiagramDataLoader(Diagram diagram, DataStrategy dataStrategy) {
+		this.diagram = diagram;
+		this.dataStrategy = dataStrategy;
+	}
+
+	public void load(){
+		List<String> objIds = diagram.getObjIds();
+		if(objIds.size() > 0)
+			objs = dataStrategy.loadObjectsById(objIds, diagram.getProjectId());
+		else
+			objs = new ArrayList<>();
+		buildNodes();
+	}
+
+	private void buildNodes() {
+		HashMap<String, ObjectNode> objMap = new HashMap<>();
+		objs.forEach(obj -> objMap.put(obj.get("id").asText(), obj));
+
+		for (DiagramNode node : diagram.getNodes()) {
+			ObjectNode obj = null;
+
+			if(node instanceof EquipmentNode) { //设备节点
+				EquipmentNode en = (EquipmentNode) node;
+				obj = objMap.get(en.getObjId());
+
+				if(en.getLegendId() != null)
+					en.setLegend(dataStrategy.getLegend(en.getLegendId(), en.getObjSystemCode()));
+			}
+
+			if(obj != null)
+				node.setDataObject(obj);
+		}
+	}
+
+	public void autoLoad() {
+		//查询备选数据
+		List<String> equipTypes = new ArrayList<>();
+		if (diagram.getTemplate() != null) {
+			containers = diagram.getTemplate().getContainers();
+			for(Container con : containers) {
+				loadEquipTypes(con, equipTypes);
+			}
+		} else
+			containers = null;
+
+		if(equipTypes.size() > 0)
+			optionalObjs = dataStrategy.loadObjectsByType(equipTypes, diagram.getProjectId(), diagram.getBuildingId(), diagram.getFloorId());
+		else
+			optionalObjs = new ArrayList<>();
+
+		//去掉已经使用的数据项
+		HashMap<String, DiagramNode> nodeMap = new HashMap<>();
+		diagram.getNodes().forEach(node -> {
+			if(node instanceof EquipmentNode) {
+				nodeMap.put(((EquipmentNode) node).getObjId(), node);
+			}
+		});
+		if(nodeMap.size() > 0) {
+			Iterator<ObjectNode> iter = optionalObjs.iterator();
+			while (iter.hasNext()) {
+				ObjectNode obj = iter.next();
+				if(nodeMap.containsKey(obj.get("id").asText()))
+					iter.remove();
+			}
+		}
+
+		addEquipNodes();
+
+		if(optionalObjs.size() > 0) {
+			List<String> objIds = new ArrayList<>(optionalObjs.size());
+			optionalObjs.forEach(obj -> objIds.add(obj.get("id").asText()));
+			optionalRels = dataStrategy.loadRelationsByType(null, objIds, diagram.getProjectId());
+		} else
+			optionalRels = new ArrayList<>();
+
+		addLines();
+	}
+
+	private void loadEquipTypes(Container con, List<String> types){
+		if (con.getEquipmentTypes() != null) {
+			for (String type : con.getEquipmentTypes()) {
+				if (!types.contains(type))
+					types.add(type);
+			}
+		}
+	}
+
+	private void addEquipNodes(){
+//		for(Container con : containers) {
+//			for(ObjectNode obj : optionalObjs){
+//				if (match(obj, con)) {
+//					EquipmentNode node = new EquipmentNode();
+//					node.setId(IdUtil.simpleUUID());
+//					node.setObjId(obj.get("id").asText());
+//					node.setObjClassCode(obj.get("classCode").asText());
+//					node.setDataObject(obj);
+//
+//					Label label = new Label();
+//					String content = null;
+//					if(obj.get("localName") != null)
+//						content = obj.get("localName").asText();
+//					if(StrUtil.isBlank(content))
+//						content = obj.get("name").asText();
+//					label.setContent(content);
+//					node.setLabel(label);
+//
+//					diagram.getNodes().add(node);
+//
+//					con.addComp(node);
+//					node.setContainerId(con.getId());
+//					node.setLayoutIndex(con.getChildren().size() - 1);
+//
+//					Legend legend = findLegend(node, obj);
+//					if(legend != null) {
+//						node.setLegendId(legend.getId());
+//						node.setLegend(legend);
+//					}
+//				}
+//			}
+//		}
+
+		//test code
+		for(ObjectNode obj : optionalObjs){
+			EquipmentNode node = new EquipmentNode();
+			node.setId(IdUtil.simpleUUID());
+			node.setObjId(obj.get("id").asText());
+			node.setObjClassCode(obj.get("classCode").asText());
+			node.setDataObject(obj);
+
+			Label label = new Label();
+			String content = null;
+			if(obj.get("localName") != null)
+				content = obj.get("localName").asText();
+			if(StrUtil.isBlank(content))
+				content = obj.get("name").asText();
+			label.setContent(content);
+			node.setLabel(label);
+
+			Container con = findContainer(node, obj);
+			if(con != null) {
+				diagram.getNodes().add(node);
+
+				con.addComp(node);
+				node.setContainerId(con.getId());
+				node.setLayoutIndex(con.getChildren().size() - 1);
+
+				Legend legend = findLegend(node, obj);
+				if(legend != null) {
+					node.setLegendId(legend.getId());
+					node.setLegend(legend);
+				}
+			}
+		}
+	}
+
+	//test code
+	private Container findContainer(EquipmentNode node, ObjectNode obj){
+		if(containers != null) {
+			for(Container con : containers) {
+				if(con.getEquipmentTypes() != null && con.getEquipmentTypes().contains(node.getObjClassCode())) {
+					if(con.getChildren() == null || con.getChildren().size() == 0)
+						return con;
+				}
+			}
+		}
+		return null;
+	}
+
+	private boolean match(ObjectNode obj, Container con) {
+		String classCode = obj.get("classCode").asText();
+		if(con.getEquipmentTypes() != null && con.getEquipmentTypes().contains(classCode)) {
+			//TODO filter
+			return true;
+		}
+		return false;
+	}
+
+	private Legend findLegend(EquipmentNode node, ObjectNode obj){
+		List<Legend> legends = dataStrategy.getLegendsForEquipment(node.getObjClassCode());
+		if(CollUtil.isNotEmpty(legends)) {
+			for(Legend legend : legends) {
+				if(diagram.getType().equals(legend.getDiagramType()))
+					return legend; //优先返回适配系统图类型的图例
+			}
+			return legends.get(0); //缺省取第一个
+		}
+		return null;
+	}
+
+	private void addLines(){
+		//System.out.println(optionalRels);
+	}
+
+}

+ 55 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/DiagramView.java

@@ -0,0 +1,55 @@
+package com.persagy.adm.diagram.core;
+
+import com.persagy.adm.diagram.core.model.Diagram;
+import com.persagy.adm.diagram.core.model.base.XY;
+import com.persagy.adm.diagram.core.model.template.MainPipe;
+import com.persagy.adm.diagram.core.model.DiagramNode;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 系统图展现层结构
+ * @author zhaoyk
+ */
+@Data
+public class DiagramView {
+
+	private String id;
+
+	private String name;
+
+	private String type;
+
+	private String system;
+
+	private String templateId;
+
+	private List<DiagramNode> nodes = new ArrayList<>();
+
+	private List lines = new ArrayList();
+
+	private List<MainPipe> mainPipes = new ArrayList<>();
+
+	private String projectId;
+
+	private String buildingId;
+
+	private XY size;
+
+	public DiagramView(Diagram diagram) {
+		this.id = diagram.getId();
+		this.name = diagram.getName();
+		this.type = diagram.getType();
+		this.system = diagram.getSystem();
+		this.templateId = diagram.getTemplateId();
+		this.nodes = diagram.getNodes();
+		this.lines = diagram.getLines();
+		this.mainPipes = diagram.getTemplate().getMainPipes();
+		this.projectId = diagram.getProjectId();
+		this.buildingId = diagram.getBuildingId();
+		this.size = diagram.getTemplate().getFrame().getSize();
+	}
+
+}

+ 69 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/AbstractLine.java

@@ -0,0 +1,69 @@
+package com.persagy.adm.diagram.core.model;
+
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.style.LineStyle;
+
+/**
+ * 线的基类
+ * @author zhaoyk
+ */
+abstract public class AbstractLine {
+
+	@Expose
+	protected String id;
+
+	@Expose
+	protected String name;
+
+	@Expose
+	protected LineStyle lineStyle;
+
+	@Expose
+	protected Label label;
+
+	/**
+	 * 绑定的数据对象,可能为设备或关系
+	 */
+	private Object dataObject;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public LineStyle getLineStyle() {
+		return lineStyle;
+	}
+
+	public void setLineStyle(LineStyle lineStyle) {
+		this.lineStyle = lineStyle;
+	}
+
+	public Label getLabel() {
+		return label;
+	}
+
+	public void setLabel(Label label) {
+		this.label = label;
+	}
+
+	public Object getDataObject() {
+		return dataObject;
+	}
+
+	public void setDataObject(Object dataObject) {
+		this.dataObject = dataObject;
+	}
+
+}

+ 42 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/ConnectPoint.java

@@ -0,0 +1,42 @@
+package com.persagy.adm.diagram.core.model;
+
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.base.XY;
+
+/**
+ * 线和图例的连接点
+ * @author zhaoyk
+ */
+public class ConnectPoint {
+
+	/**
+	 * 连接对象的类型,equipmentNode, dataPanelNode, mainPipe
+	 */
+	@Expose
+	private String hostType;
+
+	/**
+	 * 连接对象的id
+	 */
+	@Expose
+	private String hostId;
+
+	/**
+	 * 连接对象为equipmentNode时,连接的锚点编码
+	 */
+	@Expose
+	private String anchorCode;
+
+	/**
+	 * 连接对象为mainPipe时,干管上的定位点
+	 * TODO 定位点模型
+	 */
+	@Expose
+	private Object mainPipePoint;
+
+	/**
+	 * 连接点绝对坐标
+	 */
+	private XY location;
+
+}

+ 17 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/Const.java

@@ -0,0 +1,17 @@
+package com.persagy.adm.diagram.core.model;
+
+/**
+ * 通用常量
+ * @author zhaoyk
+ */
+public interface Const {
+
+	String TOP = "top";
+
+	String BOTTOM = "bottom";
+
+	String LEFT = "left";
+
+	String RIGHT = "right";
+
+}

+ 20 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/DataPanelNode.java

@@ -0,0 +1,20 @@
+package com.persagy.adm.diagram.core.model;
+
+/**
+ * 数据面板节点
+ * @author zhaoyk
+ */
+public class DataPanelNode extends DiagramNode {
+
+	public static final String TYPE = "dataPanelNode";
+
+	public DataPanelNode() {
+		this.compType = TYPE;
+	}
+
+	@Override
+	public void layout() {
+
+	}
+
+}

+ 252 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/Diagram.java

@@ -0,0 +1,252 @@
+package com.persagy.adm.diagram.core.model;
+
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.base.Container;
+import com.persagy.adm.diagram.core.model.base.XY;
+import com.persagy.adm.diagram.core.model.template.DiagramTemplate;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * 系统图
+ * @author zhaoyk
+ */
+public class Diagram {
+
+	@Expose
+	private String id;
+
+	/**
+	 * 系统图名称
+	 */
+	@Expose
+	private String name;
+
+	/**
+	 * 描述信息
+	 */
+	@Expose
+	private String remark;
+
+	/**
+	 * 系统图类型编码
+	 */
+	@Expose
+	private String type;
+
+	/**
+	 * 引用的模板id
+	 */
+	@Expose
+	private String templateId;
+
+	/**
+	 * 节点列表
+	 */
+	@Expose
+	private List<DiagramNode> nodes = new ArrayList<>();
+
+	/**
+	 * 连线列表
+	 */
+	@Expose
+	private List lines = new ArrayList();
+
+	/**
+	 * 所属的系统
+	 */
+	@Expose
+	private String system;
+
+	/**
+	 * 项目id
+	 */
+	@Expose
+	private String projectId;
+
+	/**
+	 * 建筑id
+	 */
+	@Expose
+	private String buildingId;
+
+	/**
+	 * 楼层id
+	 */
+	@Expose
+	private String floorId;
+
+	/**
+	 * 集团编码
+	 */
+	@Expose
+	private String groupCode;
+
+	//运行时
+	private DiagramTemplate template;
+
+
+	public void init(){
+		if (template != null)
+			template.init();
+
+		nodes.sort(new Comparator<DiagramNode>() {
+			@Override
+			public int compare(DiagramNode o1, DiagramNode o2) {
+				return o1.getLayoutIndex() - o2.getLayoutIndex();
+			}
+		});
+		for (DiagramNode node : nodes) {
+			Container con = template.getContainerById(node.getContainerId());
+			if (con != null)
+				con.addComp(node);
+		}
+	}
+
+	public void layout(XY location){
+		this.layout(location, false);
+	}
+
+	public void layout(XY location, boolean absoluteLocation){
+		if(template != null) {
+			//TODO
+			template.layout(location);
+
+			if(absoluteLocation) {
+				for (DiagramNode node : nodes) {
+					node.setLocation(node.locationToRoot());
+					if(node instanceof EquipmentNode) {
+						EquipmentNode en = (EquipmentNode) node;
+						if(en.getAnchorLocations() != null) {
+							for(XY point : en.getAnchorLocations().values()){
+								point.x += en.getLocation().x;
+								point.y += en.getLocation().y;
+							}
+						}
+						if(en.getLabel() != null) {
+							XY point = en.getLabel().getLocation();
+							point.x += en.getLocation().x;
+							point.y += en.getLocation().y;
+						}
+					}
+				}
+			}
+		}
+	}
+
+	public List<String> getObjIds(){
+		List<String> ids = new ArrayList<>();
+		for(DiagramNode node : nodes) {
+			if(node instanceof EquipmentNode)
+				ids.add(((EquipmentNode) node).getObjId());
+		}
+		return ids;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getRemark() {
+		return remark;
+	}
+
+	public void setRemark(String remark) {
+		this.remark = remark;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public String getTemplateId() {
+		return templateId;
+	}
+
+	public void setTemplateId(String templateId) {
+		this.templateId = templateId;
+	}
+
+	public List<DiagramNode> getNodes() {
+		return nodes;
+	}
+
+	public void setNodes(List<DiagramNode> nodes) {
+		this.nodes = nodes;
+	}
+
+	public List getLines() {
+		return lines;
+	}
+
+	public void setLines(List lines) {
+		this.lines = lines;
+	}
+
+	public String getSystem() {
+		return system;
+	}
+
+	public void setSystem(String system) {
+		this.system = system;
+	}
+
+	public String getProjectId() {
+		return projectId;
+	}
+
+	public void setProjectId(String projectId) {
+		this.projectId = projectId;
+	}
+
+	public String getBuildingId() {
+		return buildingId;
+	}
+
+	public void setBuildingId(String buildingId) {
+		this.buildingId = buildingId;
+	}
+
+	public String getFloorId() {
+		return floorId;
+	}
+
+	public void setFloorId(String floorId) {
+		this.floorId = floorId;
+	}
+
+	public String getGroupCode() {
+		return groupCode;
+	}
+
+	public void setGroupCode(String groupCode) {
+		this.groupCode = groupCode;
+	}
+
+	public DiagramTemplate getTemplate() {
+		return template;
+	}
+
+	public void setTemplate(DiagramTemplate template) {
+		this.template = template;
+	}
+
+}

+ 53 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/DiagramNode.java

@@ -0,0 +1,53 @@
+package com.persagy.adm.diagram.core.model;
+
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.base.AbstractComponent;
+
+/**
+ * 系统图上的节点(设备图例、显示标签等)
+ * @author zhaoyk
+ */
+abstract public class DiagramNode extends AbstractComponent {
+
+	/**
+	 * 所属容器id
+	 */
+	@Expose
+	private String containerId;
+
+	/**
+	 * 布局序号,为-1时表示坐标定位
+	 */
+	@Expose
+	private int layoutIndex;
+
+	/**
+	 * 绑定的数据对象
+	 */
+	private Object dataObject;
+
+	public String getContainerId() {
+		return containerId;
+	}
+
+	public void setContainerId(String containerId) {
+		this.containerId = containerId;
+	}
+
+	public int getLayoutIndex() {
+		return layoutIndex;
+	}
+
+	public void setLayoutIndex(int layoutIndex) {
+		this.layoutIndex = layoutIndex;
+	}
+
+	public Object getDataObject() {
+		return dataObject;
+	}
+
+	public void setDataObject(Object dataObject) {
+		this.dataObject = dataObject;
+	}
+
+}

+ 118 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/EquipmentNode.java

@@ -0,0 +1,118 @@
+package com.persagy.adm.diagram.core.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.base.XY;
+import com.persagy.adm.diagram.core.model.legend.Anchor;
+import com.persagy.adm.diagram.core.model.legend.Legend;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 设备节点
+ * @author zhaoyk
+ */
+public class EquipmentNode extends DiagramNode {
+
+	public static final String TYPE = "equipmentNode";
+
+	/**
+	 * 绑定的数据对象id
+	 */
+	@Expose
+	private String objId;
+
+	/**
+	 * 数据对象的classCode
+	 */
+	@Expose
+	private String objClassCode;
+
+	@Expose
+	private String legendId;
+
+	@Expose
+	private Label label;
+
+	@JsonIgnore
+	private Legend legend;
+
+	/**
+	 * 图例中锚点的位置坐标(相对于图例的位置)
+	 */
+	private Map<String, XY> anchorLocations;
+
+	public EquipmentNode() {
+		this.compType = TYPE;
+	}
+
+	@Override
+	public void layout() {
+		if(legend != null) {
+			setSize(new XY(legend.getWidth(), legend.getHeight()));
+			if(legend.getAnchors() != null) {
+				anchorLocations = new HashMap<>();
+				for(Anchor anchor : legend.getAnchors()) {
+					anchorLocations.put(anchor.getCode(), anchor.calcLocation(size));
+				}
+			}
+		} else
+			setSize(new XY(100, 100)); //TODO default size
+
+		if(label != null) {
+			label.setLocation(new XY(10, 10)); //TODO
+		}
+
+		//TODO anchors
+	}
+
+	public String getObjId() {
+		return objId;
+	}
+
+	public void setObjId(String objId) {
+		this.objId = objId;
+	}
+
+	public String getObjClassCode() {
+		return objClassCode;
+	}
+
+	public void setObjClassCode(String objClassCode) {
+		this.objClassCode = objClassCode;
+	}
+
+	public String getObjSystemCode(){
+		return objClassCode.substring(0, 4);
+	}
+
+	public Label getLabel() {
+		return label;
+	}
+
+	public void setLabel(Label label) {
+		this.label = label;
+	}
+
+	public String getLegendId() {
+		return legendId;
+	}
+
+	public void setLegendId(String legendId) {
+		this.legendId = legendId;
+	}
+
+	public void setLegend(Legend legend) {
+		this.legend = legend;
+	}
+
+	public Legend getLegend() {
+		return legend;
+	}
+
+	public Map<String, XY> getAnchorLocations() {
+		return anchorLocations;
+	}
+
+}

+ 44 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/Label.java

@@ -0,0 +1,44 @@
+package com.persagy.adm.diagram.core.model;
+
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.base.AbstractComponent;
+import com.persagy.adm.diagram.core.model.style.BaseStyle;
+
+/**
+ * 文字标签
+ * @author zhaoyk
+ */
+public class Label extends AbstractComponent {
+
+	@Expose
+	private String content;
+
+	@Expose
+	private BaseStyle style;
+
+	public Label() {
+		this.compType = "label";
+	}
+
+	@Override
+	public void layout() {
+
+	}
+
+	public String getContent() {
+		return content;
+	}
+
+	public void setContent(String content) {
+		this.content = content;
+	}
+
+	public BaseStyle getStyle() {
+		return style;
+	}
+
+	public void setStyle(BaseStyle style) {
+		this.style = style;
+	}
+
+}

+ 165 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/Line.java

@@ -0,0 +1,165 @@
+package com.persagy.adm.diagram.core.model;
+
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.base.XY;
+
+import java.util.List;
+
+/**
+ * 节点之间的连线
+ * @author zhaoyk
+ */
+public class Line extends AbstractLine {
+
+	@Expose
+	private int direction; //连线的方向(1:from->to, 0:无方向, -1:to->from)
+
+	/**
+	 * 起点
+	 */
+	@Expose
+	private ConnectPoint from;
+
+	/**
+	 * 终点
+	 */
+	@Expose
+	private ConnectPoint to;
+
+	/**
+	 * 途经点列表,依次记录相对前一点的偏移量
+	 */
+	@Expose
+	private List<XY> passingPoints;
+
+	/**
+	 * 绑定的关系数据id
+	 */
+	@Expose
+	private String relId;
+
+	/**
+	 * 关系数据的类型, graphCode/relCode
+	 */
+	@Expose
+	private String relType;
+
+	/**
+	 * 途径的干管,标注连线中与干管重合的线段部分
+	 */
+	@Expose
+	private List<PassingMainPipe> passingMainPipes;
+
+	/**
+	 * 连线的坐标点序列(包含from,to连接点坐标)
+	 */
+	private List<XY> locationPath;
+
+	public int getDirection() {
+		return direction;
+	}
+
+	public void setDirection(int direction) {
+		this.direction = direction;
+	}
+
+	public ConnectPoint getFrom() {
+		return from;
+	}
+
+	public void setFrom(ConnectPoint from) {
+		this.from = from;
+	}
+
+	public ConnectPoint getTo() {
+		return to;
+	}
+
+	public void setTo(ConnectPoint to) {
+		this.to = to;
+	}
+
+	public List<XY> getPassingPoints() {
+		return passingPoints;
+	}
+
+	public void setPassingPoints(List<XY> passingPoints) {
+		this.passingPoints = passingPoints;
+	}
+
+	public String getRelId() {
+		return relId;
+	}
+
+	public void setRelId(String relId) {
+		this.relId = relId;
+	}
+
+	public String getRelType() {
+		return relType;
+	}
+
+	public void setRelType(String relType) {
+		this.relType = relType;
+	}
+
+	public List<PassingMainPipe> getPassingMainPipes() {
+		return passingMainPipes;
+	}
+
+	public void setPassingMainPipes(List<PassingMainPipe> passingMainPipes) {
+		this.passingMainPipes = passingMainPipes;
+	}
+
+	public List<XY> getLocationPath() {
+		return locationPath;
+	}
+
+	public void setLocationPath(List<XY> locationPath) {
+		this.locationPath = locationPath;
+	}
+
+	public static class PassingMainPipe {
+
+		/**
+		 * 途径的干管id
+		 */
+		private String mainPipeId;
+
+		/**
+		 * 途径干管部分的起点序号(在连线点中的位置,from点序号为0)
+		 */
+		private int point1Index;
+
+		/**
+		 * 途径干管部分的终点序号(在连线点中的位置,from点序号为0)
+		 */
+		private int point2Index;
+
+		public String getMainPipeId() {
+			return mainPipeId;
+		}
+
+		public void setMainPipeId(String mainPipeId) {
+			this.mainPipeId = mainPipeId;
+		}
+
+		public int getPoint1Index() {
+			return point1Index;
+		}
+
+		public void setPoint1Index(int point1Index) {
+			this.point1Index = point1Index;
+		}
+
+		public int getPoint2Index() {
+			return point2Index;
+		}
+
+		public void setPoint2Index(int point2Index) {
+			this.point2Index = point2Index;
+		}
+
+	}
+
+}

+ 143 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/AbstractComponent.java

@@ -0,0 +1,143 @@
+package com.persagy.adm.diagram.core.model.base;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.google.gson.annotations.Expose;
+
+/**
+ * 布局和绘制组件的基类
+ * @author zhaoyk
+ */
+abstract public class AbstractComponent implements IComponent {
+
+	@Expose //使用gson保存文件
+	protected String id;
+
+	@Expose
+	protected String name;
+
+	@Expose
+	protected String compType;
+
+	@JsonIgnore //使用jackson输出到web端
+	protected IContainer parent;
+
+	@Expose
+	protected PositionData position;
+
+	protected XY location;
+
+	protected XY size;
+
+//	@Expose
+//	private HashMap<String, Object> datas;
+
+	protected AbstractComponent(){
+
+	}
+
+	@Override
+	public String getId() {
+		return id;
+	}
+
+	@Override
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	@Override
+	public String getName() {
+		return name;
+	}
+
+	@Override
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	@Override
+	public String getCompType() {
+		return compType;
+	}
+
+	@Override
+	public IContainer getParent() {
+		return parent;
+	}
+
+	@Override
+	public void setParent(IContainer parent) {
+		this.parent = parent;
+	}
+
+	@Override
+	public PositionData getPosition() {
+		return position;
+	}
+
+	@Override
+	public void setPosition(PositionData position) {
+		this.position = position;
+	}
+
+	@Override
+	public PositionData defaultPosition(){
+		return null;
+	}
+
+	@Override
+	public boolean isAbsolutePosition(){
+		return position != null && position.isAbsolute();
+	}
+
+	@Override
+	public XY getLocation() {
+		return location;
+	}
+
+	@Override
+	public void setLocation(XY location) {
+		this.location = location;
+	}
+
+	@Override
+	public XY getSize() {
+		return size;
+	}
+
+	public void setSize(XY size) {
+		this.size = size;
+	}
+
+//	@Override
+//	public Object getData(String name){
+//		return datas != null ? datas.get(name) : null;
+//	}
+//
+//	@Override
+//	public void setData(String name, Object value){
+//		if(datas == null)
+//			datas = new HashMap<>();
+//		datas.put(name, value);
+//	}
+
+	/**
+	 * 相对根容器的坐标位置
+	 */
+	public XY locationToRoot(){
+		XY p = new XY(0, 0);
+		IComponent c = this;
+		while (c != null){
+			XY pos = c.getLocation();
+			if(pos == null)
+				return null;
+
+			p.x += pos.x;
+			p.y += pos.y;
+
+			c = c.getParent();
+		}
+		return p;
+	}
+
+}

+ 200 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/Container.java

@@ -0,0 +1,200 @@
+package com.persagy.adm.diagram.core.model.base;
+
+import cn.hutool.core.collection.CollUtil;
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.Label;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 布局容器
+ * @author zhaoyk
+ */
+public class Container extends AbstractComponent implements IContainer {
+
+	public static final String TYPE = "container";
+
+	@Expose
+	private List<IComponent> children = new ArrayList<>();
+
+	@Expose
+	private Layout layout;
+
+	@Expose
+	private List<String> equipmentTypes;
+
+	@Expose
+	private String remark;
+
+	/**
+	 * 分组标签
+	 */
+	@Expose
+	private Label groupLabel;
+
+	public Container() {
+		this.compType = TYPE;
+	}
+
+	@Override
+	public Layout getLayout() {
+		return layout;
+	}
+
+	public void setLayout(Layout layout) {
+		this.layout = layout;
+	}
+
+	@Override
+	public void layout() {
+		XY p = new XY(layout.marginLeft, layout.marginTop);
+		boolean row = layout.getLayout() == Layout.ROW;
+
+		int childMax = 0;
+		int idx = 0;
+		for(IComponent comp : getArrangedElements()){
+			PositionData compPd = comp.getPosition();
+			if(compPd == null)
+				compPd = new PositionData();
+
+			if(row){
+				if(idx != 0)
+					p.y += layout.spaceV;
+			} else {
+				if(idx != 0)
+					p.x += layout.spaceH;
+			}
+
+			comp.setLocation(new XY(p.x + compPd.paddingLeft, p.y + compPd.paddingTop));
+			comp.layout();
+
+			XY childSize = comp.getSize();
+			if(row) {
+				p.y += childSize.y + compPd.totalPaddingHeight();
+				childMax = Math.max(childMax, childSize.x + compPd.totalPaddingWidth());
+			} else {
+				p.x += childSize.x + compPd.totalPaddingWidth();
+				childMax = Math.max(childMax, childSize.y + compPd.totalPaddingHeight());
+			}
+
+			idx++;
+		}
+
+		size = new XY(p);
+		if(row) {
+			size.y += layout.marginBottom;
+			size.x = layout.marginLeft + childMax + layout.marginRight;
+		} else {
+			size.x += layout.marginRight;
+			size.y = layout.marginTop + childMax + layout.marginBottom;
+		}
+
+		if(size.y < position.minH)
+			size.y = position.minH;
+		if(size.x < position.minW)
+			size.x = position.minW;
+
+		//自由定位的组件
+		for(IComponent comp : getScatteredElements()) {
+			comp.layout();
+
+			if(comp.getPosition().getAbsolutePoint() != null)
+				comp.setLocation(comp.getPosition().getAbsolutePoint());
+		}
+	}
+
+	@Override
+	public List<IComponent> getChildren() {
+		return children;
+	}
+
+	@Override
+	public void addComp(IComponent child){
+		children.add(child);
+		child.setParent(this);
+
+		if(child.getPosition() == null)
+			child.setPosition(child.defaultPosition());
+	}
+
+	@Override
+	public void removeComp(String childId) {
+		for(IComponent o : children) {
+			if(o.getId().equals(childId)) {
+				children.remove(o);
+				return;
+			}
+		}
+	}
+
+	public List<IComponent> getArrangedElements(){
+		return children.stream().filter(comp -> !comp.isAbsolutePosition()).collect(Collectors.toList());
+	}
+
+	public List<IComponent> getScatteredElements(){
+		return children.stream().filter(comp -> comp.isAbsolutePosition()).collect(Collectors.toList());
+	}
+
+	@Override
+	public List<String> getEquipmentTypes() {
+		return equipmentTypes;
+	}
+
+	public void setEquipmentTypes(List<String> equipmentTypes) {
+		this.equipmentTypes = equipmentTypes;
+	}
+
+	public boolean isEquipmentBox() {
+		return CollUtil.isNotEmpty(equipmentTypes);
+	}
+
+	public String getRemark() {
+		return remark;
+	}
+
+	public void setRemark(String remark) {
+		this.remark = remark;
+	}
+
+	public Label getGroupLabel() {
+		return groupLabel;
+	}
+
+	public void setGroupLabel(Label groupLabel) {
+		this.groupLabel = groupLabel;
+	}
+
+	public void initParent() {
+		List<IComponent> cs = new ArrayList<>(children);
+		children.clear();
+
+		for (IComponent comp : cs) {
+			addComp(comp);
+			if (comp instanceof Container)
+				((Container) comp).initParent();
+		}
+	}
+
+	public IComponent findComponent(String componentId){
+		for(IComponent comp : children) {
+			if(comp.getId().equals(componentId))
+				return comp;
+			if(comp instanceof Container) {
+				IComponent rtn = ((Container) comp).findComponent(componentId);
+				if(rtn != null)
+					return rtn;
+			}
+		}
+		return null;
+	}
+
+	@Override
+	public PositionData defaultPosition() {
+		PositionData data = new PositionData();
+		data.minH = data.minW = 200;
+		return data;
+	}
+
+}

+ 43 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/IComponent.java

@@ -0,0 +1,43 @@
+package com.persagy.adm.diagram.core.model.base;
+
+/**
+ * 布局和绘制组件接口
+ * @author zhaoyk
+ */
+public interface IComponent {
+
+    String getId();
+
+    void setId(String id);
+
+    String getName();
+
+    void setName(String name);
+
+    String getCompType();
+
+    IContainer getParent();
+
+    void setParent(IContainer parent);
+
+    void layout();
+
+    XY getLocation();
+
+    void setLocation(XY xy);
+
+    XY getSize();
+
+    PositionData getPosition();
+
+    void setPosition(PositionData position);
+
+    PositionData defaultPosition();
+
+    boolean isAbsolutePosition();
+
+//    Object getData(String key);
+//
+//    void setData(String key, Object value);
+
+}

+ 26 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/IContainer.java

@@ -0,0 +1,26 @@
+package com.persagy.adm.diagram.core.model.base;
+
+import java.util.List;
+
+/**
+ * 容器接口
+ * @author zhaoyk
+ */
+public interface IContainer extends IComponent {
+
+    Layout getLayout();
+
+    void setLayout(Layout layout);
+
+    List<IComponent> getChildren();
+
+    void addComp(IComponent child);
+
+    void removeComp(String childId);
+
+    /**
+     * 可以放置的设备类型
+     */
+    List<String> getEquipmentTypes();
+
+}

+ 85 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/Layout.java

@@ -0,0 +1,85 @@
+package com.persagy.adm.diagram.core.model.base;
+
+import com.google.gson.annotations.Expose;
+
+/**
+ * 容器的布局
+ * @author zhaoyk
+ */
+public class Layout {
+
+    public static final int ROW = 1;
+
+    public static final int COLUMN = 2;
+
+    public static final int TOP = 1;
+
+    public static final int RIGHT = 2;
+
+    public static final int BOTTOM = 3;
+
+    public static final int LEFT = 4;
+
+    public static final int CENTER = 5;
+
+    @Expose
+    public int layout;
+
+    public int align = LEFT;
+
+    public int valign = TOP;
+
+    @Expose
+    public int marginTop;
+
+    @Expose
+    public int marginRight;
+
+    @Expose
+    public int marginBottom;
+
+    @Expose
+    public int marginLeft;
+
+    @Expose
+    public int spaceH;
+
+    @Expose
+    public int spaceV;
+
+    public Layout(int layout){
+        this.layout = layout;
+    }
+
+    public void setLayout(int layout) {
+        this.layout = layout;
+    }
+
+    public int getLayout() {
+        return layout;
+    }
+
+    public void setMarginWidth(int marginWidth){
+        this.marginLeft = this.marginRight = marginWidth;
+    }
+
+    public void setMarginHeight(int marginHeight){
+        this.marginTop = this.marginBottom = marginHeight;
+    }
+
+    public static Layout defaultLayout(int layout) {
+        Layout l = new Layout(layout);
+        if(layout == ROW) {
+            l.align = LEFT;
+            l.valign = CENTER;
+        } else if(layout == COLUMN) {
+            l.valign = TOP;
+            l.align = CENTER;
+        }
+        l.marginTop = l.marginBottom = 20;
+        l.marginLeft = l.marginRight = 20;
+        l.spaceH = l.spaceV = 20;
+        return l;
+    }
+
+}

+ 68 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/PositionData.java

@@ -0,0 +1,68 @@
+package com.persagy.adm.diagram.core.model.base;
+
+import com.google.gson.annotations.Expose;
+
+/**
+ * 组件的定位
+ * @author zhaoyk
+ */
+public class PositionData {
+
+	@Expose
+	public int minW;
+
+	@Expose
+	public int minH;
+
+	@Expose
+	public int paddingTop;
+
+	@Expose
+	public int paddingRight;
+
+	@Expose
+	public int paddingBottom;
+
+	@Expose
+	public int paddingLeft;
+
+	/**
+	 * 是否为绝对坐标定位
+	 */
+	@Expose
+	private boolean absolute;
+
+	/**
+	 * 绝对坐标定位点
+	 */
+	@Expose
+	private XY absolutePoint;
+
+	public boolean isAbsolute() {
+		return absolute;
+	}
+
+	public void setAbsolute(boolean absolute) {
+		this.absolute = absolute;
+
+		if(!absolute)
+			absolutePoint = null;
+	}
+
+	public XY getAbsolutePoint() {
+		return absolutePoint;
+	}
+
+	public void setAbsolutePoint(XY absolutePoint) {
+		this.absolutePoint = absolutePoint;
+	}
+
+	public int totalPaddingWidth(){
+		return paddingLeft + paddingRight;
+	}
+
+	public int totalPaddingHeight(){
+		return paddingTop + paddingBottom;
+	}
+
+}

+ 27 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/base/XY.java

@@ -0,0 +1,27 @@
+package com.persagy.adm.diagram.core.model.base;
+
+import com.google.gson.annotations.Expose;
+
+/**
+ * x,y坐标对象,不使用Point名称,避免冲突
+ * @author zhaoyk
+ */
+public class XY {
+
+	@Expose
+	public int x;
+
+	@Expose
+	public int y;
+
+	public XY(int x, int y) {
+		this.x = x;
+		this.y = y;
+	}
+
+	public XY(XY xy){
+		this.x = xy.x;
+		this.y = xy.y;
+	}
+
+}

+ 149 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/legend/Anchor.java

@@ -0,0 +1,149 @@
+package com.persagy.adm.diagram.core.model.legend;
+
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.base.XY;
+
+import java.util.List;
+
+/**
+ * 连接锚点
+ * @author zhaoyk
+ */
+public class Anchor {
+
+	public static final String TYPE_NON = "non";
+
+	public static final String TYPE_IN = "in";
+
+	public static final String TYPE_OUT = "out";
+
+	@Expose
+	private String code; //锚点位置代码,TL|T1等,系统自动生成
+
+	@Expose
+	private String name; //锚点名称
+
+	@Expose
+	private String type; //进出
+
+	@Expose
+	private List<String> acceptRelations; //支持的关系类型(连线),格式: graphCode/relCode
+
+	@Expose
+	private List<String> toEquipmentTypes; //连线另一端的设备类型
+
+	@Expose
+	private boolean attachToOutline = true; //是否自动吸附到所在方向的边框上
+
+	@Expose
+	private boolean multiple; //是否支持动态多锚点,编辑时可指定数量,按默认规则(平分所在边线)进行定位
+
+	@Expose
+	private String shape; //TODO 锚点的绘制策略,待细化
+
+
+	private float xOffset; //布局处理后的水平偏移量
+
+	private float yOffset; //布局处理后的垂直偏移量
+
+
+	public Anchor() {
+
+	}
+
+	public Anchor(String code) {
+		this.code = code;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getType() {
+		return type;
+	}
+
+	public void setType(String type) {
+		this.type = type;
+	}
+
+	public List<String> getAcceptRelations() {
+		return acceptRelations;
+	}
+
+	public void setAcceptRelations(List<String> acceptRelations) {
+		this.acceptRelations = acceptRelations;
+	}
+
+	public List<String> getToEquipmentTypes() {
+		return toEquipmentTypes;
+	}
+
+	public void setToEquipmentTypes(List<String> toEquipmentTypes) {
+		this.toEquipmentTypes = toEquipmentTypes;
+	}
+
+	public boolean isAttachToOutline() {
+		return attachToOutline;
+	}
+
+	public void setAttachToOutline(boolean attachToOutline) {
+		this.attachToOutline = attachToOutline;
+	}
+
+	public boolean isMultiple() {
+		return multiple;
+	}
+
+	public void setMultiple(boolean multiple) {
+		this.multiple = multiple;
+	}
+
+	public String getShape() {
+		return shape;
+	}
+
+	public void setShape(String shape) {
+		this.shape = shape;
+	}
+
+	public XY calcLocation(XY legendSize) {
+		XY legendLocation = new XY(0, 0);
+		char c1 = code.charAt(0);
+		char c2 = code.charAt(1);
+		int idx = (int)c2 - 48;
+		int part = 6;
+		switch (c1){
+			case 'T':
+				if(c2 == 'L')
+					return new XY(legendLocation);
+				if(c2 == 'R')
+					return new XY(legendLocation.x + legendSize.x, legendLocation.y);
+				return new XY(legendLocation.x + legendSize.x / part * idx, legendLocation.y);
+			case 'B':
+				if(c2 == 'L')
+					return new XY(legendLocation.x, legendLocation.y + legendSize.y);
+				if(c2 == 'R')
+					return new XY(legendLocation.x + legendSize.x, legendLocation.y + legendSize.y);
+				return new XY(legendLocation.x + legendSize.x / part * idx, legendLocation.y + legendSize.y);
+			case 'L':
+				return new XY(legendLocation.x, legendLocation.y + legendSize.y / part * idx);
+			case 'R':
+				return new XY(legendLocation.x + legendSize.x, legendLocation.y + legendSize.y / part * idx);
+		}
+		return null;
+	}
+
+}

+ 158 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/legend/Legend.java

@@ -0,0 +1,158 @@
+package com.persagy.adm.diagram.core.model.legend;
+
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.base.XY;
+
+import java.util.List;
+
+/**
+ * 设备图例
+ * @author zhaoyk
+ */
+public class Legend {
+
+	@Expose
+	private String id;
+
+	@Expose
+	private String code;
+
+	@Expose
+	private String name;
+
+	@Expose
+	private String remark;
+
+	@Expose
+	private String system;
+
+	@Expose
+	private int width;
+
+	@Expose
+	private int height;
+
+	/**
+	 * 支持的设备类型
+	 */
+	@Expose
+	private List<String> equipmentTypes;
+
+	@Expose
+	private List<Anchor> anchors;
+
+	/**
+	 * 适用的系统图类型,优先逻辑,不做强约束
+	 */
+	@Expose
+	private String diagramType;
+
+	/**
+	 * 轮廓图形
+	 */
+	@Expose
+	private List<List<XY>> outline;
+
+	/**
+	 * 默认显示的信息点
+	 */
+	@Expose
+	private List<String> infos;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getRemark() {
+		return remark;
+	}
+
+	public void setRemark(String remark) {
+		this.remark = remark;
+	}
+
+	public String getSystem() {
+		return system;
+	}
+
+	public void setSystem(String system) {
+		this.system = system;
+	}
+
+	public int getWidth() {
+		return width;
+	}
+
+	public void setWidth(int width) {
+		this.width = width;
+	}
+
+	public int getHeight() {
+		return height;
+	}
+
+	public void setHeight(int height) {
+		this.height = height;
+	}
+
+	public List<String> getEquipmentTypes() {
+		return equipmentTypes;
+	}
+
+	public void setEquipmentTypes(List<String> equipmentTypes) {
+		this.equipmentTypes = equipmentTypes;
+	}
+
+	public List<Anchor> getAnchors() {
+		return anchors;
+	}
+
+	public void setAnchors(List<Anchor> anchors) {
+		this.anchors = anchors;
+	}
+
+	public String getDiagramType() {
+		return diagramType;
+	}
+
+	public void setDiagramType(String diagramType) {
+		this.diagramType = diagramType;
+	}
+
+	public List<List<XY>> getOutline() {
+		return outline;
+	}
+
+	public void setOutline(List<List<XY>> outline) {
+		this.outline = outline;
+	}
+
+	public List<String> getInfos() {
+		return infos;
+	}
+
+	public void setInfos(List<String> infos) {
+		this.infos = infos;
+	}
+
+}

+ 75 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/style/BaseStyle.java

@@ -0,0 +1,75 @@
+package com.persagy.adm.diagram.core.model.style;
+
+import com.google.gson.annotations.Expose;
+
+/**
+ * 样式基类
+ * @author zhaoyk
+ */
+public class BaseStyle {
+
+	@Expose
+	protected String color = "#000000";
+
+	@Expose
+	protected String backColor = "#FFFFFF";
+
+	/**
+	 * 字体大小
+	 */
+	@Expose
+	protected int fontSize = 10;
+
+	/**
+	 * 字体颜色
+	 */
+	@Expose
+	protected String fontColor = "#000000";
+
+	/**
+	 * 预定义好的样式类Id
+	 */
+	@Expose
+	protected String classId;
+
+	public String getColor() {
+		return color;
+	}
+
+	public void setColor(String color) {
+		this.color = color;
+	}
+
+	public String getBackColor() {
+		return backColor;
+	}
+
+	public void setBackColor(String backColor) {
+		this.backColor = backColor;
+	}
+
+	public int getFontSize() {
+		return fontSize;
+	}
+
+	public void setFontSize(int fontSize) {
+		this.fontSize = fontSize;
+	}
+
+	public String getFontColor() {
+		return fontColor;
+	}
+
+	public void setFontColor(String fontColor) {
+		this.fontColor = fontColor;
+	}
+
+	public String getClassId() {
+		return classId;
+	}
+
+	public void setClassId(String classId) {
+		this.classId = classId;
+	}
+
+}

+ 11 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/style/EquipNodeStyle.java

@@ -0,0 +1,11 @@
+package com.persagy.adm.diagram.core.model.style;
+
+/**
+ * 设备节点的样式
+ * @author zhaoyk
+ */
+public class EquipNodeStyle extends BaseStyle {
+
+
+
+}

+ 64 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/style/LineStyle.java

@@ -0,0 +1,64 @@
+package com.persagy.adm.diagram.core.model.style;
+
+import com.google.gson.annotations.Expose;
+
+/**
+ * 线的样式
+ * @author zhaoyk
+ */
+public class LineStyle extends BaseStyle {
+
+	public static final String STYLE_NORMAL = "normal";
+
+	public static final String STYLE_DASH = "dash";
+
+	public static final String STYLE_DOT = "dot";
+
+	public static final String STYLE_DASHDOT = "dashdot";
+
+	@Expose
+	private int lineWidth = 1;
+
+	@Expose
+	private String lineStyle = STYLE_NORMAL;
+
+	//线芯属性,有线芯时,线芯颜色使用color,背景色使用backColor
+	@Expose
+	private int coreWidth; //线芯的宽度(为0表示没有线芯)
+
+	@Expose
+	private String coreStyle = STYLE_DASH; //线芯的样式
+
+	public int getLineWidth() {
+		return lineWidth;
+	}
+
+	public void setLineWidth(int lineWidth) {
+		this.lineWidth = lineWidth;
+	}
+
+	public String getLineStyle() {
+		return lineStyle;
+	}
+
+	public void setLineStyle(String lineStyle) {
+		this.lineStyle = lineStyle;
+	}
+
+	public int getCoreWidth() {
+		return coreWidth;
+	}
+
+	public void setCoreWidth(int coreWidth) {
+		this.coreWidth = coreWidth;
+	}
+
+	public String getCoreStyle() {
+		return coreStyle;
+	}
+
+	public void setCoreStyle(String coreStyle) {
+		this.coreStyle = coreStyle;
+	}
+
+}

+ 88 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/template/ContainerRefPoint.java

@@ -0,0 +1,88 @@
+package com.persagy.adm.diagram.core.model.template;
+
+import com.persagy.adm.diagram.core.model.base.Container;
+import com.persagy.adm.diagram.core.model.base.XY;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 容器周边参考点
+ * @author zhaoyk
+ */
+public class ContainerRefPoint {
+
+	public static final String TL = "TL";
+
+	public static final String TR = "TR";
+
+	public static final String BR = "BR";
+
+	public static final String BL = "BL";
+
+	private String containerId;
+
+	private String name;
+
+	private XY location;
+
+	public ContainerRefPoint(String containerId, String name) {
+		this.containerId = containerId;
+		this.name = name;
+	}
+
+	public String getContainerId() {
+		return containerId;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public XY getLocation() {
+		return location;
+	}
+
+	public void setLocation(XY location) {
+		this.location = location;
+	}
+
+	public static List<ContainerRefPoint> getRefPoints(Container container, XY conLocation) {
+		ArrayList<ContainerRefPoint> points = new ArrayList<>(16);
+		XY size = container.getSize();
+
+		addPoint(container.getId(), TL, new XY(conLocation), points);
+		addPoint(container.getId(), TR, new XY(conLocation.x + size.x, conLocation.y), points);
+		addPoint(container.getId(), BR, new XY(conLocation.x + size.x, conLocation.y + size.y), points);
+		addPoint(container.getId(), BL, new XY(conLocation.x, conLocation.y + size.y), points);
+
+		int len = size.x / 4;
+		for (int i = 1; i <= 3; i++) {
+			addPoint(container.getId(), "T" + i, new XY(conLocation.x + len * i, conLocation.y), points);
+			addPoint(container.getId(), "B" + i, new XY(conLocation.x + len * i, conLocation.y + size.y), points);
+		}
+		len = size.y / 4;
+		for (int i = 1; i <= 3; i++) {
+			addPoint(container.getId(), "L" + i, new XY(conLocation.x, conLocation.y + len * i), points);
+			addPoint(container.getId(), "R" + i, new XY(conLocation.x + size.x, conLocation.y + len * i), points);
+		}
+
+		return points;
+	}
+
+	private static void addPoint(String containerId, String name, XY location, ArrayList<ContainerRefPoint> points){
+		ContainerRefPoint refPoint = new ContainerRefPoint(containerId, name);
+		refPoint.setLocation(location);
+		points.add(refPoint);
+	}
+
+	public static ContainerRefPoint getRefPoint(Container container, String name, XY conLocation) {
+		List<ContainerRefPoint> list = getRefPoints(container, conLocation);
+		for(ContainerRefPoint p : list) {
+			if(p.getName().equalsIgnoreCase(name))
+				return p;
+		}
+		return null;
+	}
+
+}

+ 240 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/template/DiagramTemplate.java

@@ -0,0 +1,240 @@
+package com.persagy.adm.diagram.core.model.template;
+
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.base.Container;
+import com.persagy.adm.diagram.core.model.base.IComponent;
+import com.persagy.adm.diagram.core.model.base.Layout;
+import com.persagy.adm.diagram.core.model.base.XY;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * 系统图模板
+ * @author zhaoyk
+ */
+public class DiagramTemplate {
+
+	public static final String FRAME_ID = "frame";
+
+	/**
+	 * 模板对应的系统图类型代码
+	 */
+	@Expose
+	private String diagramType;
+
+	/**
+	 * 模板id
+	 */
+	@Expose
+	private String id;
+
+	/**
+	 * 模板名称
+	 */
+	@Expose
+	private String name;
+
+	/**
+	 * 模板编码
+	 */
+	@Expose
+	private String code;
+
+	/**
+	 * 描述信息
+	 */
+	@Expose
+	private String remark;
+
+	/**
+	 * 模板容器框架
+	 */
+	@Expose
+	private Container frame;
+
+	/**
+	 * 自由定位容器列表
+	 */
+	@Expose
+	private List<Container> scatteredContainers;
+
+	/**
+	 * 预定义的干管/线
+	 */
+	@Expose
+	private List<MainPipe> mainPipes;
+
+	public String getDiagramType() {
+		return diagramType;
+	}
+
+	public void setDiagramType(String diagramType) {
+		this.diagramType = diagramType;
+	}
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getCode() {
+		return code;
+	}
+
+	public void setCode(String code) {
+		this.code = code;
+	}
+
+	public String getRemark() {
+		return remark;
+	}
+
+	public void setRemark(String remark) {
+		this.remark = remark;
+	}
+
+	public Container getFrame() {
+		return frame;
+	}
+
+	public void setFrame(Container frame) {
+		this.frame = frame;
+	}
+
+	public List<Container> getScatteredContainers() {
+		return scatteredContainers;
+	}
+
+	public void setScatteredContainers(List<Container> scatteredContainers) {
+		this.scatteredContainers = scatteredContainers;
+	}
+
+	public List<MainPipe> getMainPipes() {
+		return mainPipes;
+	}
+
+	public void setMainPipes(List<MainPipe> mainPipes) {
+		this.mainPipes = mainPipes;
+	}
+
+	public void init() {
+		frame.initParent();
+		initScattered(frame);
+	}
+
+	private void initScattered(Container con){
+		//框架中的自由定位容器移动到列表中
+		if(con.getChildren() != null) {
+			for (int i = 0; i < con.getChildren().size(); i++) {
+				IComponent comp = con.getChildren().get(i);
+				if (comp instanceof Container) {
+					Container subCon = (Container)comp;
+					if(subCon.isAbsolutePosition()){
+						if(scatteredContainers == null)
+							scatteredContainers = new ArrayList<>();
+						scatteredContainers.add(subCon);
+
+						con.getChildren().remove(i);
+						i--;
+					}
+					initScattered(subCon);
+				}
+			}
+		}
+
+		//列表里的非自由定位容器移动到框架跟容器下
+		if (scatteredContainers != null) {
+			Iterator<Container> iter = scatteredContainers.iterator();
+			while (iter.hasNext()){
+				Container c = iter.next();
+				if(!c.isAbsolutePosition()){
+					frame.addComp(c);
+					iter.remove();
+				}
+			}
+		}
+	}
+
+	public Container getContainerById(String containerId) {
+		if(frame.getId().equals(containerId))
+			return frame;
+
+		IComponent comp = frame.findComponent(containerId);
+		if(comp instanceof Container)
+			return (Container)comp;
+
+		if (scatteredContainers != null) {
+			for(Container con : scatteredContainers){
+				if(con.getId().equals(containerId))
+					return con;
+			}
+		}
+
+		return null;
+	}
+
+	public void layout(XY location) {
+		frame.setLocation(location);
+		frame.layout();
+
+		if (scatteredContainers != null) {
+			for(Container con : scatteredContainers){
+				con.layout();
+			}
+		}
+
+		if (mainPipes != null) {
+			for(MainPipe mainPipe : mainPipes) {
+				mainPipe.layout(this);
+			}
+		}
+	}
+
+	public List<Container> getContainers() {
+		List<Container> list = new ArrayList<>();
+		getContainers(frame, list);
+
+		if(scatteredContainers != null)
+			list.addAll(scatteredContainers);
+
+		return list;
+	}
+
+	private void getContainers(Container con, List<Container> list){
+		list.add(con);
+
+		if(con.getChildren() != null) {
+			for(IComponent comp : con.getChildren()) {
+				if (comp instanceof Container)
+					getContainers((Container) comp, list);
+			}
+		}
+	}
+
+
+	public static DiagramTemplate defaultTemplate() {
+		DiagramTemplate template = new DiagramTemplate();
+
+		Container frame = new Container();
+		frame.setId(FRAME_ID);
+		frame.setPosition(frame.defaultPosition());
+		frame.setLayout(Layout.defaultLayout(Layout.ROW));
+		template.setFrame(frame);
+
+		return template;
+	}
+
+}

+ 139 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/template/MainPipe.java

@@ -0,0 +1,139 @@
+package com.persagy.adm.diagram.core.model.template;
+
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.base.Container;
+import com.persagy.adm.diagram.core.model.AbstractLine;
+import com.persagy.adm.diagram.core.model.base.XY;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 干管/线
+ * @author zhaoyk
+ */
+public class MainPipe extends AbstractLine {
+
+	@Expose
+	private String remark;
+
+	@Expose
+	private String pipeType;
+
+	@Expose
+	private List<String> passbyRels; //要经过干管的关系类型,格式: graphCode/relCode
+
+	@Expose
+	private List<String> connectEquips; //可以直连干管的设备类型
+
+	@Expose
+	private List<String> equipmentTypes; //可以放置在干管上的设备类型;或是绑定的设备类型
+
+	@Expose
+	private boolean bindEquipment; //干管是否绑定具体设备
+
+	@Expose
+	private List<List<MainPipePoint>> path = new ArrayList<>();
+
+	/**
+	 * 连线的坐标点序列,多条线
+	 */
+	private List<List<XY>> locationPath;
+
+	public String getId() {
+		return id;
+	}
+
+	public void setId(String id) {
+		this.id = id;
+	}
+
+	public String getName() {
+		return name;
+	}
+
+	public void setName(String name) {
+		this.name = name;
+	}
+
+	public String getRemark() {
+		return remark;
+	}
+
+	public void setRemark(String remark) {
+		this.remark = remark;
+	}
+
+	public String getPipeType() {
+		return pipeType;
+	}
+
+	public void setPipeType(String pipeType) {
+		this.pipeType = pipeType;
+	}
+
+	public List<String> getPassbyRels() {
+		return passbyRels;
+	}
+
+	public void setPassbyRels(List<String> passbyRels) {
+		this.passbyRels = passbyRels;
+	}
+
+	public List<String> getConnectEquips() {
+		return connectEquips;
+	}
+
+	public void setConnectEquips(List<String> connectEquips) {
+		this.connectEquips = connectEquips;
+	}
+
+	public List<String> getEquipmentTypes() {
+		return equipmentTypes;
+	}
+
+	public void setEquipmentTypes(List<String> equipmentTypes) {
+		this.equipmentTypes = equipmentTypes;
+	}
+
+	public boolean isBindEquipment() {
+		return bindEquipment;
+	}
+
+	public void setBindEquipment(boolean bindEquipment) {
+		this.bindEquipment = bindEquipment;
+	}
+
+	public List<List<MainPipePoint>> getPath() {
+		return path;
+	}
+
+	public void setPath(List<List<MainPipePoint>> path) {
+		this.path = path;
+	}
+
+	public List<List<XY>> getLocationPath() {
+		return locationPath;
+	}
+
+	public void setLocationPath(List<List<XY>> locationPath) {
+		this.locationPath = locationPath;
+	}
+
+	public void layout(DiagramTemplate template) {
+		locationPath = new ArrayList<>(path.size());
+		for (List<MainPipePoint> line : path) {
+			ArrayList<XY> list = new ArrayList<>();
+			locationPath.add(list);
+
+			for(MainPipePoint p : line) {
+				Container container = template.getContainerById(p.getContainerId());
+				if(container != null && !container.isAbsolutePosition()) {
+					ContainerRefPoint rp = ContainerRefPoint.getRefPoint(container, p.getRefPointName(), container.locationToRoot());
+					list.add(new XY(rp.getLocation().x + p.getOffset().x , rp.getLocation().y + p.getOffset().y));
+				}
+			}
+		}
+	}
+
+}

+ 45 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/model/template/MainPipePoint.java

@@ -0,0 +1,45 @@
+package com.persagy.adm.diagram.core.model.template;
+
+import com.google.gson.annotations.Expose;
+import com.persagy.adm.diagram.core.model.base.XY;
+
+/**
+ * 干管/线路径点
+ * @author zhaoyk
+ */
+public class MainPipePoint {
+
+	@Expose
+	private String containerId;
+
+	@Expose
+	private String refPointName;
+
+	@Expose
+	private XY offset;
+
+	public String getContainerId() {
+		return containerId;
+	}
+
+	public void setContainerId(String containerId) {
+		this.containerId = containerId;
+	}
+
+	public String getRefPointName() {
+		return refPointName;
+	}
+
+	public void setRefPointName(String refPointName) {
+		this.refPointName = refPointName;
+	}
+
+	public XY getOffset() {
+		return offset;
+	}
+
+	public void setOffset(XY offset) {
+		this.offset = offset;
+	}
+
+}

+ 57 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/util/GsonUtil.java

@@ -0,0 +1,57 @@
+package com.persagy.adm.diagram.core.util;
+
+import com.google.gson.*;
+import com.persagy.adm.diagram.core.model.base.Container;
+import com.persagy.adm.diagram.core.model.base.IComponent;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Type;
+
+/**
+ * gson解析和输出工具
+ * @author zhaoyk
+ */
+@Component
+public class GsonUtil {
+
+	private Gson gson;
+
+	private Gson formattedGson;
+
+	public GsonUtil(){
+		GsonBuilder gsonBuilder = new GsonBuilder().excludeFieldsWithoutExposeAnnotation();
+		gsonBuilder.registerTypeAdapter(IComponent.class, new CompAdapter());
+		gson = gsonBuilder.create();
+
+		formattedGson = new GsonBuilder().setPrettyPrinting().create();
+	}
+
+	class CompAdapter implements JsonDeserializer<IComponent>, JsonSerializer<IComponent> {
+
+		@Override
+		public IComponent deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
+			Class<? extends IComponent> target = Container.class;
+			if(Container.TYPE.equals(jsonElement.getAsJsonObject().get("compType")))
+				target = Container.class;
+			else {
+				//TODO 其他类型
+			}
+			return gson.fromJson(jsonElement, target);
+		}
+
+		@Override
+		public JsonElement serialize(IComponent iComponent, Type type, JsonSerializationContext jsonSerializationContext) {
+			return gson.toJsonTree(iComponent, iComponent.getClass());
+		}
+
+	}
+
+	public Gson gson(){
+		return gson;
+	}
+
+	public Gson formattedGson(){
+		return formattedGson;
+	}
+
+}

+ 53 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/util/MyObjectMapper.java

@@ -0,0 +1,53 @@
+package com.persagy.adm.diagram.core.util;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.persagy.adm.diagram.core.model.template.DiagramTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * 自定义ObjectMapper,为jackson定义不同场景下的处理逻辑
+ * @author zhaoyk
+ */
+@Component
+public class MyObjectMapper {
+
+	private final ObjectMapper objectMapper;
+
+	private ObjectMapper mapper4Save;
+
+	@Autowired
+	public MyObjectMapper(ObjectMapper objectMapper) {
+		this.objectMapper = objectMapper;
+		configCommonMapper();
+
+		this.mapper4Save = buildMapper4Save();
+	}
+
+	private void configCommonMapper(){
+		//objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+		objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+		objectMapper.setVisibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE);
+		objectMapper.setVisibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE);
+		objectMapper.setVisibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE);
+		objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
+	}
+
+	private ObjectMapper buildMapper4Save(){
+		ObjectMapper mapper = new ObjectMapper();
+		mapper.addMixIn(DiagramTemplate.class, Template4Save.class);
+		//TODO
+		return mapper;
+	}
+
+
+	@JsonIgnoreProperties({})
+	public interface Template4Save {
+
+	}
+
+}

+ 374 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/demo/DemoDataStrategy.java

@@ -0,0 +1,374 @@
+package com.persagy.adm.diagram.demo;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.adm.diagram.entity.DiagramType;
+import com.persagy.adm.diagram.core.ContentParser;
+import com.persagy.adm.diagram.core.DataStrategy;
+import com.persagy.adm.diagram.core.model.Diagram;
+import com.persagy.adm.diagram.core.model.legend.Legend;
+import com.persagy.adm.diagram.core.model.template.DiagramTemplate;
+import com.persagy.adm.diagram.frame.BdtpRequest;
+import com.persagy.adm.diagram.service.BdtpDataService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 测试开发使用的io策略实现类
+ * @author zhaoyk
+ */
+@Service
+public class DemoDataStrategy implements DataStrategy {
+
+	@Autowired
+	private ContentParser parser;
+
+	@Override
+	public List<DiagramType> getDiagramTypes() {
+		List<DiagramType> types = new ArrayList<>();
+
+		readFiles((majCode, sysCode, typeCode, typeDir) -> {
+			DiagramType diagramType = new DiagramType();
+			diagramType.setMajor(majCode);
+			diagramType.setSystem(sysCode);
+			diagramType.setCode(typeCode);
+			diagramType.setName(typeDir.getName());
+
+			types.add(diagramType);
+			return null;
+		});
+
+		return types;
+	}
+
+	/* 模板部分 */
+	private static final String DIR_TEMPLATE = "store/template/";
+
+	@Override
+	public List<DiagramTemplate> getTemplates() {
+		List<DiagramTemplate> rtn = new ArrayList<>();
+
+		readFiles((majCode, sysCode, typeCode, typeDir) -> {
+			File[] files = typeDir.listFiles();
+			if(files != null) {
+				for (File f : files) {
+					try {
+						DiagramTemplate template = parser.parseContent(FileUtil.readUtf8String(f), DiagramTemplate.class);
+						rtn.add(template);
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+				}
+			}
+			return null;
+		});
+
+		return rtn;
+	}
+
+	private <T> T readFiles(Handler<T> handler){
+		File dir = new File(DIR_TEMPLATE);
+		File[] majList = dir.listFiles();
+		if(majList == null)
+			return null;
+
+		for(File fm : majList) {
+			String majCode = fm.getName().substring(0, 2);
+
+			File[] sysList = fm.listFiles();
+			if(sysList == null)
+				continue;
+
+			for(File fs : sysList) {
+				String sysCode = majCode + fs.getName().substring(0, 2);
+				File[] typeList = fs.listFiles();
+				if(typeList != null) {
+					for (File ft : typeList) {
+						String typeCode = sysCode + "_" + ft.getName();
+
+						T result = handler.handle(majCode, sysCode, typeCode, ft);
+						if(result != null)
+							return result;
+					}
+				}
+			}
+		}
+
+		return null;
+	}
+
+	interface Handler<T> {
+		T handle(String majCode, String sysCode, String typeCode, File typeDir);
+	}
+
+	@Override
+	public List<DiagramTemplate> getTemplates(String diagramType) {
+		List<DiagramTemplate> rtn = new ArrayList<>();
+
+		readFiles((majCode, sysCode, typeCode, typeDir) -> {
+			if(diagramType.equals(typeCode)) {
+				File[] files = typeDir.listFiles();
+				if(files != null) {
+					for (File f : files) {
+						try {
+							DiagramTemplate template = parser.parseContent(FileUtil.readUtf8String(f), DiagramTemplate.class);
+							rtn.add(template);
+						} catch (Exception e) {
+							e.printStackTrace();
+						}
+					}
+				}
+			}
+			return null;
+		});
+
+		return rtn;
+	}
+
+	@Override
+	public DiagramTemplate saveTemplate(DiagramTemplate template) {
+		return readFiles((majCode, sysCode, typeCode, typeDir) -> {
+			if(typeCode.equals(template.getDiagramType())) {
+				try {
+					String json = parser.toJson(template);
+					FileUtil.writeUtf8String(json, new File(typeDir, template.getName() + ".json"));
+					return parser.parseContent(json, DiagramTemplate.class);
+				} catch (Exception e) {
+					throw new RuntimeException(e);
+				}
+			}
+			return null;
+		});
+	}
+
+	@Override
+	public boolean deleteTemplate(String templateId) {
+		File del = readFiles((majCode, sysCode, typeCode, typeDir) -> {
+			File[] files = typeDir.listFiles();
+			if(files != null) {
+				for (File f : files) {
+					try {
+						DiagramTemplate template = parser.parseContent(FileUtil.readUtf8String(f), DiagramTemplate.class);
+						if(template.getId().equals(templateId)) {
+							f.delete();
+							return f;
+						}
+					} catch (Exception e) {
+						throw new RuntimeException(e);
+					}
+				}
+			}
+			return null;
+		});
+		return del != null;
+	}
+
+	@Override
+	public DiagramTemplate getTemplate(String templateId) {
+		return readFiles((majCode, sysCode, typeCode, typeDir) -> {
+			File[] files = typeDir.listFiles();
+			if(files != null) {
+				for (File f : files) {
+					try {
+						DiagramTemplate template = parser.parseContent(FileUtil.readUtf8String(f), DiagramTemplate.class);
+						if(template.getId().equals(templateId))
+							return template;
+					} catch (Exception e) {
+						throw new RuntimeException(e);
+					}
+				}
+			}
+			return null;
+		});
+
+	}
+
+
+	/* 图例部分 */
+	private static final String DIR_LEGEND = "store/legend/";
+
+	@Override
+	public List<Legend> getLegends(String systemCode) {
+		File file = new File(DIR_LEGEND, systemCode + ".json");
+		if(!file.exists())
+			FileUtil.writeUtf8String("[]", file);
+
+		Legend[] legends = parser.parseContent(FileUtil.readUtf8String(file), Legend[].class);
+		ArrayList<Legend> list = new ArrayList<>();
+		Collections.addAll(list, legends);
+		return list;
+	}
+
+	@Override
+	public Legend saveLegend(Legend legend, String systemCode) {
+		List<Legend> list = getLegends(systemCode);
+
+		if(StrUtil.isBlank(legend.getId())) {
+			legend.setId(IdUtil.simpleUUID());
+			list.add(legend);
+		} else {
+			for (int i = 0; i < list.size(); i++) {
+				Legend l = list.get(i);
+				if (l.getId().equals(legend.getId())) {
+					list.set(i, legend);
+					break;
+				}
+			}
+		}
+
+		File file = new File(DIR_LEGEND, systemCode + ".json");
+		FileUtil.writeUtf8String(parser.toJson(list), file);
+
+		return legend;
+	}
+
+	@Override
+	public boolean deleteLegend(String legendId, String systemCode) {
+		List<Legend> list = getLegends(systemCode);
+
+		boolean flag = false;
+		for (int i = 0; i < list.size(); i++) {
+			Legend l = list.get(i);
+			if (l.getId().equals(legendId)) {
+				list.remove(l);
+				flag = true;
+				break;
+			}
+		}
+
+		File file = new File(DIR_LEGEND, systemCode + ".json");
+		FileUtil.writeUtf8String(parser.toJson(list), file);
+
+		return flag;
+	}
+
+	@Override
+	public Legend getLegend(String legendId, String systemCode) {
+		List<Legend> list = getLegends(systemCode);
+		for(Legend legend : list) {
+			if(legend.getId().equals(legendId))
+				return legend;
+		}
+		return null;
+	}
+
+	@Override
+	public List<Legend> getLegendsForEquipment(String equipmentType) {
+		String systemCode = equipmentType.substring(0, 4);
+
+		List<Legend> rtn = new ArrayList<>();
+		List<Legend> list = getLegends(systemCode);
+		for (Legend legend : list) {
+			if (legend.getEquipmentTypes() != null && legend.getEquipmentTypes().contains(equipmentType))
+				rtn.add(legend);
+		}
+
+		return rtn;
+	}
+
+
+	/* 系统图部分 */
+	private static final String DIR_DIAGRAM = "store/diagram/";
+
+	@Override
+	public List<Diagram> getDiagrams(String projectId, String buildingId, String floorId) {
+		return getDiagrams(null, projectId, buildingId, floorId);
+	}
+
+	@Override
+	public List<Diagram> getDiagrams(String diagramType, String projectId, String buildingId, String floorId) {
+		ArrayList<Diagram> diagrams = new ArrayList<>();
+
+		File dir = new File(DIR_DIAGRAM);
+		File[] list = dir.listFiles();
+		if(list != null) {
+			for(File f : list) {
+				Diagram diagram = parser.parseContent(FileUtil.readUtf8String(f), Diagram.class);
+				if(projectId.equals(diagram.getProjectId())) {
+					if(StrUtil.isNotBlank(diagramType) && !diagramType.equals(diagram.getType()))
+						continue;
+					if(StrUtil.isNotBlank(buildingId) && !buildingId.equals(diagram.getBuildingId()))
+						continue;
+					if(StrUtil.isNotBlank(floorId) && !floorId.equals(diagram.getFloorId()))
+						continue;
+
+					diagrams.add(diagram);
+				}
+			}
+		}
+
+		return diagrams;
+	}
+
+	@Override
+	public Diagram saveDiagram(Diagram diagram) {
+		File file = new File(DIR_DIAGRAM, diagram.getId() + ".json");
+		FileUtil.writeUtf8String(parser.toJson(diagram), file);
+
+		return diagram;
+	}
+
+	@Override
+	public boolean deleteDiagram(String diagramId) {
+		File dir = new File(DIR_DIAGRAM);
+		File[] list = dir.listFiles();
+		if(list != null) {
+			for(File f : list) {
+				String fn = f.getName();
+				fn = fn.substring(0, fn.lastIndexOf('.'));
+				if(diagramId.equals(fn)) {
+					f.delete();
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	@Override
+	public Diagram getDiagram(String diagramId) {
+		File file = new File(DIR_DIAGRAM, diagramId + ".json");
+		if(file.exists())
+			return parser.parseContent(FileUtil.readUtf8String(file), Diagram.class);
+		return null;
+	}
+
+
+	//系统图数据加载
+	@Autowired
+	private BdtpDataService dataService;
+
+	@Override
+	public List<ObjectNode> loadObjectsByType(List<String> equipmentTypes, String projectId, String buildingId, String floorId) {
+		BdtpRequest req = new BdtpRequest();
+		req.setProjectId(projectId);
+		return dataService.queryObjectsByType(req, equipmentTypes, buildingId, floorId);
+	}
+
+	@Override
+	public List<ObjectNode> loadRelationsByType(List<String[]> relationTypes, List<String> objectIds, String projectId) {
+		BdtpRequest req = new BdtpRequest();
+		req.setProjectId(projectId);
+		return dataService.queryRelsByTypeAndObj(req, relationTypes, objectIds);
+	}
+
+	@Override
+	public List<ObjectNode> loadObjectsById(List<String> objectIds, String projectId) {
+		BdtpRequest req = new BdtpRequest();
+		req.setProjectId(projectId);
+		return dataService.queryObjectsById(req, objectIds);
+	}
+
+	@Override
+	public List<ObjectNode> loadRelationsById(List<String> relationIds, String projectId) {
+		return null;
+	}
+
+}

+ 58 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/DiagramEntity.java

@@ -0,0 +1,58 @@
+package com.persagy.adm.diagram.entity;
+
+import lombok.Data;
+
+/**
+ * 系统图实体
+ */
+@Data
+public class DiagramEntity {
+
+	private String id;
+
+	/**
+	 * 系统图名称
+	 */
+	private String name;
+
+	/**
+	 * 描述信息
+	 */
+	private String remark;
+
+	/**
+	 * 系统图类型编码
+	 */
+	private String type;
+
+	/**
+	 * 所属的系统
+	 */
+	private String system;
+
+	/**
+	 * 引用的模板id
+	 */
+	private String templateId;
+
+	/**
+	 * 项目id
+	 */
+	private String projectId;
+
+	/**
+	 * 建筑id
+	 */
+	private String buildingId;
+
+	/**
+	 * 集团编码
+	 */
+	private String groupCode;
+
+	/**
+	 * 系统图内容,json格式
+	 */
+	private String diagramContent;
+
+}

+ 36 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/DiagramType.java

@@ -0,0 +1,36 @@
+package com.persagy.adm.diagram.entity;
+
+import lombok.Data;
+
+/**
+ * 系统图类型实体
+ */
+@Data
+public class DiagramType {
+
+	/**
+	 * 所属专业
+	 */
+	private String major;
+
+	/**
+	 * 所属系统
+	 */
+	private String system;
+
+	/**
+	 * 系统图类型的编码
+	 */
+	private String code;
+
+	/**
+	 * 系统图类型的名称
+	 */
+	private String name;
+
+	/**
+	 * 系统图类型的描述
+	 */
+	private String remark;
+
+}

+ 32 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/LegendEntity.java

@@ -0,0 +1,32 @@
+package com.persagy.adm.diagram.entity;
+
+import lombok.Data;
+
+/**
+ * 图例实体
+ */
+@Data
+public class LegendEntity {
+
+	private String id;
+
+	private String name;
+
+	private String code;
+
+	/**
+	 * 描述信息
+	 */
+	private String remark;
+
+	/**
+	 * 所属系统代码
+	 */
+	private String system;
+
+	/**
+	 * 图例内容,json格式
+	 */
+	private String legendContent;
+
+}

+ 91 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/ModelAdapter.java

@@ -0,0 +1,91 @@
+package com.persagy.adm.diagram.entity;
+
+import com.persagy.adm.diagram.core.model.template.DiagramTemplate;
+import com.persagy.adm.diagram.core.ContentParser;
+import com.persagy.adm.diagram.core.model.Diagram;
+import com.persagy.adm.diagram.core.model.legend.Legend;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 运行时模型-实体 类型转换适配器
+ * @author zhaoyk
+ */
+@Service
+public class ModelAdapter {
+
+	@Autowired
+	private ContentParser parser;
+
+	public DiagramTemplate toTemplate(TemplateEntity templateEntity){
+		DiagramTemplate template = parser.parseContent(templateEntity.getTemplateContent(), DiagramTemplate.class);
+		template.setId(templateEntity.getId());
+		template.setName(templateEntity.getName());
+		template.setCode(templateEntity.getCode());
+		template.setRemark(templateEntity.getRemark());
+		template.setDiagramType(templateEntity.getDiagramType());
+		return template;
+	}
+
+	public TemplateEntity toTemplateEntity(DiagramTemplate template){
+		TemplateEntity entity = new TemplateEntity();
+		entity.setId(template.getId());
+		entity.setName(template.getName());
+		entity.setCode(template.getCode());
+		entity.setRemark(template.getRemark());
+		entity.setDiagramType(template.getDiagramType());
+		entity.setTemplateContent(parser.toJson(template));
+		return entity;
+	}
+
+	public Legend toLegend(LegendEntity legendEntity){
+		Legend legend = parser.parseContent(legendEntity.getLegendContent(), Legend.class);
+		legend.setId(legendEntity.getId());
+		legend.setName(legendEntity.getName());
+		legend.setCode(legendEntity.getCode());
+		legend.setRemark(legendEntity.getRemark());
+		legend.setSystem(legendEntity.getSystem());
+		return legend;
+	}
+
+	public LegendEntity toLegendEntity(Legend legend) {
+		LegendEntity entity = new LegendEntity();
+		entity.setId(legend.getId());
+		entity.setName(legend.getName());
+		entity.setCode(legend.getCode());
+		entity.setRemark(legend.getRemark());
+		entity.setSystem(legend.getSystem());
+		entity.setLegendContent(parser.toJson(legend));
+		return entity;
+	}
+
+	public Diagram toDiagram(DiagramEntity diagramEntity) {
+		Diagram diagram = parser.parseContent(diagramEntity.getDiagramContent(), Diagram.class);
+		diagram.setId(diagramEntity.getId());
+		diagram.setName(diagramEntity.getName());
+		diagram.setRemark(diagramEntity.getRemark());
+		diagram.setType(diagramEntity.getType());
+		diagram.setTemplateId(diagramEntity.getTemplateId());
+		diagram.setSystem(diagramEntity.getSystem());
+		diagram.setProjectId(diagramEntity.getProjectId());
+		diagram.setBuildingId(diagramEntity.getBuildingId());
+		diagram.setGroupCode(diagramEntity.getGroupCode());
+		return diagram;
+	}
+
+	public DiagramEntity toDiagramEntity(Diagram diagram) {
+		DiagramEntity entity = new DiagramEntity();
+		entity.setId(diagram.getId());
+		entity.setName(diagram.getName());
+		entity.setRemark(diagram.getRemark());
+		entity.setType(diagram.getType());
+		entity.setTemplateId(diagram.getTemplateId());
+		entity.setSystem(diagram.getSystem());
+		entity.setProjectId(diagram.getProjectId());
+		entity.setBuildingId(diagram.getBuildingId());
+		entity.setGroupCode(diagram.getGroupCode());
+		entity.setDiagramContent(parser.toJson(diagram));
+		return entity;
+	}
+
+}

+ 41 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/TemplateEntity.java

@@ -0,0 +1,41 @@
+package com.persagy.adm.diagram.entity;
+
+import lombok.Data;
+
+/**
+ * 模板实体
+ */
+@Data
+public class TemplateEntity {
+
+	/**
+	 * 模板对应的系统图类型代码
+	 */
+	private String diagramType;
+
+	/**
+	 * 模板id
+	 */
+	private String id;
+
+	/**
+	 * 模板名称
+	 */
+	private String name;
+
+	/**
+	 * 模板编码
+	 */
+	private String code;
+
+	/**
+	 * 描述信息
+	 */
+	private String remark;
+
+	/**
+	 * 模板内容,json格式
+	 */
+	private String templateContent;
+
+}

+ 21 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/TypeObject.java

@@ -0,0 +1,21 @@
+package com.persagy.adm.diagram.entity;
+
+import lombok.Data;
+
+/**
+ * 系统图类型和设备类型对应关系
+ */
+@Data
+public class TypeObject {
+
+	/**
+	 * 系统图类型编码
+	 */
+	private String diagramType;
+
+	/**
+	 * 设备类编码
+	 */
+	private String objClassCode;
+
+}

+ 26 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/entity/TypeRelation.java

@@ -0,0 +1,26 @@
+package com.persagy.adm.diagram.entity;
+
+import lombok.Data;
+
+/**
+ * 系统图类型和关系类型对应关系
+ */
+@Data
+public class TypeRelation {
+
+	/**
+	 * 系统图类型编码
+	 */
+	private String diagramType;
+
+	/**
+	 * 关系的图类型编码
+	 */
+	private String graphCode;
+
+	/**
+	 * 关系的边类型编码
+	 */
+	private String relCode;
+
+}

+ 27 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/frame/BdtpRequest.java

@@ -0,0 +1,27 @@
+package com.persagy.adm.diagram.frame;
+
+import lombok.Data;
+
+/**
+ * bdtp服务请求封装
+ */
+@Data
+public class BdtpRequest {
+
+	private String userId;
+
+	private String appId = "diagram-tool";
+
+	private String groupCode = "0";
+
+	private String projectId = "0";
+
+	public static BdtpRequest get(String groupCode, String projectId, String userId){
+		BdtpRequest req = new BdtpRequest();
+		req.setGroupCode(groupCode);
+		req.setProjectId(projectId);
+		req.setUserId(userId);
+		return req;
+	}
+
+}

+ 8 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/frame/DiagramAppConfig.java

@@ -0,0 +1,8 @@
+package com.persagy.adm.diagram.frame;
+
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class DiagramAppConfig {
+
+}

+ 64 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/frame/EditRequest.java

@@ -0,0 +1,64 @@
+package com.persagy.adm.diagram.frame;
+
+import com.persagy.adm.diagram.core.model.base.XY;
+import com.persagy.adm.diagram.core.model.legend.Legend;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 编辑工具通用请求封装
+ */
+@Data
+public class EditRequest {
+
+	private String currentCompId;
+
+	private String newCompId;
+
+	private String templateId;
+
+	private List<List<XY>> lines;
+
+	private Integer moveOffset;
+
+	private TemplatePropsData templatePropsData;
+
+	private String sys;
+
+	private Legend legend;
+
+	private String diagramId;
+
+	@Data
+	public static class TemplatePropsData {
+
+		private String id;
+
+		private String name;
+
+		private String remark;
+
+		private int layout;
+
+		private int margin;
+
+		private int space;
+
+		private int minH;
+
+		private int minW;
+
+		private boolean absolute;
+
+		private List<String> equipTypes;
+
+		private String pipeType;
+
+		private List<String> passbyRels;
+
+		private List<String> connectEquips;
+
+	}
+
+}

+ 33 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/frame/ExceptionAspect.java

@@ -0,0 +1,33 @@
+package com.persagy.adm.diagram.frame;
+
+import com.persagy.adm.diagram.util.CallException;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.stereotype.Component;
+
+/**
+ * 统一异常拦截处理
+ */
+@Component
+@Aspect
+@Slf4j
+public class ExceptionAspect {
+
+    @Pointcut("execution(public * com.persagy.diagram.controller.*Controller.*(..))")
+    public void handle(){
+
+    }
+
+    @Around("handle()")
+    public Object aroundInvoke(ProceedingJoinPoint point) throws Throwable {
+        try{
+            return point.proceed();
+        } catch (CallException e) {
+            return e.getErrorResult();
+        }
+    }
+
+}

+ 171 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/manage/DemoDiagramManager.java

@@ -0,0 +1,171 @@
+package com.persagy.adm.diagram.manage;
+
+import cn.hutool.core.util.IdUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.adm.diagram.entity.DiagramType;
+import com.persagy.adm.diagram.core.DataStrategy;
+import com.persagy.adm.diagram.core.DiagramDataLoader;
+import com.persagy.adm.diagram.core.model.Diagram;
+import com.persagy.adm.diagram.core.model.base.XY;
+import com.persagy.adm.diagram.frame.BdtpRequest;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 系统图编辑/展现(演示版)的相关操作
+ * @author zhaoyk
+ */
+@Service
+public class DemoDiagramManager {
+
+	private final DataStrategy dataStrategy;
+
+	@Autowired
+	public DemoDiagramManager(DataStrategy dataStrategy) {
+		this.dataStrategy = dataStrategy;
+	}
+
+	/**
+	 * 查询系统图,树形结构
+	 */
+	public List<Folder> getDiagrams(String projectId, String buildingId) {
+		List<DiagramType> types = dataStrategy.getDiagramTypes();
+		List<Diagram> diagrams = dataStrategy.getDiagrams(projectId, buildingId, null);
+		for(Diagram diagram : diagrams) {
+			buildDiagram(diagram, true);
+		}
+
+		BdtpRequest req = new BdtpRequest();
+//		List<ObjectNode> maj = bdtpDataService.getMajorList(req);
+//		List<ObjectNode> sys = bdtpDataService.getSystemList(req);
+		//TODO debug
+		List<ObjectNode> maj = new ArrayList<>();
+		List<ObjectNode> sys = new ArrayList<>();
+
+		List<Folder> folders = new ArrayList<>();
+		for (DiagramType type : types) {
+			Folder fm = Folder.getFolder(type.getMajor(), Folder.TYPE_MAJ, folders, maj);
+			Folder fs = Folder.getFolder(type.getSystem(), Folder.TYPE_SYS, (List)fm.getChildren(), sys);
+
+			Folder ft = new Folder();
+			fs.getChildren().add(ft);
+			ft.setName(type.getName());
+			ft.setId(type.getCode());
+			ft.setType(Folder.TYPE_TYPE);
+
+			for(Diagram diagram : diagrams){
+				if(diagram.getType().equals(type.getCode()))
+					ft.getChildren().add(diagram);
+			}
+		}
+
+		return folders;
+	}
+
+	private void loadTemplate(Diagram diagram){
+		if (diagram.getTemplateId() != null)
+			diagram.setTemplate(dataStrategy.getTemplate(diagram.getTemplateId()));
+	}
+
+	private void buildDiagram(Diagram diagram, boolean layout){
+		loadTemplate(diagram);
+		diagram.init();
+
+		if(layout)
+			diagram.layout(new XY(0, 0));
+	}
+
+	/**
+	 * 创建新的系统图
+	 */
+	public Diagram createDiagram(String name, String type, String projectId, String buildingId, String floorId){
+		Diagram diagram = new Diagram();
+		diagram.setId(IdUtil.simpleUUID());
+
+		diagram.setName(name);
+		diagram.setType(type);
+		diagram.setSystem(type.substring(0, 4));
+
+		diagram.setProjectId(projectId);
+		diagram.setBuildingId(buildingId);
+		diagram.setFloorId(floorId);
+
+		diagram = dataStrategy.saveDiagram(diagram);
+		buildDiagram(diagram, true);
+
+		return diagram;
+	}
+
+	/**
+	 * 删除系统图
+	 */
+	public boolean deleteDiagram(String diagramId) {
+		return dataStrategy.deleteDiagram(diagramId);
+	}
+
+	/**
+	 * 设置系统图模板
+	 */
+	public Diagram setTemplate(String diagramId, String templateId) {
+		Diagram diagram = dataStrategy.getDiagram(diagramId);
+		diagram.setTemplateId(templateId);
+
+		dataStrategy.saveDiagram(diagram);
+		buildDiagram(diagram, true);
+
+		return diagram;
+	}
+
+	@Autowired
+	private ObjectMapper objectMapper;
+	/**
+	 * 加载数据
+	 */
+	public Diagram loadData(String diagramId, boolean autoLoad){
+		Diagram diagram = dataStrategy.getDiagram(diagramId);
+		buildDiagram(diagram, false);
+
+		loadData(diagram, autoLoad);
+
+		diagram.layout(new XY(0, 0));
+
+//		//test 输出数据结构
+//		try {
+//			Diagram d = dataStrategy.getDiagram(diagramId);
+//			buildDiagram(d, false);
+//			loadData(d, autoLoad);
+//			d.layout(new XY(0, 0), true);
+//			if(d.getLines().size() == 0) {
+//				Line line = new Line();
+//				line.setFrom(new ConnectPoint());
+//				line.setTo(new ConnectPoint());
+//				line.setLineStyle(new LineStyle());
+//				Label label = new Label();
+//				label.setStyle(new BaseStyle());
+//				line.setLabel(label);
+//				d.getLines().add(line);
+//			}
+//			DiagramView dv = new DiagramView(d);
+//
+//			System.out.println("-----------------------------------------------------");
+//			System.out.println(objectMapper.writeValueAsString(dv));
+//		}catch (Exception e){
+//			e.printStackTrace();
+//		}
+
+		return diagram;
+	}
+
+	private void loadData(Diagram diagram, boolean autoLoad){
+		DiagramDataLoader loader = new DiagramDataLoader(diagram, dataStrategy);
+		loader.load();
+
+		if(autoLoad)
+			loader.autoLoad();
+	}
+
+}

+ 70 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/manage/Folder.java

@@ -0,0 +1,70 @@
+package com.persagy.adm.diagram.manage;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 树形结构对象
+ * @author zhaoyk
+ */
+@Data
+public class Folder {
+
+	public static final String TYPE_MAJ = "major";
+
+	public static final String TYPE_SYS = "system";
+
+	public static final String TYPE_TYPE = "type";
+
+	private final String objType = "folder";
+
+	private String type;
+
+	private String id;
+
+	private String name;
+
+	private List<Object> children = new ArrayList<>();
+
+	private List elements;
+
+	public static Folder getFolder(String code, String type, List<Folder> folders, List<ObjectNode> dictInfoList){
+		Folder target = null;
+		for(Folder f: folders){
+			if(f.getId().equals(code)) {
+				target = f;
+				break;
+			}
+		}
+		if (target == null) {
+			target = new Folder();
+			target.setId(code);
+			target.setType(type);
+
+			if(Folder.TYPE_MAJ.equals(type))
+				target.setName(getFolderName(code, dictInfoList));
+			else if(Folder.TYPE_SYS.equals(type))
+				target.setName(getFolderName(code, dictInfoList));
+
+			folders.add(target);
+		}
+		return target;
+	}
+
+	private static String getFolderName(String code, List<ObjectNode> dictInfoList){
+		for(ObjectNode node : dictInfoList) {
+			if(node.get("code").asText().equals(code)) {
+				JsonNode nameNode = node.get("name");
+				if(nameNode == null)
+					nameNode = node.get("label");
+				return nameNode.asText();
+			}
+		}
+		return code;
+	}
+
+}

+ 101 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/manage/LegendManager.java

@@ -0,0 +1,101 @@
+package com.persagy.adm.diagram.manage;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.persagy.adm.diagram.core.DataStrategy;
+import com.persagy.adm.diagram.core.model.legend.Legend;
+import com.persagy.adm.diagram.service.BdtpDataService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 图例编辑的相关操作
+ * @author zhaoyk
+ */
+@Service
+public class LegendManager {
+
+	private final DataStrategy dataStrategy;
+
+	@Autowired
+	public LegendManager(DataStrategy dataStrategy) {
+		this.dataStrategy = dataStrategy;
+	}
+
+	@Autowired
+	private BdtpDataService bdtpDataService;
+
+	@Autowired
+	private ObjectMapper objectMapper;
+
+	/**
+	 * 查询系统下的图例列表
+	 */
+	public List<Legend> getLegends(String sys){
+		//test 输出数据结构
+		try{
+			System.out.println("-----------------------------------------------------");
+			System.out.println(objectMapper.writeValueAsString(dataStrategy.getLegends(sys)));
+		}catch (Exception e){
+			e.printStackTrace();
+		}
+
+		return dataStrategy.getLegends(sys);
+	}
+
+	/**
+	 * 保存图例
+	 */
+	public Legend saveLegend(Legend legend, String sys) {
+		return dataStrategy.saveLegend(legend, sys);
+	}
+
+	/**
+	 * 删除图例
+	 */
+	public boolean deleteLegend(String legendId, String systemCode){
+		return dataStrategy.deleteLegend(legendId, systemCode);
+	}
+
+//	@Autowired
+//	private GsonUtil gsonUtil;
+//	public boolean fixRel(){
+//		String DIR_LEGEND = "store/legend/";
+//		File dir = new File(DIR_LEGEND);
+//		for(File file : dir.listFiles()) {
+//			String fn = file.getName();
+//			String sys = fn.substring(0, fn.lastIndexOf('.'));
+//
+//			List<Map> list = gsonUtil.gson().fromJson(FileUtil.readUtf8String(file), List.class);
+//			for(Map map : list) {
+//				List as = (List) map.get("anchors");
+//				if(as != null) {
+//					for(Object o : as) {
+//						Map am = (Map) o;
+//						List rels = (List)am.get("acceptRelations");
+//						if(rels != null) {
+//							for(int i=0;i<rels.size();i++) {
+//								Object item = rels.get(i);
+//								if(item instanceof String) {
+//									String[] arr = ((String) item).split("/");
+//									Map rm = new HashMap();
+//									rm.put("graphCode", arr[0]);
+//									rm.put("relCode", arr[1]);
+//									rels.set(i, rm);
+//								} else if(item instanceof Map) {
+//									rels.set(i, ((Map<?, ?>) item).get("graphCode") + "/" + ((Map<?, ?>) item).get("relCode"));
+//								}
+//							}
+//						}
+//					}
+//				}
+//			}
+//
+//			FileUtil.writeUtf8String(gsonUtil.gson().toJson(list), file);
+//		}
+//
+//		return true;
+//	}
+
+}

+ 329 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/manage/TemplateManager.java

@@ -0,0 +1,329 @@
+package com.persagy.adm.diagram.manage;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.IdUtil;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.adm.diagram.core.model.base.*;
+import com.persagy.adm.diagram.entity.DiagramType;
+import com.persagy.adm.diagram.frame.EditRequest;
+import com.persagy.adm.diagram.core.DataStrategy;
+import com.persagy.adm.diagram.core.model.template.ContainerRefPoint;
+import com.persagy.adm.diagram.core.model.template.DiagramTemplate;
+import com.persagy.adm.diagram.core.model.template.MainPipe;
+import com.persagy.adm.diagram.core.model.template.MainPipePoint;
+import com.persagy.adm.diagram.frame.BdtpRequest;
+import com.persagy.adm.diagram.service.BdtpDataService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * 模板编辑的相关操作
+ * @author zhaoyk
+ */
+@Service
+public class TemplateManager {
+
+	private final DataStrategy dataStrategy;
+
+	@Autowired
+	public TemplateManager(DataStrategy dataStrategy) {
+		this.dataStrategy = dataStrategy;
+	}
+
+	@Autowired
+	private BdtpDataService bdtpDataService;
+
+	/**
+	 * 查询系统图模板,树形结构
+	 */
+	public List<Folder> getTemplates(){
+		List<DiagramType> types = dataStrategy.getDiagramTypes();
+
+		List<DiagramTemplate> templates = dataStrategy.getTemplates();
+		for(DiagramTemplate template : templates) {
+			buildTemplate(template);
+		}
+
+		BdtpRequest req = new BdtpRequest();
+//		List<ObjectNode> maj = bdtpDataService.getMajorList(req);
+//		List<ObjectNode> sys = bdtpDataService.getSystemList(req);
+		//TODO debug
+		List<ObjectNode> maj = new ArrayList<>();
+		List<ObjectNode> sys = new ArrayList<>();
+
+		List<Folder> folders = new ArrayList<>();
+		for (DiagramType type : types) {
+			Folder fm = Folder.getFolder(type.getMajor(), Folder.TYPE_MAJ, folders, maj);
+			Folder fs = Folder.getFolder(type.getSystem(), Folder.TYPE_SYS, (List)fm.getChildren(), sys);
+
+			Folder ft = new Folder();
+			fs.getChildren().add(ft);
+			ft.setName(type.getName());
+			ft.setId(type.getCode());
+			ft.setType(Folder.TYPE_TYPE);
+
+			for(DiagramTemplate template : templates){
+				if(template.getDiagramType().equals(type.getCode()))
+					ft.getChildren().add(template);
+			}
+		}
+
+		return folders;
+	}
+
+	/**
+	 * 查询某个系统图类型下的模板列表
+	 */
+	public List<DiagramTemplate> getTemplates(String diagramType) {
+		return dataStrategy.getTemplates(diagramType);
+	}
+
+	/**
+	 * 创建新的模板
+	 */
+	public DiagramTemplate createTemplate(String name, String type){
+		DiagramTemplate template = DiagramTemplate.defaultTemplate();
+
+		template.setName(name);
+		template.setDiagramType(type);
+		template.setId(IdUtil.simpleUUID());
+
+		template.setMainPipes(new ArrayList<>());
+
+		return saveTemplate(template);
+	}
+
+	/**
+	 * 删除模板
+	 */
+	public boolean deleteTemplate(String id){
+		return dataStrategy.deleteTemplate(id);
+	}
+
+	/**
+	 * 保存模板
+	 */
+	public DiagramTemplate saveTemplate(DiagramTemplate template){
+		template = dataStrategy.saveTemplate(template);
+		buildTemplate(template);
+		return template;
+	}
+
+	private void buildTemplate(DiagramTemplate template){
+		template.init();
+		template.layout(new XY(0, 0));
+	}
+
+	/**
+	 * 添加容器
+	 */
+	public DiagramTemplate addCon(String newContainerId, String containerId, String templateId){
+		DiagramTemplate template = dataStrategy.getTemplate(templateId);
+
+		Container newCon = new Container();
+		newCon.setId(newContainerId);
+		newCon.setLayout(Layout.defaultLayout(Layout.ROW));
+
+		Container parent = template.getContainerById(containerId);
+		parent.addComp(newCon);
+
+		return saveTemplate(template);
+	}
+
+	/**
+	 * 删除容器
+	 */
+	public DiagramTemplate delCon(String containerId, String templateId){
+		DiagramTemplate template = dataStrategy.getTemplate(templateId);
+		template.init();
+
+		Container con = template.getContainerById(containerId);
+		if(con.getParent() != null)
+			con.getParent().removeComp(containerId);
+
+		return saveTemplate(template);
+	}
+
+	/**
+	 * 移动容器
+	 */
+	public DiagramTemplate moveCon(String containerId, String templateId, int offset){
+		DiagramTemplate template = dataStrategy.getTemplate(templateId);
+		template.init();
+
+		Container con = template.getContainerById(containerId);
+		IContainer parent = con.getParent();
+		if(parent != null) {
+			List<IComponent> list = parent.getChildren();
+
+			int idx = list.indexOf(con);
+			idx += offset;
+
+			if(idx >=0 && idx < list.size()) {
+				list.remove(con);
+				list.add(idx, con);
+
+				return saveTemplate(template);
+			}
+		}
+
+		return null;
+	}
+
+	/**
+	 * 修改容器
+	 */
+	public DiagramTemplate modifyCon(EditRequest.TemplatePropsData propsData, String containerId, String templateId){
+		DiagramTemplate template = dataStrategy.getTemplate(templateId);
+		template.init();
+
+		Container con = template.getContainerById(containerId);
+
+		con.setId(propsData.getId());
+		con.setName(propsData.getName());
+		con.setRemark(propsData.getRemark());
+
+		con.getLayout().setLayout(propsData.getLayout());
+		con.getLayout().setMarginHeight(propsData.getMargin());
+		con.getLayout().setMarginWidth(propsData.getMargin());
+		con.getLayout().spaceH = con.getLayout().spaceV = propsData.getSpace();
+
+		con.getPosition().minH = propsData.getMinH();
+		con.getPosition().minW = propsData.getMinW();
+		con.getPosition().setAbsolute(propsData.isAbsolute());
+
+		con.setEquipmentTypes(CollUtil.isEmpty(propsData.getEquipTypes()) ? null : propsData.getEquipTypes());
+
+		return saveTemplate(template);
+	}
+
+	/**
+	 * 绘制新的干管
+	 */
+	public DiagramTemplate addMainPipe(List<List<XY>> lines, String templateId){
+		//合并同一条线上的多个点
+		List<List<XY>> fixed = new ArrayList<>();
+		for(List<XY> line : lines) {
+			List<XY> fixedLine = new ArrayList<>();
+			fixed.add(fixedLine);
+
+			XY p1 = null;
+			XY p2 = null;
+			for (XY point : line) {
+				if(p1 == null)
+					p1 = point;
+				else if(p2 == null)
+					p2 = point;
+				else {
+					if ((p1.x == p2.x && p2.x == point.x)
+							|| (p1.y == p2.y && p2.y == point.y)) {
+						fixedLine.remove(p2);
+					} else
+						p1 = p2;
+					p2 = point;
+				}
+				fixedLine.add(point);
+			}
+		}
+
+		DiagramTemplate template = dataStrategy.getTemplate(templateId);
+		buildTemplate(template);
+
+		List<ContainerRefPoint> allRefPoints = new ArrayList<>();
+
+		LinkedList<Container> cons = new LinkedList<>();
+		cons.add(template.getFrame());
+		while (cons.size() > 0) {
+			Container con = cons.removeFirst();
+
+			if(con.getParent() != null && !con.isAbsolutePosition()) //不使用frame和自由定位容器做定位
+				allRefPoints.addAll(ContainerRefPoint.getRefPoints(con, con.locationToRoot()));
+
+			for(IComponent comp : con.getChildren()){
+				if(comp instanceof Container)
+					cons.add((Container) comp);
+			}
+		}
+
+		MainPipe mainPipe = new MainPipe();
+		mainPipe.setId(IdUtil.simpleUUID()); //TODO
+
+		for(List<XY> line : fixed) {
+			List<MainPipePoint> ps = new ArrayList<>();
+			mainPipe.getPath().add(ps);
+
+			for (XY point : line) {
+				double min = Double.MAX_VALUE;
+				ContainerRefPoint targetRp = null;
+				for(ContainerRefPoint rp : allRefPoints) {
+					double d = distance(point, rp.getLocation());
+					if(d < min) {
+						min = d;
+						targetRp = rp;
+					}
+				}
+				MainPipePoint mp = new MainPipePoint();
+				mp.setContainerId(targetRp.getContainerId());
+				mp.setRefPointName(targetRp.getName());
+				mp.setOffset(new XY(point.x - targetRp.getLocation().x, point.y - targetRp.getLocation().y));
+				ps.add(mp);
+			}
+		}
+		if(template.getMainPipes() == null)
+			template.setMainPipes(new ArrayList<>());
+		template.getMainPipes().add(mainPipe);
+
+		return saveTemplate(template);
+	}
+
+	private double distance(XY p1, XY p2){
+		return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
+	}
+
+	/**
+	 * 删除干管
+	 */
+	public DiagramTemplate delMainPipe(String mainPipeId, String templateId){
+		DiagramTemplate template = dataStrategy.getTemplate(templateId);
+
+		if(template.getMainPipes() != null) {
+			for(MainPipe mainPipe : template.getMainPipes()) {
+				if(mainPipe.getId().equals(mainPipeId)) {
+					template.getMainPipes().remove(mainPipe);
+					break;
+				}
+			}
+		}
+
+		return saveTemplate(template);
+	}
+
+	/**
+	 * 修改干管
+	 */
+	public DiagramTemplate modifyMainPipe(EditRequest.TemplatePropsData propsData, String containerId, String templateId){
+		DiagramTemplate template = dataStrategy.getTemplate(templateId);
+
+		if(template.getMainPipes() != null) {
+			for(MainPipe mainPipe : template.getMainPipes()) {
+				if(mainPipe.getId().equals(containerId)) {
+					mainPipe.setId(propsData.getId());
+					mainPipe.setName(propsData.getName());
+					mainPipe.setRemark(propsData.getRemark());
+					mainPipe.setPipeType(propsData.getPipeType());
+					mainPipe.setEquipmentTypes(propsData.getEquipTypes());
+					mainPipe.setPassbyRels(propsData.getPassbyRels());
+					mainPipe.setConnectEquips(propsData.getConnectEquips());
+					break;
+				}
+			}
+		}
+
+		return saveTemplate(template);
+	}
+
+}

+ 144 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/service/BdtpDataService.java

@@ -0,0 +1,144 @@
+package com.persagy.adm.diagram.service;
+
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.adm.diagram.client.DicClient;
+import com.persagy.adm.diagram.util.Pagination;
+import com.persagy.adm.diagram.util.ServiceUtil;
+import com.persagy.adm.diagram.client.RwdClient;
+import com.persagy.adm.diagram.frame.BdtpRequest;
+import com.persagy.dmp.basic.model.QueryCriteria;
+import com.persagy.dmp.define.entity.GraphDefine;
+import com.persagy.dmp.define.entity.RelationDefine;
+import com.persagy.dmp.digital.client.DigitalObjectClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * bdtp数据服务调用服务封装
+ */
+@Service
+public class BdtpDataService {
+
+	@Autowired
+	private RwdClient rwdClient;
+
+	@Autowired
+	private DicClient dicClient;
+
+	@Autowired
+	private DigitalObjectClient objectClient;
+
+	@Autowired
+	private ObjectMapper objectMapper;
+
+	public List<ObjectNode> getMajorList(BdtpRequest req){
+		//查询专业定义
+		QueryCriteria majorCriteria = ServiceUtil.getQueryCriteria(objectMapper);
+		majorCriteria.getCriteria().put("type", "major");
+		return ServiceUtil.call (() -> dicClient.dataQuery(req.getGroupCode(), req.getProjectId(), req.getAppId(), req.getUserId(), majorCriteria));
+	}
+
+	public List<ObjectNode> getSystemList(BdtpRequest req){
+		//查询系统定义
+		QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, "system");
+		return ServiceUtil.queryAllPage(() -> rwdClient.queryObjectType(req.getGroupCode(), req.getProjectId(), req.getAppId(), req.getUserId(), criteria), criteria, new Pagination(500));
+	}
+
+	/**
+	 * 查询系统下的设备类型定义
+	 */
+	public List<ObjectNode> getEquipTypeList(BdtpRequest req, String system){
+		QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, "equipment");
+		if(StrUtil.isNotBlank(system))
+			criteria.getCriteria().put("systemCode", system);
+		return ServiceUtil.queryAllPage(() -> rwdClient.queryObjectType(req.getGroupCode(), req.getProjectId(), req.getAppId(), req.getUserId(), criteria), criteria, new Pagination(500));
+	}
+
+	/**
+	 * 查询数据字典关系类型
+	 */
+	public List<Map<String, Object>> getRelTypeTree(BdtpRequest req){
+		List<RelationDefine> relList = ServiceUtil.call(() -> rwdClient.queryRelDef(req.getGroupCode(), req.getProjectId(), req.getAppId(), req.getUserId(), new QueryCriteria()));
+		List<GraphDefine> graph = ServiceUtil.call (() -> rwdClient.queryGraphDef(req.getGroupCode(), req.getProjectId(), req.getAppId(), req.getUserId(), new QueryCriteria()));
+
+		List<Map<String, Object>> rels = new ArrayList<>();
+		for(GraphDefine gd : graph){
+			HashMap<String, Object> m = new HashMap<>();
+			m.put("code", gd.getCode());
+			m.put("name", gd.getName());
+			m.put("type", "graph");
+			rels.add(m);
+
+			ArrayList<RelationDefine> chidren = new ArrayList<>();
+			m.put("children", chidren);
+			for(RelationDefine relDef : relList) {
+				if(relDef.getGraphCode().equals(gd.getCode()))
+					chidren.add(relDef);
+			}
+		}
+		return rels;
+	}
+
+	public List<ObjectNode> queryObjectsByType(BdtpRequest req, List<String> equipmentTypes, String buildingId, String floorId){
+		QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, "equipment");
+
+		ArrayNode types = criteria.getCriteria().putArray("classCode");
+		for(String code : equipmentTypes) {
+			types.add(code);
+		}
+
+		if(StrUtil.isNotBlank(buildingId))
+			criteria.getCriteria().put("buildingId", buildingId);
+		if(StrUtil.isNotBlank(floorId))
+			criteria.getCriteria().put("floorId", floorId);
+
+		List<ObjectNode> objs = ServiceUtil.queryAllPage(() -> objectClient.query(req.getGroupCode(), req.getProjectId(), req.getAppId(), req.getUserId(), criteria), criteria, new Pagination(500));
+		if(objs == null)
+			objs = new ArrayList<>(0);
+		return objs;
+	}
+
+	public List<ObjectNode> queryObjectsById(BdtpRequest req, List<String> ids){
+		QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper, "equipment");
+		ArrayNode arr = criteria.getCriteria().putArray("id");
+		for(String id : ids) {
+			arr.add(id);
+		}
+
+		List<ObjectNode> objs = ServiceUtil.queryAllPage(() -> objectClient.query(req.getGroupCode(), req.getProjectId(), req.getAppId(), req.getUserId(), criteria), criteria, new Pagination(500));
+		if(objs == null)
+			objs = new ArrayList<>(0);
+		return objs;
+	}
+
+	public List<ObjectNode> queryRelsByTypeAndObj(BdtpRequest req, List<String[]> relTypes, List<String> objIds){
+		List<ObjectNode> rtn = new ArrayList<>();
+
+		QueryCriteria criteria = ServiceUtil.getQueryCriteria(objectMapper);
+		ArrayNode arr = criteria.getCriteria().putArray("objFrom");
+		for(String id : objIds) {
+			arr.add(id);
+		}
+		List<ObjectNode> rels = ServiceUtil.queryAllPage(() -> rwdClient.queryRelation(req.getGroupCode(), req.getProjectId(), req.getAppId(), req.getUserId(), criteria), criteria, new Pagination(500));
+		if(rels != null)
+			rtn.addAll(rels);
+
+		criteria.getCriteria().set("objTo", criteria.getCriteria().remove("objFrom"));
+		rels = ServiceUtil.queryAllPage(() -> rwdClient.queryRelation(req.getGroupCode(), req.getProjectId(), req.getAppId(), req.getUserId(), criteria), criteria, new Pagination(500));
+		if(rels != null)
+			rtn.addAll(rels);
+
+		//TODO relTypes参数处理
+
+		return rtn;
+	}
+
+}

+ 20 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/util/CallException.java

@@ -0,0 +1,20 @@
+package com.persagy.adm.diagram.util;
+
+import com.persagy.dmp.common.model.response.CommonResult;
+
+/**
+ * 服务调用统一异常
+ */
+public class CallException extends RuntimeException {
+
+	private CommonResult<Object> errorResult;
+
+	public CallException(CommonResult<Object> errorResult) {
+		this.errorResult = errorResult;
+	}
+
+	public CommonResult<Object> getErrorResult() {
+		return errorResult;
+	}
+
+}

+ 31 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/util/Pagination.java

@@ -0,0 +1,31 @@
+package com.persagy.adm.diagram.util;
+
+import lombok.Data;
+
+/**
+ * 调用服务时记录分页信息
+ */
+@Data
+public class Pagination {
+
+	private int pageNum = 1;
+
+	private int pageSize;
+
+	private int total = -1;
+
+	private boolean hasNext = true;
+
+	public Pagination(int pageSize) {
+		this.pageSize = pageSize;
+	}
+
+	public void next() {
+		pageNum++;
+		if ((pageNum - 1) * pageSize >= total)
+			hasNext = false;
+		else
+			hasNext = true;
+	}
+
+}

+ 66 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/util/ServiceUtil.java

@@ -0,0 +1,66 @@
+package com.persagy.adm.diagram.util;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.dmp.basic.model.QueryCriteria;
+import com.persagy.dmp.common.model.response.CommonResult;
+
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * 服务调用工具类
+ */
+public class ServiceUtil {
+
+	public static final String SYNC_APP_IMPL = "syncAppImpl";
+
+	private static final String SUCCESS = "success";
+
+	public static QueryCriteria getQueryCriteria(ObjectMapper objectMapper, String... objType){
+		QueryCriteria queryCriteria = new QueryCriteria();
+
+		ObjectNode objectNode = objectMapper.createObjectNode();
+		if(objType.length  == 1)
+			objectNode.put("objType", objType[0]);
+		else if(objType.length > 1) {
+			ArrayNode arrayNode = objectNode.putArray("objType");
+			for(String type : objType) {
+				arrayNode.add(type);
+			}
+		}
+
+		queryCriteria.setCriteria(objectNode);
+		return queryCriteria;
+	}
+
+	public static <T> T call(Supplier<CommonResult<T>> action) {
+		return call(action, null);
+	}
+
+	private static <T> T call(Supplier<CommonResult<T>> action, Pagination pagination) {
+		CommonResult<T> result = action.get();
+		if(SUCCESS.equals(result.getResult())) {
+			if(pagination != null)
+				pagination.setTotal(result.getCount().intValue());
+			return result.getData();
+		}
+		throw new CallException(new CommonResult<>(result.getResult(), result.getMessage()));
+	}
+
+	public static <T> List<T> queryAllPage(Supplier<CommonResult<List<T>>> action, QueryCriteria criteria, Pagination pagination){
+		List<T> list = null;
+		while (pagination.isHasNext()){
+			criteria.setPage((long)pagination.getPageNum());
+			criteria.setSize((long)pagination.getPageSize());
+			if(list == null)
+				list = ServiceUtil.call(action, pagination);
+			else
+				list.addAll(ServiceUtil.call(action, pagination));
+			pagination.next();
+		}
+		return list;
+	}
+
+}