浏览代码

试验容器相关

liuyc 2 年之前
父节点
当前提交
18b47b25b1
共有 20 个文件被更改,包括 1492 次插入34 次删除
  1. 34 0
      blade-common/src/main/java/org/springblade/common/utils/FileUtils.java
  2. 17 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialContainerClassificationDTO.java
  3. 26 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialContainerClassificationFieldBean.java
  4. 19 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialContainerDataBean.java
  5. 28 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialContainerDataDTO.java
  6. 20 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialContainerVerificationDTO.java
  7. 69 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/TrialContainerClassification.java
  8. 26 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/TrialContainerClassificationPageVO.java
  9. 28 0
      blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/TrialContainerClassificationVO.java
  10. 2 0
      blade-service/blade-business/src/main/java/org/springblade/BusinessApplication.java
  11. 123 0
      blade-service/blade-business/src/main/java/org/springblade/business/controller/TrialContainerController.java
  12. 40 0
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/TrialContainerClassificationMapper.java
  13. 95 0
      blade-service/blade-business/src/main/java/org/springblade/business/mapper/TrialContainerClassificationMapper.xml
  14. 35 0
      blade-service/blade-business/src/main/java/org/springblade/business/service/ITrialContainerClassificationService.java
  15. 366 0
      blade-service/blade-business/src/main/java/org/springblade/business/service/impl/TrialContainerClassificationServiceImpl.java
  16. 48 0
      blade-service/blade-business/src/main/java/org/springblade/business/utils/BeanFieldUtils.java
  17. 512 0
      blade-service/blade-business/src/main/java/org/springblade/business/utils/ExcelUtils.java
  18. 0 32
      blade-service/blade-manager/src/main/java/org/springblade/manager/excel/WbsExcelUtil.java
  19. 2 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeContractServiceImpl.java
  20. 2 1
      blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeServiceImpl.java

+ 34 - 0
blade-common/src/main/java/org/springblade/common/utils/FileUtils.java

@@ -0,0 +1,34 @@
+package org.springblade.common.utils;
+
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Objects;
+
+public class FileUtils  {
+
+    /**
+     * 获取文件path
+     * @param file
+     * @return
+     * @throws IOException
+     */
+    public static File convert(MultipartFile file) throws IOException {
+        File convertFile = new File(Objects.requireNonNull(Objects.requireNonNull(file.getOriginalFilename())));
+        FileOutputStream fos = null;
+        try {
+            convertFile.createNewFile();
+            fos = new FileOutputStream(convertFile);
+            fos.write(file.getBytes());
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            assert fos != null;
+            fos.close();
+        }
+        return convertFile;
+    }
+
+}

+ 17 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialContainerClassificationDTO.java

@@ -0,0 +1,17 @@
+package org.springblade.business.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springblade.business.entity.TrialContainerClassification;
+
+import java.util.List;
+
+
+@Data
+public class TrialContainerClassificationDTO extends TrialContainerClassification {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("字段集合fieldList")
+    private List<TrialContainerClassificationFieldBean> fieldList;
+
+}

+ 26 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialContainerClassificationFieldBean.java

@@ -0,0 +1,26 @@
+package org.springblade.business.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class TrialContainerClassificationFieldBean implements Serializable {
+
+    @ApiModelProperty(value = "容器id-字段关联容器")
+    private Long containerId;
+
+    @ApiModelProperty("字段id")
+    private Long fieldId;
+
+    @ApiModelProperty("字段类型")
+    private Integer fieldType;
+
+    @ApiModelProperty("字段名")
+    private String fieldName;
+
+    @ApiModelProperty(value = "实体表字段名")
+    private String fieldKey;
+
+}

+ 19 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialContainerDataBean.java

@@ -0,0 +1,19 @@
+package org.springblade.business.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+
+@Data
+public class TrialContainerDataBean implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "容器数据字段key")
+    private String fieldKey;
+
+    @ApiModelProperty(value = "容器数据字段value")
+    private String fieldValue;
+
+}

+ 28 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialContainerDataDTO.java

@@ -0,0 +1,28 @@
+package org.springblade.business.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+@Data
+public class TrialContainerDataDTO implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键id")
+    private Long id;
+
+    @ApiModelProperty(value = "容器实体表名称")
+    private String containerInitTabName;
+
+    @ApiModelProperty(value = "容器数据新增入参字段beanList")
+    private List<TrialContainerDataBean> beanList;
+
+    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @ApiModelProperty(value = "校准时间")
+    private Date fieldCalibrationTime;
+
+}

+ 20 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/dto/TrialContainerVerificationDTO.java

@@ -0,0 +1,20 @@
+package org.springblade.business.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class TrialContainerVerificationDTO implements Serializable {
+
+    @ApiModelProperty("主键id")
+    private Long id;
+
+    @ApiModelProperty(value = "容器实体表名称")
+    private String containerInitTabName;
+
+    @ApiModelProperty("数据编号的fieldKey、fieldValue")
+    private TrialContainerDataBean bean;
+
+}

+ 69 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/entity/TrialContainerClassification.java

@@ -0,0 +1,69 @@
+package org.springblade.business.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.mp.base.BaseEntity;
+
+@Data
+@TableName("u_trial_container_classification")
+@EqualsAndHashCode(callSuper = true)
+public class TrialContainerClassification extends BaseEntity {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 合同段id
+     */
+    @ApiModelProperty(value = "合同段id")
+    private Long contractId;
+
+    /**
+     * 容器名称
+     */
+    @ApiModelProperty(value = "容器名称")
+    private String containerName;
+
+    /**
+     * 容器id-字段关联容器
+     */
+    @ApiModelProperty(value = "容器id-字段关联容器")
+    private Long containerId;
+
+    /**
+     * 字段id
+     */
+    @ApiModelProperty(value = "字段id")
+    private Long fieldId;
+
+    /**
+     * 字段数据类型
+     */
+    @ApiModelProperty(value = "字段数据类型")
+    private Integer fieldType;
+
+    /**
+     * 字段名
+     */
+    @ApiModelProperty(value = "字段名")
+    private String fieldName;
+
+    /**
+     * 容器实体表名称
+     */
+    @ApiModelProperty(value = "容器实体表名称")
+    private String containerInitTabName;
+
+    /**
+     * 实体表字段名
+     */
+    @ApiModelProperty(value = "实体表字段名")
+    private String fieldKey;
+
+    /**
+     * 排序
+     */
+    @ApiModelProperty(value = "排序")
+    private Integer sort;
+
+}

+ 26 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/TrialContainerClassificationPageVO.java

@@ -0,0 +1,26 @@
+package org.springblade.business.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class TrialContainerClassificationPageVO implements Serializable {
+
+    @ApiModelProperty("容器id")
+    private String id;
+
+    @ApiModelProperty("当前页")
+    private Integer current;
+
+    @ApiModelProperty("每页的数量")
+    private Integer size;
+
+    @ApiModelProperty("查询输入框 编号")
+    private String queryValue;
+
+    @ApiModelProperty("查询编号的fieldKey")
+    private String fieldKey;
+
+}

+ 28 - 0
blade-service-api/blade-business-api/src/main/java/org/springblade/business/vo/TrialContainerClassificationVO.java

@@ -0,0 +1,28 @@
+package org.springblade.business.vo;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springblade.business.dto.TrialContainerClassificationFieldBean;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+public class TrialContainerClassificationVO implements Serializable {
+
+    @ApiModelProperty("主键id")
+    private Long id;
+
+    @ApiModelProperty("合同段id")
+    private Long contractId;
+
+    @ApiModelProperty("容器名称")
+    private String containerName;
+
+    @ApiModelProperty("容器实体表名称")
+    private String containerInitTabName;
+
+    @ApiModelProperty("字段集合")
+    private List<TrialContainerClassificationFieldBean> fieldList;
+
+}

+ 2 - 0
blade-service/blade-business/src/main/java/org/springblade/BusinessApplication.java

@@ -6,6 +6,7 @@ import org.springblade.core.launch.BladeApplication;
 import org.springframework.cloud.client.SpringCloudApplication;
 import org.springframework.scheduling.annotation.EnableAsync;
 
+
 /**
  * 客户端启动类
  */
@@ -19,3 +20,4 @@ public class BusinessApplication {
     }
 
 }
+

+ 123 - 0
blade-service/blade-business/src/main/java/org/springblade/business/controller/TrialContainerController.java

