Browse Source

增加 获取场景点云和深度图

xiewj 1 year ago
parent
commit
6f2b31c5c0

+ 15 - 1
pom.xml

@@ -30,6 +30,7 @@
         <protobuf-java.version>3.2.0</protobuf-java.version>
         <commons-pool2.version>2.5.0</commons-pool2.version>
         <zxing.version>2.1</zxing.version>
+        <poi.version>4.1.2</poi.version>
     </properties>
     <dependencies>
         <dependency>
@@ -61,6 +62,14 @@
                     <groupId>com.aliyun</groupId>
                     <artifactId>aliyun-java-sdk-core</artifactId>
                 </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.apache.poi</groupId>
+                    <artifactId>poi-ooxml</artifactId>
+                </exclusion>
             </exclusions>
         </dependency>
 
@@ -206,6 +215,12 @@
             <artifactId>guava</artifactId>
             <version>30.1-jre</version>
         </dependency>
+        <!-- excel工具 -->
+        <dependency>
+            <groupId>org.apache.poi</groupId>
+            <artifactId>poi-ooxml</artifactId>
+            <version>${poi.version}</version>
+        </dependency>
 
     </dependencies>
     <dependencyManagement>
@@ -312,7 +327,6 @@
                 <artifactId>log4j-to-slf4j</artifactId>
                 <version>2.17.0</version>
             </dependency>
-
         </dependencies>
     </dependencyManagement>
 

+ 22 - 0
src/main/java/com/fdkankan/openApi/bean/vo/SceneDataDownloadVO.java

@@ -0,0 +1,22 @@
+package com.fdkankan.openApi.bean.vo;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+
+/**
+ * Created by Xiewj 2022年11月21日11:15:28
+ * 配置表
+ */
+@Data
+@AllArgsConstructor
+public class SceneDataDownloadVO  {
+
+   /** "文件名") */
+    private String fileName;
+
+   /** "oss路径") */
+    private String url;
+
+
+}

+ 48 - 0
src/main/java/com/fdkankan/openApi/config/ScheduledConfig.java

@@ -0,0 +1,48 @@
+package com.fdkankan.openApi.config;
+
+import org.springframework.boot.autoconfigure.batch.BatchProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.scheduling.annotation.SchedulingConfigurer;
+import org.springframework.scheduling.config.ScheduledTaskRegistrar;
+import org.springframework.util.CollectionUtils;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.concurrent.Executors;
+
+@Configuration
+public class ScheduledConfig implements SchedulingConfigurer {
+
+    /**
+     * 此配置实现:
+     * 1、同一个定时任务等待上一次执行完毕后再执行
+     * 2、不同定时任务同时执行
+     * @param taskRegistrar
+     */
+    @Override
+    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
+        Method[] methods = BatchProperties.Job.class.getMethods();
+        int defaultPoolSize = 3;
+        int corePoolSize = 0;
+
+        if (!CollectionUtils.isEmpty(Arrays.asList(methods))) {
+
+            for (Method method : methods) {
+
+                Scheduled annotation = method.getAnnotation(Scheduled.class);
+
+                if (annotation != null) {
+
+                    corePoolSize++;
+                }
+            }
+            if (defaultPoolSize > corePoolSize) {
+
+                corePoolSize = defaultPoolSize;
+            }
+
+            taskRegistrar.setScheduler(Executors.newScheduledThreadPool(corePoolSize));
+        }
+    }
+}

+ 20 - 1
src/main/java/com/fdkankan/openApi/controller/www/SceneController.java

@@ -4,11 +4,12 @@ package com.fdkankan.openApi.controller.www;
 import cn.dev33.satoken.annotation.SaIgnore;
 import com.fdkankan.common.constant.ErrorCode;
 import com.fdkankan.common.exception.BusinessException;
-import com.fdkankan.openApi.common.PageInfo;
 import com.fdkankan.openApi.aop.RedisLimit;
 import com.fdkankan.openApi.aop.ValidateApi;
+import com.fdkankan.openApi.common.PageInfo;
 import com.fdkankan.openApi.controller.BaseController;
 import com.fdkankan.openApi.entity.www.ScenePlus;
