From 4c33acfe648e9b008f91c5e2cf446550e6b0174d Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期二, 22 七月 2025 17:35:57 +0800
Subject: [PATCH] 1.正式库导出优化 2.销售导出 3.设备使用状态优化 4.定时任务优化

---
 main-business/src/main/java/com/ruoyi/business/service/impl/EquipmentManagementServiceImpl.java                   |    3 
 main-business/src/main/resources/db/migration/postgresql/V20250614134700__create_table_inspection_task.sql        |    3 
 main-business/src/main/java/com/ruoyi/business/controller/SalesRecordController.java                              |   10 
 main-business/src/main/java/com/ruoyi/business/entity/SalesRecord.java                                            |    7 
 main-business/src/main/java/com/ruoyi/business/entity/InspectionTask.java                                         |    5 
 main-business/src/main/java/com/ruoyi/business/controller/OfficialInventoryController.java                        |   10 
 main-business/src/main/java/com/ruoyi/business/entity/EquipmentUsageRecord.java                                   |    4 
 main-business/src/main/java/com/ruoyi/business/service/impl/EquipmentUsageRecordServiceImpl.java                  |   55 ++-
 main-business/src/main/java/com/ruoyi/business/dto/SalesRecordDto.java                                            |    4 
 main-business/src/main/resources/db/migration/postgresql/V20250707155600__create_table_equipment_usage_record.sql |    4 
 main-business/src/main/java/com/ruoyi/business/service/impl/OfficialInventoryServiceImpl.java                     |  196 ++++++++++++++
 main-business/src/main/java/com/ruoyi/business/service/impl/SalesRecordServiceImpl.java                           |   67 +++++
 main-business/src/main/java/com/ruoyi/business/dto/OfficialInventoryDto.java                                      |    3 
 main-business/src/main/java/com/ruoyi/business/service/OfficialInventoryService.java                              |    3 
 main-business/src/main/java/com/ruoyi/business/vo/SalesRecordExportVo.java                                        |   94 +++++++
 main-business/src/main/resources/db/migration/postgresql/V20250701142700__create_table_equipment_management.sql   |    3 
 ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java                                              |    2 
 main-business/src/main/java/com/ruoyi/business/service/SalesRecordService.java                                    |    3 
 main-business/src/main/java/com/ruoyi/business/entity/EquipmentManagement.java                                    |    5 
 main-business/src/main/java/com/ruoyi/business/task/TimingTaskJob.java                                            |    1 
 main-business/src/main/java/com/ruoyi/business/utils/DynamicExcelUtil.java                                        |  200 +++++++++++++++
 main-business/src/main/resources/db/migration/postgresql/V20250611160300__create_table_sales_record.sql           |    2 
 main-business/src/main/java/com/ruoyi/business/vo/OfficialInventoryExportVo.java                                  |   94 +++++++
 23 files changed, 738 insertions(+), 40 deletions(-)

diff --git a/main-business/src/main/java/com/ruoyi/business/controller/OfficialInventoryController.java b/main-business/src/main/java/com/ruoyi/business/controller/OfficialInventoryController.java
index bdddb30..45c9060 100644
--- a/main-business/src/main/java/com/ruoyi/business/controller/OfficialInventoryController.java
+++ b/main-business/src/main/java/com/ruoyi/business/controller/OfficialInventoryController.java
@@ -3,10 +3,12 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.business.dto.OfficialInventoryDto;
+import com.ruoyi.business.dto.SalesRecordDto;
 import com.ruoyi.business.entity.OfficialInventory;
 import com.ruoyi.business.service.OfficialInventoryService;
 import com.ruoyi.business.vo.OfficialInventoryVo;
 import com.ruoyi.common.core.domain.R;
+import jakarta.servlet.http.HttpServletResponse;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
@@ -79,4 +81,12 @@
     public R<List<OfficialInventoryDto>> coalBlendingList() {
         return R.ok(officialInventoryService.coalBlendingList());
     }
+
+    /**
+     * 姝e紡搴撹褰曡〃瀵煎嚭
+     */
+    @PostMapping("/export")
+    public void officialInventoryExport(HttpServletResponse response, OfficialInventoryDto officialInventoryDto) {
+        officialInventoryService.officialInventoryExport(response, officialInventoryDto);
+    }
 }
diff --git a/main-business/src/main/java/com/ruoyi/business/controller/SalesRecordController.java b/main-business/src/main/java/com/ruoyi/business/controller/SalesRecordController.java
index 75f43aa..714bdc7 100644
--- a/main-business/src/main/java/com/ruoyi/business/controller/SalesRecordController.java
+++ b/main-business/src/main/java/com/ruoyi/business/controller/SalesRecordController.java
@@ -2,10 +2,12 @@
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.basic.dto.SupplyDto;
 import com.ruoyi.business.dto.SalesRecordDto;
 import com.ruoyi.business.entity.SalesRecord;
 import com.ruoyi.business.service.SalesRecordService;
 import com.ruoyi.common.core.domain.R;
+import jakarta.servlet.http.HttpServletResponse;
 import lombok.AllArgsConstructor;
 import org.springframework.web.bind.annotation.*;
 
@@ -49,4 +51,12 @@
     public R remove(@RequestBody Long[] ids) {
         return R.ok(salesRecordService.delByIds(ids));
     }
+
+    /**
+     * 閿�鍞褰曡〃瀵煎嚭
+     */
+    @PostMapping("/export")
+    public void salesRecordExport(HttpServletResponse response, SalesRecordDto salesRecordDto) {
+        salesRecordService.salesRecordExport(response, salesRecordDto);
+    }
 }
diff --git a/main-business/src/main/java/com/ruoyi/business/dto/OfficialInventoryDto.java b/main-business/src/main/java/com/ruoyi/business/dto/OfficialInventoryDto.java
index 0d0e2c2..4bc29b1 100644
--- a/main-business/src/main/java/com/ruoyi/business/dto/OfficialInventoryDto.java
+++ b/main-business/src/main/java/com/ruoyi/business/dto/OfficialInventoryDto.java
@@ -24,4 +24,7 @@
 
     private List<Map<String, String>> coalValues; //濯掕川鏂规瀛楁鍊�
 
+    private List<Long> exportIds;//瀵煎嚭閫変腑鐨刬d
+
+
 }
diff --git a/main-business/src/main/java/com/ruoyi/business/dto/SalesRecordDto.java b/main-business/src/main/java/com/ruoyi/business/dto/SalesRecordDto.java
index 32560a2..a37dcb1 100644
--- a/main-business/src/main/java/com/ruoyi/business/dto/SalesRecordDto.java
+++ b/main-business/src/main/java/com/ruoyi/business/dto/SalesRecordDto.java
@@ -3,8 +3,12 @@
 import com.ruoyi.business.entity.SalesRecord;
 import lombok.Data;
 
+import java.util.List;
+
 @Data
 public class SalesRecordDto extends SalesRecord {
 
     private String coal;
+
+    private List<Long> exportIds;//瀵煎嚭閫変腑鐨刬d
 }
diff --git a/main-business/src/main/java/com/ruoyi/business/entity/EquipmentManagement.java b/main-business/src/main/java/com/ruoyi/business/entity/EquipmentManagement.java
index 742d5c4..7ad6c05 100644
--- a/main-business/src/main/java/com/ruoyi/business/entity/EquipmentManagement.java
+++ b/main-business/src/main/java/com/ruoyi/business/entity/EquipmentManagement.java
@@ -76,4 +76,9 @@
      */
     @TableField(value = "purchase_price")
     private BigDecimal purchasePrice;
