Parcourir la source

计量单元相关

liuyc il y a 1 an
Parent
commit
d7f28cc063

+ 6 - 0
blade-service-api/blade-manager-api/src/main/java/org/springblade/manager/entity/ProjectInfo.java

@@ -173,4 +173,10 @@ public class ProjectInfo extends BaseEntity {
     @ApiModelProperty(value = "审批类型(电签方式) 1=垂直审批(顺序审批) 2=平行审批")
     private Integer approvalType;
 
+    /**
+     * 计量单元模板id
+     */
+    @ApiModelProperty(value = "系统计量单元模板id")
+    private Long meterTemplateId;
+
 }

+ 1 - 1
blade-service-api/blade-meter-api/src/main/java/org/springblade/meter/entity/MeterTreeContract.java

@@ -38,7 +38,7 @@ public class MeterTreeContract extends BaseEntity {
     @ApiModelProperty(value = "数据源类型 1=原始引用、2=新增、3=导入、4=复制")
     private Integer dataSourceType;
 
-    @ApiModelProperty(value = "源节点id(系统树节点id)")
+    @ApiModelProperty(value = "源节点id(项目树节点id)")
     private Long sourceNodeId;
 
     @ApiModelProperty(value = "编辑状态 0=未编辑,1=已编辑")

+ 12 - 0
blade-service-api/blade-meter-api/src/main/java/org/springblade/meter/feign/MeterTreeSystemClient.java

@@ -0,0 +1,12 @@
+package org.springblade.meter.feign;
+
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import static org.springblade.core.launch.constant.AppConstant.APPLICATION_NAME_PREFIX;
+
+@FeignClient(value = APPLICATION_NAME_PREFIX + "meter")
+public interface MeterTreeSystemClient {
+
+}

+ 0 - 4
blade-service-api/blade-meter-api/src/main/java/org/springblade/meter/feign/test.java

@@ -1,4 +0,0 @@
-package org.springblade.meter.feign;
-
-public class test {
-}

+ 9 - 0
blade-service/blade-manager/pom.xml

@@ -172,6 +172,15 @@
             <artifactId>commons-net</artifactId>
             <version>3.7</version>
         </dependency>
+
+        <!--计量-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-meter-api</artifactId>
+            <version>2.9.1.RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+
     </dependencies>
     <build>
         <plugins>

+ 68 - 31
blade-service/blade-meter/src/main/java/org/springblade/meter/controller/MeterTreeController.java

@@ -80,6 +80,7 @@ public class MeterTreeController extends BladeController {
                 systemTree.setNodeName(obj.getName());
                 systemTree.setParentId(0L);
                 systemTree.setAncestor("0");
+                systemTree.setSort(1);
                 return R.data(meterTreeSystemService.save(systemTree));
             }
         }
@@ -269,15 +270,46 @@ public class MeterTreeController extends BladeController {
         return R.data(null);
     }
 
-    @GetMapping("/project/detail")
+    @GetMapping("/project/refresh")
     @ApiOperationSupport(order = 13)
+    @ApiOperation(value = "项目树初始化(新增、增量同步)", notes = "传入项目projectId")
+    public R<Object> projectRefresh(@RequestParam String projectId) {
+        if (StringUtils.isEmpty(projectId)) {
+            throw new ServiceException("未获取到项目信息");
+        }
+        ProjectInfo projectInfo = jdbcTemplate.query("SELECT * FROM m_project_info WHERE id = " + projectId, new BeanPropertyRowMapper<>(ProjectInfo.class)).stream().findAny().orElse(null);
+        if (projectInfo != null && ObjectUtil.isNotEmpty(projectInfo.getMeterTemplateId())) {
+
+            /*加锁*/
+            String redisKey = "meter:project:refresh-tree:" + projectId;
+            String redisValue = bladeRedis.get(redisKey);
+            if (StringUtils.isNotEmpty(redisValue) && redisValue.equals("1")) {
+                return R.fail(400, "请勿重复提交,60秒后再尝试");
+            }
+            bladeRedis.set(redisKey, "1");
+            bladeRedis.expire(redisKey, 60);
+
+            /*初始化 或 增量同步 项目计量树*/
+            boolean result = meterTreeProjectService.projectTreeInitOrSync(projectInfo.getMeterTemplateId(), Long.parseLong(projectId));
+            if (result) {
+                return R.success("操作成功");
+            } else {
+                return R.fail("操作失败");
+            }
+        } else {
+            throw new ServiceException("未获取到项目关联的系统单元信息,操作失败");
+        }
+    }
+
+    @GetMapping("/project/detail")
+    @ApiOperationSupport(order = 14)
     @ApiOperation(value = "项目树节点详情", notes = "传入id")
     public R<MeterTreeProject> projectDetail(@RequestParam String id) {
         return R.data(meterTreeProjectService.getById(id));
     }
 
     @PostMapping("/project/save")