+import com.fdkankan.openApi.service.system.SceneDataDownloadService;
 import com.fdkankan.openApi.service.laser.SceneService;
 import com.fdkankan.openApi.service.www.IScenePlusService;
 import com.fdkankan.openApi.vo.BaseSceneParamVo;
@@ -38,6 +39,8 @@ public class SceneController extends BaseController {
 
     @Autowired
     private SceneService sceneService;
+    @Autowired
+    private SceneDataDownloadService sceneDataDownloadService;
 
     /**
      * 获取场景列表
@@ -124,5 +127,21 @@ public class SceneController extends BaseController {
     public ResultData getSceneVideo(@RequestBody @Validated BaseSceneParamVo param) {
         return ResultData.ok(scenePlusService.getSceneVideo(param.getSceneCode(), this.getUserId()));
     }
+    /**
+     * 获取点云和深度图接口
+     * @return
+     */
+    @SaIgnore
+    @PostMapping("/getSceneRaw")
+    @RedisLimit(name = "scene/getSceneRaw", limitCount = 1, period = 1)
+//    @ValidateApi(method = "scene:getSceneRaw")
+    public ResultData getSceneRaw(@RequestBody @Validated BaseSceneParamVo param) {
+        ScenePlus scenePlus = scenePlusService.getByNumAndUserId(this.getUserId(), param.getSceneCode());
+        if (ObjectUtils.isEmpty(scenePlus)) {
+            throw new BusinessException(ErrorCode.FAILURE_CODE_5005);
+        }
+        return sceneDataDownloadService.sceneDownloadDepthMapAndPly(param.getSceneCode(),this.getUserId());
+    }
+
 }
 

+ 34 - 0
src/main/java/com/fdkankan/openApi/entity/system/SceneDataDownloadEntity.java

@@ -0,0 +1,34 @@
+package com.fdkankan.openApi.entity.system;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.util.Date;
+
+
+/**
+ * Created by Xiewj 2022年11月21日11:15:28
+ * 配置表
+ */
+@Data
+@TableName("t_scene_data_download")
+public class SceneDataDownloadEntity extends SystemBaseEntity {
+
+   /** "类别 1,点云和深度图") 2下载文档 */
+    private int type;
+   /** "场景码") */
+    private String sceneCode;
+   /** "文件名") */
+    private String fileName;
+
+   /** "oss路径") */
+    private String ossKey;
+
+   /** "bucket") */
+    private String bucket;
+
+   /** "删除状态 null未删除") */
+    private Date ossDelete;
+
+    private Long userId;
+}

+ 20 - 0
src/main/java/com/fdkankan/openApi/service/system/SceneDataDownloadService.java

@@ -0,0 +1,20 @@
+package com.fdkankan.openApi.service.system;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fdkankan.openApi.entity.system.SceneDataDownloadEntity;
+import com.fdkankan.web.response.ResultData;
+
+import java.util.List;
+
+/**
+ * Created by Xiewj on 2022年11月21日11:22:02
+ */
+public interface SceneDataDownloadService extends IService<SceneDataDownloadEntity> {
+    ResultData sceneDownloadDepthMapAndPly(String sceneCode, Long userId);
+    List<SceneDataDownloadEntity> findByOssDeleteIsNull();
+
+    List<SceneDataDownloadEntity>  findByOssDeleteIsNullBySceneCode(String sceneCode);
+    SceneDataDownloadEntity  findBySceneCodeAndFileNameAndOssDeleteIsNull(String sceneCode,String fileName);
+
+}

+ 162 - 0
src/main/java/com/fdkankan/openApi/service/system/impl/SceneDataDownloadServiceImpl.java