@@ -0,0 +1,123 @@
+package org.springblade.business.controller;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import org.springblade.business.dto.TrialContainerClassificationDTO;
+import org.springblade.business.dto.TrialContainerDataDTO;
+import org.springblade.business.dto.TrialContainerVerificationDTO;
+import org.springblade.business.entity.TrialContainerClassification;
+import org.springblade.business.mapper.TrialContainerClassificationMapper;
+import org.springblade.business.service.ITrialContainerClassificationService;
+import org.springblade.business.vo.TrialContainerClassificationPageVO;
+import org.springblade.business.vo.TrialContainerClassificationVO;
+import org.springblade.core.boot.ctrl.BladeController;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.Func;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+
+@RestController
+@AllArgsConstructor
+@RequestMapping("/container")
+@Api(value = "试验容器", tags = "试验容器接口")
+public class TrialContainerController extends BladeController {
+
+    private final ITrialContainerClassificationService iTrialContainerClassificationService;
+    private final TrialContainerClassificationMapper trialContainerClassificationMapper;
+
+    @GetMapping("/classification/detail")
+    @ApiOperationSupport(order = 1)
+    @ApiOperation(value = "试验容器分类详情", notes = "传入id")
+    public R<TrialContainerClassification> classificationDetail(@Valid @RequestParam Long id) {
+        return R.data(iTrialContainerClassificationService.classificationDetail(id));
+    }
+
+    @PostMapping("/classification/submit")
+    @ApiOperationSupport(order = 2)
+    @ApiOperation(value = "试验容器新增或修改", notes = "传入TrialContainerClassificationDTO")
+    public R<Object> classificationSubmit(@RequestBody TrialContainerClassificationDTO obj) {
+        return R.status(iTrialContainerClassificationService.classificationSubmit(obj));
+    }
+
+    @GetMapping("/classification/list")
+    @ApiOperationSupport(order = 3)
+    @ApiOperation(value = "查询容器分类集合", notes = "传入合同段id")
+    public R<List<TrialContainerClassificationVO>> classificationList(@RequestParam Long contractId) {
+        return R.data(iTrialContainerClassificationService.classificationList(contractId));
+    }
+
+    @PostMapping("/classification/remove")
+    @ApiOperationSupport(order = 4)
+    @ApiOperation(value = "删除容器", notes = "传入容器id")
+    public R<Object> classificationRemove(@RequestParam String id) {
+        List<TrialContainerClassification> result = trialContainerClassificationMapper.selectList(Wrappers.<TrialContainerClassification>lambdaQuery().eq(TrialContainerClassification::getContainerId, id));
+        TrialContainerClassification obj = trialContainerClassificationMapper.selectById(id);
+        if (result.size() > 0) {
+            throw new ServiceException("当前容器下存在字段,删除失败");
+        }
+        //删除实体表
+        trialContainerClassificationMapper.deleteTable(obj.getContainerInitTabName());
+        return R.status(iTrialContainerClassificationService.deleteLogic(Func.toLongList(id)));
+    }
+
+    @PostMapping("/data/field/remove")
+    @ApiOperationSupport(order = 5)
+    @ApiOperation(value = "删除容器中的字段", notes = "传入字段fieldId")
+    public R<Object> tabFieldRemove(@RequestParam String fieldId) {
+        TrialContainerClassification obj = trialContainerClassificationMapper.selectOne(Wrappers.<TrialContainerClassification>lambdaQuery().eq(TrialContainerClassification::getFieldId, fieldId));
+        Integer integer = trialContainerClassificationMapper.selectDataByTabName(obj.getContainerInitTabName(), obj.getFieldKey());
+        if (integer != 0) {
+            throw new ServiceException("当前字段下存在数据,删除失败");
+        }
+        //删除实体表中对应字段
+        trialContainerClassificationMapper.deleteTableField(obj.getContainerInitTabName(), obj.getFieldKey());
+        return R.status(iTrialContainerClassificationService.deleteLogic(Func.toLongList(obj.getId().toString())));
+    }
+
+    @PostMapping("/data/page")
+    @ApiOperationSupport(order = 6)
+    @ApiOperation(value = "根据容器类型条件分页查询", notes = "传入TrialContainerClassificationPageVO")
+    public R<Page<HashMap<Object, Object>>> dataPage(@RequestBody TrialContainerClassificationPageVO obj) {
+        return R.data(iTrialContainerClassificationService.dataPage(obj));
+    }
+
+    @PostMapping("/data/submit")
+    @ApiOperationSupport(order = 7)
+    @ApiOperation(value = "容器数据新增或修改", notes = "传入TrialContainerDataDTO")
+    public R<Object> dataSubmit(@RequestBody TrialContainerDataDTO obj) {
+        return R.status(iTrialContainerClassificationService.dataSubmit(obj));
+    }
+
+    @PostMapping("/data/remove")
+    @ApiOperationSupport(order = 8)
+    @ApiOperation(value = "容器数据删除", notes = "传入id、容器对应实体表名containerInitTabName")
+    public R<Object> dataRemove(@RequestParam Long id, @RequestParam String containerInitTabName) {
+        return R.status(iTrialContainerClassificationService.dataRemove(id, containerInitTabName));
+    }
+
+    @PostMapping("/data/import-excel")
+    @ApiOperationSupport(order = 9)
+    @ApiOperation(value = "容器数据导入excel", notes = "传入容器对应实体表名containerInitTabName、文件file(excel模板格式:当前容器内的所有字段名(校准时间不填,默认当前时间))")
+    public R<Object> dataImportExcel(@RequestParam String containerInitTabName, @RequestPart MultipartFile file) throws IOException {
+        return R.status(iTrialContainerClassificationService.dataImportExcel(containerInitTabName, file));
+    }
+
+    @GetMapping("/data/verification-no")
+    @ApiOperationSupport(order = 10)
+    @ApiOperation(value = "校验容器数据编号是否唯一", notes = "传入TrialContainerVerificationDTO")
+    public R<Object> dataVerificationNo(@RequestBody TrialContainerVerificationDTO dto) {
+        return R.data(iTrialContainerClassificationService.dataVerificationNo(dto.getBean(), dto.getId(), dto.getContainerInitTabName()));
+    }
+
+
+}

+ 40 - 0
blade-service/blade-business/src/main/java/org/springblade/business/mapper/TrialContainerClassificationMapper.java

@@ -0,0 +1,40 @@
+package org.springblade.business.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
+import org.springblade.business.entity.TrialContainerClassification;
+
+import java.util.HashMap;
+import java.util.List;
+
+public interface TrialContainerClassificationMapper extends BaseMapper<TrialContainerClassification> {
+
+    void addTrialContainerInitTab(@Param("tabName") String tabName);
+
+    void addTrialContainerInitTabBySql(@Param("tabName") String tabName, @Param("sqlStr") String sqlStr);
+
+    void updateFiledType(@Param("tabName") String containerInitTabName, @Param("fieldName") String initTabFieldName, @Param("fieldType") String fieldType, @Param("fieldLength") Integer fieldLength);
+
+    Integer selectDataByTabName(@Param("tabName") String containerInitTabName, @Param("fieldName") String initTabFieldName);
+
+    void alterTabFiled(@Param("tabName") String containerInitTabName, @Param("fieldName") String initTabFieldName, @Param("fieldType") String fieldType, @Param("fieldLength") Integer fieldLength);
+
+    void deleteTable(@Param("tabName") String containerInitTabName);
+
+    void deleteTableField(@Param("tabName") String initTabName, @Param("fieldName") String fieldName);
+
+    List<HashMap<Object, Object>> selectTabDataAll(@Param("tabName") String containerInitTabName);
+
+    List<HashMap<Object, Object>> selectLikeByFieldKey(@Param("tabName") String containerInitTabName, @Param("queryValue") String queryValue, @Param("fieldKey") String fieldKey, @Param("current") long current, @Param("size") long size);
+
+    List<HashMap<Object, Object>> selectLikeByFieldKey2(@Param("tabName") String containerInitTabName, @Param("current") long current, @Param("size") long size);
+
+    int selectCountByField(@Param("tabName") String containerInitTabName, @Param("queryValue") String queryValue, @Param("fieldKey") String fieldKey);
+
+    int insertData(@Param("tabName") String containerInitTabName, @Param("id") Long id, @Param("time") String fieldCalibrationTime, @Param("fieldKeys") String fieldKeys, @Param("fieldValues") String fieldValues);
+
+    int updateData(@Param("tabName") String containerInitTabName, @Param("id") Long id, @Param("time") String fieldCalibrationTime, @Param("values") String values);
+
+    int removeData(@Param("id") Long id, @Param("tabName") String containerInitTabName);
+
+}

