From e8e30e5474c1fd0c14390710066e40c17155ad37 Mon Sep 17 00:00:00 2001
From: mitao <2763622819@qq.com>
Date: 星期三, 22 十月 2025 17:41:01 +0800
Subject: [PATCH] 广告物料零星结算导入

---
 ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialQuotation.java                            |    3 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdController.java                              |   28 --
 ruoyi-system/src/main/java/com/ruoyi/system/annotation/ExcelValid.java                                     |   14 +
 ruoyi-system/src/main/java/com/ruoyi/system/export/AssetAdMaterialSporadicSettlementImport.java            |   72 +++++++
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/AssetAdMaterialSporadicSettlementServiceImpl.java |  121 +++++++++++
 ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdDTO.java                                      |    2 
 ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdMaterialPriceDTO.java                         |    4 
 ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialSporadicSettlement.java                   |    3 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdMaterialPriceController.java                 |   21 -
 ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdMaterialSporadicSettlementDTO.java            |   30 +++
 ruoyi-system/src/main/java/com/ruoyi/system/service/AssetAdMaterialSporadicSettlementService.java          |    3 
 /dev/null                                                                                                  |   34 ---
 ruoyi-system/src/main/java/com/ruoyi/system/service/impl/OssServiceImpl.java                               |   33 +++
 ruoyi-system/src/main/java/com/ruoyi/system/utils/FastExcelUtil.java                                       |   63 ++++++
 ruoyi-system/src/main/java/com/ruoyi/system/utils/ExcelImportValid.java                                    |   33 +++
 ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialIntegralSettlement.java                   |    3 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetHouseInspectionItemController.java             |   32 ++-
 ruoyi-system/src/main/java/com/ruoyi/system/service/OssService.java                                        |    3 
 ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdMaterialSporadicSettlementController.java    |   63 ++++++
 19 files changed, 478 insertions(+), 87 deletions(-)

diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdController.java
index 0a860f6..9941ebd 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdController.java
@@ -1,7 +1,7 @@
 package com.ruoyi.web.controller.api;
 
 
-import cn.idev.excel.FastExcel;
+import cn.afterturn.easypoi.excel.ExcelImportUtil;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.domain.R;
@@ -12,9 +12,9 @@
 import com.ruoyi.system.query.AssetAdQuery;
 import com.ruoyi.system.service.AssetAdRentalRecordService;
 import com.ruoyi.system.service.AssetAdService;
+import com.ruoyi.system.utils.FastExcelUtil;
 import com.ruoyi.system.vo.asset.AssetAdDetailVO;
 import com.ruoyi.system.vo.asset.AssetAdVO;
-import com.ruoyi.web.controller.listener.AssetAdValidatorListener;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
@@ -36,9 +36,7 @@
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
 import java.io.IOException;
-import java.net.URLEncoder;
 import java.util.Collections;