@@ -0,0 +1,162 @@
+package com.fdkankan.openApi.service.system.impl;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fdkankan.common.exception.BusinessException;
+import com.fdkankan.fyun.face.FYunFileServiceInterface;
+import com.fdkankan.openApi.bean.vo.SceneDataDownloadVO;
+import com.fdkankan.openApi.entity.system.SceneDataDownloadEntity;
+import com.fdkankan.openApi.mapper.laser.SceneDataDownloadMapper;
+import com.fdkankan.openApi.service.laser.SceneService;
+import com.fdkankan.openApi.service.system.SceneDataDownloadService;
+import com.fdkankan.web.response.ResultData;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+/**
+ * Created by Xiewj on 2022年11月21日11:22:02
+ */
+@Slf4j
+@Service
+public class SceneDataDownloadServiceImpl extends ServiceImpl<SceneDataDownloadMapper, SceneDataDownloadEntity> implements SceneDataDownloadService {
+    @Value("${laserConfig.defaultFolder}")
+    private String laserDefaultFolder;
+    @Value("${laserConfig.ossUrl}")
+    private String laserOSSurl;
+    @Value("${openApiPath.temp}")
+    private String tempPath;
+
+
+
+    private String laserBucket="laser-data";
+
+    @Autowired
+    SceneService sceneService;
+    @Autowired
+    private FYunFileServiceInterface fYunFileService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public ResultData sceneDownloadDepthMapAndPly(String sceneCode, Long userId) {
+        List<SceneDataDownloadEntity> sceneDataDownloadEntityList = findByOssDeleteIsNullBySceneCode(sceneCode);
+        String xlsxUrl="sceneRawData/"+sceneCode+"/"+sceneCode+".xlsx";
+
+        String e57Url=laserDefaultFolder + "/" + sceneCode + "/data/"+sceneCode+"_e57.zip";
+        boolean e57Flag=false;
+        if(fYunFileService.fileExist(laserBucket,e57Url)){
+            e57Flag=true;
+        }
+        boolean e57DBFlag = sceneDataDownloadEntityList.stream().allMatch(a -> a.getFileName().contains("e57"));
+        if (CollectionUtil.isNotEmpty(sceneDataDownloadEntityList) &&fYunFileService.fileExist(laserBucket,xlsxUrl)&&e57Flag&&e57DBFlag){
+            log.info("场景存在库");
+            List<SceneDataDownloadVO> sceneDataDownloadVOS = sceneDataDownloadEntityList.stream()
+                    .map(sceneData -> new SceneDataDownloadVO(sceneData.getFileName(),laserOSSurl+sceneData.getOssKey()))
+                    .collect(Collectors.toList());
+            return ResultData.ok(sceneDataDownloadVOS);
+        }else {
+            List<String> copyFiles=new ArrayList<>();
+            //先检查e57
+            if(e57Flag){
+                String e57CopyUrl="sceneRawData/"+sceneCode+"/"+ FileUtil.getName(e57Url);
+                fYunFileService.copyFileInBucket(laserBucket,e57Url,e57CopyUrl);
+                copyFiles.add(e57CopyUrl);
+                e57Flag=true;
+            }
+            List<String> strings = fYunFileService.listRemoteFiles(laserBucket, laserDefaultFolder + "/" + sceneCode + "/data/" + sceneCode + "/depthmap/");
+            boolean has_cloud=false;
+            boolean has_png=false;
+            ArrayList<Map<String, Object>> rows =new ArrayList<>();
+            for (String file : strings) {
+                String name = FileUtil.getName(file);
+                if (name.contains("ply")&&e57Flag){
+                    continue;
+                }
+                if (FileUtil.extName(file).equalsIgnoreCase("png")&& has_png==false){
+                    has_png=true;
+                }
+                if (FileUtil.extName(file).equalsIgnoreCase("ply")&& has_cloud==false){
+                    has_cloud=true;
+                }
+                String copyUrl="sceneRawData/"+sceneCode+"/"+ FileUtil.getName(file);
+                fYunFileService.copyFileInBucket(laserBucket,file,copyUrl);
+                copyFiles.add(copyUrl);
+            }
+            if ((has_png && has_cloud )|| (has_png && e57Flag)){
+                return  saveAndDownLoad(sceneCode, copyFiles,userId);
+            }else if (!has_png  && !has_cloud ){
+                throw new BusinessException(-1,"数据不全,请重算后再尝试下载");
+            }else if (has_png && (!has_cloud||!e57Flag  )){
+                throw new BusinessException(-1,"点云数据不全,请重算后再尝试下载");
+            }else if (!has_png  && has_cloud){
+                throw new BusinessException(-1,"深度图数据不全,请重算后再尝试下载");
+            }
+        }
+        return ResultData.ok();
+    }
+    private ResultData saveAndDownLoad(String sceneCode,   List<String> data,Long userId) {
+        List<SceneDataDownloadEntity> dataDownloadEntities=new ArrayList<>();
+        for (String file : data) {
+             String name = FileUtil.getName(file);
+             //上传OSS并且入库
+            dataDownloadEntities.add(findAndSave(sceneCode, userId, file, name, 1));
+        }
+        List<SceneDataDownloadVO> sceneDataDownloadVOS = dataDownloadEntities.stream()
+                .map(sceneData -> new SceneDataDownloadVO(sceneData.getFileName(),laserOSSurl+sceneData.getOssKey()))
+                .collect(Collectors.toList());
+        return ResultData.ok(sceneDataDownloadVOS);
+    }
+
+    private SceneDataDownloadEntity findAndSave(String sceneCode, Long userId, String xlsxUrl,String fileName,int type) {
+        SceneDataDownloadEntity sceneDataDownload = findBySceneCodeAndFileNameAndOssDeleteIsNull( sceneCode,fileName);
+        if (ObjectUtil.isNull(sceneDataDownload)){
+            sceneDataDownload=new SceneDataDownloadEntity();
+            sceneDataDownload.setOssKey(xlsxUrl);
+            sceneDataDownload.setFileName(fileName);
+            sceneDataDownload.setType(type);
+            sceneDataDownload.setSceneCode(sceneCode);
+            sceneDataDownload.setBucket(laserBucket);
+            sceneDataDownload.setUserId(userId);
+            save(sceneDataDownload);
+            return sceneDataDownload;
+        }
+        return sceneDataDownload;
+    }
+
+
+//
+
+    @Override
+    public List<SceneDataDownloadEntity> findByOssDeleteIsNull() {
+        return list(Wrappers.<SceneDataDownloadEntity>lambdaQuery().isNull(SceneDataDownloadEntity::getOssDelete));
+    }
+
+    @Override
+    public List<SceneDataDownloadEntity> findByOssDeleteIsNullBySceneCode(String sceneCode) {
+        LambdaQueryWrapper<SceneDataDownloadEntity> wrapper = Wrappers.lambdaQuery();
+        wrapper.isNull(SceneDataDownloadEntity::getOssDelete);
+        wrapper.eq(SceneDataDownloadEntity::getSceneCode,sceneCode);
+        return list(wrapper);
+    }
+
+    @Override
+    public SceneDataDownloadEntity findBySceneCodeAndFileNameAndOssDeleteIsNull(String sceneCode, String fileName) {
+        LambdaQueryWrapper<SceneDataDownloadEntity> wrapper = Wrappers.lambdaQuery();
+        wrapper.isNull(SceneDataDownloadEntity::getOssDelete);
+        wrapper.eq(SceneDataDownloadEntity::getSceneCode,sceneCode);
+        wrapper.eq(SceneDataDownloadEntity::getFileName,fileName);
+        return getOne(wrapper);
+    }
+}

