Просмотр исходного кода

feat(park-overview-service): 添加用水信息管理功能

- 新增 WaterUsageData 实体类和对应的 Mapper、Service、Controller
- 实现用水信息的增删改查、分页查询等功能
- 添加用水信息的 Excel 导入导出功能
- 新增用水信息模板下载功能
- 重构 SmartEmployment 相关代码,优化导入导出功能
林仔 8 месяцев назад
Родитель
Сommit
48924709ff
18 измененных файлов с 659 добавлено и 12 удалено
  1. 2 0
      park-overview-service/src/main/java/com/zksy/park/ParkOverviewServiceApplication.java
  2. 12 4
      park-overview-service/src/main/java/com/zksy/park/controller/SmartEmploymentController.java
  3. 105 0
      park-overview-service/src/main/java/com/zksy/park/controller/WaterUsageDataController.java
  4. 78 0
      park-overview-service/src/main/java/com/zksy/park/domain/WaterUsageData.java
  5. 1 1
      park-overview-service/src/main/java/com/zksy/park/listener/SmartEmploymentListener.java
  6. 53 0
      park-overview-service/src/main/java/com/zksy/park/listener/WaterUsageDataListener.java
  7. 20 0
      park-overview-service/src/main/java/com/zksy/park/mapper/WaterUsageDataMapper.java
  8. 16 0
      park-overview-service/src/main/java/com/zksy/park/service/WaterUsageDataService.java
  9. 1 4
      park-overview-service/src/main/java/com/zksy/park/service/impl/FileGeneralServiceImpl.java
  10. 1 1
      park-overview-service/src/main/java/com/zksy/park/service/impl/ParkInfoServiceImpl.java
  11. 1 1
      park-overview-service/src/main/java/com/zksy/park/service/impl/ParkStemScreenServiceImpl.java
  12. 1 1
      park-overview-service/src/main/java/com/zksy/park/service/impl/ParkVisualVideoServiceImpl.java
  13. 37 0
      park-overview-service/src/main/java/com/zksy/park/service/impl/WaterUsageDataServiceImpl.java
  14. 74 0
      park-overview-service/src/main/java/com/zksy/park/utils/DowntemplateUtil.java
  15. 147 0
      park-overview-service/src/main/java/com/zksy/park/utils/ExcelExportUtil.java
  16. 110 0
      park-overview-service/src/main/java/com/zksy/park/utils/ExcelImportUtil.java
  17. BIN
      park-overview-service/src/main/resources/template/智慧用工信息.xlsx
  18. BIN
      park-overview-service/src/main/resources/template/用水信息.xlsx

+ 2 - 0
park-overview-service/src/main/java/com/zksy/park/ParkOverviewServiceApplication.java

@@ -1,11 +1,13 @@
 package com.zksy.park;
 package com.zksy.park;
 
 
+import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.EnableScheduling;
 
 
 @SpringBootApplication
 @SpringBootApplication
 @EnableScheduling
 @EnableScheduling
