浏览代码

合同工程清单导入

qianxb 1 年之前
父节点
当前提交
39b6c4f561

+ 4 - 0
blade-service-api/blade-meter-api/pom.xml

@@ -18,6 +18,10 @@
             <groupId>org.springblade</groupId>
             <artifactId>blade-common</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-excel</artifactId>
+        </dependency>
     </dependencies>
     <packaging>jar</packaging>
 

+ 43 - 0
blade-service-api/blade-meter-api/src/main/java/org/springblade/meter/entity/ContractInventoryForm.java

@@ -16,6 +16,8 @@
  */
 package org.springblade.meter.entity;
 
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
 import com.baomidou.mybatisplus.annotation.TableName;
 
 import java.io.Serializable;
@@ -43,112 +45,153 @@ public class ContractInventoryForm extends BaseEntity {
      * 项目id
      */
     @ApiModelProperty(value = "项目id")
+    @ExcelIgnore
     private Long projectId;
     /**
      * 合同段id
      */
     @ApiModelProperty(value = "合同段id")
+    @ExcelIgnore
     private Long contractId;
+    /**
+     * 导入编号
+     */
+    @ApiModelProperty(value = "导入编号")
+    @ExcelProperty("导入编号")
+    private String importNumber;
     /**
      * 清单编号
      */
     @ApiModelProperty(value = "清单编号")
+    @ExcelProperty("清单编号")
     private String formNumber;
     /**
      * 清单名称
      */
     @ApiModelProperty(value = "清单名称")
+    @ExcelProperty("清单名称")
     private String formName;
     /**
      * 清单类型
      */
     @ApiModelProperty(value = "清单类型")
+    @ExcelProperty("清单类型")
     private Integer formType;
     /**
      * 是否清单节点0否1是
      */
     @ApiModelProperty(value = "是否清单节点0否1是")
+    @ExcelIgnore
     private Integer isFormNode;
     /**
      * 单位
      */
     @ApiModelProperty(value = "单位")
+    @ExcelProperty("单位")
     private String unit;
     /**
      * 中标单价
      */
     @ApiModelProperty(value = "中标单价")
+    @ExcelProperty("单价")
     private BigDecimal bidPrice;
     /**
      * 当前单价
      */
     @ApiModelProperty(value = "当前单价")
+    @ExcelIgnore
     private BigDecimal currentPrice;
     /**
      * 变更后单价
      */
     @ApiModelProperty(value = "变更后单价")
+    @ExcelIgnore
     private BigDecimal changePrice;
     /**
      * 章编号
      */
     @ApiModelProperty(value = "章编号")
+    @ExcelProperty("章编号")
     private String chapterNumber;
     /**
      * 合同数量
      */
     @ApiModelProperty(value = "合同数量")
+    @ExcelProperty("数量")
     private Integer contractTotal;
     /**
      * 变更后数量
      */
     @ApiModelProperty(value = "变更后数量")
+    @ExcelIgnore
     private Integer changeTotal;
     /**
      * 合同金额
      */
     @ApiModelProperty(value = "合同金额")
+    @ExcelIgnore
     private BigDecimal contractMoney;
     /**
      * 变更后金额
      */
     @ApiModelProperty(value = "变更后金额")
+    @ExcelIgnore
     private BigDecimal changeMoney;
     /**
      * 清单标识
      */
     @ApiModelProperty(value = "清单标识")
+    @ExcelIgnore
     private String formTag;
     /**
      * 父id
      */
     @ApiModelProperty(value = "父id")
+    @ExcelIgnore
     private Long parentId;
+    /**
+     * 父编号
+     */
+    @ApiModelProperty(value = "父编号")
+    @ExcelIgnore
+    private String parentNumber;
     /**
      * 祖级节点
      */
     @ApiModelProperty(value = "祖级节点")
+    @ExcelIgnore
     private String ancestors;
     /**
      * 备注
      */
     @ApiModelProperty(value = "备注")
+    @ExcelProperty("备注")
     private String remark;
     /**
      * 排序
      */
     @ApiModelProperty(value = "排序")
+    @ExcelIgnore
     private Integer sort;
     /**
      * 是否锁定
      */
     @ApiModelProperty(value = "是否锁定")
+    @ExcelIgnore
     private Integer isLock;
     /**
      * 是否增补
      */
     @ApiModelProperty(value = "是否增补")
+    @ExcelIgnore
     private Integer isSupplement;
+    /**
+     * 节点层级
+     */
+    @ApiModelProperty(value = "节点层级")
+    @ExcelIgnore
+    private Integer nodeTier;
+
 
 
 }