+ 69 - 0
src/main/java/com/fdkankan/openApi/task/SceneOssDataTask.java

@@ -0,0 +1,69 @@
+package com.fdkankan.openApi.task;
+
+/**
+ * @author Xiewj
+ * @date 2022年11月21日11:25:30
+ */
+
+import cn.hutool.core.date.DateUnit;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import com.fdkankan.fyun.face.FYunFileServiceInterface;
+import com.fdkankan.openApi.entity.system.SceneDataDownloadEntity;
+import com.fdkankan.openApi.service.system.SceneDataDownloadService;
+import com.fdkankan.openApi.util.DateUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.List;
+
+
+/**
+ * 清理场景下载OSS任务
+ *
+ * @author
+ */
+@Component("SceneOssDataTask")
+@Slf4j
+public class SceneOssDataTask {
+    @Autowired
+    SceneDataDownloadService sceneDataDownloadService;
+    @Autowired
+    private FYunFileServiceInterface fYunFileService;
+
+    @Scheduled(cron = "0 0 1 * * ?")
+    public void handel() {
+        // 每天凌晨1点执行的任务逻辑
+        log.info("开始执行清理下载任务");
+        int count=0;
+        List<SceneDataDownloadEntity> sceneDataDownloadEntityList = sceneDataDownloadService.findByOssDeleteIsNull();
+        int totalCount=sceneDataDownloadEntityList.size();
+        for (SceneDataDownloadEntity sceneDataDownloadEntity : sceneDataDownloadEntityList) {
+            long betweenHour = DateUtils.getDatePoorUnit(DateUtil.date(), sceneDataDownloadEntity.getCreateTime(), DateUnit.HOUR);
+            if (StrUtil.isNotEmpty(sceneDataDownloadEntity.getOssKey())&&
+                StrUtil.isNotEmpty(sceneDataDownloadEntity.getBucket())&&
+                betweenHour>(7*24)
+            ){
+                String ossKey=sceneDataDownloadEntity.getOssKey();
+                String bucket=sceneDataDownloadEntity.getBucket();
+                if (fYunFileService.fileExist(bucket,ossKey)) {
+                    log.info("ossKey删除的bucket--{},地址是{}",bucket,ossKey);
+                    try {
+                        fYunFileService.deleteFile(bucket,ossKey);
+                    } catch (IOException e) {
+                        throw new RuntimeException(e);
+                    }
+                    sceneDataDownloadEntity.setOssDelete(DateUtil.date());
+                    boolean b = sceneDataDownloadService.updateById(sceneDataDownloadEntity);
+                    log.info("数据库修改状态为::{}",b);
+
+                }
+            }
+        }
+        log.info("执行清理下载任务结束,共计::{},实际处理::{}",totalCount,count);
+
+    }
+}