+@MapperScan("com.zksy.park.mapper")
 public class ParkOverviewServiceApplication {
 public class ParkOverviewServiceApplication {
     public static void main(String[] args) {
     public static void main(String[] args) {
         SpringApplication.run(ParkOverviewServiceApplication.class, args);
         SpringApplication.run(ParkOverviewServiceApplication.class, args);

+ 12 - 4
park-overview-service/src/main/java/com/zksy/park/controller/SmartEmploymentController.java

@@ -8,7 +8,11 @@ import com.zksy.common.core.domain.Result;
 import com.zksy.common.enums.BusinessType;
 import com.zksy.common.enums.BusinessType;
 import com.zksy.common.utils.SearchUtil;
 import com.zksy.common.utils.SearchUtil;
 import com.zksy.park.domain.SmartEmployment;
 import com.zksy.park.domain.SmartEmployment;
+import com.zksy.park.domain.WaterUsageData;
 import com.zksy.park.service.SmartEmploymentService;
 import com.zksy.park.service.SmartEmploymentService;
+import com.zksy.park.utils.DowntemplateUtil;
+import com.zksy.park.utils.ExcelExportUtil;
+import com.zksy.park.utils.ExcelImportUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import lombok.extern.slf4j.Slf4j;
@@ -95,16 +99,20 @@ public class SmartEmploymentController {
     @GetMapping("/exportExcel")
     @GetMapping("/exportExcel")
     @ApiOperation(value = "导出智慧用工信息Excel")
     @ApiOperation(value = "导出智慧用工信息Excel")
     @Log(title = "导出智慧用工信息Excel", businessType = BusinessType.EXPORT)
     @Log(title = "导出智慧用工信息Excel", businessType = BusinessType.EXPORT)
-    public void exportExcel(HttpServletResponse response) throws IOException {
-        service.exportExcel(response);
+    public void exportExcel(HttpServletResponse response,String conditionJson) throws Exception {
+        ExcelExportUtil.exportExcel(response,service.list(SearchUtil.parseWhereSql(conditionJson)), SmartEmployment.class, "智慧用工信息","智慧用工信息");
     }
     }
 
 
     @PostMapping("/importExcel")
     @PostMapping("/importExcel")
     @ApiOperation(value = "导入智慧用工信息Excel")
     @ApiOperation(value = "导入智慧用工信息Excel")
     @Log(title = "导入智慧用工信息Excel", businessType = BusinessType.IMPORT)
     @Log(title = "导入智慧用工信息Excel", businessType = BusinessType.IMPORT)
     public Result<Object> importExcel(@RequestParam("file") MultipartFile file) throws IOException {
     public Result<Object> importExcel(@RequestParam("file") MultipartFile file) throws IOException {
-        return service.importExcel(file);
+        return Result.ok(service.saveOrUpdateBatch(ExcelImportUtil.importExcel(file, SmartEmployment.class)));
+    }
+    @GetMapping("/downloadTemplateWord")
+    @ApiOperation(value = "下载智慧用工信息模板", notes = "下载智慧用工信息模板")
+    public void getUploadTemplate(HttpServletResponse response) throws IOException {
+        DowntemplateUtil.downloadTemplate(response,"智慧用工信息");
     }
     }
-
 
 
 }
 }

+ 105 - 0
park-overview-service/src/main/java/com/zksy/park/controller/WaterUsageDataController.java

@@ -0,0 +1,105 @@
+package com.zksy.park.controller;
+
+
+import cn.hutool.core.date.DateTime;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zksy.common.annotation.Log;
+import com.zksy.common.core.domain.Result;
+import com.zksy.common.enums.BusinessType;
+import com.zksy.common.utils.SearchUtil;
+import com.zksy.park.domain.SmartEmployment;
+import com.zksy.park.domain.WaterUsageData;
+import com.zksy.park.service.WaterUsageDataService;
+import com.zksy.park.utils.DowntemplateUtil;
+import com.zksy.park.utils.ExcelExportUtil;
+import com.zksy.park.utils.ExcelImportUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * 智慧用工
+ */
+@Slf4j
+@RestController
+@RequestMapping("/waterUsageData")
+@Api(tags = "用水信息", description = "用水信息")
+public class WaterUsageDataController {
+
+    @Autowired
+    private WaterUsageDataService service;
+
+    @GetMapping("/getById/{id}")
+    @ApiOperation(value = "用水信息搜索getById")
+    public Result getById(@PathVariable String id) {
+        return Result.ok(service.getById(id));
+    }
+
+    @GetMapping("/findByPage")
+    @ApiOperation(value = "用水信息分页")
+    public Result findByPage(long pageNum, long pageSize, String conditionJson) throws Exception {
+        return Result.ok(service.page(new Page<>(pageNum, pageSize), SearchUtil.parseWhereSql(conditionJson)));
+    }
+
+    @GetMapping("/getList")
+    @ApiOperation(value = "查询所有用水信息")
+    public Result getList(String conditionJson) throws Exception {
+        return Result.ok(service.list(SearchUtil.parseWhereSql(conditionJson)));
+    }
+
+    @PostMapping("/save")
+    @ApiOperation(value = "新增用水信息")
+    @Log(title = "用水信息", businessType = BusinessType.INSERT)
+    public Result<Object> save(@RequestBody WaterUsageData entity) {
+        entity.setCreateTime(LocalDateTime.now());
+        return Result.ok(service.save(entity),"新增成功");
+    }
+
+    @PutMapping("/updateById")
+    @ApiOperation(value = "用水信息修改")
+    @Log(title = "修改用水信息", businessType = BusinessType.UPDATE)
+    public Result<Object> updateById(@RequestBody WaterUsageData entity) {
+        return Result.ok(service.updateById(entity),"修改成功");
+    }
+
+    @DeleteMapping("/deleteById")
+    @ApiOperation(value = "用水信息删除")
+    @Log(title = "删除v", businessType = BusinessType.DELETE)
+    public Result<Object> deleteById(String id) {
+        return Result.ok(service.removeById(id),"删除成功");
+    }
+
+    @DeleteMapping("/deleteBatchById")
+    @ApiOperation(value = "用水信息批量删除")
+    @Log(title = "用水信息批量删除", businessType = BusinessType.DELETE)
+    public Result<Object> deleteBatchById(@RequestParam List<String> ids) {
+        return Result.ok(service.removeByIds(ids),"删除成功");
+    }
+
+    @GetMapping("/exportExcel")
+    @ApiOperation(value = "导出用水信息Excel")
+    @Log(title = "导出用水信息Excel", businessType = BusinessType.EXPORT)
+    public void exportExcel(HttpServletResponse response,String conditionJson) throws Exception {
+        ExcelExportUtil.exportExcel(response,service.list(SearchUtil.parseWhereSql(conditionJson)), WaterUsageData.class, "用水信息","用水信息");
+    }
+
+    @PostMapping("/importExcel")
+    @ApiOperation(value = "导入用水信息Excel")
+    @Log(title = "导入用水信息Excel", businessType = BusinessType.IMPORT)
+    public Result<Object> importExcel(@RequestParam("file") MultipartFile file) {
+        return Result.ok(service.saveOrUpdateBatch(ExcelImportUtil.importExcel(file, WaterUsageData.class)));
+    }
+    @GetMapping("/downloadTemplateWord")
+    @ApiOperation(value = "下载用水信息模板", notes = "下载用水信息模板")
+    public void getUploadTemplate(HttpServletResponse response) throws IOException {
+        DowntemplateUtil.downloadTemplate(response,"用水信息");
+    }
+}

+ 78 - 0
park-overview-service/src/main/java/com/zksy/park/domain/WaterUsageData.java

@@ -0,0 +1,78 @@
+package com.zksy.park.domain;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.annotation.write.style.ColumnWidth;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 用水数据
+ * @TableName wate_usage_data
+ */
+@TableName(value ="water_usage_data")
+@Data
+public class WaterUsageData implements Serializable {
+    /**
+     * 主键
+     */
+    @ApiModelProperty("主键")
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 客户编码
+     */
+    @ApiModelProperty("客户编码")
+    @ExcelProperty("客户编码")
+    @ColumnWidth(20)
+    private Long customerCode;
+
+    /**
+     * 客户名称
+     */
+    @ApiModelProperty("客户名称")
+    @ExcelProperty("客户名称")
+    @ColumnWidth(20)
+    private String customerName;
+
+    /**
+     * 水量
+     */
+    @ApiModelProperty("水量")
+    @ExcelProperty("水量")
+    @ColumnWidth(20)
+    private Long waterVolume;
+
+    /**
+     * 金额
+     */
+    @ApiModelProperty("金额")
+    @ExcelProperty("金额")
+    @ColumnWidth(20)
+    private BigDecimal amount;
+
+    /**
+     * 统计时间(年月)
+     */
+    @ApiModelProperty("统计时间(年月)")
+    @ExcelProperty("统计时间")
+    @ColumnWidth(20)
+    private String statisticalTime;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    @TableField(exist = false)
+    private static final long serialVersionUID = 1L;
+}

+ 1 - 1
park-overview-service/src/main/java/com/zksy/park/listener/SmartEmploymentListener.java

@@ -44,7 +44,7 @@ public class SmartEmploymentListener extends AnalysisEventListener<SmartEmployme
     }
     }
 
 
     @Transactional
     @Transactional
-    protected void saveData() {
+    public void saveData() {
         if (!cachedDataList.isEmpty()) {
         if (!cachedDataList.isEmpty()) {
             service.saveBatch(cachedDataList);
             service.saveBatch(cachedDataList);
         }
         }

+ 53 - 0
park-overview-service/src/main/java/com/zksy/park/listener/WaterUsageDataListener.java

@@ -0,0 +1,53 @@
+package com.zksy.park.listener;
+
+
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import com.zksy.park.domain.WaterUsageData;
+import com.zksy.park.domain.WaterUsageData;
+import com.zksy.park.service.WaterUsageDataService;
+import com.zksy.park.service.WaterUsageDataService;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+
+public class WaterUsageDataListener extends AnalysisEventListener<WaterUsageData> {
+
+    private static final int BATCH_COUNT = 1000;
+    private final WaterUsageDataService service;
+    private final List<WaterUsageData> cachedDataList = new ArrayList<>();
+
+    public WaterUsageDataListener(WaterUsageDataService service) {
+        this.service = service;
+    }
+
+    @Override
+    public void invoke(WaterUsageData data, AnalysisContext context) {
+        LocalDateTime now = LocalDateTime.now();
+        data.setId(null);
+        //TODO 时间处理
+        if(data.getCreateTime()==null){
+            data.setCreateTime(now);
+        }
+
+        cachedDataList.add(data);
+        if (cachedDataList.size() >= BATCH_COUNT) {
+            saveData();
+            cachedDataList.clear();
+        }
+    }
+
+    @Override
+    public void doAfterAllAnalysed(AnalysisContext context) {
+        saveData();
+    }
+
+    @Transactional
+    public void saveData() {
+        if (!cachedDataList.isEmpty()) {
+            service.saveBatch(cachedDataList);
+        }
+    }
+}

+ 20 - 0
park-overview-service/src/main/java/com/zksy/park/mapper/WaterUsageDataMapper.java

@@ -0,0 +1,20 @@
+package com.zksy.park.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zksy.park.domain.WaterUsageData;
+import org.mapstruct.Mapper;
+
+/**
+* @author Administrator
+* @description 针对表【wate_usage_data(用水数据)】的数据库操作Mapper
+* @createDate 2025-09-16 14:51:14
+* @Entity com.zksy.park.domain.WateUsageData
+*/
+@Mapper
+public interface WaterUsageDataMapper extends BaseMapper<WaterUsageData> {
+
+}
+
+
+
+

+ 16 - 0
park-overview-service/src/main/java/com/zksy/park/service/WaterUsageDataService.java

@@ -0,0 +1,16 @@
+package com.zksy.park.service;
+
+import com.zksy.common.core.domain.Result;
+import com.zksy.park.domain.WaterUsageData;
+import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+
+/**
+* @author Administrator
+* @description 针对表【wate_usage_data(用水数据)】的数据库操作Service
+* @createDate 2025-09-16 14:51:14
+*/
+public interface WaterUsageDataService extends IService<WaterUsageData> {
+}

+ 1 - 4
park-overview-service/src/main/java/com/zksy/park/service/imp/FileGeneralServiceImpl.java → park-overview-service/src/main/java/com/zksy/park/service/impl/FileGeneralServiceImpl.java

@@ -1,13 +1,10 @@
-package com.zksy.park.service.imp;
+package com.zksy.park.service.impl;
 
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zksy.park.domain.FileGeneral;
 import com.zksy.park.domain.FileGeneral;
-import com.zksy.park.domain.ParkInfo;
 import com.zksy.park.mapper.FileGeneralMapper;
 import com.zksy.park.mapper.FileGeneralMapper;
-import com.zksy.park.mapper.ParkInfoMapper;
 import com.zksy.park.service.FileGeneralService;
 import com.zksy.park.service.FileGeneralService;
-import com.zksy.park.service.ParkInfoService;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import java.util.List;
 import java.util.List;

+ 1 - 1
park-overview-service/src/main/java/com/zksy/park/service/imp/ParkInfoServiceImpl.java → park-overview-service/src/main/java/com/zksy/park/service/impl/ParkInfoServiceImpl.java

@@ -1,4 +1,4 @@
-package com.zksy.park.service.imp;
+package com.zksy.park.service.impl;
 
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;

+ 1 - 1
park-overview-service/src/main/java/com/zksy/park/service/imp/ParkStemScreenServiceImpl.java → park-overview-service/src/main/java/com/zksy/park/service/impl/ParkStemScreenServiceImpl.java

@@ -1,4 +1,4 @@
-package com.zksy.park.service.imp;
+package com.zksy.park.service.impl;
 
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.StrUtil;

+ 1 - 1
park-overview-service/src/main/java/com/zksy/park/service/imp/ParkVisualVideoServiceImpl.java → park-overview-service/src/main/java/com/zksy/park/service/impl/ParkVisualVideoServiceImpl.java

@@ -1,4 +1,4 @@
-package com.zksy.park.service.imp;
+package com.zksy.park.service.impl;
 
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zksy.park.domain.ParkVisualVideo;
 import com.zksy.park.domain.ParkVisualVideo;

+ 37 - 0
park-overview-service/src/main/java/com/zksy/park/service/impl/WaterUsageDataServiceImpl.java

@@ -0,0 +1,37 @@
+package com.zksy.park.service.impl;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zksy.common.core.domain.Result;
+import com.zksy.park.domain.WaterUsageData;
+import com.zksy.park.listener.WaterUsageDataListener;
+import com.zksy.park.service.WaterUsageDataService;
+import com.zksy.park.mapper.WaterUsageDataMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+* @author Administrator
+* @description 针对表【wate_usage_data(用水数据)】的数据库操作Service实现
+* @createDate 2025-09-16 14:51:14
+*/
+@Service
+public class WaterUsageDataServiceImpl extends ServiceImpl<WaterUsageDataMapper, WaterUsageData>
+    implements WaterUsageDataService {
+
+}
+
+
+
+

+ 74 - 0
park-overview-service/src/main/java/com/zksy/park/utils/DowntemplateUtil.java

@@ -0,0 +1,74 @@
+package com.zksy.park.utils;
+
+
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.util.FileCopyUtils;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class DowntemplateUtil {
+
+    private final static String FILE_PATH = "template/";
+    private final static String FILE_SUFFIX = ".xlsx";
+    private final static String FILE_WORD = ".doc";
+    /**
+     * 给一个请求体对象再给个template下的文件   后缀需要为.xlsx的文件 就能给前端下载了
+     * @param response
+     * @param fileName
+     * @throws IOException
+     */
+    public static void downloadTemplate(HttpServletResponse response,String fileName) throws IOException {
+        ClassPathResource resource = new ClassPathResource(FILE_PATH + fileName + FILE_SUFFIX);
+        if (!resource.exists()) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND, "没找到这个: " + FILE_PATH + fileName + FILE_SUFFIX + "文件");
+            throw new IOException("没找到这个: " + FILE_PATH + fileName + FILE_SUFFIX + "文件");
+        }
+
+        // 设置响应头以支持文件下载
+        HttpHeaders headers = new HttpHeaders();
+        headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + new String((fileName + FILE_SUFFIX).getBytes("UTF-8"), "ISO-8859-1"));
+        headers.add(HttpHeaders.CONTENT_TYPE, "application/vnd.ms-excel");
+
+        // 从资源读取输入流并写入响应的输出流
+        try (InputStream inputStream = resource.getInputStream();
+             OutputStream outputStream = response.getOutputStream()) {
+
+            response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()));
+
+            FileCopyUtils.copy(inputStream, outputStream);
+            outputStream.flush();
+        }
+    }
+    /**
+     * 给一个请求体对象再给个template下的文件   后缀需要为.xlsx的文件 就能给前端下载了
+     * @param response
+     * @param fileName
+     * @throws IOException
+     */
+    public static void downloadTemplateWord(HttpServletResponse response,String fileName) throws IOException {
+        ClassPathResource resource = new ClassPathResource(FILE_PATH + fileName + FILE_WORD);
+        if (!resource.exists()) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND, "没找到这个: " + FILE_PATH + fileName + FILE_WORD + "文件");
+            throw new IOException("没找到这个: " + FILE_PATH + fileName + FILE_WORD + "文件");
+        }
+
+        // 设置响应头以支持文件下载
+        HttpHeaders headers = new HttpHeaders();
+        headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + new String((fileName + FILE_WORD).getBytes("UTF-8"), "ISO-8859-1"));
+        headers.add(HttpHeaders.CONTENT_TYPE, "application/vnd.ms-excel");
+
+        // 从资源读取输入流并写入响应的输出流
+        try (InputStream inputStream = resource.getInputStream();
+             OutputStream outputStream = response.getOutputStream()) {
+
+            response.setHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(resource.contentLength()));
+
+            FileCopyUtils.copy(inputStream, outputStream);
+            outputStream.flush();
+        }
+    }
+}