+ 61 - 0
blade-service-api/blade-meter-api/src/main/java/org/springblade/meter/vo/FormTreeVO.java

@@ -0,0 +1,61 @@
+package org.springblade.meter.vo;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tool.node.INode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Param
+ * @Author wangwl
+ * @Date 2023/11/29 15:25
+ **/
+@Data
+public class FormTreeVO implements INode<FormTreeVO> {
+
+    /**
+     * 主键ID
+     */
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long id;
+
+    /**
+     * 父节点ID
+     */
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long parentId;
+
+    /**
+     * 子孙节点
+     */
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    private List<FormTreeVO> children;
+
+    /**
+     * 是否有子孙节点
+     */
+    @JsonInclude(JsonInclude.Include.NON_EMPTY)
+    private Boolean hasChildren;
+
+    @Override
+    public List<FormTreeVO> getChildren() {
+        if (this.children == null) {
+            this.children = new ArrayList<>();
+        }
+        return this.children;
+    }
+
+    @ApiModelProperty(value = "ture有子节点,false没有子节点")
+    private Boolean hasChild;
+
+    @ApiModelProperty(value = "清单名称")
+    private String formName;
+
+
+}

+ 32 - 5
blade-service/blade-meter/src/main/java/org/springblade/meter/controller/ContractInventoryFormController.java

@@ -16,9 +16,7 @@
  */
 package org.springblade.meter.controller;
 
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.*;
 import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
 import lombok.AllArgsConstructor;
 import javax.validation.Valid;
@@ -27,12 +25,17 @@ import org.springblade.core.mp.support.Condition;
 import org.springblade.core.mp.support.Query;
 import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.utils.Func;
+import org.springblade.meter.vo.FormTreeVO;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.bind.annotation.RequestParam;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import org.springblade.meter.entity.ContractInventoryForm;
 import org.springblade.meter.service.IContractInventoryFormService;
 import org.springblade.core.boot.ctrl.BladeController;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.List;
 
 /**
  * 合同工程清单表 控制器
@@ -48,11 +51,35 @@ public class ContractInventoryFormController extends BladeController {
 
 	private final IContractInventoryFormService contractInventoryFormService;
 
+
+	/**
+	 * 获取合同清单树
+	 */
+	@GetMapping("getFormTree")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "获取合同清单树", notes = "返回全加载树")
+	@ApiImplicitParams(value = {
+			@ApiImplicitParam(name = "projectId", value = "projectId", required = true),
+			@ApiImplicitParam(name = "contractId", value = "contractId", required = true)
+	})
+	public R<List<FormTreeVO>> getFormTree(Long projectId, Long contractId){
+		List<FormTreeVO> treeAll = contractInventoryFormService.getFormTree(projectId,contractId);
+		return R.data(treeAll);
+	}
+
+
+	@PostMapping("importExcel")
+	@ApiOperationSupport(order = 1)
+	@ApiOperation(value = "导入excel", notes = "导入excel")
+	public R<String> importExcel(@RequestParam("file") MultipartFile file,Long projectId, Long contractId) {
+		return contractInventoryFormService.importExcel(file,projectId,contractId);
+	}
+
 	/**
 	 * 详情
 	 */
 	@GetMapping("/detail")