+ 202 - 0
src/main/java/com/fdkankan/openApi/util/DateUtils.java

@@ -0,0 +1,202 @@
+package com.fdkankan.openApi.util;
+
+import cn.hutool.core.date.DateUnit;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateFormatUtils;
+
+import java.lang.management.ManagementFactory;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.*;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * 时间工具类
+ *
+ * @author fdkk
+ */
+@Slf4j
+public class DateUtils extends org.apache.commons.lang3.time.DateUtils
+{
+    public static String YYYY = "yyyy";
+
+    public static String YYYY_MM = "yyyy-MM";
+
+    public static String YYYY_MM_DD = "yyyy-MM-dd";
+
+    public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss";
+
+    public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
+
+    private static String[] parsePatterns = {
+            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM",
+            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
+            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
+
+    /**
+     * 获取当前Date型日期
+     *
+     * @return Date() 当前日期
+     */
+    public static Date getNowDate()
+    {
+        return new Date();
+    }
+    /**
+     * 把时间戳转成北京时间的字符串表示
+     *
+     * @param input
+     * @return
+     */
+    public static String convert2CST(long input) {
+        SimpleDateFormat dff = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        dff.setTimeZone(TimeZone.getTimeZone("GMT+08"));
+
+        try {
+            return dff.format(new Date(input));
+        } catch (Exception e) {
+            log.error("convert2CST meet exception.", e);
+        }
+
+        return "";
+    }
+    /**
+     * 获取当前日期, 默认格式为yyyy-MM-dd
+     *
+     * @return String
+     */
+    public static String getDate()
+    {
+        return dateTimeNow(YYYY_MM_DD);
+    }
+
+    public static final String getTime()
+    {
+        return dateTimeNow(YYYY_MM_DD_HH_MM_SS);
+    }
+
+    public static final String dateTimeNow()
+    {
+        return dateTimeNow(YYYYMMDDHHMMSS);
+    }
+
+    public static final String dateTimeNow(final String format)
+    {
+        return parseDateToStr(format, new Date());
+    }
+
+    public static final String dateTime(final Date date)
+    {
+        return parseDateToStr(YYYY_MM_DD, date);
+    }
+
+    public static final String parseDateToStr(final String format, final Date date)
+    {
+        return new SimpleDateFormat(format).format(date);
+    }
+
+    public static final Date dateTime(final String format, final String ts)
+    {
+        try
+        {
+            return new SimpleDateFormat(format).parse(ts);
+        }
+        catch (ParseException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * 日期路径 即年/月/日 如2018/08/08
+     */
+    public static final String datePath()
+    {
+        Date now = new Date();
+        return DateFormatUtils.format(now, "yyyy/MM/dd");
+    }
+
+    /**
+     * 日期路径 即年/月/日 如20180808
+     */
+    public static final String dateTime()
+    {
+        Date now = new Date();
+        return DateFormatUtils.format(now, "yyyyMMdd");
+    }
+
+    /**
+     * 日期型字符串转化为日期 格式
+     */
+    public static Date parseDate(Object str)
+    {
+        if (str == null)
+        {
+            return null;
+        }
+        try
+        {
+            return parseDate(str.toString(), parsePatterns);
+        }
+        catch (ParseException e)
+        {
+            return null;
+        }
+    }
+
+    /**
+     * 获取服务器启动时间
+     */
+    public static Date getServerStartDate()
+    {
+        long time = ManagementFactory.getRuntimeMXBean().getStartTime();
+        return new Date(time);
+    }
+
+    /**
+     * 计算两个时间差
+     */
+    public static String getDatePoor(Date endDate, Date nowDate)
+    {
+        long nd = 1000 * 24 * 60 * 60;
+        long nh = 1000 * 60 * 60;
+        long nm = 1000 * 60;
+        // long ns = 1000;
+        // 获得两个时间的毫秒时间差异
+        long diff = endDate.getTime() - nowDate.getTime();
+        // 计算差多少天
+        long day = diff / nd;
+        // 计算差多少小时
+        long hour = diff % nd / nh;
+        // 计算差多少分钟
+        long min = diff % nd % nh / nm;
+        // 计算差多少秒//输出结果
+        // long sec = diff % nd % nh % nm / ns;
+        return day + "天" + hour + "小时" + min + "分钟";
+    }
+    /**
+     * 增加 LocalDateTime ==> Date
+     */
+    public static Date toDate(LocalDateTime temporalAccessor)
+    {
+        ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault());
+        return Date.from(zdt.toInstant());
+    }
+
+    /**
+     * 增加 LocalDate ==> Date
+     */
+    public static Date toDate(LocalDate temporalAccessor)
+    {
+        LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0));
+        ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault());
+        return Date.from(zdt.toInstant());
+    }
+    public static long getDatePoorUnit(Date endDate, Date nowDate, DateUnit unit) {
+        // 获得两个时间的毫秒时间差异
+        long diff = endDate.getTime() - nowDate.getTime();
+
+        return diff / unit.getMillis();
+    }
+}