+    /**
+     * 鏄惁鑰楁潗绫�
+     */
+    @TableField(value = "is_consumables")
+    private boolean isConsumables;
 }
\ No newline at end of file
diff --git a/main-business/src/main/java/com/ruoyi/business/entity/EquipmentUsageRecord.java b/main-business/src/main/java/com/ruoyi/business/entity/EquipmentUsageRecord.java
index 9337b33..5f20912 100644
--- a/main-business/src/main/java/com/ruoyi/business/entity/EquipmentUsageRecord.java
+++ b/main-business/src/main/java/com/ruoyi/business/entity/EquipmentUsageRecord.java
@@ -44,10 +44,10 @@
     @TableField(value = "department_id")
     private Long departmentId;
     /**
-     * 璁惧鐘舵�侊紙浣跨敤涓紝宸插綊杩橈級
+     * 璁惧鐘舵�侊紙1浣跨敤涓紝2宸插綊杩橈級
      */
     @TableField(value = "equipment_status")
-    private String equipmentStatus;
+    private Integer equipmentStatus;
     /**
      * 浣跨敤寮�濮嬫椂闂�
      */
diff --git a/main-business/src/main/java/com/ruoyi/business/entity/InspectionTask.java b/main-business/src/main/java/com/ruoyi/business/entity/InspectionTask.java
index 7354a13..bff9044 100644
--- a/main-business/src/main/java/com/ruoyi/business/entity/InspectionTask.java
+++ b/main-business/src/main/java/com/ruoyi/business/entity/InspectionTask.java
@@ -61,4 +61,9 @@
      */
     @TableField(value = "frequency_type")
     private String frequencyType;
+    /**
+     * 鏃堕棿缁嗚妭
+     */
+    @TableField(value = "frequency_detail")
+    private String frequencyDetail;
 }
\ No newline at end of file
diff --git a/main-business/src/main/java/com/ruoyi/business/entity/SalesRecord.java b/main-business/src/main/java/com/ruoyi/business/entity/SalesRecord.java
index 6012216..b2bb962 100644
--- a/main-business/src/main/java/com/ruoyi/business/entity/SalesRecord.java
+++ b/main-business/src/main/java/com/ruoyi/business/entity/SalesRecord.java
@@ -5,6 +5,7 @@
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
 import com.ruoyi.common.core.domain.MyBaseEntity;
 import lombok.Data;
 
@@ -33,6 +34,7 @@
      */
     @TableField(value = "sale_date")
     @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "閿�鍞棩鏈�")
     private LocalDate saleDate;
     /**
      * 瀹㈡埛id
@@ -120,4 +122,9 @@
     @TableField(value = "registration_date")
     @JsonFormat(pattern = "yyyy-MM-dd")
     private LocalDate registrationDate;
+    /**
+     * 鏄惁娣诲姞鑷冲緟琛ュ簱
+     */
+    @TableField(value = "is_add")
+    private boolean isAdd;
 }
\ No newline at end of file
diff --git a/main-business/src/main/java/com/ruoyi/business/service/OfficialInventoryService.java b/main-business/src/main/java/com/ruoyi/business/service/OfficialInventoryService.java
index 67b885b..eb67537 100644
--- a/main-business/src/main/java/com/ruoyi/business/service/OfficialInventoryService.java
+++ b/main-business/src/main/java/com/ruoyi/business/service/OfficialInventoryService.java
@@ -6,6 +6,7 @@
 import com.ruoyi.business.entity.OfficialInventory;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ruoyi.business.vo.OfficialInventoryVo;
+import jakarta.servlet.http.HttpServletResponse;
 
 import java.math.BigDecimal;
 import java.util.List;
@@ -34,4 +35,6 @@
     Map<String, BigDecimal> selectOfficialAllInfo();
 
     List<OfficialInventoryDto> coalBlendingList();
+
+    void officialInventoryExport(HttpServletResponse response, OfficialInventoryDto officialInventoryDto);
 }
diff --git a/main-business/src/main/java/com/ruoyi/business/service/SalesRecordService.java b/main-business/src/main/java/com/ruoyi/business/service/SalesRecordService.java
index 4a2f691..b4dcdb8 100644
--- a/main-business/src/main/java/com/ruoyi/business/service/SalesRecordService.java
+++ b/main-business/src/main/java/com/ruoyi/business/service/SalesRecordService.java
@@ -6,6 +6,7 @@
 import com.ruoyi.business.dto.SalesRecordDto;
 import com.ruoyi.business.dto.YearlyQueryDto;
 import com.ruoyi.business.entity.SalesRecord;
+import jakarta.servlet.http.HttpServletResponse;
 import jakarta.validation.Valid;
 
 import java.util.Map;
@@ -29,4 +30,6 @@
     Map<String, Object> selectAllInfo();
 
     Map<String, Object> getYearlyMonthlySales(@Valid YearlyQueryDto query);
+
+    void salesRecordExport(HttpServletResponse response, SalesRecordDto salesRecordDto);
 }
diff --git a/main-business/src/main/java/com/ruoyi/business/service/impl/EquipmentManagementServiceImpl.java b/main-business/src/main/java/com/ruoyi/business/service/impl/EquipmentManagementServiceImpl.java
index ece6d7b..e2fe1bf 100644
--- a/main-business/src/main/java/com/ruoyi/business/service/impl/EquipmentManagementServiceImpl.java
+++ b/main-business/src/main/java/com/ruoyi/business/service/impl/EquipmentManagementServiceImpl.java
@@ -70,7 +70,8 @@
 
                     // 鏌ヨ璇ヨ澶囩殑浣跨敤鎬婚噺锛坲sageQuantity锛�
                     LambdaQueryWrapper<EquipmentUsageRecord> usageQueryWrapper = new LambdaQueryWrapper<>();
-                    usageQueryWrapper.eq(EquipmentUsageRecord::getEquipmentId, entity.getId());
+                    usageQueryWrapper.eq(EquipmentUsageRecord::getEquipmentId, entity.getId())
+                            .eq(EquipmentUsageRecord::getEquipmentStatus, 1);
 
                     // 璁$畻鎬讳娇鐢ㄩ噺
                     Integer totalUsage = equipmentUsageRecordMapper.selectList(usageQueryWrapper).stream()
diff --git a/main-business/src/main/java/com/ruoyi/business/service/impl/EquipmentUsageRecordServiceImpl.java b/main-business/src/main/java/com/ruoyi/business/service/impl/EquipmentUsageRecordServiceImpl.java
index 5c2366c..fde6bd6 100644
--- a/main-business/src/main/java/com/ruoyi/business/service/impl/EquipmentUsageRecordServiceImpl.java
+++ b/main-business/src/main/java/com/ruoyi/business/service/impl/EquipmentUsageRecordServiceImpl.java
@@ -85,7 +85,6 @@
             }
             return dto;
         }).toList();
-
         dtoPage.setRecords(dtoRecords);
         return dtoPage;
     }
@@ -125,46 +124,54 @@
 
             // 璁剧疆浣跨敤寮�濮嬫椂闂翠负褰撳墠鏃堕棿
             equipmentUsageRecord.setUsageStartTime(LocalDate.now());
-
-            // 濡傛灉鐘舵�佷负1(宸插綊杩�)锛屽垯璁剧疆缁撴潫鏃堕棿
-            if ("1".equals(equipmentUsageRecordDto.getEquipmentStatus())) {
-                equipmentUsageRecord.setUsageEndTime(LocalDate.now());
-            }
-
             return equipmentUsageRecordMapper.insert(equipmentUsageRecord);