-    @ApiOperationSupport(order = 14)
+    @ApiOperationSupport(order = 15)
     @ApiOperation(value = "项目树节点新增", notes = "传入MeterTreeProject(上级的id赋值到parentId字段上提交)")
     public R<Object> projectSave(@RequestBody MeterTreeProject obj) {
         /*计算parentId、ancestor*/
@@ -302,7 +334,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @PostMapping("/project/update")
-    @ApiOperationSupport(order = 15)
+    @ApiOperationSupport(order = 16)
     @ApiOperation(value = "项目树节点修改", notes = "传入MeterTreeProject")
     public R<Object> projectUpdate(@RequestBody MeterTreeProject obj) {
         obj.setUpdateStatus(1); //编辑
@@ -310,7 +342,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @GetMapping("/project/remove")
-    @ApiOperationSupport(order = 16)
+    @ApiOperationSupport(order = 17)
     @ApiOperation(value = "项目树节点删除", notes = "传入id")
     public R<Object> projectRemove(@RequestParam String id) {
         if (StringUtils.isNotEmpty(id)) {
@@ -353,7 +385,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @GetMapping("/project/same-list")
-    @ApiOperationSupport(order = 17)
+    @ApiOperationSupport(order = 18)
     @ApiOperation(value = "项目树节点同级列表", notes = "传入parentId")
     public R<List<MeterTreeProject>> projectSameList(@RequestParam String parentId) {
         if (StringUtils.isNotEmpty(parentId)) {
@@ -366,7 +398,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @PostMapping("/project/sort")
-    @ApiOperationSupport(order = 18)
+    @ApiOperationSupport(order = 19)
     @ApiOperation(value = "项目树节点同级排序", notes = "传入节点ids逗号拼接字符串,按照顺序从上到下")
     public R<Object> projectSort(@RequestBody String ids) {
         if (StringUtils.isNotEmpty(ids)) {
@@ -384,7 +416,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @GetMapping("/project/lazy")
-    @ApiOperationSupport(order = 19)
+    @ApiOperationSupport(order = 20)
     @ApiOperation(value = "项目树节点懒加载", notes = "传入projectId、节点id(根节点id=0)")
     public R<List<MeterTreeProject>> projectLazy(@RequestParam String projectId, @RequestParam String id) {
         if (StringUtils.isNotEmpty(id) && StringUtils.isNotEmpty(projectId)) {
@@ -398,16 +430,19 @@ public class MeterTreeController extends BladeController {
     }
 
     @GetMapping("/contract/refresh")
-    @ApiOperationSupport(order = 20)
-    @ApiOperation(value = "合同段树新增、同步(重加载树)", notes = "传入项目projectId、合同段contractId")
+    @ApiOperationSupport(order = 21)
+    @ApiOperation(value = "合同段树初始化(新增、增量同步)", notes = "传入项目projectId、合同段contractId")
     public R<Object> contractRefresh(@RequestParam String projectId, @RequestParam String contractId) {
         boolean result = false;
         if (StringUtils.isNotEmpty(projectId) && StringUtils.isNotEmpty(contractId)) {
+            /*加锁*/
             String redisKey = "meter:contract:refresh-tree:" + projectId + "_" + contractId;
             String redisValue = bladeRedis.get(redisKey);
             if (StringUtils.isNotEmpty(redisValue) && redisValue.equals("1")) {
                 return R.fail(400, "请勿重复提交,60秒后再尝试");
             }
+            bladeRedis.set(redisKey, "1");
+            bladeRedis.expire(redisKey, 60);
 
             /*首先判断是否存在项目树*/
             MeterTreeProject rootNode = meterTreeProjectService.getBaseMapper().selectOne(Wrappers.<MeterTreeProject>lambdaQuery()
@@ -418,12 +453,6 @@ public class MeterTreeController extends BladeController {
                     .eq(MeterTreeProject::getStatus, 1));
 
             if (rootNode != null) {
-                /*当前项目所有树*/
-                List<MeterTreeProject> meterTreeProjects = meterTreeProjectService.getBaseMapper().selectList(Wrappers.<MeterTreeProject>lambdaQuery()
-                        .eq(MeterTreeProject::getTemplateId, rootNode.getTemplateId())
-                        .eq(MeterTreeProject::getProjectId, projectId)
-                        .eq(MeterTreeProject::getStatus, 1));
-
                 /*判断合同段是否存在树,不存在为初始化,存在则为同步*/
                 Long count = meterTreeContractService.getBaseMapper().selectCount(Wrappers.<MeterTreeContract>lambdaQuery()
                         .eq(MeterTreeContract::getTemplateId, rootNode.getTemplateId())
@@ -433,18 +462,26 @@ public class MeterTreeController extends BladeController {
                         .eq(MeterTreeContract::getStatus, 1));
 
                 if (count == 0) {
-                    /*初始化新增到合同段*/
+                    /*当前项目所有树*/
+                    List<MeterTreeProject> meterTreeProjects = meterTreeProjectService.getBaseMapper().selectList(Wrappers.<MeterTreeProject>lambdaQuery()
+                            .eq(MeterTreeProject::getTemplateId, rootNode.getTemplateId())
+                            .eq(MeterTreeProject::getProjectId, projectId)
+                            .eq(MeterTreeProject::getStatus, 1));
+
+                    /*初始化 合同段计量树*/
                     result = meterTreeContractService.contractTreeInit(meterTreeProjects, Long.parseLong(contractId));
 
                 } else {
-                    /*同步修改到合同段(增量)*/
+                    /*当前项目所有树*/
+                    List<MeterTreeProject> meterTreeProjects = meterTreeProjectService.getBaseMapper().selectList(Wrappers.<MeterTreeProject>lambdaQuery()
+                            .select(MeterTreeProject::getId, MeterTreeProject::getParentId)
+                            .eq(MeterTreeProject::getTemplateId, rootNode.getTemplateId())
+                            .eq(MeterTreeProject::getProjectId, projectId)
+                            .eq(MeterTreeProject::getStatus, 1));
+
+                    /*增量同步 合同段计量树*/
                     result = meterTreeContractService.contractTreeSync(meterTreeProjects, rootNode.getTemplateId(), Long.parseLong(contractId), Long.parseLong(projectId));
                 }
-
-                /*加锁*/
-                bladeRedis.set(redisKey, "1");
-                bladeRedis.expire(redisKey, 60);
-
             } else {
                 throw new ServiceException("未获取到项目计量单元信息!");
             }
@@ -456,7 +493,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @GetMapping("/contract/left-list")
-    @ApiOperationSupport(order = 21)
+    @ApiOperationSupport(order = 22)
     @ApiOperation(value = "合同段-新增/增补单元-左边节点列表", notes = "传入id")
     public R<List<MeterTreeProject>> leftList(@RequestParam String id) {
         if (StringUtils.isNotEmpty(id)) {
@@ -479,7 +516,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @PostMapping("/contract/save")
-    @ApiOperationSupport(order = 22)
+    @ApiOperationSupport(order = 23)
     @ApiOperation(value = "合同段树节点新增", notes = "传入MeterTreeContractSaveBatchDTO")
     public R<Object> contractSave(@RequestBody MeterTreeContractSaveBatchDTO dto) {
         if (ObjectUtil.isNotEmpty(dto.getDataList()) && dto.getDataList().size() > 0 && (dto.getRequestType().equals(1) || dto.getRequestType().equals(2))) {
@@ -491,7 +528,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @PostMapping("/contract/update")
-    @ApiOperationSupport(order = 23)
+    @ApiOperationSupport(order = 24)
     @ApiOperation(value = "合同段树节点修改", notes = "传入MeterTreeContractDTO")
     public R<Object> contractUpdate(@RequestBody MeterTreeContractDTO dto) {
         if (ObjectUtil.isNotEmpty(dto.getDecompositionList()) && dto.getDecompositionList().size() > 0) {
@@ -513,7 +550,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @GetMapping("/contract/detail")
-    @ApiOperationSupport(order = 24)
+    @ApiOperationSupport(order = 25)
     @ApiOperation(value = "合同段树节点详情", notes = "传入id")
     public R<MeterTreeContractVO> contractDetail(@RequestParam String id) {
         MeterTreeContract basicInfo = meterTreeContractService.getById(id);
@@ -529,7 +566,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @GetMapping("/contract/lazy")
-    @ApiOperationSupport(order = 25)
+    @ApiOperationSupport(order = 26)
     @ApiOperation(value = "合同段树节点懒加载", notes = "传入contractId、节点id(根节点id=0)")
     public R<List<MeterTreeContract>> contractLazy(@RequestParam String contractId, @RequestParam String id) {
         if (StringUtils.isNotEmpty(id) && StringUtils.isNotEmpty(contractId)) {
@@ -543,7 +580,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @GetMapping("/contract/same-list")
-    @ApiOperationSupport(order = 26)
+    @ApiOperationSupport(order = 27)
     @ApiOperation(value = "合同段树节点同级列表", notes = "传入parentId")
     public R<List<MeterTreeContract>> contractSameList(@RequestParam String parentId) {
         if (StringUtils.isNotEmpty(parentId)) {
@@ -556,7 +593,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @PostMapping("/contract/sort")
-    @ApiOperationSupport(order = 27)
+    @ApiOperationSupport(order = 28)
     @ApiOperation(value = "合同段树节点同级排序", notes = "传入节点ids逗号拼接字符串,按照顺序从上到下")
     public R<Object> contractSort(@RequestBody String ids) {
         if (StringUtils.isNotEmpty(ids)) {
@@ -574,7 +611,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @GetMapping("/contract/remove")
-    @ApiOperationSupport(order = 28)
+    @ApiOperationSupport(order = 29)
     @ApiOperation(value = "合同段树节点删除", notes = "传入id")
     public R<Object> contractRemove(@RequestParam String id) {
         if (StringUtils.isNotEmpty(id)) {
@@ -605,7 +642,7 @@ public class MeterTreeController extends BladeController {
     }
 
     @GetMapping("/contract/lock")
-    @ApiOperationSupport(order = 29)
+    @ApiOperationSupport(order = 30)
     @ApiOperation(value = "合同段树节点锁定/解锁", notes = "传入id、lockStatus=1(锁定),lockStatus=0(解锁)")
     public R<Object> contractLock(@RequestParam String id, @RequestParam Integer lockStatus) {
         if (StringUtils.isNotEmpty(id) && (lockStatus.equals(0) || lockStatus.equals(1))) {

+ 11 - 0
blade-service/blade-meter/src/main/java/org/springblade/meter/feign/MeterTreeSystemClientImpl.java

@@ -0,0 +1,11 @@
+package org.springblade.meter.feign;
+
+
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@AllArgsConstructor
+public class MeterTreeSystemClientImpl implements MeterTreeSystemClient {
+
+}

+ 2 - 0
blade-service/blade-meter/src/main/java/org/springblade/meter/service/MeterTreeProjectService.java

@@ -7,4 +7,6 @@ public interface MeterTreeProjectService extends BaseService<MeterTreeProject> {
 
     Integer selectMaxSort(Long parentId);
 
+    boolean projectTreeInitOrSync(Long meterTemplateId, Long projectId);
+
 }

+ 24 - 14
blade-service/blade-meter/src/main/java/org/springblade/meter/service/impl/MeterTreeContractServiceImpl.java

@@ -50,6 +50,9 @@ public class MeterTreeContractServiceImpl extends BaseServiceImpl<MeterTreeContr
 
             /*数据源id*/
             cObj.setSourceNodeId(pObj.getId());
+            cObj.setDataSourceType(1); //项目引用
+            cObj.setUpdateStatus(0); //非编辑
+
             /*id*/
             cObj.setId(newNodeIdMap.get(pObj.getId()));
             /*parentId、ancestor*/
@@ -64,9 +67,6 @@ public class MeterTreeContractServiceImpl extends BaseServiceImpl<MeterTreeContr
                 cObj.setAncestor(null); //后面重新计算子级ancestor
             }
 
-            cObj.setUpdateStatus(0); //非编辑
-            cObj.setDataSourceType(1); //项目引用
-
             meterTreeContractResultData.add(cObj);
         }
 
@@ -96,6 +96,7 @@ public class MeterTreeContractServiceImpl extends BaseServiceImpl<MeterTreeContr
     public boolean contractTreeSync(List<MeterTreeProject> meterTreeProjects, Long templateId, Long contractId, Long projectId) {
         /*获取合同段树*/
         List<MeterTreeContract> meterTreeContracts = baseMapper.selectList(Wrappers.<MeterTreeContract>lambdaQuery()
+                .select(MeterTreeContract::getSourceNodeId)
                 .eq(MeterTreeContract::getTemplateId, templateId)
                 .eq(MeterTreeContract::getContractId, contractId)
                 .eq(MeterTreeContract::getDataSourceType, 1) //此处只判断引用的(因为是靠引用id来获取父节点信息)
@@ -109,7 +110,10 @@ public class MeterTreeContractServiceImpl extends BaseServiceImpl<MeterTreeContr
 
         /*增量同步(该接口逻辑与contractSave接口中的划分子级一样)*/
         if (!difference.isEmpty()) {
-            /*获取所有要新增的根节点ids*/
+            /*构造差集节点id=parentId关系Maps*/
+            Map<Long, Long> idAndParentIdMaps = difference.stream().collect(Collectors.toMap(MeterTreeProject::getId, MeterTreeProject::getParentId));
+
+            /*获取所有要新增的项目级的根节点ids*/
             Set<Long> rootNodeIds = this.getRootNodeIds(difference);
             /*获取根节点的父级节点ids*/
             List<MeterTreeProject> meterTreeProjectsParentNodes = meterTreeProjectMapper.selectList(Wrappers.<MeterTreeProject>lambdaQuery()
@@ -126,7 +130,7 @@ public class MeterTreeContractServiceImpl extends BaseServiceImpl<MeterTreeContr
                     .eq(MeterTreeContract::getStatus, 1)
                     .eq(MeterTreeContract::getContractId, contractId)
                     .eq(MeterTreeContract::getTemplateId, templateId)
-                    .in(MeterTreeContract::getSourceNodeId, parentRootNodeIds) //项目根节点父级 指向合同段
+                    .in(MeterTreeContract::getSourceNodeId, parentRootNodeIds) //项目根节点父级 指向合同段原始id字段
             );
             Map<Long, List<MeterTreeContract>> meterTreeContractNodesMaps = meterTreeContractNodesList.stream().collect(Collectors.groupingBy(MeterTreeContract::getSourceNodeId));
 
@@ -136,6 +140,12 @@ public class MeterTreeContractServiceImpl extends BaseServiceImpl<MeterTreeContr
                 for (MeterTreeContract meterTreeContractNode : contractParentRootNodeList) {
 
                     for (Long rootNodeId : rootNodeIds) {
+                        /*归纳到自己节点下 TODO 待测试,这里是否需要,项目同步是需要判断的*/
+                        Long rootIdRecordParentId = idAndParentIdMaps.get(rootNodeId);
+                        if (!rootIdRecordParentId.equals(parentRootId)) {
+                            continue;
+                        }
+
                         /*获取新增节点的所有子级相关节点*/
                         List<MeterTreeProject> projectNode = this.getMeterTreeProjectNode(projectId, templateId, rootNodeId);
 
@@ -155,6 +165,9 @@ public class MeterTreeContractServiceImpl extends BaseServiceImpl<MeterTreeContr
 
                             /*数据源id*/
                             cObj.setSourceNodeId(pObj.getId());
+                            cObj.setDataSourceType(1); //项目引用
+                            cObj.setUpdateStatus(0); //非编辑
+
                             /*id*/
                             cObj.setId(newRootAndChildNodeIdMap.get(pObj.getId()));
                             /*parentId*/
@@ -170,9 +183,6 @@ public class MeterTreeContractServiceImpl extends BaseServiceImpl<MeterTreeContr
                                 cObj.setAncestor(null); //后面重新计算子级ancestor
                             }
 
-                            cObj.setUpdateStatus(0); //非编辑
-                            cObj.setDataSourceType(1); //项目引用
-
                             meterTreeContractResultData.add(cObj);
                         }
 
@@ -235,6 +245,9 @@ public class MeterTreeContractServiceImpl extends BaseServiceImpl<MeterTreeContr
 
                         /*数据源id=左边树节点id*/
                         cObj.setSourceNodeId(pObj.getId());
+                        cObj.setDataSourceType(2); //用户手动新增
+                        cObj.setUpdateStatus(0); //非编辑
+
                         /*id*/
                         cObj.setId(SnowFlakeUtil.getId());
                         /*parentId=合同段计量树选择节点的id,新增到选择节点下面*/
@@ -242,9 +255,6 @@ public class MeterTreeContractServiceImpl extends BaseServiceImpl<MeterTreeContr
                         /*ancestor*/
                         cObj.setAncestor(meterTreeContractNode.getAncestor() + "," + cObj.getParentId());
 
-                        cObj.setUpdateStatus(0); //非编辑
-                        cObj.setDataSourceType(2); //用户手动新增
-
                         baseMapper.insert(cObj);
                     }
 
@@ -283,6 +293,9 @@ public class MeterTreeContractServiceImpl extends BaseServiceImpl<MeterTreeContr
 
                         /*数据源id*/
                         cObj.setSourceNodeId(pObj.getId());
+                        cObj.setDataSourceType(2); //用户手动新增
+                        cObj.setUpdateStatus(0); //非编辑
+
                         /*id*/
                         cObj.setId(newRootAndChildNodeIdMap.get(pObj.getId()));
                         /*parentId*/
@@ -299,9 +312,6 @@ public class MeterTreeContractServiceImpl extends BaseServiceImpl<MeterTreeContr
                             cObj.setAncestor(null); //后面重新计算子级ancestor
                         }
 
-                        cObj.setUpdateStatus(0); //非编辑
-                        cObj.setDataSourceType(2); //用户手动新增
-
                         meterTreeContractResultData.add(cObj);
                     }
 

+ 304 - 0
blade-service/blade-meter/src/main/java/org/springblade/meter/service/impl/MeterTreeProjectServiceImpl.java

@@ -1,17 +1,321 @@
 package org.springblade.meter.service.impl;
 
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.AllArgsConstructor;
+import org.springblade.common.utils.SnowFlakeUtil;
+import org.springblade.core.log.exception.ServiceException;
 import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.core.tool.utils.BeanUtil;
 import org.springblade.meter.entity.MeterTreeProject;
+import org.springblade.meter.entity.MeterTreeSystem;
 import org.springblade.meter.mapper.MeterTreeProjectMapper;
+import org.springblade.meter.mapper.MeterTreeSystemMapper;
 import org.springblade.meter.service.MeterTreeProjectService;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
 
 @Service
+@AllArgsConstructor
 public class MeterTreeProjectServiceImpl extends BaseServiceImpl<MeterTreeProjectMapper, MeterTreeProject> implements MeterTreeProjectService {
 
+    private final MeterTreeSystemMapper meterTreeSystemMapper;
+
     @Override
     public Integer selectMaxSort(Long parentId) {
         return baseMapper.selectMaxSort(parentId);
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean projectTreeInitOrSync(Long meterTemplateId, Long projectId) {
+        /*首先判断是否存在项目树*/
+        MeterTreeProject rootNode = baseMapper.selectOne(Wrappers.<MeterTreeProject>lambdaQuery()
+                .select(MeterTreeProject::getTemplateId)
+                .eq(MeterTreeProject::getProjectId, projectId)
+                .eq(MeterTreeProject::getParentId, 0L)
+                .eq(MeterTreeProject::getAncestor, "0")
+                .eq(MeterTreeProject::getStatus, 1));
+
+        if (rootNode == null) {
+            /*================== 新增初始化 ==================*/
+            /*获取系统单元树信息*/
+            List<MeterTreeSystem> meterTreeSystems = meterTreeSystemMapper.selectList(Wrappers.<MeterTreeSystem>lambdaQuery()
+                    .eq(MeterTreeSystem::getTemplateId, meterTemplateId)
+                    .eq(MeterTreeSystem::getStatus, 1)
+            );
+
+            /*创建旧节点ID和新节点ID的映射newNodeIdMap*/
+            List<Long> systemNodeIds = meterTreeSystems.stream().map(MeterTreeSystem::getId).collect(Collectors.toList());
+            Map<Long, Long> newNodeIdMap = new HashMap<>(systemNodeIds.size());
+            for (Long oldNodeId : systemNodeIds) {
+                newNodeIdMap.put(oldNodeId, SnowFlakeUtil.getId());
+            }
+
+            /*把项目节点id、parentId、ancestor替换成新的,构造成MeterTreeProject对象*/
+            List<MeterTreeProject> meterTreeProjectsResultData = new ArrayList<>(systemNodeIds.size());
+            for (MeterTreeSystem sObj : meterTreeSystems) {
+                MeterTreeProject pObj = new MeterTreeProject();
+                BeanUtil.copyProperties(sObj, pObj);
+                pObj.setProjectId(projectId);
+
+                /*数据源id*/
+                pObj.setSourceNodeId(sObj.getId());
+                pObj.setDataSourceType(1); //系统引用
+                pObj.setUpdateStatus(0); //非编辑
+
+                /*id*/
+                pObj.setId(newNodeIdMap.get(sObj.getId()));
+                /*parentId、ancestor*/
+                Long parentId = newNodeIdMap.getOrDefault(sObj.getParentId(), null);
+                if (parentId == null) {
+                    /*根节点*/
+                    pObj.setParentId(0L);
+                    pObj.setAncestor("0");
+                } else {
+                    /*非根节点*/
+                    pObj.setParentId(parentId);
+                    pObj.setAncestor(null); //后面重新计算子级ancestor
+                }
+
+                meterTreeProjectsResultData.add(pObj);
+            }
+
+            /*层级结构排序*/
+            List<MeterTreeProject> sortedList = this.sortMeterTreeProjects(meterTreeProjectsResultData);
+
+            /*构造ancestor*/
+            for (MeterTreeProject project : sortedList) {
+                if (project.getAncestor() == null) {
+                    //如果当前元素是子级(ancestor为null),找到对应的父级
+                    Long parentId = project.getParentId();
+                    String parentAncestor = this.getAncestorForParentId(meterTreeProjectsResultData, parentId);
+                    //构造新的ancestor字段
+                    String newAncestor = parentAncestor + "," + parentId;
+                    project.setAncestor(newAncestor);
+                }
+            }
+
+            if (sortedList.size() == systemNodeIds.size()) {
+                return this.saveBatch(sortedList, 1000);
+            } else {
+                throw new ServiceException("数据构造异常!");
+            }
+
+        } else {
+            /*================== 增量同步 ==================*/
+            /*获取系统单元树信息*/
+            List<MeterTreeSystem> meterTreeSystems = meterTreeSystemMapper.selectList(Wrappers.<MeterTreeSystem>lambdaQuery()
+                    .select(MeterTreeSystem::getId, MeterTreeSystem::getParentId)
+                    .eq(MeterTreeSystem::getTemplateId, meterTemplateId)
+                    .eq(MeterTreeSystem::getStatus, 1)
+            );
+
+            /*获取项目单元树信息*/
+            List<MeterTreeProject> meterTreeProjects = baseMapper.selectList(Wrappers.<MeterTreeProject>lambdaQuery()
+                    .select(MeterTreeProject::getSourceNodeId)
+                    .eq(MeterTreeProject::getProjectId, projectId)
+                    .eq(MeterTreeProject::getTemplateId, meterTemplateId)
+                    .eq(MeterTreeProject::getDataSourceType, 1) //只对初始化引用的节点做处理,项目用户手动新增的节点没有原始id
+                    .eq(MeterTreeProject::getStatus, 1));
+
+            /*获取meterTreeSystems差集*/
+            List<MeterTreeSystem> difference = meterTreeSystems.stream()
+                    .filter(system -> meterTreeProjects.stream()
+                            .noneMatch(project -> project.getSourceNodeId().equals(system.getId())))
+                    .collect(Collectors.toList());
+
+            /*增量同步(该接口逻辑与MeterTreeContractServiceImpl.contractSave()接口中的划分子级一样)*/
+            if (!difference.isEmpty()) {
+                /*构造差集节点id=parentId关系Maps*/
+                Map<Long, Long> idAndParentIdMaps = difference.stream().collect(Collectors.toMap(MeterTreeSystem::getId, MeterTreeSystem::getParentId));
+
+                /*获取所有要新增的系统级的根节点ids*/
+                Set<Long> rootNodeIds = this.getRootNodeIds(difference);
+                /*获取根节点的父级节点ids*/
+                List<MeterTreeSystem> meterTreeSystemsParentNodes = meterTreeSystemMapper.selectList(Wrappers.<MeterTreeSystem>lambdaQuery()
+                        .select(MeterTreeSystem::getParentId)
+                        .eq(MeterTreeSystem::getStatus, 1)
+                        .eq(MeterTreeSystem::getTemplateId, meterTemplateId)
+                        .in(MeterTreeSystem::getId, rootNodeIds)
+                );
+                Set<Long> parentRootNodeIds = meterTreeSystemsParentNodes.stream().map(MeterTreeSystem::getParentId).collect(Collectors.toSet());
+
+                /*获取父级节点(即contractSave接口的合同段计量树选择新增节点meterTreeContractNode对象,对应项目级就是meterTreeProjectNode对象)*/
+                List<MeterTreeProject> meterTreeProjectNodesList = baseMapper.selectList(Wrappers.<MeterTreeProject>lambdaQuery()
+                        .eq(MeterTreeProject::getStatus, 1)
+                        .eq(MeterTreeProject::getProjectId, projectId)
+                        .eq(MeterTreeProject::getTemplateId, meterTemplateId)
+                        .in(MeterTreeProject::getSourceNodeId, parentRootNodeIds) //系统级根节点父级 指向项目原始id字段
+                );
+                Map<Long, List<MeterTreeProject>> meterTreeProjectsNodesMaps = meterTreeProjectNodesList.stream().collect(Collectors.groupingBy(MeterTreeProject::getSourceNodeId));
+
+                for (Long parentRootId : parentRootNodeIds) {
+                    /*项目与合同段不同之处,合同段有N份(合同段新增就是复制,sourceNodeId存在多份一样的),在项目中sourceNodeId源节点id应该是对应一个父级节点*/
+                    List<MeterTreeProject> projectParentRootNodeList = meterTreeProjectsNodesMaps.get(parentRootId);
+                    for (MeterTreeProject meterTreeProjectNode : projectParentRootNodeList) {
+
+                        for (Long rootNodeId : rootNodeIds) {
+                            /*归纳到自己节点下*/
+                            Long rootIdRecordParentId = idAndParentIdMaps.get(rootNodeId);
+                            if (!rootIdRecordParentId.equals(parentRootId)) {
+                                continue;
+                            }
+
+                            /*获取新增节点的所有子级相关节点*/
+                            List<MeterTreeSystem> systemNode = this.getMeterTreeSystemNode(meterTemplateId, rootNodeId);
+
+                            /*构造数据*/
+                            /*创建旧节点ID和新节点ID的映射Map*/
+                            Map<Long, Long> newRootAndChildNodeIdMap = new HashMap<>(systemNode.size());
+                            List<Long> systemNodeIdList = systemNode.stream().map(MeterTreeSystem::getId).collect(Collectors.toList());
+                            for (Long oldNodeId : systemNodeIdList) {
+                                newRootAndChildNodeIdMap.put(oldNodeId, SnowFlakeUtil.getId());
+                            }
+
+                            /*把项目节点id、parentId、ancestor替换成新的,构造成MeterTreeProject对象*/
+                            List<MeterTreeProject> meterTreeProjectsResultData = new ArrayList<>(systemNodeIdList.size());
+                            for (MeterTreeSystem sObj : systemNode) {
+                                MeterTreeProject pObj = new MeterTreeProject();
+                                BeanUtil.copyProperties(sObj, pObj);
+                                pObj.setProjectId(projectId);
+
+                                /*数据源id*/
+                                pObj.setSourceNodeId(sObj.getId());
+                                pObj.setDataSourceType(1); //系统引用
+                                pObj.setUpdateStatus(0); //非编辑
+
+                                /*id*/
+                                pObj.setId(newRootAndChildNodeIdMap.get(sObj.getId()));
+                                /*parentId、ancestor*/
+                                Long parentId = newRootAndChildNodeIdMap.getOrDefault(sObj.getParentId(), null);
+                                if (parentId == null) {
+                                    /*根节点*/
+                                    pObj.setParentId(meterTreeProjectNode.getId());
+                                    /*ancestor(根节点ancestor指向就是 项目 的父级节点)*/
+                                    pObj.setAncestor(meterTreeProjectNode.getAncestor() + "," + pObj.getParentId());
+                                } else {
+                                    /*非根节点*/
+                                    pObj.setParentId(parentId);
+                                    pObj.setAncestor(null); //后面重新计算子级ancestor
+                                }
+
+                                meterTreeProjectsResultData.add(pObj);
+                            }
+
+                            /*层级结构排序*/
+                            List<MeterTreeProject> sortedList = this.sortMeterTreeProjects(meterTreeProjectsResultData);
+
+                            /*构造ancestor*/
+                            for (MeterTreeProject project : sortedList) {
+                                if (project.getAncestor() == null) {
+                                    //如果当前元素是子级(ancestor为null),找到对应的父级
+                                    Long parentId = project.getParentId();
+                                    String parentAncestor = this.getAncestorForParentId(meterTreeProjectsResultData, parentId);
+                                    //构造新的ancestor字段
+                                    String newAncestor = parentAncestor + "," + parentId;
+                                    project.setAncestor(newAncestor);
+                                }
+                            }
+
+                            /*入库*/
+                            this.saveBatch(sortedList, 1000);
+                        }
+                    }
+                }
+                return true;
+            } else {
+                throw new ServiceException("未获取到需要同步的系统计量单元信息!");
+            }
+        }
+    }
+
+    /**
+     * 获取对应的父级的ancestor字段
+     *
+     * @param projects
+     * @param parentId
+     * @return
+     */
+    private String getAncestorForParentId(List<MeterTreeProject> projects, Long parentId) {
+        for (MeterTreeProject project : projects) {
+            if (project.getId().equals(parentId)) {
+                return project.getAncestor();
+            }
+        }
+        return "";
+    }
+
+    /**
+     * 按照层级结构排序
+     *
+     * @param inputList
+     * @return
+     */
+    private List<MeterTreeProject> sortMeterTreeProjects(List<MeterTreeProject> inputList) {
+        List<MeterTreeProject> sortedList = new ArrayList<>();
+        //找到根节点并递归构建树
+        for (MeterTreeProject project : inputList) {
+            if (project.getAncestor() != null) {
+                //ancestor有值说明是根节点
+                buildTree(project, inputList, sortedList, 1);
+            }
+        }
+        return sortedList;
+    }
+
+    private void buildTree(MeterTreeProject projectContract, List<MeterTreeProject> inputList, List<MeterTreeProject> sortedList, int level) {
+        sortedList.add(projectContract);
+        //找到当前节点的子节点并递归构建树
+        for (MeterTreeProject child : inputList) {
+            if (projectContract.getId().equals(child.getParentId())) {
+                buildTree(child, inputList, sortedList, level + 1);
+            }
+        }
+    }
+
+    /**
+     * 获取系统单元节点下的所有相关节点信息
+     *
+     * @return
+     */
+    private List<MeterTreeSystem> getMeterTreeSystemNode(Long templateId, Long rootId) {
+        return meterTreeSystemMapper.selectList(Wrappers.<MeterTreeSystem>lambdaQuery()
+                .eq(MeterTreeSystem::getStatus, 1)
+                .eq(MeterTreeSystem::getTemplateId, templateId)
+                .like(MeterTreeSystem::getAncestor, rootId) /*所有子级节点*/
+                .or()
+                .eq(MeterTreeSystem::getId, rootId) /*左侧选择的节点视为根节点节点*/
+        );
+    }
+
+    /**
+     * 获取当前树形结构的所有根节点IDS
+     *
+     * @param meterTreeSystems
+     * @return
+     */
+    private Set<Long> getRootNodeIds(List<MeterTreeSystem> meterTreeSystems) {
+        Set<Long> rootIds = new HashSet<>();
+        for (MeterTreeSystem node : meterTreeSystems) {
+            //如果父节点id为null或者不存在于列表中的id,则说明是根节点
+            if (node.getParentId() == null || !containsId(meterTreeSystems, node.getParentId())) {
+                rootIds.add(node.getId());
+            }
+        }
+        return rootIds;
+    }
+
+    //判断列表中是否包含指定id的节点
+    private boolean containsId(List<MeterTreeSystem> meterTreeSystems, Long id) {
+        for (MeterTreeSystem node : meterTreeSystems) {
+            if (node.getId().equals(id)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
 }