+ 55 - 0
src/main/java/com/fdkankan/openApi/util/ExcelHutoolUtil.java

@@ -0,0 +1,55 @@
+package com.fdkankan.openApi.util;
+
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.poi.excel.ExcelUtil;
+import cn.hutool.poi.excel.ExcelWriter;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Excel相关处理
+ *
+ * @author fdkk
+ */
+public class ExcelHutoolUtil {
+    /**
+     * 对list数据源将其里面的数据导入到excel表单
+     *
+     * @param response 返回数据
+     * @param list 导出数据集合
+     * @param filename 工作表的名称
+     * @return 结果
+     */
+    public static void exportExcelByHutool(HttpServletResponse response, List<Map<String, Object>> list, String filename) throws IOException {
+        // 通过工具类创建writer,默认创建xls格式
+        ExcelWriter writer = ExcelUtil.getWriter();
+        // 一次性写出内容,使用默认样式,强制输出标题
+        writer.write(list, true);
+        //out为OutputStream,需要写出到的目标流
+        filename = encodingFilename(filename);
+        //response为HttpServletResponse对象
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
+        //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码
+        response.setHeader("Content-Disposition","attachment;filename="+filename);
+        ServletOutputStream out=response.getOutputStream();
+
+        writer.flush(out, true);
+        // 关闭writer,释放内存
+        writer.close();
+        //此处记得关闭输出Servlet流
+        IoUtil.close(out);
+    }
+    /**
+     * 编码文件名
+     */
+    public static String encodingFilename(String filename)
+    {
+        filename = UUID.randomUUID().toString() + "_" + filename + ".xlsx";
+        return filename;
+    }
+}