-        }
-        // 鏇存柊璁板綍閫昏緫
-        else {
-            // 1. 鏌ヨ鍘熼鐢ㄨ褰�
+        } else {
+            // 缂栬緫璁板綍閫昏緫
             EquipmentUsageRecord originalRecord = equipmentUsageRecordMapper.selectById(equipmentUsageRecordDto.getId());
             if (originalRecord == null) {
                 throw new RuntimeException("棰嗙敤璁板綍涓嶅瓨鍦�");
             }
 
-            // 2. 璁$畻搴撳瓨鍙樺寲閲忥紙鏂版暟閲� - 鏃ф暟閲忥級
-            int quantityDelta = newUsageQuantity - originalRecord.getUsageQuantity();
+            // 澶勭悊褰掕繕閫昏緫
+            if (equipmentUsageRecordDto.getEquipmentStatus() == 2) {
+                // 妫�鏌ュ綊杩樻暟閲忔槸鍚﹀悎娉�
+                if (newUsageQuantity > originalRecord.getUsageQuantity()) {
+                    throw new RuntimeException("褰掕繕鏁伴噺涓嶈兘瓒呰繃鍘熼鐢ㄦ暟閲�");
+                }
 
-            if (quantityDelta != 0) {
-                // 3. 妫�鏌ヨ皟鏁村悗搴撳瓨鏄惁鍏呰冻
-                int newInventory = equipment.getQuantity() - quantityDelta;
+                // 璁$畻瀹為檯褰掕繕鏁伴噺锛堝師棰嗙敤鏁伴噺 - 鏂伴鐢ㄦ暟閲忥級
+                int returnedQuantity = originalRecord.getUsageQuantity() - newUsageQuantity;
+
+                // 鎭㈠閮ㄥ垎搴撳瓨
+                equipment.setQuantity(equipment.getQuantity() + returnedQuantity);
+                equipmentManagementMapper.updateById(equipment);
+
+                // 濡傛灉鍏ㄩ儴褰掕繕锛岃缃綊杩樻椂闂�
+                if (newUsageQuantity == 0) {
+                    equipmentUsageRecord.setUsageEndTime(LocalDate.now());
+                }
+                return equipmentUsageRecordMapper.updateById(equipmentUsageRecord);
+            }
+
+            // 澶勭悊鏅�氱紪杈戦�昏緫锛堥潪褰掕繕鐘舵�侊級
+            if (!newUsageQuantity.equals(originalRecord.getUsageQuantity())) {
+                // 璁$畻搴撳瓨鍙樺寲閲忥紙鏃ф暟閲� - 鏂版暟閲忥級
+                int quantityDelta = originalRecord.getUsageQuantity() - newUsageQuantity;
+
+                // 妫�鏌ヨ皟鏁村悗搴撳瓨鏄惁鍏呰冻
+                int newInventory = equipment.getQuantity() + quantityDelta;
                 if (newInventory < 0) {
                     throw new RuntimeException("搴撳瓨涓嶈冻锛岃皟鏁村悗搴撳瓨灏嗕负锛�" + newInventory);
                 }
 
-                // 4. 璋冩暣搴撳瓨
+                // 璋冩暣搴撳瓨
                 equipment.setQuantity(newInventory);
                 if (equipmentManagementMapper.updateById(equipment) == 0) {
                     throw new RuntimeException("搴撳瓨鏇存柊澶辫触锛屽彲鑳藉凡琚叾浠栨搷浣滀慨鏀�");
                 }
             }
 
-            // 5. 濡傛灉鐘舵�佸彉涓�1(宸插綊杩�)锛屽垯璁剧疆缁撴潫鏃堕棿涓哄綋鍓嶆椂闂�
-            if ("1".equals(equipmentUsageRecordDto.getEquipmentStatus()) &&
-                    (originalRecord.getEquipmentStatus() == null || !"1".equals(originalRecord.getEquipmentStatus()))) {
-                equipmentUsageRecord.setUsageEndTime(LocalDate.now());
-            }
-
-            // 6. 鏇存柊棰嗙敤璁板綍
+            // 鏇存柊棰嗙敤璁板綍
             return equipmentUsageRecordMapper.updateById(equipmentUsageRecord);
         }
     }
diff --git a/main-business/src/main/java/com/ruoyi/business/service/impl/OfficialInventoryServiceImpl.java b/main-business/src/main/java/com/ruoyi/business/service/impl/OfficialInventoryServiceImpl.java
index aa53535..be1daf3 100644
--- a/main-business/src/main/java/com/ruoyi/business/service/impl/OfficialInventoryServiceImpl.java
+++ b/main-business/src/main/java/com/ruoyi/business/service/impl/OfficialInventoryServiceImpl.java
@@ -15,16 +15,24 @@
 import com.ruoyi.basic.mapper.CoalValueMapper;
 import com.ruoyi.basic.mapper.SupplyMapper;
 import com.ruoyi.business.dto.OfficialInventoryDto;
+import com.ruoyi.business.dto.SalesRecordDto;
 import com.ruoyi.business.entity.OfficialInventory;
+import com.ruoyi.business.entity.SalesRecord;
 import com.ruoyi.business.mapper.OfficialInventoryMapper;
 import com.ruoyi.business.service.OfficialInventoryService;
+import com.ruoyi.business.utils.DynamicExcelUtil;
+import com.ruoyi.business.vo.OfficialInventoryExportVo;
 import com.ruoyi.business.vo.OfficialInventoryVo;
+import com.ruoyi.business.vo.SalesRecordExportVo;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.exception.base.BaseException;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.system.mapper.SysUserMapper;
+import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -43,6 +51,7 @@
  * @since 2025-06-04
  */
 @Service
+@Slf4j
 @RequiredArgsConstructor
 public class OfficialInventoryServiceImpl extends ServiceImpl<OfficialInventoryMapper, OfficialInventory> implements OfficialInventoryService {
 
@@ -355,6 +364,160 @@
     }
 
     @Override