+ 147 - 0
park-overview-service/src/main/java/com/zksy/park/utils/ExcelExportUtil.java

@@ -0,0 +1,147 @@
+package com.zksy.park.utils;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.style.WriteCellStyle;
+import com.alibaba.excel.write.metadata.style.WriteFont;
+import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.util.List;
+
+public class ExcelExportUtil {
+
+    /**
+     * 通用 Excel 导出方法,只导出有 @ExcelProperty 注解的字段
+     * @param response HTTP 响应对象
+     * @param dataList 数据列表
+     * @param head 表头类型
+     * @param fileName 文件名
+     * @param sheetName 工作表名
+     * @param <T> 数据类型
+     */
+    public static <T> void exportExcel(HttpServletResponse response, List<T> dataList, Class<T> head,
+                                       String fileName, String sheetName) {
+        try {
+            // 设置响应头,明确指定xlsx格式
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("UTF-8");
+            // 确保文件名有.xlsx后缀
+            if (!fileName.toLowerCase().endsWith(".xlsx")) {
+                fileName += ".xlsx";
+            }
+            // 使用ISO-8859-1编码处理文件名,防止中文乱码
+            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"));
+
+            // 配置表头样式
+            WriteCellStyle headWriteCellStyle = new WriteCellStyle();
+            headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+            WriteFont headWriteFont = new WriteFont();
+            headWriteFont.setFontHeightInPoints((short)12);
+            headWriteCellStyle.setWriteFont(headWriteFont);
+
+            // 配置内容样式
+            WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
+            contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+
+            // 应用样式策略
+            HorizontalCellStyleStrategy horizontalCellStyleStrategy =
+                    new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
+
+            // 写入 Excel 数据,使用指定的类作为表头,会自动过滤没有 @ExcelProperty 注解的字段
+            EasyExcel.write(response.getOutputStream(), head)
+                    .registerWriteHandler(horizontalCellStyleStrategy)
+                    .sheet(sheetName)
+                    .doWrite(dataList);
+        } catch (IOException e) {
+            // 记录异常日志
+            e.printStackTrace();
+            // 抛出运行时异常,由Spring MVC统一异常处理
+            throw new RuntimeException("导出Excel失败", e);
+        }
+    }
+
+    /**
+     * 导出多 sheet 的 Excel 文件,只导出有 @ExcelProperty 注解的字段
+     * @param response HTTP 响应对象
+     * @param sheetDataList 多个 sheet 的数据列表
+     * @param fileName 文件名
+     */
+    public static void exportMultiSheetExcel(HttpServletResponse response,
+                                             List<SheetData<?>> sheetDataList,
+                                             String fileName) {
+        try {
+            // 设置响应头,明确指定xlsx格式
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding("UTF-8");
+            // 确保文件名有.xlsx后缀
+            if (!fileName.toLowerCase().endsWith(".xlsx")) {
+                fileName += ".xlsx";
+            }
+            // 使用ISO-8859-1编码处理文件名,防止中文乱码
+            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"));
+
+            // 配置表头样式
+            WriteCellStyle headWriteCellStyle = new WriteCellStyle();
+            headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+            WriteFont headWriteFont = new WriteFont();
+            headWriteFont.setFontHeightInPoints((short)12);
+            headWriteCellStyle.setWriteFont(headWriteFont);
+
+            // 配置内容样式
+            WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
+            contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+
+            // 应用样式策略
+            HorizontalCellStyleStrategy horizontalCellStyleStrategy =
+                    new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
+
+            // 写入多sheet Excel数据
+            var excelWriter = EasyExcel.write(response.getOutputStream()).registerWriteHandler(horizontalCellStyleStrategy).build();
+            try {
+                for (SheetData<?> sheetData : sheetDataList) {
+                    WriteSheet writeSheet = EasyExcel.writerSheet(sheetData.getSheetName())
+                            .head(sheetData.getHead()).build();
+                    excelWriter.write(sheetData.getDataList(), writeSheet);
+                }
+            } finally {
+                // 关闭 writer,释放内存
+                excelWriter.finish();
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            // 抛出运行时异常,由Spring MVC统一异常处理
+            throw new RuntimeException("导出Excel失败", e);
+        }
+    }
+
+    /**
+     * 内部类,用于存储 sheet 数据
+     */
+    public static class SheetData<T> {
+        private String sheetName;
+        private Class<T> head;
+        private List<T> dataList;
+
+        public SheetData(String sheetName, Class<T> head, List<T> dataList) {
+            this.sheetName = sheetName;
+            this.head = head;
+            this.dataList = dataList;
+        }
+
+        public String getSheetName() {
+            return sheetName;
+        }
+
+        public Class<T> getHead() {
+            return head;
+        }
+
+        public List<T> getDataList() {
+            return dataList;
+        }
+    }
+}

+ 110 - 0
park-overview-service/src/main/java/com/zksy/park/utils/ExcelImportUtil.java

@@ -0,0 +1,110 @@
+package com.zksy.park.utils;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.read.listener.ReadListener;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+@Slf4j
+public class ExcelImportUtil {
+
+    /**
+     * 通用 Excel 导入方法
+     * @param file 上传的 Excel 文件
+     * @param head 表头类型
+     * @param <T> 数据类型
+     * @return 解析后的对象列表
+     */
+    public static <T> List<T> importExcel(MultipartFile file, Class<T> head) {
+        List<T> dataList = new ArrayList<>();
+
+        try {
+            EasyExcel.read(file.getInputStream(), head, new ReadListener<T>() {
+                @Override
+                public void invoke(T data, AnalysisContext context) {
+                    dataList.add(data);
+                }
+
+                @Override
+                public void doAfterAllAnalysed(AnalysisContext context) {
+                    log.info("Excel数据解析完成,共解析 {} 条记录", dataList.size());
+                }
+
+                @Override
+                public void onException(Exception exception, AnalysisContext context) {
+                    log.error("解析Excel行 {} 时发生异常: {}",
+                            context.readRowHolder().getRowIndex(), exception.getMessage());
+                    // 可以选择跳过当前行继续解析
+                    throw new RuntimeException("Excel解析异常", exception);
+                }
+            }).sheet().doRead();
+        } catch (IOException e) {
+            // 记录异常日志,避免在生产环境打印堆栈信息
+            e.printStackTrace();
+        }
+
+        return dataList;
+    }
+
+    /**
+     * 流式处理 Excel 数据的导入方法,适合处理大数据量
+     * @param file 上传的 Excel 文件
+     * @param head 表头类型
+     * @param consumer 数据处理函数
+     * @param <T> 数据类型
+     */
+    public static <T> void importExcelStream(MultipartFile file, Class<T> head, Consumer<T> consumer) {
+        try {
+            EasyExcel.read(file.getInputStream(), head, new ReadListener<T>() {
+                @Override
+                public void invoke(T data, AnalysisContext context) {
+                    // 每读取一行数据就立即处理,无需等待全部数据加载完成
+                    consumer.accept(data);
+                }
+
+                @Override
+                public void doAfterAllAnalysed(AnalysisContext context) {
+                    // 所有数据解析完成后的操作
+                }
+            }).sheet().doRead();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 导入指定 sheet 的 Excel 数据
+     * @param file 上传的 Excel 文件
+     * @param head 表头类型
+     * @param sheetNo sheet 编号,从 0 开始
+     * @param <T> 数据类型
+     * @return 解析后的对象列表
+     */
+    public static <T> List<T> importExcelBySheet(MultipartFile file, Class<T> head, int sheetNo) {
+        List<T> dataList = new ArrayList<>();
+
+        try {
+            EasyExcel.read(file.getInputStream(), head, new ReadListener<T>() {
+                @Override
+                public void invoke(T data, AnalysisContext context) {
+                    dataList.add(data);
+                }
+
+                @Override
+                public void doAfterAllAnalysed(AnalysisContext context) {
+                    // 所有数据解析完成后的操作
+                }
+            }).sheet(sheetNo).doRead();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        return dataList;
+    }
+}

BIN
park-overview-service/src/main/resources/template/智慧用工信息.xlsx


BIN
park-overview-service/src/main/resources/template/用水信息.xlsx