-import java.util.List;
 
 /**
  * <p>
@@ -90,18 +88,7 @@
     @ApiOperation("下载导入模板")
     @GetMapping("/template")
     public void getTemplate(HttpServletResponse response){
-        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
-        response.setCharacterEncoding("utf-8");
-        String fileName = null;
-        try {
-            fileName = URLEncoder.encode("广告无形资产导入模板", "UTF-8").replaceAll("\\+", "%20");
-            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
-            FastExcel.write(response.getOutputStream(), AssetAdDTO.class)
-                    .sheet("广告无形资产")
-                    .doWrite(Collections.emptyList());
-        } catch (IOException e) {
-            log.error("下载导入模板异常", e);
-        }
+        FastExcelUtil.exportData(response, "广告无形资产导入模板", "广告无形资产", AssetAdDTO.class, Collections.emptyList());
     }
 
     @ApiOperation("导入")
@@ -112,14 +99,15 @@
             return R.fail("请选择一个文件上传!");
         }
         try {
-            List<AssetAdDTO> list = FastExcel.read(file.getInputStream(), AssetAdDTO.class, new AssetAdValidatorListener())
-                    .sheet()
-                    .doReadSync();
-            assetAdService.importAssetAd(list);
+            //List<AssetAdDTO> list = FastExcelUtil.readMultipartFile(file.getInputStream(), AssetAdDTO.class);
+            //assetAdService.importAssetAd(list);
+            ExcelImportUtil.importExcel(file.getInputStream(), AssetAdDTO.class, null);
             return R.ok();
         } catch (IOException e) {
             log.error("文件处理失败", e);
             return R.fail("文件处理失败!");
+        } catch (Exception e) {
+            throw new RuntimeException(e);
         }
     }
 
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdMaterialPriceController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdMaterialPriceController.java
index e5716e8..284bb88 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdMaterialPriceController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdMaterialPriceController.java
@@ -1,6 +1,5 @@
 package com.ruoyi.web.controller.api;
 
-import cn.idev.excel.FastExcel;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.core.domain.R;
@@ -8,6 +7,7 @@
 import com.ruoyi.system.dto.asset.AssetAdMaterialPriceDTO;
 import com.ruoyi.system.query.AssetAdMaterialPriceQuery;
 import com.ruoyi.system.service.AssetAdMaterialPriceService;
+import com.ruoyi.system.utils.FastExcelUtil;
 import com.ruoyi.system.vo.asset.AssetAdMaterialPriceVO;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
@@ -30,7 +30,6 @@
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
 import java.io.IOException;
-import java.net.URLEncoder;
 import java.util.Collections;
 import java.util.List;
 
@@ -91,18 +90,7 @@
     @ApiOperation("下载导入模板")
     @GetMapping("/template")
     public void getTemplate(HttpServletResponse response){
-        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
-        response.setCharacterEncoding("utf-8");
-        String fileName = null;
-        try {
-            fileName = URLEncoder.encode("广告物料单价导入模板", "UTF-8").replaceAll("\\+", "%20");
-            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
-            FastExcel.write(response.getOutputStream(), AssetAdMaterialPriceDTO.class)
-                    .sheet("广告物料单价")
-                    .doWrite(Collections.emptyList());
-        } catch (IOException e) {
-            log.error("下载导入模板异常", e);
-        }
+        FastExcelUtil.exportData(response, "广告物料单价导入模板", "广告物料单价", AssetAdMaterialPriceDTO.class, Collections.emptyList());
     }
 
     @ApiOperation("导入")
@@ -113,10 +101,7 @@
             return R.fail("请选择一个文件上传!");
         }
         try {
-            List<AssetAdMaterialPriceDTO> list = FastExcel.read(file.getInputStream())
-                    .head(AssetAdMaterialPriceDTO.class)
-                    .sheet()
-                    .doReadSync();
+            List<AssetAdMaterialPriceDTO> list = FastExcelUtil.readMultipartFile(file.getInputStream(), AssetAdMaterialPriceDTO.class);
             assetAdMaterialPriceService.importAssetAdMaterialPrice(list);
             return R.ok();
         } catch (IOException e) {
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdMaterialSporadicSettlementController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdMaterialSporadicSettlementController.java
index ae6035f..a3af75b 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdMaterialSporadicSettlementController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetAdMaterialSporadicSettlementController.java
@@ -1,9 +1,32 @@
 package com.ruoyi.web.controller.api;
 
 
+import cn.afterturn.easypoi.excel.ExcelExportUtil;
+import cn.afterturn.easypoi.excel.entity.ExportParams;
+import com.google.common.collect.Lists;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.system.dto.asset.AssetAdMaterialSporadicSettlementDTO;
+import com.ruoyi.system.export.AssetAdMaterialSporadicSettlementImport;
+import com.ruoyi.system.service.AssetAdMaterialSporadicSettlementService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
-
+import org.springframework.web.bind.annotation.RequestPart;
 import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.net.URLEncoder;
 
 /**
  * <p>
@@ -13,8 +36,46 @@
  * @author mitao
  * @since 2025-10-17
  */
+@Slf4j
+@Validated
+@Api(tags = {"广告物料零星结算相关接口"})
 @RestController
 @RequestMapping("/asset-ad-material-sporadic-settlement")