+    public void officialInventoryExport(HttpServletResponse response, OfficialInventoryDto officialInventoryDto) {
+        try {
+            // 鑾峰彇鐓よ川瀛楁閰嶇疆
+            List<CoalField> allCoalFields = coalFieldMapper.selectList(null);
+            List<String> dynamicHeaders = allCoalFields.stream()
+                    .map(CoalField::getFieldName)
+                    .collect(Collectors.toList());
+
+            // 鑾峰彇鏁版嵁
+            List<OfficialInventory> list = officialInventoryDto.getExportIds() != null && !officialInventoryDto.getExportIds().isEmpty()
+                    ? officialInventoryMapper.selectByIds(officialInventoryDto.getExportIds())
+                    : officialInventoryMapper.selectList(null);
+
+            // 杞崲涓哄鍑篤O
+            List<OfficialInventoryExportVo> exportData = convertToExportVo(list, allCoalFields);
+
+            // 浣跨敤澧炲己鐨凟xcel宸ュ叿瀵煎嚭
+            DynamicExcelUtil<OfficialInventoryExportVo> util =
+                    new DynamicExcelUtil<>(OfficialInventoryExportVo.class, dynamicHeaders);
+
+            // 瀵煎嚭Excel
+            util.exportExcel(response, exportData, "搴撳瓨鏁版嵁");
+
+        } catch (Exception e) {
+            log.error("搴撳瓨瀵煎嚭澶辫触", e);
+            // 鍙互鑰冭檻杩斿洖鏇村弸濂界殑閿欒淇℃伅缁欏墠绔�
+            throw new RuntimeException("瀵煎嚭澶辫触: " + e.getMessage());
+        }
+    }
+
+    private List<OfficialInventoryExportVo> convertToExportVo(List<OfficialInventory> list, List<CoalField> coalFields) {
+        if (CollectionUtils.isEmpty(list)) {
+            return Collections.emptyList();
+        }
+
+        // 鏀堕泦鎵�鏈夐渶瑕佹煡璇㈢殑ID
+        Set<Long> coalIds = list.stream()
+                .map(OfficialInventory::getCoalId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+        Set<Long> supplierIds = list.stream()
+                .map(OfficialInventory::getSupplierId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+        Set<Long> registrantIds = list.stream()
+                .map(OfficialInventory::getRegistrantId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+        Set<Long> planIds = list.stream()
+                .map(OfficialInventory::getCoalPlanId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+        // 鎵归噺鏌ヨ鍏宠仈鏁版嵁
+        Map<Long, CoalInfo> coalInfoMap = getCoalInfoMap(coalIds);
+        Map<Long, Supply> supplyMap = getSupplyMap(supplierIds);
+        Map<Long, SysUser> userMap = getUserMap(registrantIds);
+        Map<Long, List<CoalValue>> coalValuesMap = getCoalValuesMap(planIds);
+
+        // 鏋勫缓瀛楁ID鍒板瓧娈靛悕绉扮殑鏄犲皠锛屾彁楂樻煡鎵炬晥鐜�
+        Map<String, String> fieldIdToNameMap = coalFields.stream()
+                .collect(Collectors.toMap(CoalField::getFields, CoalField::getFieldName));
+
+        // 杞崲鏁版嵁
+        return list.stream().map(record -> {
+            OfficialInventoryExportVo vo = new OfficialInventoryExportVo();
+            vo.initDynamicFields(); // 鍒濆鍖�
+
+            // 璁剧疆鍩虹灞炴��
+            BeanUtils.copyProperties(record, vo);
+
+            // 璁剧疆鍏宠仈淇℃伅
+            setCoalInfo(vo, record.getCoalId(), coalInfoMap);
+            setSupplierInfo(vo, record.getSupplierId(), supplyMap);
+            setUserMapInfo(vo, record.getRegistrantId(), userMap);
+
+            // 鍔ㄦ�佸瓧娈靛鐞�
+            if (record.getCoalPlanId() != null) {
+                List<CoalValue> values = coalValuesMap.getOrDefault(record.getCoalPlanId(), Collections.emptyList());
+                setDynamicFields(vo, values, fieldIdToNameMap);
+            }
+
+            return vo;
+        }).collect(Collectors.toList());
+    }
+
+    private void setCoalInfo(OfficialInventoryExportVo vo, Long coalId, Map<Long, CoalInfo> coalInfoMap) {
+        if (coalId != null && coalInfoMap.containsKey(coalId)) {
+            vo.setCoal(coalInfoMap.get(coalId).getCoal());
+        }
+    }
+
+    private void setSupplierInfo(OfficialInventoryExportVo vo, Long supplierId, Map<Long, Supply> supplyMap) {
+        if (supplierId != null && supplyMap.containsKey(supplierId)) {
+            vo.setSupplierName(supplyMap.get(supplierId).getSupplierName());
+        }
+    }
+
+    private void setUserMapInfo(OfficialInventoryExportVo vo, Long registrantId, Map<Long, SysUser> userMap) {
+        if (registrantId != null && userMap.containsKey(registrantId)) {
+            vo.setRegistrant(userMap.get(registrantId).getNickName());
+        }
+    }
+
+    private void setDynamicFields(OfficialInventoryExportVo vo, List<CoalValue> values, Map<String, String> fieldIdToNameMap) {
+        for (CoalValue value : values) {
+            String fieldName = fieldIdToNameMap.get(value.getFields());
+            if (fieldName != null) {
+                vo.getCoalQualityProperties().put(fieldName, value.getCoalValue());
+            }
+        }
+    }
+
+    private Map<Long, List<CoalValue>> getCoalValuesMap(Set<Long> planIds) {
+        if (CollectionUtils.isEmpty(planIds)) {
+            return Collections.emptyMap();
+        }
+
+        List<CoalValue> allValues = coalValueMapper.selectList(
+                new LambdaQueryWrapper<CoalValue>()
+                        .in(CoalValue::getPlanId, planIds));
+
+        return allValues.stream()
+                .collect(Collectors.groupingBy(CoalValue::getPlanId));
+    }
+
+    private Map<Long, CoalInfo> getCoalInfoMap(Set<Long> coalIds) {
+        if (CollectionUtils.isEmpty(coalIds)) {
+            return Collections.emptyMap();
+        }
+        return coalInfoMapper.selectByIds(coalIds).stream()
+                .collect(Collectors.toMap(CoalInfo::getId, Function.identity()));
+    }
+
+    private Map<Long, Supply> getSupplyMap(Set<Long> supplierIds) {
+        if (CollectionUtils.isEmpty(supplierIds)) {
+            return Collections.emptyMap();
+        }
+        return supplyMapper.selectByIds(supplierIds).stream()
+                .collect(Collectors.toMap(Supply::getId, Function.identity()));
+    }
+
+    private Map<Long, SysUser> getUserMap(Set<Long> registrantIds) {
+        if (CollectionUtils.isEmpty(registrantIds)) {
+            return Collections.emptyMap();
+        }
+        return sysUserMapper.selectBatchIds(registrantIds).stream()
+                .collect(Collectors.toMap(SysUser::getUserId, Function.identity()));
+    }
+
+    @Override
     public List<OfficialInventoryDto> coalBlendingList() {
         // 1. 鏌ヨ鍩虹搴撳瓨鏁版嵁
         List<OfficialInventory> officialInventories = officialInventoryMapper.selectList(null);
@@ -369,16 +532,33 @@
             planIds.add(inventory.getCoalPlanId());
         });
         // 3. 鎵归噺鏌ヨ鍏宠仈鏁版嵁
-        Map<Long, CoalInfo> coalInfoMap = coalInfoMapper.selectByIds(coalIds).stream()
-                .collect(Collectors.toMap(CoalInfo::getId, Function.identity()));
+        Map<Long, CoalInfo> coalInfoMap;
+        if (!coalIds.isEmpty()) {
+            coalInfoMap = coalInfoMapper.selectByIds(coalIds).stream()
+                    .collect(Collectors.toMap(CoalInfo::getId, Function.identity()));
+        } else {
+            coalInfoMap = new HashMap<>();
+        }
 
-        Map<Long, Supply> supplyMap = supplyMapper.selectByIds(supplierIds).stream()
-                .collect(Collectors.toMap(Supply::getId, Function.identity()));
+        Map<Long, Supply> supplyMap;
+        if (!supplierIds.isEmpty()) {
+            supplyMap = supplyMapper.selectByIds(supplierIds).stream()
+                    .collect(Collectors.toMap(Supply::getId, Function.identity()));
+        } else {
+            supplyMap = new HashMap<>();
+            log.warn("supplierIds 涓虹┖锛岃烦杩囨煡璇� Supply");
+        }
 
-        Map<Long, List<CoalValue>> coalValuesMap = coalValueMapper.selectList(
-                        new LambdaQueryWrapper<CoalValue>().in(CoalValue::getPlanId, planIds))
-                .stream()
-                .collect(Collectors.groupingBy(CoalValue::getPlanId));
+        Map<Long, List<CoalValue>> coalValuesMap;
+        if (!planIds.isEmpty()) {
+            coalValuesMap = coalValueMapper.selectList(
+                            new LambdaQueryWrapper<CoalValue>().in(CoalValue::getPlanId, planIds))
+                    .stream()
+                    .collect(Collectors.groupingBy(CoalValue::getPlanId));
+        } else {
+            coalValuesMap = new HashMap<>();
+            log.warn("planIds 涓虹┖锛岃烦杩囨煡璇� CoalValue");
+        }
         // 4. 缁勮DTO
         return officialInventories.stream()
                 .map(inventory -> {
diff --git a/main-business/src/main/java/com/ruoyi/business/service/impl/SalesRecordServiceImpl.java b/main-business/src/main/java/com/ruoyi/business/service/impl/SalesRecordServiceImpl.java
index 01c3e54..1a9c849 100644
--- a/main-business/src/main/java/com/ruoyi/business/service/impl/SalesRecordServiceImpl.java
+++ b/main-business/src/main/java/com/ruoyi/business/service/impl/SalesRecordServiceImpl.java
@@ -4,10 +4,12 @@
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.basic.entity.CoalInfo;
 import com.ruoyi.basic.entity.Customer;
+import com.ruoyi.basic.entity.Supply;
 import com.ruoyi.basic.mapper.CoalInfoMapper;
 import com.ruoyi.basic.mapper.CustomerMapper;
 import com.ruoyi.business.dto.SalesRecordDto;
@@ -17,12 +19,16 @@
 import com.ruoyi.business.mapper.OfficialInventoryMapper;
 import com.ruoyi.business.mapper.SalesRecordMapper;
 import com.ruoyi.business.service.SalesRecordService;
+import com.ruoyi.business.vo.SalesRecordExportVo;
 import com.ruoyi.common.core.domain.entity.SysUser;
 import com.ruoyi.common.exception.base.BaseException;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.system.mapper.SysUserMapper;
+import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -51,6 +57,7 @@
  * @since 2025-06-11
  */
 @Service
+@Slf4j
 @RequiredArgsConstructor
 public class SalesRecordServiceImpl extends ServiceImpl<SalesRecordMapper, SalesRecord> implements SalesRecordService {
 
@@ -155,7 +162,10 @@
             throw new BaseException("閿�鍞暟閲忎笉鑳藉ぇ浜庡簱瀛樻暟閲�");
         }
         officialInventory.setInventoryQuantity(officialInventory.getInventoryQuantity().subtract(salesRecordDto.getSaleQuantity()));
-        officialInventory.setPendingReplenishment(salesRecordDto.getSaleQuantity());
+
+        if (salesRecordDto.isAdd()){
+            officialInventory.setPendingReplenishment(salesRecordDto.getSaleQuantity());
+        }
         officialInventoryMapper.updateById(officialInventory);
 
         // 鏋勫缓閿�鍞褰曞疄浣�
@@ -193,7 +203,7 @@
         if (registrant == null) {
             throw new BaseException("鐧昏浜轰俊鎭笉瀛樺湪");
         }
-        record.setRegistrant(registrant.getUserName());
+        record.setRegistrant(registrant.getNickName());
 
         // 璁剧疆瀹㈡埛淇℃伅
         Customer customer = customerMapper.selectById(dto.getCustomerId());
@@ -255,6 +265,59 @@
     }
 
     @Override
+    public void salesRecordExport(HttpServletResponse response, SalesRecordDto salesRecordDto) {
+        List<Long> ids = salesRecordDto.getExportIds();
+        List<SalesRecord> list;
+        if (ids != null && !ids.isEmpty()) {
+            list = salesRecordMapper.selectByIds(ids);
+        } else {
+            list = salesRecordMapper.selectList(null);
+        }
+        List<SalesRecordExportVo> exportData = convertToExportVo(list);
+        ExcelUtil<SalesRecordExportVo> util = new ExcelUtil<>(SalesRecordExportVo.class);
+        util.exportExcel(response, exportData, "閿�鍞嚭搴撴暟鎹�");
+    }
+
+    private List<SalesRecordExportVo> convertToExportVo(List<SalesRecord> list) {
+        // 1. 鎻愬墠鏀堕泦鎵�鏈夐渶瑕佹煡璇㈢殑coalId锛岄伩鍏峃+1鏌ヨ闂
+        Set<Long> coalIds = list.stream()
+                .filter(Objects::nonNull)
+                .map(SalesRecord::getCoalId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+
+        // 2. 鎵归噺鏌ヨcoalInfo鏁版嵁
+        Map<Long, CoalInfo> coalInfoMap = CollectionUtils.isEmpty(coalIds)
+                ? Collections.emptyMap()
+                : coalInfoMapper.selectByIds(coalIds).stream()
+                .filter(Objects::nonNull)
+                .collect(Collectors.toMap(CoalInfo::getId, Function.identity()));
+
+        // 3. 杞崲鏁版嵁
+        return list.stream()
+                .filter(Objects::nonNull)
+                .map(record -> {
+                    try {
+                        SalesRecordExportVo vo = new SalesRecordExportVo();
+
+                        // 鎷疯礉鍩虹灞炴��
+                        BeanUtils.copyProperties(record, vo);
+
+                        // 璁剧疆鍏宠仈鐨刢oal淇℃伅
+                        Optional.ofNullable(record.getCoalId())
+                                .map(coalInfoMap::get)
+                                .ifPresent(coalInfo -> vo.setCoal(coalInfo.getCoal()));
+                        return vo;
+                    } catch (Exception e) {
+                        log.error("杞崲閿�鍞褰昖O寮傚父锛岃褰旾D: {}", record.getId(), e);
+                        return null;
+                    }
+                })
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
+    @Override
     public Map<String, Object> getYearlyMonthlySales(YearlyQueryDto query) {
         // 1. 澶勭悊榛樿鏌ヨ锛堜笉浼犲弬鏁版椂锛�
         if (query == null || query.getTimeRange() == null || query.getTimeRange().length == 0) {
diff --git a/main-business/src/main/java/com/ruoyi/business/task/TimingTaskJob.java b/main-business/src/main/java/com/ruoyi/business/task/TimingTaskJob.java
index bf4d1dc..e642756 100644
--- a/main-business/src/main/java/com/ruoyi/business/task/TimingTaskJob.java
+++ b/main-business/src/main/java/com/ruoyi/business/task/TimingTaskJob.java
@@ -60,6 +60,7 @@
         inspectionTask.setRemarks("鑷姩鐢熸垚鑷畾鏃朵换鍔D: " + timingTask.getId());
         inspectionTask.setRegistrantId(timingTask.getRegistrantId());
         inspectionTask.setFrequencyType(timingTask.getFrequencyType());
+        inspectionTask.setFrequencyDetail(timingTask.getFrequencyDetail());
 
         return inspectionTask;
     }
diff --git a/main-business/src/main/java/com/ruoyi/business/utils/DynamicExcelUtil.java b/main-business/src/main/java/com/ruoyi/business/utils/DynamicExcelUtil.java
new file mode 100644
index 0000000..87fb4ff
--- /dev/null
+++ b/main-business/src/main/java/com/ruoyi/business/utils/DynamicExcelUtil.java
@@ -0,0 +1,200 @@
+package com.ruoyi.business.utils;
+
+import com.ruoyi.business.vo.OfficialInventoryExportVo;
+import com.ruoyi.common.annotation.Excel;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+
+import java.lang.reflect.Field;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@Slf4j
+public class DynamicExcelUtil<T> extends ExcelUtil<T> {
+    private final List<String> dynamicHeaders;
+    private final List<Field> fields;
+
+    public DynamicExcelUtil(Class<T> clazz, List<String> dynamicHeaders) {
+        super(clazz);
+        this.dynamicHeaders = dynamicHeaders;
+        this.fields = getAllFields(clazz);
+    }
+
+    private List<Field> getAllFields(Class<?> clazz) {
+        List<Field> fields = new ArrayList<>();
+        Class<?> currentClass = clazz;
+        while (currentClass != null) {
+            fields.addAll(Arrays.asList(currentClass.getDeclaredFields()));
+            currentClass = currentClass.getSuperclass();
+        }
+        return fields;
+    }
+
+    @Override
+    public void exportExcel(HttpServletResponse response, List<T> list, String sheetName) {
+        try {
+            String validSheetName = StringUtils.isBlank(sheetName) ? "Sheet1" : sheetName;
+
+            // Create workbook directly instead of using parent's createWorkbook
+            Workbook workbook = new SXSSFWorkbook();
+            Sheet sheet = workbook.createSheet(validSheetName);
+
+            // Create styles
+            createCustomStyles(workbook);
+
+            // Create dynamic header
+            createDynamicHeader(sheet, workbook);
+
+            // Fill data
+            fillDynamicData(sheet, list);
+
+            // Set response
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(validSheetName + ".xlsx", "UTF-8"));
+
+            workbook.write(response.getOutputStream());
+        } catch (Exception e) {
+            log.error("瀵煎嚭Excel澶辫触", e);
+            throw new RuntimeException("瀵煎嚭Excel澶辫触: " + e.getMessage(), e);
+        }
+    }
+
+    private void createCustomStyles(Workbook workbook) {
+        // Create basic styles similar to parent class if needed
+        CellStyle headerStyle = workbook.createCellStyle();
+        Font headerFont = workbook.createFont();
+        headerFont.setBold(true);
+        headerStyle.setFont(headerFont);
+        headerStyle.setAlignment(HorizontalAlignment.CENTER);
+        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+    }
+
+    /**
+     * 鍒涘缓鍔ㄦ�佽〃澶达紙鐖惰〃澶�"鐓よ川" + 瀛愯〃澶达級
+     */
+    private void createDynamicHeader(Sheet sheet, Workbook workbook) {
+        // 涓昏〃澶达紙绗�1琛岋級鍜屽瓙琛ㄥご锛堢2琛岋級
+        Row mainHeaderRow = sheet.createRow(0);
+        Row subHeaderRow = sheet.createRow(1);
+
+        // 鍒涘缓琛ㄥご鏍峰紡锛堝眳涓� + 鍔犵矖锛�
+        CellStyle headerStyle = workbook.createCellStyle();
+        Font headerFont = workbook.createFont();
+        headerFont.setBold(true);
+        headerStyle.setFont(headerFont);
+        headerStyle.setAlignment(HorizontalAlignment.CENTER);
+        headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+
+        int colIndex = 0;
+
+        // 閬嶅巻鎵�鏈夊瓧娈碉紙鍥哄畾瀛楁 + 鍔ㄦ�佸瓧娈碉級
+        for (Field field : fields) {
+            Excel excel = field.getAnnotation(Excel.class);
+            if (excel != null) {
+                // 1. 濡傛灉鏄� "鐓よ川" 瀛楁锛岃烦杩囷紙鍚庨潰鍗曠嫭澶勭悊锛�
+                if (excel.name().equals("鐓よ川")) {
+                    continue;
+                }
+
+                // 2. 澶勭悊鏅�氬瓧娈碉紙闈� "鐓よ川" 鐨勫垪锛�
+                // 2.1 鍦ㄤ富琛ㄥご锛堢1琛岋級璁剧疆瀛楁鍚�
+                Cell mainHeaderCell = mainHeaderRow.createCell(colIndex);
+                mainHeaderCell.setCellValue(excel.name());
+                mainHeaderCell.setCellStyle(headerStyle);
+
+                // 2.2 鍦ㄥ瓙琛ㄥご锛堢2琛岋級鐣欑┖锛屼絾鍚堝苟鍗曞厓鏍间娇鍏跺崰鎹袱琛岄珮搴�
+                Cell subHeaderCell = subHeaderRow.createCell(colIndex);
+                subHeaderCell.setCellStyle(headerStyle); // 淇濇寔鏍峰紡涓�鑷�
+
+                // 2.3 鍚堝苟褰撳墠鍒楃殑绗�1琛屽拰绗�2琛岋紙鍨傜洿鍚堝苟锛�
+                sheet.addMergedRegion(new CellRangeAddress(
+                        0, 1,  // 浠庣1琛屽埌绗�2琛�
+                        colIndex, colIndex  // 褰撳墠鍒�
+                ));
+
+                colIndex++; // 绉诲姩鍒颁笅涓�鍒�
+            }
+        }
+
+        // 3. 澶勭悊鍔ㄦ�佸瓧娈碉紙"鐓よ川" 涓嬬殑瀛愯〃澶达級
+        if (!dynamicHeaders.isEmpty()) {
+            // 3.1 鍦ㄤ富琛ㄥご锛堢1琛岋級璁剧疆 "鐓よ川" 澶ф爣棰橈紙妯悜鍚堝苟锛�
+            Cell coalHeaderCell = mainHeaderRow.createCell(colIndex);
+            coalHeaderCell.setCellValue("鐓よ川");
+            coalHeaderCell.setCellStyle(headerStyle);
+
+            // 妯悜鍚堝苟锛堢1琛岋紝璺ㄨ秺澶氬垪锛�
+            sheet.addMergedRegion(new CellRangeAddress(
+                    0, 0,  // 浠呯1琛�
+                    colIndex, colIndex + dynamicHeaders.size() - 1  // 璺ㄨ秺澶氬垪
+            ));
+
+            // 3.2 鍦ㄥ瓙琛ㄥご锛堢2琛岋級璁剧疆鍔ㄦ�佸瓧娈碉紙濡� "鐏板垎"銆�"纭垎"锛�
+            for (int i = 0; i < dynamicHeaders.size(); i++) {
+                Cell subCell = subHeaderRow.createCell(colIndex + i);
+                subCell.setCellValue(dynamicHeaders.get(i));
+                subCell.setCellStyle(headerStyle);
+            }
+        }
+    }
+
+    /**
+     * 濉厖鏁版嵁锛堝浐瀹氬瓧娈� + 鍔ㄦ�佸瓧娈碉級
+     */
+    private void fillDynamicData(Sheet sheet, List<T> list) {
+        int rowIndex = 2; // Data starts from row 3
+        CellStyle dataStyle = sheet.getWorkbook().createCellStyle();
+        dataStyle.setAlignment(HorizontalAlignment.CENTER);
+
+        for (T item : list) {
+            Row row = sheet.createRow(rowIndex++);
+            int colIndex = 0;
+
+            // Fill fixed fields
+            for (Field field : fields) {
+                Excel excel = field.getAnnotation(Excel.class);
+                if (excel != null && !excel.name().equals("鐓よ川")) {
+                    try {
+                        field.setAccessible(true);
+                        Object value = field.get(item);
+                        Cell cell = row.createCell(colIndex++);
+                        cell.setCellStyle(dataStyle);
+                        setCellValue(cell, value);
+                    } catch (Exception e) {
+                        log.error("濉厖鍥哄畾瀛楁澶辫触", e);
+                        row.createCell(colIndex++).setCellValue("-");
+                    }
+                }
+            }
+
+            // Fill dynamic fields
+            if (item instanceof OfficialInventoryExportVo vo) {
+                for (String header : dynamicHeaders) {
+                    Cell cell = row.createCell(colIndex++);
+                    cell.setCellStyle(dataStyle);
+                    cell.setCellValue(vo.getDynamicProperty(header));
+                }
+            }
+        }
+    }
+
+    /**
+     * 璁剧疆鍗曞厓鏍煎��
+     */
+    private void setCellValue(Cell cell, Object value) {
+        if (value == null) {
+            cell.setCellValue("-");
+        } else if (value instanceof Number) {
+            cell.setCellValue(((Number) value).doubleValue());
+        } else {
+            cell.setCellValue(value.toString());
+        }
+    }
+}
\ No newline at end of file
diff --git a/main-business/src/main/java/com/ruoyi/business/vo/OfficialInventoryExportVo.java b/main-business/src/main/java/com/ruoyi/business/vo/OfficialInventoryExportVo.java
new file mode 100644
index 0000000..8d1277c
--- /dev/null
+++ b/main-business/src/main/java/com/ruoyi/business/vo/OfficialInventoryExportVo.java
@@ -0,0 +1,94 @@
+package com.ruoyi.business.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.HashMap;
+import java.util.Map;
+
+@Data
+public class OfficialInventoryExportVo {
+
+    /**
+     * 渚涜揣鍟�
+     */
+    @Excel(name = "渚涜揣鍟�")
+    private String supplierName;
+
+    /**
+     * 鐓ょ
+     */
+    @Excel(name = "鐓ょ")
+    private String coal;
+    /**
+     * 鍗曚綅
+     */
+    @Excel(name = "鍗曚綅")
+    private String unit;
+    /**
+     * 搴撳瓨鏁伴噺
+     */
+    @Excel(name = "搴撳瓨鏁伴噺")
+    private BigDecimal inventoryQuantity;
+    /**
+     * 鍗曚环锛堝惈绋庯級
+     */
+    @Excel(name = "鍗曚环锛堝惈绋庯級")
+    private BigDecimal priceIncludingTax;
+    /**
+     * 鎬讳环锛堝惈绋庯級
+     */
+    @Excel(name = "鎬讳环锛堝惈绋庯級")
+    private BigDecimal totalPriceIncludingTax;
+    /**
+     * 涓嶅惈绋庡崟浠�
+     */
+    @Excel(name = "涓嶅惈绋庡崟浠�")
+    private BigDecimal priceExcludingTax;
+    /**
+     * 涓嶅惈绋庢�讳环
+     */
+    @Excel(name = "涓嶅惈绋庢�讳环")
+    private BigDecimal totalPriceExcludingTax;
+    /**
+     * 寰呰ˉ搴�
+     */
+    @Excel(name = "寰呰ˉ搴�")
+    private BigDecimal pendingReplenishment;
+    /**
+     * 鐧昏浜�
+     */
+    @Excel(name = "鐧昏浜�")
+    private String registrant;
+
+    /**
+     * 鐧昏鏃ユ湡
+     */
+    @Excel(name = "鐧昏鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate registrationDate;
+
+    /**
+     * 鍔ㄦ�佺叅璐ㄥ睘鎬э紙瀛樺偍鎵�鏈夌叅璐ㄥ瓙琛ㄥご鏁版嵁锛�
+     */
+    // 鍔ㄦ�佸瓧娈靛鍣紙涓嶅弬涓庡鍑猴級
+    private transient Map<String, String> coalQualityProperties;
+
+    // 鍔ㄦ�佸瓧娈佃闂柟娉�
+    public String getDynamicProperty(String fieldName) {
+        return coalQualityProperties != null ?
+                coalQualityProperties.getOrDefault(fieldName, "-") : "-";
+    }
+
+    // 鍒濆鍖栨柟娉曪紙鍦╟onvertToExportVo涓皟鐢級
+    public void initDynamicFields() {
+        // 纭繚map涓嶄负null
+        if (this.coalQualityProperties == null) {
+            this.coalQualityProperties = new HashMap<>();
+        }
+    }
+
+}
diff --git a/main-business/src/main/java/com/ruoyi/business/vo/SalesRecordExportVo.java b/main-business/src/main/java/com/ruoyi/business/vo/SalesRecordExportVo.java
new file mode 100644
index 0000000..c0f6308
--- /dev/null
+++ b/main-business/src/main/java/com/ruoyi/business/vo/SalesRecordExportVo.java
@@ -0,0 +1,94 @@
+package com.ruoyi.business.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.common.annotation.Excel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@Data
+public class SalesRecordExportVo {
+    /**
+     * 閿�鍞棩鏈�
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "閿�鍞棩鏈�")
+    private LocalDate saleDate;
+    /**
+     * 瀹㈡埛
+     */
+    @Excel(name = "瀹㈡埛")
+    private String customer;
+    /**
+     * 鐓ょ
+     */
+    @Excel(name = "鐓ょ")
+    private String coal;
+    /**
+     * 鍗曚綅
+     */
+    @Excel(name = "鍗曚綅")
+    private String unit;
+    /**
+     * 鍗曚环锛堝惈绋庯級
+     */
+    @Excel(name = "鍗曚环锛堝惈绋庯級")
+    private BigDecimal priceIncludingTax;
+    /**
+     * 搴撳瓨鏁伴噺
+     */
+    @Excel(name = "搴撳瓨鏁伴噺")
+    private BigDecimal inventoryQuantity;
+    /**
+     * 閿�鍞暟閲�
+     */
+    @Excel(name = "閿�鍞暟閲�")
+    private BigDecimal saleQuantity;
+    /**
+     * 閿�鍞崟浠� (鍚◣)
+     */
+    @Excel(name = "閿�鍞崟浠� (鍚◣)")
+    private BigDecimal salePrice;
+    /**
+     * 閿�鍞�讳环 (鍚◣)
+     */
+    @Excel(name = "閿�鍞�讳环 (鍚◣)")
+    private BigDecimal totalAmount;
+    /**
+     * 璐攢鐓ょ◣鐜�13%
+     */
+    @Excel(name = "璐攢鐓ょ◣鐜� (13%)")
+    private String taxCoal;
+    /**
+     * 杩愯緭绋庣巼9%
+     */
+    @Excel(name = "杩愯緭绋庣巼 (9%)")
+    private String taxTrans;
+    /**
+     * 姣涘埄娑�
+     */
+    @Excel(name = "姣涘埄娑�")
+    private BigDecimal grossProfit;
+    /**
+     * 鍑�鍒╂鼎
+     */
+    @Excel(name = "鍑�鍒╂鼎")
+    private BigDecimal netProfit;
+    /**
+     * 杩愯垂
+     */
+    @Excel(name = "杩愯垂")
+    private BigDecimal freight;
+    /**
+     * 鐧昏浜�
+     */
+    @Excel(name = "鐧昏浜�")
+    private String registrant;
+    /**
+     * 鐧昏鏃ユ湡
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "鐧昏鏃ユ湡")
+    private LocalDate registrationDate;
+}
diff --git a/main-business/src/main/resources/db/migration/postgresql/V20250611160300__create_table_sales_record.sql b/main-business/src/main/resources/db/migration/postgresql/V20250611160300__create_table_sales_record.sql
index f26b885..6e28b8a 100644
--- a/main-business/src/main/resources/db/migration/postgresql/V20250611160300__create_table_sales_record.sql
+++ b/main-business/src/main/resources/db/migration/postgresql/V20250611160300__create_table_sales_record.sql
@@ -20,6 +20,7 @@
     registrant_id       BIGINT,                           -- 鐧昏浜篿d
     registrant          VARCHAR(255)   ,          -- 鐧昏浜�
     registration_date   DATE           ,          -- 鐧昏鏃ユ湡
+    is_add              BOOLEAN      NOT NULL DEFAULT FALSE, -- 鏄惁娣诲姞鑷冲緟琛ュ簱
 
     create_time         TIMESTAMP WITHOUT TIME ZONE,          -- 涓婁紶鏃堕棿锛岄粯璁ゅ綋鍓嶆椂闂�
     update_time         TIMESTAMP WITHOUT TIME ZONE,          -- 鏈�鍚庢洿鏂版椂闂达紝榛樿褰撳墠鏃堕棿
@@ -50,6 +51,7 @@
 COMMENT ON COLUMN sales_record.registrant IS '鐧昏浜�';
 COMMENT ON COLUMN sales_record.registrant_id IS '鐧昏浜篿d';
 COMMENT ON COLUMN sales_record.registration_date IS '鐧昏鏃ユ湡';
+COMMENT ON COLUMN sales_record.is_add IS '鏄惁娣诲姞鑷冲緟琛ュ簱';
 
 COMMENT ON COLUMN sales_record.deleted IS '杞垹闄ゆ爣蹇楋紝0=鏈垹闄わ紝1=宸插垹闄�';
 COMMENT ON COLUMN sales_record.create_by IS '鍒涘缓璇ヨ褰曠殑鐢ㄦ埛';
diff --git a/main-business/src/main/resources/db/migration/postgresql/V20250614134700__create_table_inspection_task.sql b/main-business/src/main/resources/db/migration/postgresql/V20250614134700__create_table_inspection_task.sql
index dd1451e..3918bfb 100644
--- a/main-business/src/main/resources/db/migration/postgresql/V20250614134700__create_table_inspection_task.sql
+++ b/main-business/src/main/resources/db/migration/postgresql/V20250614134700__create_table_inspection_task.sql
@@ -10,6 +10,7 @@
     registrant    VARCHAR(100),                -- 鐧昏浜哄憳
     frequency_type     VARCHAR(100),           -- 棰戞
     inspection_location     VARCHAR(255),           -- 宸℃鍦扮偣璇︾粏鎻忚堪
+    frequency_detail     VARCHAR(255),           -- 鏃堕棿缁嗚妭
 
     deleted       INT NOT NULL DEFAULT 0,      -- 杞垹闄ゆ爣蹇楋細0=鏈垹闄わ紝1=宸插垹闄�
     create_by     VARCHAR(255),                -- 鍒涘缓浜虹敤鎴峰悕
@@ -36,6 +37,8 @@
 ON COLUMN inspection_task.frequency_type IS '棰戞';
 COMMENT
 ON COLUMN inspection_task.inspection_location IS '宸℃鍦扮偣璇︾粏鎻忚堪';
+COMMENT
+ON COLUMN inspection_task.frequency_detail IS '鏃堕棿缁嗚妭';
 
 COMMENT
 ON COLUMN inspection_task.deleted IS '杞垹闄ゆ爣蹇楋紝0=鏈垹闄わ紝1=宸插垹闄�';
diff --git a/main-business/src/main/resources/db/migration/postgresql/V20250701142700__create_table_equipment_management.sql b/main-business/src/main/resources/db/migration/postgresql/V20250701142700__create_table_equipment_management.sql
index c599d4f..dbcd9a8 100644
--- a/main-business/src/main/resources/db/migration/postgresql/V20250701142700__create_table_equipment_management.sql
+++ b/main-business/src/main/resources/db/migration/postgresql/V20250701142700__create_table_equipment_management.sql
@@ -13,6 +13,7 @@
     storage_location VARCHAR(100) NOT NULL,           -- 瀛樻斁浣嶇疆
     purchase_date    DATE,                            -- 閲囪喘鏃ユ湡
     purchase_price   DECIMAL(10, 2),                  -- 閲囪喘浠锋牸
+    is_consumables   BOOLEAN      NOT NULL DEFAULT FALSE, -- 鏄惁鑰楁潗绫�
 
     deleted          INT          NOT NULL DEFAULT 0, -- 杞垹闄ゆ爣蹇楋細0=鏈垹闄わ紝1=宸插垹闄�
     create_by        VARCHAR(255),                    -- 鍒涘缓浜虹敤鎴峰悕
@@ -46,6 +47,8 @@
 ON COLUMN equipment_management.purchase_date IS ' 閲囪喘鏃ユ湡 ';
 COMMENT
 ON COLUMN equipment_management.purchase_price IS ' 閲囪喘浠锋牸 ';
+COMMENT
+ON COLUMN equipment_management.is_consumables IS ' 鏄惁鑰楁潗绫� ';
 
 COMMENT
 ON COLUMN inspection_task.deleted IS '杞垹闄ゆ爣蹇楋紝0=鏈垹闄わ紝1=宸插垹闄�';
diff --git a/main-business/src/main/resources/db/migration/postgresql/V20250707155600__create_table_equipment_usage_record.sql b/main-business/src/main/resources/db/migration/postgresql/V20250707155600__create_table_equipment_usage_record.sql
index eb2446d..af01796 100644
--- a/main-business/src/main/resources/db/migration/postgresql/V20250707155600__create_table_equipment_usage_record.sql
+++ b/main-business/src/main/resources/db/migration/postgresql/V20250707155600__create_table_equipment_usage_record.sql
@@ -6,7 +6,7 @@
     usage_quantity       INT            NOT NULL,           -- 浣跨敤鏁伴噺锛屼笉鍏佽涓虹┖
     user_id              BIGINT         NOT NULL,           -- 浣跨敤浜篒D锛屽叧鑱旂敤鎴疯〃锛屼笉鍏佽涓虹┖
     department_id        BIGINT         ,                   -- 閮ㄩ棬ID锛屽叧鑱旈儴闂ㄨ〃锛屼笉鍏佽涓虹┖
-    equipment_status     VARCHAR(50)    NOT NULL,           -- 璁惧鐘舵�侊紙濡傦細姝e父銆佹晠闅溿�佺淮淇腑锛夛紝涓嶅厑璁镐负绌�
+    equipment_status     BIGINT    NOT NULL,                -- 璁惧鐘舵��:1.浣跨敤涓� 2.宸插綊杩�
     usage_start_time     TIMESTAMP      NOT NULL,           -- 浣跨敤寮�濮嬫椂闂达紝涓嶅厑璁镐负绌�
     usage_end_time       TIMESTAMP,                         -- 浣跨敤缁撴潫鏃堕棿锛屽彲涓虹┖锛堟湭褰掕繕鏃讹級
     remarks              VARCHAR(1000),                     -- 澶囨敞淇℃伅锛屽彲涓虹┖
@@ -27,7 +27,7 @@
 COMMENT ON COLUMN equipment_usage_record.usage_quantity IS '浣跨敤鏁伴噺';
 COMMENT ON COLUMN equipment_usage_record.user_id IS '浣跨敤浜篒D';
 COMMENT ON COLUMN equipment_usage_record.department_id IS '閮ㄩ棬ID';
-COMMENT ON COLUMN equipment_usage_record.equipment_status IS '璁惧鐘舵�侊紙姝e父/鏁呴殰/缁翠慨涓級';
+COMMENT ON COLUMN equipment_usage_record.equipment_status IS '璁惧鐘舵��:1.浣跨敤涓� 2.宸插綊杩�';
 COMMENT ON COLUMN equipment_usage_record.usage_start_time IS '浣跨敤寮�濮嬫椂闂�';
 COMMENT ON COLUMN equipment_usage_record.usage_end_time IS '浣跨敤缁撴潫鏃堕棿';
 COMMENT ON COLUMN equipment_usage_record.remarks IS '澶囨敞淇℃伅';
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
index b230fa7..192ff9c 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java
@@ -114,7 +114,7 @@
     /**
      * 宸ヤ綔琛ㄥ悕绉�
      */
-    private String sheetName;
+    protected String sheetName;
 
     /**
      * 瀵煎嚭绫诲瀷锛圗XPORT:瀵煎嚭鏁版嵁锛汭MPORT锛氬鍏ユā鏉匡級

--
Gitblit v1.9.3