+ 95 - 0
blade-service/blade-business/src/main/java/org/springblade/business/mapper/TrialContainerClassificationMapper.xml

@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.springblade.business.mapper.TrialContainerClassificationMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="ResultEntityMap" type="org.springblade.business.entity.TrialContainerClassification">
+        <result column="id" property="id"/>
+        <result column="contract_id" property="contractId"/>
+        <result column="container_name" property="containerName"/>
+        <result column="container_id" property="containerId"/>
+        <result column="field_id" property="fieldId"/>
+        <result column="field_type" property="fieldType"/>
+        <result column="field_name" property="fieldName"/>
+        <result column="field_key" property="fieldKey"/>
+        <result column="container_init_tab_name" property="containerInitTabName"/>
+        <result column="sort" property="sort"/>
+        <result column="create_user" property="createUser"/>
+        <result column="create_dept" property="createDept"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_user" property="updateUser"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="status" property="status"/>
+        <result column="is_deleted" property="isDeleted"/>
+    </resultMap>
+
+    <insert id="insertData">
+        insert into ${tabName}(id,field_calibration_time,${fieldKeys}) values(${id},#{time},${fieldValues})
+    </insert>
+
+    <update id="addTrialContainerInitTab">
+        create table ${tabName} (
+            `id` bigint(20) NOT NULL COMMENT '主键id',
+            `field_calibration_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '校准时间',
+            `pdf_url` varchar(255) COMMENT 'pdfURL地址',
+            PRIMARY KEY (`id`) USING BTREE
+        ) ENGINE=InnoDB DEFAULT CHARSET=utf8
+    </update>
+
+    <update id="updateFiledType">
+        alter table ${tabName} MODIFY ${fieldName} ${fieldType}(${fieldLength})
+    </update>
+
+    <update id="addTrialContainerInitTabBySql">
+        create table ${tabName}
+        (
+            `id` bigint(20) NOT NULL COMMENT '主键id',
+            `field_calibration_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '校准时间',
+            `pdf_url` varchar(255) COMMENT 'pdfURL地址',
+            ${sqlStr} PRIMARY KEY (`id`) USING BTREE
+        ) ENGINE=InnoDB DEFAULT CHARSET=utf8
+    </update>
+
+    <update id="alterTabFiled">
+        alter table ${tabName} add column ${fieldName} ${fieldType}(${fieldLength})
+    </update>
+
+    <update id="deleteTable">
+        drop table ${tabName}
+    </update>
+
+    <update id="deleteTableField">
+        alter table ${tabName} drop column ${fieldName}
+    </update>
+
+    <update id="updateData">
+        update ${tabName} set field_calibration_time = #{time}
+        <if test="values != null and values != '' ">${values}</if>
+        where id = ${id}
+    </update>
+
+    <delete id="removeData">
+        delete #{tabName} where id = #{id}
+    </delete>
+
+    <select id="selectDataByTabName" resultType="java.lang.Integer">
+        select count(${fieldName}) from ${tabName}
+    </select>
+
+    <select id="selectTabDataAll" resultType="java.util.HashMap">
+        select * from ${tabName} order by id
+    </select>
+
+    <select id="selectLikeByFieldKey" resultType="java.util.HashMap">
+        select * from ${tabName} where ${fieldKey} like concat('%',#{queryValue},'%') order by id limit ${current},${size}
+    </select>
+
+    <select id="selectLikeByFieldKey2" resultType="java.util.HashMap">
+        select * from ${tabName} order by id limit ${current},${size}
+    </select>
+
+    <select id="selectCountByField" resultType="java.lang.Integer">
+         select count(1) from ${tabName} where ${fieldKey} like concat('%',#{queryValue},'%')
+    </select>
+
+</mapper>

+ 35 - 0
blade-service/blade-business/src/main/java/org/springblade/business/service/ITrialContainerClassificationService.java

@@ -0,0 +1,35 @@
+package org.springblade.business.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import org.springblade.business.dto.TrialContainerClassificationDTO;
+import org.springblade.business.dto.TrialContainerDataBean;
+import org.springblade.business.dto.TrialContainerDataDTO;
+import org.springblade.business.entity.TrialContainerClassification;
+import org.springblade.business.vo.TrialContainerClassificationPageVO;
+import org.springblade.business.vo.TrialContainerClassificationVO;
+import org.springblade.core.mp.base.BaseService;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+
+public interface ITrialContainerClassificationService extends BaseService<TrialContainerClassification> {
+
+    TrialContainerClassification classificationDetail(Long id);
+
+    boolean classificationSubmit(TrialContainerClassificationDTO obj);
+
+    List<TrialContainerClassificationVO> classificationList(Long contractId);
+
+    Page<HashMap<Object, Object>> dataPage(TrialContainerClassificationPageVO obj);
+
+    boolean dataSubmit(TrialContainerDataDTO obj);
+
+    boolean dataRemove(Long id, String containerInitTabName);
+
+    boolean dataImportExcel(String containerInitTabName, MultipartFile file) throws IOException;
+
+    String dataVerificationNo(TrialContainerDataBean bean, Long id, String containerInitTabName);
+
+}

+ 366 - 0
blade-service/blade-business/src/main/java/org/springblade/business/service/impl/TrialContainerClassificationServiceImpl.java

@@ -0,0 +1,366 @@
+package org.springblade.business.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import lombok.AllArgsConstructor;
+import org.springblade.business.dto.*;
+import org.springblade.business.entity.TrialContainerClassification;
+import org.springblade.business.mapper.TrialContainerClassificationMapper;
+import org.springblade.business.service.ITrialContainerClassificationService;
+import org.springblade.business.utils.BeanFieldUtils;
+import org.springblade.business.utils.ExcelUtils;
+import org.springblade.business.vo.TrialContainerClassificationPageVO;
+import org.springblade.business.vo.TrialContainerClassificationVO;
+import org.springblade.common.utils.FileUtils;
+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.core.tool.utils.DateUtil;
+import org.springblade.core.tool.utils.ObjectUtil;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+@AllArgsConstructor
+public class TrialContainerClassificationServiceImpl
+        extends BaseServiceImpl<TrialContainerClassificationMapper, TrialContainerClassification>
+        implements ITrialContainerClassificationService {
+
+    @Override
+    public TrialContainerClassification classificationDetail(Long id) {
+        return baseMapper.selectById(id);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean classificationSubmit(TrialContainerClassificationDTO obj) {
+        if (ObjectUtil.isEmpty(obj.getId())) {
+            obj.setId(SnowFlakeUtil.getId());
+            //新增容器分类信息
+            TrialContainerClassification beanObj = BeanUtil.copyProperties(obj, TrialContainerClassification.class);
+            if (beanObj != null) {
+                beanObj.setFieldId(null);
+                beanObj.setFieldName(null);
+                beanObj.setFieldType(null);
+                beanObj.setContainerId(null);
+                beanObj.setContainerInitTabName("u_trial_container_" + obj.getId());
+                obj.setContainerInitTabName(beanObj.getContainerInitTabName());
+                baseMapper.insert(beanObj);
+
+                //新增容器分类字段信息
+                List<TrialContainerClassification> initFieldsData = new ArrayList<>();
+                if (obj.getFieldList().size() > 0) {
+                    int filedNumber = 1;
+                    for (TrialContainerClassificationFieldBean bean : obj.getFieldList()) {
+                        TrialContainerClassification beanObjField = BeanUtil.copyProperties(bean, TrialContainerClassification.class);
+                        if (beanObjField != null) {
+                            beanObjField.setFieldKey("key_" + filedNumber++);
+                            beanObjField.setContractId(obj.getContractId());
+                            beanObjField.setContainerId(obj.getId());
+                            beanObjField.setFieldId(SnowFlakeUtil.getId());
+                            beanObjField.setContainerInitTabName(beanObj.getContainerInitTabName());
+                            baseMapper.insert(beanObjField);
+                            initFieldsData.add(beanObjField);
+                        }
+                    }
+                }
+
+                //新增容器对应实体表信息
+                this.addTrialContainerInitTab(obj, initFieldsData);
+            }
+        } else {
+            //修改
+            //获取存在的字段List
+            List<TrialContainerClassification> filedList = baseMapper.selectList(Wrappers.<TrialContainerClassification>query().lambda().eq(TrialContainerClassification::getContainerId, obj.getId()));
+
+            TrialContainerClassification trialContainerClassification;
+            if (filedList.size() > 0) {
+                Collections.reverse(filedList);
+                trialContainerClassification = filedList.stream().findFirst().orElse(filedList.get(filedList.size() - 1));
+            } else {
+                trialContainerClassification = new TrialContainerClassification();
+                trialContainerClassification.setFieldKey("key_0");
+            }
+
+            int keyName = Integer.parseInt(trialContainerClassification.getFieldKey().split("_")[1]) + 1;
+
+            //修改容器名称
+            this.update(Wrappers.<TrialContainerClassification>update().lambda().set(TrialContainerClassification::getContainerName, obj.getContainerName()).eq(TrialContainerClassification::getId, obj.getId()));
+
+            if (filedList.size() == 0) {
+                for (TrialContainerClassificationFieldBean bean : obj.getFieldList()) {
+                    //不存在fieldId,证明不存在该字段,做新增处理
+                    keyName = this.alterTabFiled(obj, keyName, bean);
+                }
+
+            } else {
+                //判断字段List是否发生改变,修改对应字段信息
+                for (TrialContainerClassificationFieldBean beanOut : obj.getFieldList()) {
+                    if (ObjectUtil.isEmpty(beanOut.getFieldId())) {
+                        //不存在fieldId,证明不存在该字段,做新增处理
+                        keyName = this.alterTabFiled(obj, keyName, beanOut);
+                    } else {
+                        //存在fieldId,证明存在该字段,做修改处理
+                        for (TrialContainerClassification beanInDB : filedList) {
+                            if (beanOut.getFieldId().equals(beanInDB.getFieldId())) {
+                                if (ObjectUtil.isEmpty(beanOut.getFieldName()) || ObjectUtil.isEmpty(beanOut.getFieldType())) {
+                                    throw new ServiceException("字段名、类型不能为空,请重新填写");
+                                }
+                                if (ObjectUtil.isNotEmpty(beanOut.getFieldName()) && ObjectUtil.isNotEmpty(beanInDB.getFieldName())
+                                        && !beanOut.getFieldName().equals(beanInDB.getFieldName())) {
+                                    //修改字段基础名称,不修改实体表字段名
+                                    this.update(Wrappers.<TrialContainerClassification>update().lambda().set(TrialContainerClassification::getFieldName, beanOut.getFieldName()).eq(TrialContainerClassification::getFieldId, beanOut.getFieldId()));
+                                }
+                                if (ObjectUtil.isNotEmpty(beanOut.getFieldType()) && ObjectUtil.isNotEmpty(beanInDB.getFieldType())
+                                        && !beanOut.getFieldType().equals(beanInDB.getFieldType())) {
+                                    //修改字段类型,同步修改实体表字段类型
+                                    this.update(Wrappers.<TrialContainerClassification>update().lambda().set(TrialContainerClassification::getFieldType, beanOut.getFieldType()).eq(TrialContainerClassification::getFieldId, beanOut.getFieldId()));
+                                    //判断实体表字段下是否存在数据,存在则不允许修改类型
+                                    Integer result = baseMapper.selectDataByTabName(obj.getContainerInitTabName(), beanOut.getFieldKey());
+                                    if (result > 0) {
+                                        throw new ServiceException("当前字段下存在数据,不允许修改类型");
+                                    }
+                                    if (result == 0) {
+                                        baseMapper.updateFiledType(obj.getContainerInitTabName(), beanOut.getFieldKey(), BeanFieldUtils.getFieldType(beanOut.getFieldType()), BeanFieldUtils.getFieldLength(beanOut.getFieldType()));
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    private int alterTabFiled(TrialContainerClassificationDTO obj, int keyName, TrialContainerClassificationFieldBean bean) {
+        TrialContainerClassification beanObj = BeanUtil.copyProperties(bean, TrialContainerClassification.class);
+        if (beanObj != null) {
+            beanObj.setContainerId(obj.getId());
+            beanObj.setFieldId(SnowFlakeUtil.getId());
+            beanObj.setFieldKey("key_" + keyName++);
+            beanObj.setContainerInitTabName(obj.getContainerInitTabName());
+            baseMapper.insert(beanObj);
+            //追加实体表字段
+            baseMapper.alterTabFiled(obj.getContainerInitTabName(), beanObj.getFieldKey(), BeanFieldUtils.getFieldType(beanObj.getFieldType()), BeanFieldUtils.getFieldLength(beanObj.getFieldType()));
+        }
+        return keyName;
+    }
+
+    private void addTrialContainerInitTab(TrialContainerClassificationDTO obj, List<TrialContainerClassification> initFieldsData) {
+        if (obj.getFieldList().size() == 0) {
+            baseMapper.addTrialContainerInitTab(obj.getContainerInitTabName());
+        } else {
+            String sql = this.createSQL(initFieldsData);
+            baseMapper.addTrialContainerInitTabBySql(obj.getContainerInitTabName(), sql);
+        }
+    }
+
+    private String createSQL(List<TrialContainerClassification> initFieldsData) {
+        StringBuilder sbr = new StringBuilder();
+        for (TrialContainerClassification bean : initFieldsData) {
+            sbr.append(bean.getFieldKey()).append(" ").append(BeanFieldUtils.getFieldType(bean.getFieldType())).append("(").append(BeanFieldUtils.getFieldLength(bean.getFieldType())).append(") ,\n");
+        }
+        return String.valueOf(sbr);
+    }
+
+    @Override
+    public List<TrialContainerClassificationVO> classificationList(Long contractId) {
+        List<TrialContainerClassification> resultList = baseMapper.selectList(Wrappers.<TrialContainerClassification>lambdaQuery().eq(TrialContainerClassification::getContractId, contractId));
+        List<TrialContainerClassification> containerList = resultList.stream().filter(f -> ObjectUtil.isNotEmpty(f.getContainerName())).collect(Collectors.toList());
+        List<TrialContainerClassification> fieldList = resultList.stream().filter(f -> ObjectUtil.isEmpty(f.getContainerName())).collect(Collectors.toList());
+        Map<String, List<TrialContainerClassification>> result = containerList.stream().collect(Collectors.groupingBy(TrialContainerClassification::getContainerInitTabName));
+        List<TrialContainerClassificationVO> responseList = new ArrayList<>();
+        for (Map.Entry<String, List<TrialContainerClassification>> listEntry : result.entrySet()) {
+            for (TrialContainerClassification trialContainerClassification : listEntry.getValue()) {
+                List<TrialContainerClassification> collect = fieldList.stream().filter(f -> f.getContainerId().equals(trialContainerClassification.getId())).collect(Collectors.toList());
+
+                TrialContainerClassificationVO vo = new TrialContainerClassificationVO();
+                vo.setId(trialContainerClassification.getId());
+                vo.setContractId(trialContainerClassification.getContractId());
+                vo.setContainerName(trialContainerClassification.getContainerName());
+                vo.setContainerInitTabName(trialContainerClassification.getContainerInitTabName());
+
+                List<TrialContainerClassificationFieldBean> beans = new ArrayList<>();
+                for (TrialContainerClassification obj : collect) {
+                    beans.add(BeanUtil.copyProperties(obj, TrialContainerClassificationFieldBean.class));
+                }
+
+                vo.setFieldList(beans);
+                responseList.add(vo);
+            }
+        }
+        return responseList.stream().sorted(Comparator.comparing(TrialContainerClassificationVO::getId)).collect(Collectors.toList());
+    }
+
+    @Override
+    public Page<HashMap<Object, Object>> dataPage(TrialContainerClassificationPageVO obj) {
+        TrialContainerClassification trialContainerClassification = baseMapper.selectById(obj.getId());
+        List<HashMap<Object, Object>> hashMaps = baseMapper.selectTabDataAll(trialContainerClassification.getContainerInitTabName());
+        Page<HashMap<Object, Object>> page = new Page<>(obj.getCurrent(), obj.getSize(), hashMaps.size());
+        long size = page.getSize();
+        long current = (page.getCurrent() - 1) * size;
+
+        if (StringUtils.isNotEmpty(obj.getQueryValue()) && StringUtils.isNotEmpty(obj.getFieldKey())) {
+            //模糊查询
+            hashMaps = baseMapper.selectLikeByFieldKey(trialContainerClassification.getContainerInitTabName(), obj.getQueryValue(), obj.getFieldKey(), current, size);
+            int row = baseMapper.selectCountByField(trialContainerClassification.getContainerInitTabName(), obj.getQueryValue(), obj.getFieldKey());
+            page.setTotal(row);
+        } else {
+            //全部查询
+            hashMaps = baseMapper.selectLikeByFieldKey2(trialContainerClassification.getContainerInitTabName(), current, size);
+        }
+        return page.setRecords(hashMaps);
+    }
+
+    @Override
+    public boolean dataSubmit(TrialContainerDataDTO obj) {
+        if (obj.getBeanList().size() == 0) {
+            throw new ServiceException("数据不能为空");
+        }
+        if (ObjectUtil.isEmpty(obj.getId())) {
+            //新增
+            StringBuilder key = new StringBuilder();
+            StringBuilder value = new StringBuilder();
+            for (TrialContainerDataBean bean : obj.getBeanList()) {
+                key.append(bean.getFieldKey()).append(",");
+                value.append("'").append(ObjectUtil.isNotEmpty(bean) ? bean.getFieldValue() : null).append("',");
+            }
+            String fieldKeys = key.substring(0, key.length() - 1);
+            String fieldValues = value.substring(0, value.length() - 1).replace("'null'", "null");
+
+            return baseMapper.insertData(obj.getContainerInitTabName(), SnowFlakeUtil.getId(), DateUtil.format(obj.getFieldCalibrationTime(), "yyyy-MM-dd"), fieldKeys, fieldValues) > 0;
+
+        } else {
+            //修改
+            StringBuilder keys = new StringBuilder();
+            for (TrialContainerDataBean bean : obj.getBeanList()) {
+                keys.append(bean.getFieldKey()).append("='").append(ObjectUtil.isNotEmpty(bean) ? bean.getFieldValue() : null).append("',");
+            }
+            String values = "," + keys.substring(0, keys.length() - 1);
+
+            return baseMapper.updateData(obj.getContainerInitTabName(), obj.getId(), DateUtil.format(obj.getFieldCalibrationTime(), "yyyy-MM-dd"), values) > 0;
+        }
+    }
+
+    @Override
+    public boolean dataRemove(Long id, String containerInitTabName) {
+        return baseMapper.removeData(id, containerInitTabName) > 0;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean dataImportExcel(String containerInitTabName, MultipartFile file) throws IOException {
+        File convert = FileUtils.convert(file);
+        String path = convert.getPath();
+        try {
+            List<List<Map<String, Object>>> lists = ExcelUtils.importExcel(path, 1, 1, 1);
+            //获取第一行title
+            List<Object> titleName = new ArrayList<>();
+            for (List<Map<String, Object>> list : lists) {
+                for (Map<String, Object> row : list) {
+                    for (Map.Entry<String, Object> col : row.entrySet()) {
+                        titleName.add(col.getValue());
+                    }
+                    break;
+                }
+                break;
+            }
+
+            //根据title对应fieldName、构造fieldKeys
+            List<TrialContainerClassification> fieldBeans = baseMapper.selectList(Wrappers.<TrialContainerClassification>lambdaQuery()
+                    .eq(TrialContainerClassification::getContainerInitTabName, containerInitTabName)
+                    .isNotNull(TrialContainerClassification::getFieldId)
+            );
+            List<String> collect = titleName.stream().map(String::valueOf).collect(Collectors.toList());
+            StringBuilder keys = new StringBuilder();
+            for (TrialContainerClassification fieldBean : fieldBeans) {
+                for (String name : collect) {
+                    if (fieldBean.getFieldName().equals(name)) {
+                        keys.append(fieldBean.getFieldKey()).append(",");
+                        break;
+                    }
+                }
+            }
+            String fieldKeys = keys.substring(0, keys.length() - 1);
+
+            //解析构造插入数据
+            for (List<Map<String, Object>> list : lists) {
+                for (int i = 0; i < list.size(); i++) {
+                    if (i == 0) {
+                        //跳过title
+                        continue;
+                    }
+                    StringBuilder values = new StringBuilder();
+                    for (Map.Entry<String, Object> col : list.get(i).entrySet()) {
+                        values.append("'").append(ObjectUtil.isNotEmpty(col.getValue()) ? col.getValue() : null).append("',");
+                    }
+                    //fieldValues
+                    String fieldValues = values.substring(0, values.length() - 1).replace("'null'", "null");
+
+                    //插入一行的数据
+                    baseMapper.insertData(containerInitTabName, SnowFlakeUtil.getId(), DateUtil.format(new Date(), "yyyy-MM-dd"), fieldKeys, fieldValues);
+                }
+            }
+        } catch (Exception e) {
+            throw new ServiceException("导入时发生了异常,请检查excel模板格式是否与当前容器字段相对应");
+        } finally {
+            File file2 = new File(path);
+            if (file2.isFile() && file2.exists()) {
+                if (file2.delete()) {
+                    System.gc();
+                }
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public String dataVerificationNo(TrialContainerDataBean bean, Long id, String containerInitTabName) {
+        List<HashMap<Object, Object>> hashMaps = baseMapper.selectTabDataAll(containerInitTabName);
+        List<String> values = new ArrayList<>();
+        Map<Long, String> beanMap = new HashMap<>();
+        for (HashMap<Object, Object> hashMap : hashMaps) {
+            Long beanId = null;
+            String beanNumber = null;
+            for (Map.Entry<Object, Object> map : hashMap.entrySet()) {
+                if (("id").equals(map.getKey())) {
+                    beanId = (Long) map.getValue();
+                }
+                if (bean.getFieldKey().equals(map.getKey())) {
+                    beanNumber = String.valueOf(map.getValue());
+                    values.add(String.valueOf(map.getValue()));
+                }
+            }
+            if (beanId != null && beanNumber != null) {
+                beanMap.put(beanId, beanNumber);
+            }
+        }
+        if (ObjectUtil.isEmpty(id)) {
+            String obj = values.stream().filter(f -> f.equals(bean.getFieldValue())).findAny().orElse(null);
+            if (obj != null) {
+                return "编号已存在,请重新输入";
+            }
+        } else {
+            Map.Entry<Long, String> map = beanMap.entrySet().stream().filter(f -> f.getKey().equals(id)).findAny().orElse(null);
+            assert map != null;
+            if (!map.getValue().equals(bean.getFieldValue())) {
+                String obj = values.stream().filter(f -> f.equals(bean.getFieldValue())).findAny().orElse(null);
+                if (obj != null) {
+                    return "编号已存在,请重新输入";
+                }
+            }
+        }
+        return "编号可以使用";
+    }
+
+
+}

+ 48 - 0
blade-service/blade-business/src/main/java/org/springblade/business/utils/BeanFieldUtils.java

@@ -0,0 +1,48 @@
+package org.springblade.business.utils;
+
+import org.jetbrains.annotations.NotNull;
+
+public class BeanFieldUtils {
+
+    public static String getFieldType(Integer type) {
+        return getString(type);
+    }
+
+    @NotNull
+    public static String getString(Integer type) {
+        if (type != null) {
+            switch (type) {
+                case 1:
+                    return "varchar";
+                case 2:
+                    return "datetime";
+                case 3:
+                    return "bigint";
+                case 4:
+                    return "decimal";
+            }
+        }
+        return "varchar";
+    }
+
+    public static Integer getFieldLength(Integer type) {
+        return getInteger(type);
+    }
+
+    @NotNull
+    public static Integer getInteger(Integer type) {
+        if (type != null) {
+            switch (type) {
+                case 1:
+                    return 100;
+                case 2:
+                    return 0;
+                case 3:
+                case 4:
+                    return 10;
+            }
+        }
+        return 10;
+    }
+
+}

+ 512 - 0
blade-service/blade-business/src/main/java/org/springblade/business/utils/ExcelUtils.java

@@ -0,0 +1,512 @@
+package org.springblade.business.utils;
+
+import org.apache.log4j.Logger;
+import org.apache.poi.hssf.usermodel.*;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.*;
+
+import java.io.*;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Excel工具类
+ */
+public class ExcelUtils {
+
+    /*public static void main(String[] args) throws IOException {
+        String url = "C:\\Users\\56808\\Desktop\\A.xlsx";
+        File files = new File(url);
+        List<List<Map<String, Object>>> lists = ExcelUtils.importExcel(files.getCanonicalPath(), 2, 1, 1);
+        System.out.println(lists);
+    }*/
+
+    // 导出时excel的扩展名
+    public static final String EXTENSION_NAME = ".xlsx";
+    // 03版excel扩展名
+    private static final String XLS = ".xls";
+    // 07版excel扩展名
+    private static final String XLSX = ".xlsx";
+
+    private static Logger logger = Logger.getLogger(ExcelUtils.class);
+
+    /**
+     * 导入Excel到数据库
+     *
+     * @param filePath 导入的excel文件所在的绝对路径
+     * @param startRow 开始解析的行数
+     * @param startCol 开始解析的列数
+     * @param sheetNum 开始解析的sheet序号,如果不指定,默认传值为-1,则会解析所有sheet
+     * @return
+     */
+    public static List<List<Map<String, Object>>> importExcel(String filePath, int startRow, int startCol, int sheetNum) {
+        logger.info("========================= ExcelUtils.java ->> importExcel()从Excel表格中获取数据 ->> 开始 =========================");
+
+        // 用于存储最终整个Excel表格的数据
+        List<List<Map<String, Object>>> resultList = new ArrayList<>();
+
+        // 得到指定路径的文件File对象
+        File file = new File(filePath);
+        // 如果不存在
+        if (!file.exists()) {
+            logger.info("ExcelUtils.java ->> importExcel() ->> 错误操作:要读取Excel文件在指定路径(" + filePath + ")下找不到");
+            throw new RuntimeException("错误操作:要读取Excel文件在指定路径(" + filePath + ")下找不到");
+        }
+
+        InputStream input = null;
+        Workbook workbook = null;
+        try {
+            // 得到文件的资源输入流
+            input = new FileInputStream(file);
+            // 得到处理excel的Workbook对象
+            workbook = ExcelUtils.getWorkbookByExtensionName(input, filePath);
+            // 创建一个公式计算器,用于计算并得到Excel中的公式结果
+            FormulaEvaluator formulaEvaluator = workbook.getCreationHelper().createFormulaEvaluator();
+
+            // 得到Excel表格中sheet的数量
+            int totalSheetNum = workbook.getNumberOfSheets();
+            logger.info("ExcelUtils.java ->> importExcel() ->> 用户指定解析的Sheet表格序号sheetNum = " + sheetNum);
+            Sheet sheet = null;
+            if (sheetNum == -1) {
+                // 循环遍历sheet
+                for (int m = 0; m < totalSheetNum; m++) {
+                    logger.info("ExcelUtils.java ->> importExcel() ->> 开始解析第" + (m + 1) + "个Sheet表格");
+                    // 获取每一个sheet
+                    sheet = workbook.getSheetAt(m);
+                    // 保存Sheet中的数据到List集合中
+                    List<Map<String, Object>> sheetList = ExcelUtils.getDataBySheet(sheet, startRow, startCol, formulaEvaluator);
+                    // 保存存储有每个sheet数据的List到结果集List中去
+                    resultList.add(sheetList);
+                }
+            } else if (sheetNum > 0 && (sheetNum - 1) < totalSheetNum) {
+                logger.info("ExcelUtils.java ->> importExcel() ->> 开始解析第" + sheetNum + "个Sheet表格");
+                // 获取指定sheet序号的sheet表格
+                sheet = workbook.getSheetAt((sheetNum - 1));
+                // 保存Sheet中的数据到List集合中
+                List<Map<String, Object>> sheetList = ExcelUtils.getDataBySheet(sheet, startRow, startCol, formulaEvaluator);
+
+                resultList.add(sheetList);
+            } else {
+                logger.info("ExcelUtils.java ->> importExcel() ->> 该Excel表格只有" + totalSheetNum + "个Sheet表,而用户指定解析的Sheet表序号为" + sheetNum + ",不在范围内");
+                throw new RuntimeException("异常信息:该Excel表格只有" + totalSheetNum + "个Sheet表,而用户指定解析的Sheet表序号为" + sheetNum + ",不在范围内");
+            }
+
+        } catch (Exception e) {
+            logger.info("ExcelUtils.java ->> importExcel() ->> 异常信息:" + e);
+            throw new RuntimeException(e);
+        } finally {
+            try {
+                if (workbook != null) {
+                    workbook.close();
+                    logger.info("ExcelUtils.java ->> importExcel() ->> 关闭Workbook资源");
+                }
+                if (input != null) {
+                    input.close();
+                    logger.info("ExcelUtils.java ->> importExcel() ->> 关闭InputStream资源");
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            logger.info("========================= ExcelUtils.java ->> importExcel()从Excel表格中获取数据 ->> 结束 =========================");
+        }
+
+        return resultList;
+    }
+
+
+    /**
+     * 根据文件扩展名(.xls或.xlsx)获取对应的Workbook类型(HSSFWorkbook或XSSFWorkbook)
+     *
+     * @param input    关联excel文件的资源输入流
+     * @param filePath 文件路径
+     * @return
+     * @throws Exception
+     */
+    private static Workbook getWorkbookByExtensionName(InputStream input, String filePath) throws Exception {
+        logger.info("========================= ExcelUtils.java ->> getWorkbookByExtensionName()根据excel文件后缀(.xls或.xlsx)获取Workbook的方法 ->> 开始 =========================");
+
+
+        if (filePath == null || "".equals(filePath) || filePath.trim().isEmpty()) {
+            logger.info("ExcelUtils.java ->> getWorkbookByExtensionName() ->> 异常信息:excel文件路径不能为空");
+            throw new RuntimeException("异常信息:excel文件路径不能为空");
+        }
+
+        int index = filePath.lastIndexOf(".");
+        if (index == -1) {
+            logger.info("ExcelUtils.java ->> getWorkbookByExtensionName() ->> 异常信息:filePath指定的文件不带有扩展名,不属于文件类型");
+            throw new RuntimeException("异常信息:filePath指定的文件不带有扩展名,不属于文件类型");
+        }
+
+        Workbook wk = null;
+
+        String suffix = filePath.substring(index);
+        logger.info("ExcelUtils.java ->> getWorkbookByExtensionName() ->> filePath指定的文件扩展名为 = " + suffix);
+        if (ExcelUtils.XLS.equals(suffix)) {
+            wk = new HSSFWorkbook(input);
+        } else if (ExcelUtils.XLSX.equals(suffix)) {
+            wk = new XSSFWorkbook(input);
+        } else {
+            logger.info("ExcelUtils.java ->> getWorkbookByExtensionName() ->> 异常信息:filePath指定的文件扩展名不是excel文件格式(只能是.xls和.xlsx格式)");
+            throw new RuntimeException("异常信息:filePath指定的文件扩展名不是excel文件格式(只能是.xls和.xlsx格式)");
+        }
+
+        logger.info("========================= ExcelUtils.java ->> getWorkbookByExtensionName()根据excel文件后缀(.xls或.xlsx)获取Workbook的方法 ->> 结束 =========================");
+        return wk;
+    }
+
+
+    /**
+     * 获取sheet中的数据并返回一个List集合
+     *
+     * @param sheet            Sheet对象
+     * @param startRow         开始解析的行数
+     * @param startCol         开始解析的列数
+     * @param formulaEvaluator 公式计算器实例
+     * @return
+     * @throws Exception
+     */
+    private static List<Map<String, Object>> getDataBySheet(Sheet sheet, int startRow, int startCol, FormulaEvaluator formulaEvaluator) throws Exception {
+
+        logger.info("========================= ExcelUtils.java ->> getDataBySheet()从Sheet表格中获取数据 ->> 开始 =========================");
+
+        List<Map<String, Object>> sheetList = new ArrayList<>();
+
+        /*
+            Sheet中的getPhysicalNumberOfRows()和getLastRowNum()区别:
+                > getPhysicalNumberOfRows():获取的是物理行数,即会跳过空行的情况。
+                > getLastRowNum():获取的是最后一行的行编号(编号从0开始)。
+         */
+        // 得到表格中总共的行数,会比实际的行数小1
+        int totalRowNum = sheet.getLastRowNum() + 1;
+        logger.info("ExcelUtils.java ->> getDataBySheet() ->> 当前Sheet表格中总行数totalRowNum = " + totalRowNum);
+
+        // 循环当前表格中所有行
+        for (int i = (startRow - 1); i < totalRowNum; i++) {
+            // 得到Row行对象
+            Row row = sheet.getRow(i);
+            if (row == null || row.toString().trim().isEmpty()
+                    || "".equals(row.toString()) || "null".equals(row.toString())) {
+                logger.info("ExcelUtils.java ->> getDataBySheet() ->> 第" + (i + 1) + "行的内容为空,因此解析下一行");
+                continue;
+            }
+
+            /*
+                Row中的getPhysicalNumberOfCells()和getLastCellNum()区别:
+                    > getPhysicalNumberOfCells():获取的是物理列数,即会跳过空列的情况。
+                    > getLastCellNum():获取的是最后一列的列编号(编号从0开始)。
+             */
+            // 得到当前行中所有的单元格数量
+            int totalCellNum = row.getLastCellNum();
+            logger.info("ExcelUtils.java ->> getDataBySheet() ->> 第" + (i + 1) + "行的总列数totalCellNum = " + totalCellNum);
+            // 创建Map集合用于存储当前行中所有的单元格数据
+            Map<String, Object> rowMap = new HashMap<>();
+            // 循环当前行中所有单元格
+            for (int j = (startCol - 1); j < totalCellNum; j++) {
+                // 得到Cell列对象
+                Cell cell = row.getCell(j);
+
+                // 如果等于空
+                if (cell == null || cell.toString().trim().isEmpty()
+                        || "".equals(cell.toString()) || "null".equals(cell.toString())) {
+                    rowMap.put((i + 1) + "-" + (j + 1), "");
+                    logger.info("ExcelUtils.java ->> getDataBySheet() ->> 第" + (i + 1) + "行的第" + (j + 1) + "列" + "的单元格的内容为空,因此解析下一个单元格");
+                    continue;
+                }
+
+                // 进行公式解析,最后只存在Boolean、Numeric和String三种数据类型,此外就是Error了
+                // 其余数据类型,根据官方文档,完全可以忽略:http://poi.apache.org/spreadsheet/eval.html
+                CellValue cellValue = formulaEvaluator.evaluate(cell);
+
+                // 得到对应单元格的内容
+                String result = ExcelUtils.getCellResultByCellType(cell, cellValue, formulaEvaluator);
+                logger.info("ExcelUtils.java ->> getDataBySheet() ->> 第" + (i + 1) + "行的第" + (j + 1) + "列的单元格内容 = " + result);
+                // 保存到Map集合
+                //rowMap.put("Row" + (i + 1) + "-Col" + (j + 1), result);
+                rowMap.put((i + 1) + "-" + (j + 1), result);
+            }
+            // 将每个行对象保存到List中
+            sheetList.add(rowMap);
+        }
+
+        logger.info("========================= ExcelUtils.java ->> getDataBySheet()从Sheet表格中获取数据 ->> 结束 =========================");
+
+        return sheetList;
+    }
+
+
+    /**
+     * 根据单元格数据类型获取单元格的值
+     *
+     * @param cell             Cell单元格类型
+     * @param cellValue        CellValue单元格值类型
+     * @param formulaEvaluator 公式计算器
+     * @return
+     */
+    private static String getCellResultByCellType(Cell cell, CellValue cellValue, FormulaEvaluator formulaEvaluator) {
+
+        String result = null;
+        CellType cellTypeEnum = cellValue.getCellTypeEnum();
+
+        if (cellTypeEnum == CellType.NUMERIC) {
+            // 判断当前单元格是否为日期格式
+            if (DateUtil.isCellDateFormatted(cell)) {
+                logger.info("ExcelUtils.java ->> getCellResultByCellType() ->> 当前单元格类型为数值型中的日期类型");
+                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+                result = sdf.format(cell.getDateCellValue());
+            } else {
+                logger.info("ExcelUtils.java ->> getCellResultByCellType() ->> 当前单元格类型为数值型中的整数型");
+                DataFormatter dataFormatter = new DataFormatter();
+                result = dataFormatter.formatCellValue(cell, formulaEvaluator);
+            }
+        } else if (cellTypeEnum == CellType.STRING) {
+            logger.info("ExcelUtils.java ->> getCellResultByCellType() ->> 当前单元格类型为字符串");
+            result = cellValue.getStringValue();
+        } else if (cellTypeEnum == CellType.BOOLEAN) {
+            logger.info("ExcelUtils.java ->> getCellResultByCellType() ->> 当前单元格类型为布尔类型");
+            result = String.valueOf(cellValue.getBooleanValue());
+        }
+
+        return result;
+    }
+
+
+
+
+    /*
+     * 上面是Excel导入功能
+     * ====================================================================================================================================
+     * 下面是Excel导出功能
+     */
+
+
+    /**
+     * 导出数据到Excel
+     *
+     * @param title    Excel表格中sheet的名称以及大标题行的标题
+     * @param rowName  小标题行的标题
+     * @param dataList 主体数据
+     * @param out      输出流
+     * @throws Exception
+     */
+    public static void exportExcel(String title, String[] rowName, List<Object[]> dataList, OutputStream out) {
+        logger.info("========================= ExcelUtils.java ->> exportExcel()导出数据到Excel中 ->> 开始 =========================");
+
+        XSSFWorkbook workbook = null;
+        try {
+            /*
+                1,创建工作簿对象,然后创建大标题行,并设置标题
+             */
+            // 创建工作簿对象
+            workbook = new XSSFWorkbook();
+            // 创建一个表格对象
+            XSSFSheet sheet = workbook.createSheet("Sheet1");
+
+            String mark = "title";
+            // 定义大标题行的样式
+            XSSFCellStyle titleCellStyle = ExcelUtils.getCellStyle(workbook, mark);
+            // 如果参数title不等空,则设置Sheet表格的大标题
+            if (!"null".equals(title) && title != null
+                    && !"".equals(title) && !title.trim().isEmpty()) {
+                // 创建表格大标题行
+                XSSFRow titleRow = sheet.createRow(0);
+                // 创建表格大标题行的第一个单元格
+                XSSFCell titleCell = titleRow.createCell(0);
+
+                // 定义大标题行的宽度和高度(合并单元格)
+                sheet.addMergedRegion(new CellRangeAddress(0, 1, 0, (rowName.length - 1)));
+                // 设置大标题行的单元格样式
+                titleCell.setCellStyle(titleCellStyle);
+                // 设置大标题行的单元格名称
+                titleCell.setCellValue(title);
+                logger.info("ExcelUtils.java ->> exportExcel() ->> 导出到Excel中Sheet表格的标题title = " + title);
+            }
+
+            /*
+                2,创建小标题行并设置标题
+             */
+            // 定义所需列数 = 参数数组长度
+            int columnNum = rowName.length;
+            logger.info("ExcelUtils.java ->> exportExcel() ->> 导出到Excel中Sheet表格的标题列数columnNum = " + columnNum);
+            // 创建小标题行,由于0行和1行用作大标题行,所以小标题行从2开始
+            XSSFRow subTitleRow = sheet.createRow(2);
+
+            // 将列头设置到sheet的单元格中
+            for (int i = 0; i < columnNum; i++) {
+                // 创建小标题行的单元格
+                XSSFCell subTitleCell = subTitleRow.createCell(i);
+                // 设置单元格的单元格类型
+                subTitleCell.setCellType(CellType.STRING);
+                // 使用数组中的数据作为单元格的文本来创建小标题
+                XSSFRichTextString text = new XSSFRichTextString(rowName[i]);
+                // 设置小标题单元格的样式
+                subTitleCell.setCellStyle(titleCellStyle);
+                // 设置文本到小标题单元格中
+                subTitleCell.setCellValue(text);
+                logger.info("ExcelUtils.java ->> exportExcel() ->> 设置小标题行的第" + (i + 1) + "列的标题为 = " + text);
+            }
+
+
+            /*
+                3,开始循环主体数据,并设置到sheet表格中
+             */
+            logger.info("ExcelUtils.java ->> exportExcel() ->> 要导入到Excel表格中的数据行数 = " + dataList.size());
+            mark = "";
+            // 定义普通行的样式
+            XSSFCellStyle style = ExcelUtils.getCellStyle(workbook, mark);
+            // 循环遍历参数主体数据
+            for (int i = 0; i < dataList.size(); i++) {
+                // 遍历每个Object数组
+                Object[] objArr = dataList.get(i);
+                // 创建当前要填充数据的行对象
+                XSSFRow currentRow = sheet.createRow(i + 3);
+                // 循环遍历Object数组
+                for (int j = 0; j < objArr.length; j++) {
+
+                    XSSFCell cell = null;
+                    // 如果是每行的第一个单元格
+                    if (j == 0) {
+                        // 创建单元格,且设置单元格类型为数值型
+                        cell = currentRow.createCell(j, CellType.NUMERIC);
+                        cell.setCellValue(i + 1);
+                        // 设置单元格的样式
+                        cell.setCellStyle(titleCellStyle);
+                        logger.info("ExcelUtils.java ->> exportExcel() ->> 每行的第一个单元格作为行序号标记,当前序号为 = " + (i + 1));
+                    } else {
+                        // 创建单元格,且设置单元格类型为字符串型
+                        cell = currentRow.createCell(j, CellType.STRING);
+                        // 如果数组中的内容不为空
+                        if (objArr[j] != null && !"".equals(objArr[j])) {
+                            // 设置到单元格中
+                            cell.setCellValue(objArr[j].toString());
+                        }
+                        // 设置单元格的样式
+                        cell.setCellStyle(style);
+                        logger.info("ExcelUtils.java ->> exportExcel() ->> 第" + (i + 1) + "行的第" + (j + 1) + "个单元格的内容为 = " + (i + 1));
+                    }
+                }
+            }
+
+            // 让列宽随着导出的列长自动适应
+            for (int colNum = 0; colNum < columnNum; colNum++) {
+                //
+                int columnWidth = sheet.getColumnWidth(colNum) / 256;
+                for (int rowNum = 0; rowNum < sheet.getLastRowNum(); rowNum++) {
+                    XSSFRow currentRow;
+                    if (sheet.getRow(rowNum) == null) {
+                        currentRow = sheet.createRow(rowNum);
+                    } else {
+                        currentRow = sheet.getRow(rowNum);
+                    }
+                    if (currentRow.getCell(colNum) != null) {
+                        XSSFCell currentCell = currentRow.getCell(colNum);
+                        if (currentCell.getCellTypeEnum() == CellType.STRING) {
+                            int length = currentCell.getStringCellValue().getBytes().length;
+                            if (columnWidth < length) {
+                                columnWidth = length;
+                            }
+                        }
+                    }
+                }
+                // 设置列宽
+                if (colNum == 0) { // 如果是首列
+                    sheet.setColumnWidth(colNum, (columnWidth - 2) * 256);
+                } else { // 否则
+                    sheet.setColumnWidth(colNum, (columnWidth + 4) * 256);
+                }
+            }
+
+            workbook.write(out);
+        } catch (Exception e) {
+            logger.info("ExcelUtils.java ->> exportExcel() ->> 异常信息:" + e);
+            throw new RuntimeException(e);
+        } finally {
+            try {
+                if (workbook != null) {
+                    workbook.close();
+                    logger.info("ExcelUtils.java ->> exportExcel() ->> 关闭Workbook资源");
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            logger.info("========================= ExcelUtils.java ->> exportExcel()导出数据到Excel中 ->> 结束 =========================");
+        }
+    }
+
+    /**
+     * 得到单元格样式
+     *
+     * @param workbook XSSFWorkbook类型对象
+     * @param mark     标记,当且仅当为title时获取标题行的样式,否则为普通单元格样式
+     * @return
+     */
+    private static XSSFCellStyle getCellStyle(XSSFWorkbook workbook, String mark) {
+
+        // 创建字体对象
+        XSSFFont font = workbook.createFont();
+
+        // 创建单元格样式对象
+        XSSFCellStyle cellStyle = workbook.createCellStyle();
+
+        if ("title".equals(mark)) {
+            // 设置字体的大小
+            font.setFontHeightInPoints((short) 11);
+            // 设置字体加粗
+            font.setBold(true);
+            // 设置字体高度
+//            font.setFontHeight((short) 240);
+        } else {
+            // 设置字体的大小
+            font.setFontHeightInPoints((short) 10);
+            // 设置字体加粗
+            font.setBold(false);
+
+            // 设置单元格左边框
+            cellStyle.setBorderLeft(BorderStyle.THIN);
+            // 设置单元格左边框颜色
+            cellStyle.setLeftBorderColor(new XSSFColor());
+        }
+
+        // 设置单元格的边框样式
+        ExcelUtils.setCellCommonStyle(cellStyle, font);
+
+        return cellStyle;
+    }
+
+    /**
+     * 设置单元格的边框样式
+     *
+     * @param cellStyle XSSFCellStyle单元格样式对象
+     * @param font      XSSFFont字体对象
+     */
+    private static void setCellCommonStyle(XSSFCellStyle cellStyle, XSSFFont font) {
+        // 设置字体的格式
+        font.setFontName("等线");
+        // 设置单元格上边框
+        cellStyle.setBorderTop(BorderStyle.THIN);
+        // 设置单元格上边框颜色
+        cellStyle.setTopBorderColor(new XSSFColor());
+        // 设置单元格右边框
+        cellStyle.setBorderRight(BorderStyle.THIN);
+        // 设置单元格右边框颜色
+        cellStyle.setRightBorderColor(new XSSFColor());
+        // 设置单元格底边框
+        cellStyle.setBorderBottom(BorderStyle.THIN);
+        // 设置单元格底边框颜色
+        cellStyle.setBottomBorderColor(new XSSFColor());
+
+        // 在单元格样式用应用设置的字体
+        cellStyle.setFont(font);
+        // 设置是否自动换行
+        cellStyle.setWrapText(false);
+        // 设置水平对齐的样式为居中对齐
+        cellStyle.setAlignment(HorizontalAlignment.CENTER);
+        // 设置垂直对齐的样式为居中对齐
+        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+    }
+
+}
+

+ 0 - 32
blade-service/blade-manager/src/main/java/org/springblade/manager/excel/WbsExcelUtil.java

@@ -7,22 +7,12 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.ss.usermodel.*;
 import org.apache.poi.ss.util.CellRangeAddress;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
-import org.springblade.common.utils.SnowFlakeUtil;
-import org.springblade.core.secure.BladeUser;
-import org.springblade.core.secure.utils.SecureUtil;
-import org.springblade.core.tool.utils.Func;
-import org.springblade.manager.vo.ImportWbsNodeVO;
-import org.springblade.manager.vo.WbsTreeContractVO;
-import org.springframework.web.multipart.MultipartFile;
 
 import java.io.*;
 import java.util.*;
-import java.util.stream.Collectors;
-
 
 public class WbsExcelUtil {
 
-
     /**
      * 读取excel数据
      *
@@ -344,28 +334,6 @@ public class WbsExcelUtil {
         }
     }
 
-    /**
-     * 获取path路径
-     *
-     * @param file
-     * @return
-     * @throws IOException
-     */
-    public static File convert(MultipartFile file) throws IOException {
-        File convFile = new File(Objects.requireNonNull(Objects.requireNonNull(file.getOriginalFilename())));
-        FileOutputStream fos = null;
-        try {
-            convFile.createNewFile();
-            fos = new FileOutputStream(convFile);
-            fos.write(file.getBytes());
-        } catch (IOException e) {
-            e.printStackTrace();
-        } finally {
-            fos.close();
-        }
-        return convFile;
-    }
-
     public ArrayList<Map<String, String>> getDataListByType(ArrayList<Map<String, String>> result, Integer type) {
         String nodeName = getNodeNameByType(type);
         ArrayList<Map<String, String>> list = new ArrayList<>();

+ 2 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeContractServiceImpl.java

@@ -9,6 +9,7 @@ import lombok.AllArgsConstructor;
 import org.apache.commons.lang.StringUtils;
 import org.springblade.business.entity.ConstructionLedger;
 import org.springblade.business.feign.ConstructionLedgerFeignClient;
+import org.springblade.common.utils.FileUtils;
 import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.log.exception.ServiceException;
 import org.springblade.core.mp.base.BaseServiceImpl;
@@ -423,7 +424,7 @@ public class WbsTreeContractServiceImpl extends BaseServiceImpl<WbsTreeContractM
     @Override
     @Transactional(rollbackFor = Exception.class)
     public WbsTreeContractVO4 importWbsContract(MultipartFile excelFile, String primaryKeyId, Integer isSplicingNumber) throws IOException {
-        File file = WbsExcelUtil.convert(excelFile);
+        File file = FileUtils.convert(excelFile);
         String canonicalPath = file.getCanonicalPath();
         try {
             if (StringUtils.isNotEmpty(primaryKeyId) && isSplicingNumber != null) {

+ 2 - 1
blade-service/blade-manager/src/main/java/org/springblade/manager/service/impl/WbsTreeServiceImpl.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import lombok.AllArgsConstructor;
 import org.apache.commons.lang.StringUtils;
+import org.springblade.common.utils.FileUtils;
 import org.springblade.common.utils.SnowFlakeUtil;
 import org.springblade.core.log.exception.ServiceException;
 import org.springblade.core.redis.cache.BladeRedis;
@@ -347,7 +348,7 @@ public class WbsTreeServiceImpl extends BaseServiceImpl<WbsTreeMapper, WbsTree>
     public boolean importWbsTree(MultipartFile excelFile, WbsTree wbsTreeFu, WbsTree wbsTree1) throws IOException {
         wbsTreeFu.setAncestors("0");
         wbsTree1.setProjectNodeId(wbsTree1.getId());
-        File file = WbsExcelUtil.convert(excelFile);
+        File file = FileUtils.convert(excelFile);
         String canonicalPath = file.getCanonicalPath();
         try {
             //解析excel