-	@ApiOperationSupport(order = 1)
+	@ApiOperationSupport(order = 2)
 	@ApiOperation(value = "详情", notes = "传入contractInventoryForm")
 	public R<ContractInventoryForm> detail(ContractInventoryForm contractInventoryForm) {
 		ContractInventoryForm detail = contractInventoryFormService.getOne(Condition.getQueryWrapper(contractInventoryForm));
@@ -63,7 +90,7 @@ public class ContractInventoryFormController extends BladeController {
 	 * 分页 合同工程清单表
 	 */
 	@GetMapping("/list")
-	@ApiOperationSupport(order = 2)
+	@ApiOperationSupport(order = 3)
 	@ApiOperation(value = "分页", notes = "传入contractInventoryForm")
 	public R<IPage<ContractInventoryForm>> list(ContractInventoryForm contractInventoryForm, Query query) {
 		IPage<ContractInventoryForm> pages = contractInventoryFormService.page(Condition.getPage(query), Condition.getQueryWrapper(contractInventoryForm));

+ 5 - 0
blade-service/blade-meter/src/main/java/org/springblade/meter/mapper/ContractInventoryFormMapper.java

@@ -16,8 +16,12 @@
  */
 package org.springblade.meter.mapper;
 
+import org.apache.ibatis.annotations.Param;
 import org.springblade.meter.entity.ContractInventoryForm;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springblade.meter.vo.FormTreeVO;
+
+import java.util.List;
 
 /**
  * 合同工程清单表 Mapper 接口
@@ -28,4 +32,5 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 public interface ContractInventoryFormMapper extends BaseMapper<ContractInventoryForm> {
 
 
+    List<FormTreeVO> getAllNode(@Param("projectId") Long projectId,@Param("contractId") Long contractId);
 }

+ 8 - 2
blade-service/blade-meter/src/main/java/org/springblade/meter/mapper/ContractInventoryFormMapper.xml

@@ -35,8 +35,14 @@
         <result column="is_lock" property="isLock"/>
         <result column="is_supplement" property="isSupplement"/>
     </resultMap>
-
-
+    <select id="getAllNode" resultType="org.springblade.meter.vo.FormTreeVO">
+        select id,parent_id,
+               concat(scif.form_number, scif.form_name) as form_name,
+               (select COUNT(1) from s_contract_inventory_form
+                    WHERE contract_id = #{contractId} and is_deleted=0 and parent_id = scif.id) as hasChild
+        from s_contract_inventory_form scif
+        where project_id = #{projectId} and contract_id = #{contractId} and is_deleted = 0
+    </select>
 
 
 </mapper>

+ 8 - 0
blade-service/blade-meter/src/main/java/org/springblade/meter/service/IContractInventoryFormService.java

@@ -16,8 +16,13 @@
  */
 package org.springblade.meter.service;
 
+import org.springblade.core.tool.api.R;
 import org.springblade.meter.entity.ContractInventoryForm;
 import org.springblade.core.mp.base.BaseService;
+import org.springblade.meter.vo.FormTreeVO;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
 
 /**
  * 合同工程清单表 服务类
@@ -28,4 +33,7 @@ import org.springblade.core.mp.base.BaseService;
 public interface IContractInventoryFormService extends BaseService<ContractInventoryForm> {
 
 
+    R<String> importExcel(MultipartFile file,Long projectId, Long contractId);
+
+    List<FormTreeVO> getFormTree(Long projectId, Long contractId);
 }

+ 181 - 0
blade-service/blade-meter/src/main/java/org/springblade/meter/service/impl/ContractInventoryFormServiceImpl.java

@@ -16,11 +16,28 @@
  */
 package org.springblade.meter.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.apache.commons.lang.StringUtils;
+import org.springblade.common.utils.SnowFlakeUtil;
+import org.springblade.core.excel.util.ExcelUtil;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.tool.api.R;
 import org.springblade.meter.entity.ContractInventoryForm;
 import org.springblade.meter.mapper.ContractInventoryFormMapper;
 import org.springblade.meter.service.IContractInventoryFormService;
 import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.meter.utils.ForestNodeMerger;
+import org.springblade.meter.vo.FormTreeVO;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * 合同工程清单表 服务实现类
@@ -32,5 +49,169 @@ import org.springframework.stereotype.Service;
 public class ContractInventoryFormServiceImpl extends BaseServiceImpl<ContractInventoryFormMapper, ContractInventoryForm> implements IContractInventoryFormService {
 
 
+    /**
+     * 导入excel
+     * @param file
+     * @return
+     */
+    @Override
+    @Transactional
+    public R<String> importExcel(MultipartFile file,Long projectId, Long contractId) {
+        //校验文件类型
+        String filename = file.getOriginalFilename();
+        String fileSuffix = filename.substring(filename.lastIndexOf(".")+1);
+        if (!"xls,xlsx".contains(fileSuffix)){
+            throw new ServiceException("请传入excel文件");
+        }
+        List<ContractInventoryForm> excels = ExcelUtil.read(file, ContractInventoryForm.class);
+        List<Integer> firstNode = new ArrayList<>();
+        //校验必填字段是否为空
+        for (int i = 0; i < excels.size(); i++) {
+            ContractInventoryForm excel = excels.get(i);
+            if (StringUtils.isBlank(excel.getImportNumber()) || StringUtils.isBlank(excel.getFormNumber() ) || StringUtils.isBlank(excel.getFormName())){
+                throw new ServiceException("excel中有必填项未填写,请检查后重新导入");
+            }
+            excel.setProjectId(projectId);
+            excel.setContractId(contractId);
+            excel.setId(SnowFlakeUtil.getId());
+            //保存每个首节点位置
+            if (!excel.getImportNumber().contains("-")){
+                firstNode.add(i);
+            }
+        }
+        int size = firstNode.size();
+        if (size == 0){
+            throw new ServiceException("未找到首节点,请检查后重新导入");
+        }
+        //根据首节点分成多个数组
+        List<List<ContractInventoryForm>> lists = new ArrayList<>();
+        //只有一个节点,直接单独处理
+        if (size == 1){
+            lists.add(excels);
+        }else {
+            for (int i = 0; i < size-1; i++) {
+                lists.add(excels.subList(firstNode.get(i),firstNode.get(i+1)));
+            }
+            lists.add(excels.subList(firstNode.get(size-1),excels.size()));
+        }
+        try {
+            //获取当前合同的首节点
+            ContractInventoryForm one = this.getOne(new LambdaQueryWrapper<ContractInventoryForm>()
+                    .eq(ContractInventoryForm::getContractId, contractId)
+                    .eq(ContractInventoryForm::getParentId, 0));
+            if (one == null){
+                throw new ServiceException("未找到当前合同根节点,请联系管理员");
+            }
+            Long id = one.getId();
+            Map<String,ContractInventoryForm> lastMap = new HashMap<>();
+            Map<String,ContractInventoryForm> thisMap = new HashMap<>();
+            //循环保存
+            for (List<ContractInventoryForm> list : lists) {
+                ContractInventoryForm form = list.get(0);
+                String number = form.getImportNumber();
+                form.setAncestors("0,"+id);
+                form.setParentId(id);
+                lastMap.put(number,form);
+                //循环判断层级
+                for (ContractInventoryForm fo : list) {
+                    String num = fo.getImportNumber();
+                    int i = num.split("-").length - 1;
+                    fo.setNodeTier(i);
+                    //判断单位,数量,单价是否存在数据,如果有一个存在数据,则为清单节点
+                    if (fo.getContractTotal() != null || StringUtils.isNotBlank(fo.getUnit()) || fo.getBidPrice() != null){
+                        fo.setIsFormNode(1);
+                        if (fo.getContractTotal() != null){
+                            fo.setContractTotal(fo.getContractTotal());
+                            fo.setChangeTotal(fo.getContractTotal());
+                        }
+                        if (fo.getBidPrice() != null){
+                            fo.setCurrentPrice(fo.getBidPrice());
+                            fo.setChangePrice(fo.getBidPrice());
+                        }
+                        if (fo.getContractTotal() != null && fo.getBidPrice() != null){
+                            fo.setContractMoney(new BigDecimal(fo.getContractTotal()).multiply(fo.getBidPrice()));
+                            fo.setChangeMoney(fo.getChangeMoney());
+                        }
+                    }else {
+                        fo.setIsFormNode(0);
+                    }
+                    if (i == 0){
+                        continue;
+                    }else if (i == 1){
+                        fo.setParentNumber(number);
+                    }else{
+                        fo.setParentNumber(num.substring(0, num.lastIndexOf("-")));
+                    }
+                }
+                Map<Integer, List<ContractInventoryForm>> listMap = list.parallelStream().collect(Collectors.groupingBy(ContractInventoryForm::getNodeTier));
+                //根据层级循环,为每一层设置数据
+                for (int i = 1; i < listMap.size(); i++) {
+                    List<ContractInventoryForm> forms = listMap.get(i);
+                    //如果当前层级不存在数据直接跳出
+                    if (forms == null || forms.size() == 0){
+                        break;
+                    }
+                    for (ContractInventoryForm f : forms) {
+                        ContractInventoryForm fo = lastMap.get(f.getParentNumber());
+                        if (fo.getIsFormNode() == 1){
+                            throw new ServiceException("清单节点:"+fo.getFormName()+"下还有节点,检查后重新上传excel");
+                        }
+                        //设置章编号和清单类型,必须根父节点一致
+                        f.setChapterNumber(fo.getChapterNumber());
+                        f.setFormType(fo.getFormType());
+                        //设置父id和祖籍id
+                        f.setParentId(fo.getId());
+                        f.setAncestors(fo.getAncestors()+","+fo.getId());
+                        thisMap.put(f.getImportNumber(),f);
+                    }
+                    lastMap = thisMap;
+                    thisMap = new HashMap<>();
+                }
+                lastMap.clear();
+            }
+            //判断是否有节点没有找到上级节点
+            StringBuilder sb = new StringBuilder();
+            for (ContractInventoryForm excel : excels) {
+                if (excel.getParentId() == null){
+                    sb.append(excel.getFormName()+",");
+                }
+            }
+            if (sb.length() > 0){
+                throw new ServiceException("以下清单名称:"+sb.deleteCharAt(sb.length()-1)+"。未找到上级节点,请修改excel后重新导入");
+            }
+            //所有节点设置好数据,比对已经存在的节点,然后移除集合,只保存不存在的
+
+            this.saveBatch(excels);
+
+        }catch (Exception e){
+            throw new ServiceException(e.getMessage());
+        }
+
+        return R.data("导入成功");
+    }
 
+    /**
+     * 获取合同清单树
+     */
+    @Override
+    public List<FormTreeVO> getFormTree(Long projectId, Long contractId) {
+        //获取当前合同下所有节点
+        List<FormTreeVO> vos = baseMapper.getAllNode(projectId, contractId);
+        //如果当前合同下不存在节点,则创建根节点
+        if (vos == null || vos.size() == 0){
+            ContractInventoryForm info = new ContractInventoryForm();
+            info.setParentId(0L);
+            info.setAncestors("0");
+            info.setProjectId(projectId);
+            info.setContractId(contractId);
+            info.setImportNumber("-1");
+            info.setFormNumber("");
+            info.setFormName("合同工程清单");
+            info.setIsFormNode(0);
+            baseMapper.insert(info);
+            vos = baseMapper.getAllNode(projectId, contractId);
+            return vos;
+        }
+        return ForestNodeMerger.merge(vos);
+    }
 }

+ 27 - 0
blade-service/blade-meter/src/main/java/org/springblade/meter/utils/CollectionUtils.java

@@ -0,0 +1,27 @@
+package org.springblade.meter.utils;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * @Param   集合相关工具
+ * @Author wangwl
+ * @Date 2023/11/30 14:38
+ **/
+public class CollectionUtils {
+
+    /**
+     * 根据属性去重
+     *
+     * @param key
+     * @param <T>
+     * @return
+     */
+    public static <T> Predicate<T> distinctByKey(final Function<? super T, ?> key) {
+        final Map<Object, Boolean> seen = new ConcurrentHashMap<>();
+        return t -> Objects.isNull(seen.putIfAbsent(key.apply(t), Boolean.TRUE));
+    }
+}

+ 60 - 0
blade-service/blade-meter/src/main/java/org/springblade/meter/utils/ForestNodeMerger.java

@@ -0,0 +1,60 @@
+package org.springblade.meter.utils;
+
+import org.springblade.common.utils.INodeEx;
+import org.springblade.core.tool.node.ForestNodeManager;
+import org.springblade.core.tool.node.INode;
+
+import java.util.List;
+
+/**
+ *  树工具
+ */
+public class ForestNodeMerger {
+    public static <T extends INode<T>> List<T> merge(List<T> items) {
+        ForestNodeManager<T> forestNodeManager = new ForestNodeManager(items);
+        items.forEach((forestNode) -> {
+            if (forestNode.getParentId() != null && forestNode.getParentId() != 0L) {
+                INode<T> node = forestNodeManager.getTreeNodeAt(forestNode.getParentId());
+                if (node != null) {
+                    node.getChildren().add(forestNode);
+                } else {
+                    forestNodeManager.addParentId(forestNode.getId());
+                }
+            }
+
+        });
+        return forestNodeManager.getRoot();
+    }
+
+    public static <T extends INode<T>> void getTreeList(T tree, List<T> nodes) {
+        if (tree == null) {
+            return;
+        }
+
+        nodes.add(tree);
+
+        List<T> childrens = tree.getChildren();
+        if (childrens != null) {
+            for (T child : childrens) {
+                getTreeList(child, nodes);
+            }
+        }
+    }
+
+    public static <T extends INodeEx<T>> void getTreeListEx(T tree, List<T> nodes) {
+        if (tree == null) {
+            return;
+        }
+
+        nodes.add(tree);
+
+        List<T> childrens = tree.getChildren();
+        if (childrens != null) {
+            for (T child : childrens) {
+                getTreeListEx(child, nodes);
+            }
+        }
+    }
+
+
+}