+@RequiredArgsConstructor(onConstructor_ = {@Lazy})
 public class AssetAdMaterialSporadicSettlementController {
 
+    private final AssetAdMaterialSporadicSettlementService assetAdMaterialSporadicSettlementService;
+
+    @ApiOperation("下载导入模板")
+    @GetMapping("/template")
+    public void downloadTemplate(HttpServletResponse response) {
+        Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), AssetAdMaterialSporadicSettlementImport.class,
+                Lists.newArrayList(new AssetAdMaterialSporadicSettlementImport()));
+        ServletOutputStream outputStream = null;
+        try {
+            String fileName = URLEncoder.encode("广告物料零星结算导入模板.xls", "utf-8");
+            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
+            response.setHeader("content-Type", "application/vnd.ms-excel");
+            response.setHeader("Pragma", "no-cache");
+            response.setHeader("Cache-Control", "no-cache");
+            outputStream = response.getOutputStream();
+            workbook.write(outputStream);
+        } catch (IOException e) {
+            log.error("房屋巡检导入模板下载失败!", e);
+        } finally {
+            try {
+                outputStream.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @ApiOperation("导入零星资产结算数据")
+    @PostMapping("/import")
+    public R<?> importData(@RequestPart("file") MultipartFile file, @Valid AssetAdMaterialSporadicSettlementDTO dto) {
+        assetAdMaterialSporadicSettlementService.importData(file, dto);
+        return R.ok();
+    }
+
 }
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetHouseInspectionItemController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetHouseInspectionItemController.java
index 58bd53f..b7c8bfa 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetHouseInspectionItemController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/api/AssetHouseInspectionItemController.java
@@ -6,7 +6,6 @@
 import cn.afterturn.easypoi.excel.entity.ExportParams;
 import cn.afterturn.easypoi.excel.entity.ImportParams;
 import cn.hutool.json.JSONObject;
-import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.ruoyi.common.annotation.Log;
 import com.ruoyi.common.basic.PageInfo;
@@ -18,27 +17,40 @@
 import com.ruoyi.common.utils.WebUtils;
 import com.ruoyi.framework.web.service.TokenService;
 import com.ruoyi.system.dto.AddAssetHouseInspection;
-import com.ruoyi.system.dto.AddAssetRepairRequestDTO;
 import com.ruoyi.system.export.AssetHouseInspectionImport;
-import com.ruoyi.system.model.*;
+import com.ruoyi.system.model.AssetHouseInspectionDetail;
+import com.ruoyi.system.model.AssetHouseInspectionItem;
+import com.ruoyi.system.model.AssetHouseInspectionRecord;
+import com.ruoyi.system.model.AssetMain;
+import com.ruoyi.system.model.AssetPropertyExt;
+import com.ruoyi.system.model.AssetType;
 import com.ruoyi.system.query.AssetHouseInspectionRecordListQuery;
-import com.ruoyi.system.query.AssetRepairRequestListQuery;
-import com.ruoyi.system.service.*;
+import com.ruoyi.system.service.AssetHouseInspectionDetailService;
+import com.ruoyi.system.service.AssetHouseInspectionItemService;
+import com.ruoyi.system.service.AssetHouseInspectionRecordService;
+import com.ruoyi.system.service.AssetMainService;
+import com.ruoyi.system.service.AssetPropertyExtService;
+import com.ruoyi.system.service.AssetTypeService;
+import com.ruoyi.system.service.ISysUserService;
+import com.ruoyi.system.service.TDeptService;
 import com.ruoyi.system.vo.AssetHouseInspectionVO;
-import com.ruoyi.system.vo.AssetRepairRequestVO;
 import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiModelProperty;
 import io.swagger.annotations.ApiOperation;
-import io.swagger.models.auth.In;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.poi.ss.usermodel.Workbook;
-import org.checkerframework.checker.units.qual.A;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.ServletOutputStream;
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/listener/AssetAdValidatorListener.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/listener/AssetAdValidatorListener.java
deleted file mode 100644
index 9aaecc6..0000000
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/listener/AssetAdValidatorListener.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.ruoyi.web.controller.listener;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.idev.excel.context.AnalysisContext;
-import cn.idev.excel.event.AnalysisEventListener;
-import com.ruoyi.common.exception.ServiceException;
-import com.ruoyi.common.utils.StringUtils;
-import com.ruoyi.system.dto.asset.AssetAdDTO;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @author mitao
- * @date 2025/10/21
- */
-public class AssetAdValidatorListener extends AnalysisEventListener<AssetAdDTO> {
-    private List<String> errList = new ArrayList<>();
-    @Override
-    public void invoke(AssetAdDTO assetAdDTO, AnalysisContext analysisContext) {
-        Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
-        rowIndex += 1;
-        if (StringUtils.isBlank(assetAdDTO.getAssetName())) {
-            errList.add(String.format("第%d行", rowIndex));
-        }
-    }
-
-    @Override
-    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
-        if (CollUtil.isNotEmpty(errList)) {
-            throw new ServiceException(String.format("%s,资产名称不能为空",String.join(",", errList)));
-        }
-    }
-}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/annotation/ExcelValid.java b/ruoyi-system/src/main/java/com/ruoyi/system/annotation/ExcelValid.java
new file mode 100644
index 0000000..b1b88e5
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/annotation/ExcelValid.java
@@ -0,0 +1,14 @@
+package com.ruoyi.system.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+/**
+* <p>Excel导入必填校验注解</p>
+*/
+@Target({ ElementType.FIELD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ExcelValid {
+   String message() default "导入有未填入的字段";
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdDTO.java b/ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdDTO.java
index e3ef974..4d952e6 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdDTO.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdDTO.java
@@ -3,6 +3,7 @@
 import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
 import cn.idev.excel.annotation.ExcelProperty;
 import cn.idev.excel.annotation.write.style.ColumnWidth;
+import com.ruoyi.system.annotation.ExcelValid;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -24,6 +25,7 @@
     @ApiModelProperty(value = "资产名称")
     @ExcelProperty("资产名称")
     @ColumnWidth(11)
+    @ExcelValid(message = "名称不能为空")
     @NotBlank(message = "资产名称")
     private String assetName;
 
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdMaterialPriceDTO.java b/ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdMaterialPriceDTO.java
index d707a51..6b6c811 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdMaterialPriceDTO.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdMaterialPriceDTO.java
@@ -3,6 +3,7 @@
 import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
 import cn.idev.excel.annotation.ExcelProperty;
 import cn.idev.excel.annotation.write.style.ColumnWidth;
+import com.ruoyi.system.annotation.ExcelValid;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -29,6 +30,7 @@
     @ApiModelProperty(value = "物料名称")
     @ExcelProperty("物料名称")
     @ColumnWidth(15)
+    @ExcelValid(message = "名称不能为空")
     @NotBlank(message = "物料名称不能为空")
     private String materialName;
 
@@ -36,12 +38,14 @@
     @ExcelProperty("单价")
     @ColumnWidth(10)
     @NotNull(message = "单价不能为空")
+    @ExcelValid(message = "单价不能为空")
     @DecimalMin(value = "0.01", message = "单价必须大于0")
     private BigDecimal unitPrice;
 
     @ApiModelProperty(value = "单位(元、平方米等)")
     @ExcelProperty("单位")
     @ColumnWidth(8)
+    @ExcelValid(message = "单位不能为空")
     @NotBlank(message = "单位不能为空")
     private String priceUnit;
 
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdMaterialSporadicSettlementDTO.java b/ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdMaterialSporadicSettlementDTO.java
new file mode 100644
index 0000000..05269b3
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/dto/asset/AssetAdMaterialSporadicSettlementDTO.java
@@ -0,0 +1,30 @@
+package com.ruoyi.system.dto.asset;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * @author mitao
+ * @date 2025/10/22
+ */
+@Data
+@ApiModel("广告物料零星结算表数据传输对象")
+public class AssetAdMaterialSporadicSettlementDTO {
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+
+    @ApiModelProperty(value = "标题")
+    @NotBlank(message = "标题不能为空")
+    private String title;
+
+    @ApiModelProperty(value = "结算项目")
+    @NotBlank(message = "结算项目不能为空")
+    private String settlementProject;
+
+    @ApiModelProperty(value = "结算单位")
+    @NotBlank(message = "结算单位不能为空")
+    private String settlementInstitution;
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/export/AssetAdMaterialSporadicSettlementImport.java b/ruoyi-system/src/main/java/com/ruoyi/system/export/AssetAdMaterialSporadicSettlementImport.java
new file mode 100644
index 0000000..9779f73
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/export/AssetAdMaterialSporadicSettlementImport.java
@@ -0,0 +1,72 @@
+package com.ruoyi.system.export;
+
+import cn.afterturn.easypoi.excel.annotation.Excel;
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * <p>
+ * 广告物料零星结算表关联表
+ * </p>
+ *
+ * @author mitao
+ * @since 2025-10-17
+ */
+@Data
+@ApiModel(value="AssetAdMaterialSporadicSettlementInfo对象", description="广告物料零星结算表关联表")
+public class AssetAdMaterialSporadicSettlementImport {
+
+    @Excel(name = "日期", width = 20)
+    private LocalDate settlementDate;
+
+    @Excel(name = "安装地点", width = 20)
+    private String address;
+
+    @Excel(name = "名称", width = 20)
+    private String adName;
+
+    @Excel(name = "材质工艺", width = 20)
+    private String material;
+
+    /**
+     * 设计图例oss地址
+     */
+    private String designLegend;
+
+    @Excel(name = "设计图例",type = 2, width = 20)
+    private String designLegendTemp;
+
+    /**
+     * 安装图例oss地址
+     */
+    private String installationLegend;
+
+    @Excel(name = "安装图例",type = 2, width = 20)
+    private String installationLegendTemp;
+
+    @Excel(name = "长(米)", width = 20)
+    private Double length;
+
+    @Excel(name = "高(米)", width = 20)
+    private Double height;
+
+    @Excel(name = "数量", width = 20)
+    private Integer quantity;
+
+    @Excel(name = "小计", width = 20)
+    private String subtotal;
+
+    @Excel(name = "单价(元)", width = 20)
+    private BigDecimal unitPrice;
+
+    @Excel(name = "金额(元)", width = 20)
+    private BigDecimal totalAmount;
+
+    @Excel(name = "备注", width = 20)
+    private String remark;
+
+
+}
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialIntegralSettlement.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialIntegralSettlement.java
index 7fe32ff..c542705 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialIntegralSettlement.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialIntegralSettlement.java
@@ -35,6 +35,9 @@
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
 
+    @ApiModelProperty(value = "所属部门ID")
+    private Integer deptId;
+
     @ApiModelProperty(value = "标题")
     private String title;
 
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialQuotation.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialQuotation.java
index 196e85d..0847f74 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialQuotation.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialQuotation.java
@@ -34,6 +34,9 @@
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
 
+    @ApiModelProperty(value = "所属部门ID")
+    private Integer deptId;
+
     @ApiModelProperty(value = "标题")
     private String title;
 
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialSporadicSettlement.java b/ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialSporadicSettlement.java
index c3829ab..de1b051 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialSporadicSettlement.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/model/AssetAdMaterialSporadicSettlement.java
@@ -34,6 +34,9 @@
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
 
+    @ApiModelProperty(value = "所属部门ID")
+    private Integer deptId;
+
     @ApiModelProperty(value = "标题")
     private String title;
 
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/AssetAdMaterialSporadicSettlementService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/AssetAdMaterialSporadicSettlementService.java
index 8008405..e321801 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/AssetAdMaterialSporadicSettlementService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/AssetAdMaterialSporadicSettlementService.java
@@ -1,7 +1,9 @@
 package com.ruoyi.system.service;
 
+import com.ruoyi.system.dto.asset.AssetAdMaterialSporadicSettlementDTO;
 import com.ruoyi.system.model.AssetAdMaterialSporadicSettlement;
 import com.baomidou.mybatisplus.extension.service.IService;
+import org.springframework.web.multipart.MultipartFile;
 
 /**
  * <p>
@@ -13,4 +15,5 @@
  */
 public interface AssetAdMaterialSporadicSettlementService extends IService<AssetAdMaterialSporadicSettlement> {
 
+    void importData(MultipartFile file, AssetAdMaterialSporadicSettlementDTO dto);
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/OssService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/OssService.java
index 2b6d049..df07888 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/OssService.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/OssService.java
@@ -3,6 +3,7 @@
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.IOException;
+import java.io.InputStream;
 
 /**
  * @author mitao
@@ -25,4 +26,6 @@
      * @return
      */
     String upload(String storagePath, MultipartFile file) throws IOException;
+
+    String uploadByInputStream(InputStream inputStream);
 }
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/AssetAdMaterialSporadicSettlementServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/AssetAdMaterialSporadicSettlementServiceImpl.java
index 5936b4a..94a38ae 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/AssetAdMaterialSporadicSettlementServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/AssetAdMaterialSporadicSettlementServiceImpl.java
@@ -1,10 +1,33 @@
 package com.ruoyi.system.service.impl;
 
-import com.ruoyi.system.model.AssetAdMaterialSporadicSettlement;
-import com.ruoyi.system.mapper.AssetAdMaterialSporadicSettlementMapper;
-import com.ruoyi.system.service.AssetAdMaterialSporadicSettlementService;
+import cn.afterturn.easypoi.excel.ExcelImportUtil;
+import cn.afterturn.easypoi.excel.entity.ImportParams;
+import cn.hutool.core.bean.BeanUtil;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.core.exception.ServiceException;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.system.dto.asset.AssetAdMaterialSporadicSettlementDTO;
+import com.ruoyi.system.export.AssetAdMaterialSporadicSettlementImport;
+import com.ruoyi.system.mapper.AssetAdMaterialSporadicSettlementMapper;
+import com.ruoyi.system.model.AssetAdMaterialSporadicSettlement;
+import com.ruoyi.system.model.AssetAdMaterialSporadicSettlementInfo;
+import com.ruoyi.system.service.AssetAdMaterialSporadicSettlementInfoService;
+import com.ruoyi.system.service.AssetAdMaterialSporadicSettlementService;
+import com.ruoyi.system.service.OssService;
+import lombok.Cleanup;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
 
 /**
  * <p>
@@ -15,6 +38,98 @@
  * @since 2025-10-17
  */
 @Service
+@RequiredArgsConstructor(onConstructor_ = {@Lazy})
 public class AssetAdMaterialSporadicSettlementServiceImpl extends ServiceImpl<AssetAdMaterialSporadicSettlementMapper, AssetAdMaterialSporadicSettlement> implements AssetAdMaterialSporadicSettlementService {
+    private final OssService ossService;
+    private final AssetAdMaterialSporadicSettlementInfoService assetAdMaterialSporadicSettlementInfoService;
 
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void importData(MultipartFile file, AssetAdMaterialSporadicSettlementDTO dto) {
+        try {
+            ImportParams importParams = new ImportParams();
+            importParams.setTitleRows(0);
+            importParams.setHeadRows(1);
+            @Cleanup
+            InputStream inputStream = file.getInputStream();
+            List<AssetAdMaterialSporadicSettlementImport> list = ExcelImportUtil.importExcel(inputStream, AssetAdMaterialSporadicSettlementImport.class, importParams);
+            validateFields(list);
+            uploadToOss(list);
+            //保存
+            AssetAdMaterialSporadicSettlement assetAdMaterialSporadicSettlement = BeanUtil.copyProperties(dto, AssetAdMaterialSporadicSettlement.class);
+            assetAdMaterialSporadicSettlement.setCreateBy(SecurityUtils.getLoginUser().getUser().getNickName());
+            assetAdMaterialSporadicSettlement.setDeptId(Integer.parseInt(SecurityUtils.getLoginUser().getUser().getDeptId()));
+            save(assetAdMaterialSporadicSettlement);
+            //保存明细
+            List<AssetAdMaterialSporadicSettlementInfo> assetAdMaterialSporadicSettlementInfos = BeanUtil.copyToList(list, AssetAdMaterialSporadicSettlementInfo.class);
+            assetAdMaterialSporadicSettlementInfos.forEach(item -> item.setAdMaterialSporadicSettlementId(assetAdMaterialSporadicSettlement.getId()));
+            assetAdMaterialSporadicSettlementInfoService.saveBatch(assetAdMaterialSporadicSettlementInfos);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private void validateFields(List<AssetAdMaterialSporadicSettlementImport> list) {
+        Optional<AssetAdMaterialSporadicSettlementImport> any = list.stream()
+                .filter(item -> StringUtils.isBlank(item.getAdName())).findAny();
+        if (any.isPresent()) {
+            throw new ServiceException("【名称】不能为空,请检查该列是否填写完整");
+        }
+        Optional<AssetAdMaterialSporadicSettlementImport> quantityOpt = list.stream()
+                .filter(item -> Objects.isNull(item.getQuantity())).findAny();
+        if (quantityOpt.isPresent()) {
+            throw new ServiceException("【数量】不能为空,请检查该列是否填写完整");
+        }
+        Optional<AssetAdMaterialSporadicSettlementImport> priceOpt = list.stream()
+                .filter(item -> Objects.isNull(item.getUnitPrice())).findAny();
+        if (priceOpt.isPresent()) {
+            throw new ServiceException("【单价】不能为空,请检查该列是否填写完整");
+        }
+    }
+    /**
+     * 文件转存
+     * @param list
+     */
+    private void uploadToOss(List<AssetAdMaterialSporadicSettlementImport> list) {
+        //遍历每一条数据
+        for (AssetAdMaterialSporadicSettlementImport settlementImport : list) {
+            if (StringUtils.isNotEmpty(settlementImport.getDesignLegendTemp())) {
+                String ossUrl = getOssUrl(settlementImport.getDesignLegendTemp());
+                settlementImport.setDesignLegend(ossUrl);
+            }
+            if (StringUtils.isNotEmpty(settlementImport.getInstallationLegendTemp())) {
+                String ossUrl = getOssUrl(settlementImport.getInstallationLegendTemp());
+                settlementImport.setInstallationLegend(ossUrl);
+            }
+        }
+        //删除图片缓存
+        for (AssetAdMaterialSporadicSettlementImport settlementImport : list) {
+            if (StringUtils.isNotEmpty(settlementImport.getDesignLegendTemp())) {
+                deleteImageCache(settlementImport.getDesignLegendTemp());
+            }
+            if (StringUtils.isNotEmpty(settlementImport.getInstallationLegendTemp())) {
+                deleteImageCache(settlementImport.getInstallationLegendTemp());
+            }
+        }
+    }
+
+    private String getOssUrl(String filePath) {
+        try {
+            //获取到暂存的文件
+            File tmpFile = new File(filePath);
+            FileInputStream fileInputStream = new FileInputStream(tmpFile);
+            //转换为 multipartFile 类
+            return ossService.uploadByInputStream(fileInputStream);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+    private void deleteImageCache(String filePath) {
+        //获取到暂存的文件
+        File tmpFile = new File(filePath);
+        if (tmpFile.exists() && tmpFile.isFile()) {
+            tmpFile.delete();
+        }
+    }
 }
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/OssServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/OssServiceImpl.java
index 3bb8466..0ca8128 100644
--- a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/OssServiceImpl.java
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/OssServiceImpl.java
@@ -93,7 +93,38 @@
             ossClient.shutdown();
         }
     }
-    
+
+    @Override
+    public String uploadByInputStream(InputStream inputStream) {
+        OSS ossClient = null;
+        try {
+            CredentialsProvider credentialsProvider = new DefaultCredentialProvider(OssConfig.ACCESS_KEY_ID, OssConfig.ACCESS_KEY_SECRET);
+            String region = "cn-chengdu";
+            // 创建OSSClient实例。
+            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
+            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
+            ossClient = OSSClientBuilder.create()
+                    .endpoint(OssConfig.UPLOAD_ENDPOINT)
+                    .credentialsProvider(credentialsProvider)
+                    .clientConfiguration(clientBuilderConfiguration)
+                    .region(region)
+                    .build();
+
+            String fileExt = "png";
+            String fileName = UUID.randomUUID().toString();
+            // 设置文件名
+            String filePathName = generateRelativeStoragePath(OssConfig.FOLDER, fileExt, fileName);
+            // 创建PutObjectRequest对象。
+            PutObjectRequest putObjectRequest = new PutObjectRequest(OssConfig.BUCKET_NAME, filePathName, inputStream);
+            // 创建PutObject请求。
+            PutObjectResult result = ossClient.putObject(putObjectRequest);
+
+            return OssConfig.DOWNLOAD_ENDPOINT + filePathName;
+        } finally {
+            ossClient.shutdown();
+        }
+    }
+
     /**
      * <pre>
      * 获取存储的相对路径
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/ExcelImportValid.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/ExcelImportValid.java
new file mode 100644
index 0000000..9a1a4bc
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/ExcelImportValid.java
@@ -0,0 +1,33 @@
+package com.ruoyi.system.utils;
+
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.system.annotation.ExcelValid;
+
+import java.lang.reflect.Field;
+import java.util.Objects;
+public class ExcelImportValid {
+    /**
+     * Excel导入字段非空校验
+     *
+     * @param object 校验的JavaBean 其属性须有自定义注解
+     */
+    public static void valid(Object object) throws Exception {
+        Field[] fields = object.getClass().getDeclaredFields();
+        for (Field field : fields) {
+            // 设置可访问
+            field.setAccessible(true);
+            // 属性的值
+            Object fieldValue = null;
+            try {
+                fieldValue = field.get(object);
+            } catch (IllegalAccessException e) {
+                throw new ServiceException("导入参数检查失败!");
+            }
+            // 是否包含必填校验注解
+            boolean isRequiredValid = field.isAnnotationPresent(ExcelValid.class);
+            if (isRequiredValid && Objects.isNull(fieldValue)) {
+                throw new ServiceException(field.getAnnotation(ExcelValid.class).message());
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/utils/FastExcelUtil.java b/ruoyi-system/src/main/java/com/ruoyi/system/utils/FastExcelUtil.java
new file mode 100644
index 0000000..52a8f23
--- /dev/null
+++ b/ruoyi-system/src/main/java/com/ruoyi/system/utils/FastExcelUtil.java
@@ -0,0 +1,63 @@
+package com.ruoyi.system.utils;
+
+import cn.idev.excel.FastExcel;
+import cn.idev.excel.context.AnalysisContext;
+import cn.idev.excel.event.AnalysisEventListener;
+import com.ruoyi.common.exception.ServiceException;
+import lombok.extern.slf4j.Slf4j;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+import java.util.List;
+
+/**
+ * @author mitao
+ * @date 2025/10/22
+ */
+@Slf4j
+public class FastExcelUtil {
+    public static <T> void exportData(HttpServletResponse response, String fileName, String sheetName, Class<T> clazz, List<T> data) {
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+        try {
+            fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
+            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
+            FastExcel.write(response.getOutputStream(), clazz)
+                    .sheet(sheetName)
+                    .doWrite(data);
+        } catch (IOException e) {
+            log.error("导出数据异常", e);
+            throw new ServiceException("导出数据异常");
+        }
+    }
+    /**
+     * 读取文件数据,并返回一个包含指定类型数据的列表。
+     *
+     * @param inputStream 文件对象输入流
+     * @param clazz       数据对象的类型
+     * @param <T>         泛型类型,表示数据对象的类型
+     * @return 包含解析后数据对象的列表
+     */
+    public static <T> List<T> readMultipartFile(InputStream inputStream, Class<T> clazz) {
+        return FastExcel.read(inputStream, clazz, new AnalysisEventListener<T>() {
+            @Override
+            public void invoke(Object o, AnalysisContext analysisContext) {
+                Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
+                rowIndex += 1; // 行号从0开始,需要加1
+                try {
+                    // 调用字段校验方法
+                    ExcelImportValid.valid(o);
+                } catch (Exception e) {
+                    throw new ServiceException(String.format("第%d行:%s", rowIndex, e.getMessage()));
+                }
+            }
+
+            @Override
+            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
+
+            }
+        }).sheet().doReadSync(); // 读取 Excel 文件中的第一个工作表
+    }
+}

--
Gitblit v1.7.1