Browse Source

补回 df32f8e ours 合并丢失的远端改动(91文件): 自 25075a9 恢复 fileupload/config/alarm 后端与 mybatis xml、前端统计分析与数据补传页面; README 保留本地 python 3.10 改动

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
taosj 12 hours ago
parent
commit
030841ddfe
91 changed files with 2751 additions and 523 deletions
  1. 104 12
      forecast-backend-server/src/main/java/com/ufo/project/alarm/controller/AlarmEventRecordController.java
  2. 69 6
      forecast-backend-server/src/main/java/com/ufo/project/alarm/domain/AlarmEventRecord.java
  3. 95 0
      forecast-backend-server/src/main/java/com/ufo/project/alarm/domain/vo/AlarmEventRecordExportStat.java
  4. 12 1
      forecast-backend-server/src/main/java/com/ufo/project/alarm/mapper/AlarmEventRecordMapper.java
  5. 9 1
      forecast-backend-server/src/main/java/com/ufo/project/alarm/service/IAlarmEventRecordService.java
  6. 21 1
      forecast-backend-server/src/main/java/com/ufo/project/alarm/service/impl/AlarmEventRecordServiceImpl.java
  7. 68 5
      forecast-backend-server/src/main/java/com/ufo/project/config/controller/ConfigDeviceController.java
  8. 6 6
      forecast-backend-server/src/main/java/com/ufo/project/config/controller/ConfigStationChannelController.java
  9. 6 6
      forecast-backend-server/src/main/java/com/ufo/project/config/controller/ConfigStationDeviceController.java
  10. 6 6
      forecast-backend-server/src/main/java/com/ufo/project/config/controller/ConfigStationPointController.java
  11. 6 6
      forecast-backend-server/src/main/java/com/ufo/project/config/controller/ConfigStationPowerLimitController.java
  12. 6 6
      forecast-backend-server/src/main/java/com/ufo/project/config/controller/ConfigStationPvModuleController.java
  13. 18 8
      forecast-backend-server/src/main/java/com/ufo/project/config/domain/ConfigDevice.java
  14. 22 22
      forecast-backend-server/src/main/java/com/ufo/project/config/domain/ConfigStationChannel.java
  15. 20 20
      forecast-backend-server/src/main/java/com/ufo/project/config/domain/ConfigStationDevice.java
  16. 24 24
      forecast-backend-server/src/main/java/com/ufo/project/config/domain/ConfigStationPoint.java
  17. 20 20
      forecast-backend-server/src/main/java/com/ufo/project/config/domain/ConfigStationPowerLimit.java
  18. 22 22
      forecast-backend-server/src/main/java/com/ufo/project/config/domain/ConfigStationPvModule.java
  19. 171 0
      forecast-backend-server/src/main/java/com/ufo/project/config/domain/vo/ConfigDeviceExportVo.java
  20. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/mapper/ConfigStationChannelMapper.java
  21. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/mapper/ConfigStationDeviceMapper.java
  22. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/mapper/ConfigStationPointMapper.java
  23. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/mapper/ConfigStationPowerLimitMapper.java
  24. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/mapper/ConfigStationPvModuleMapper.java
  25. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/service/IConfigStationChannelService.java
  26. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/service/IConfigStationDeviceService.java
  27. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/service/IConfigStationPointService.java
  28. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/service/IConfigStationPowerLimitService.java
  29. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/service/IConfigStationPvModuleService.java
  30. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/service/impl/ConfigStationChannelServiceImpl.java
  31. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/service/impl/ConfigStationDeviceServiceImpl.java
  32. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/service/impl/ConfigStationPointServiceImpl.java
  33. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/service/impl/ConfigStationPowerLimitServiceImpl.java
  34. 1 1
      forecast-backend-server/src/main/java/com/ufo/project/config/service/impl/ConfigStationPvModuleServiceImpl.java
  35. 16 16
      forecast-backend-server/src/main/java/com/ufo/project/data/task/MockDataTask.java
  36. 56 0
      forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/controller/DataReportLogController.java
  37. 9 2
      forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/domain/DataReportLog.java
  38. 71 0
      forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/domain/vo/ReUploadCountRow.java
  39. 71 0
      forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/domain/vo/UploadRateCountRow.java
  40. 103 0
      forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/domain/vo/UploadRateStatQuery.java
  41. 194 0
      forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/domain/vo/UploadRateStatVO.java
  42. 39 0
      forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/mapper/DataReportLogMapper.java
  43. 25 0
      forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/service/IDataReportLogService.java
  44. 324 0
      forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/service/impl/DataReportLogServiceImpl.java
  45. 20 2
      forecast-backend-server/src/main/resources/mybatis/alarm/AlarmEventRecordMapper.xml
  46. 6 6
      forecast-backend-server/src/main/resources/mybatis/alarm/AlarmEventRuleMapper.xml
  47. 6 6
      forecast-backend-server/src/main/resources/mybatis/alarm/AlarmEventRuleTemplateMapper.xml
  48. 6 6
      forecast-backend-server/src/main/resources/mybatis/alarm/AlarmStatRecordMapper.xml
  49. 3 3
      forecast-backend-server/src/main/resources/mybatis/alarm/MonitoringCheckRecordMapper.xml
  50. 3 3
      forecast-backend-server/src/main/resources/mybatis/alarm/MonitoringSendRecordMapper.xml
  51. 6 6
      forecast-backend-server/src/main/resources/mybatis/api/ApiAccessControlMapper.xml
  52. 3 3
      forecast-backend-server/src/main/resources/mybatis/api/ApiAccessRecordMapper.xml
  53. 6 6
      forecast-backend-server/src/main/resources/mybatis/api/ApiRecordMapper.xml
  54. 6 6
      forecast-backend-server/src/main/resources/mybatis/api/ApiVersionReleaseMapper.xml
  55. 6 6
      forecast-backend-server/src/main/resources/mybatis/assessment/ConfigAssessmentIndicatorMapper.xml
  56. 6 6
      forecast-backend-server/src/main/resources/mybatis/assessment/ConfigAssessmentRuleMapper.xml
  57. 6 6
      forecast-backend-server/src/main/resources/mybatis/assessment/ConfigAssessmentTaskMapper.xml
  58. 6 6
      forecast-backend-server/src/main/resources/mybatis/config/ConfigAreaMapper.xml
  59. 25 4
      forecast-backend-server/src/main/resources/mybatis/config/ConfigDeviceMapper.xml
  60. 10 8
      forecast-backend-server/src/main/resources/mybatis/config/ConfigStationMapper.xml
  61. 6 6
      forecast-backend-server/src/main/resources/mybatis/config/ConfigSupplierMapper.xml
  62. 6 6
      forecast-backend-server/src/main/resources/mybatis/config/StopReducePowerRecordMapper.xml
  63. 6 6
      forecast-backend-server/src/main/resources/mybatis/datasource/DataSourceConfigMapper.xml
  64. 6 6
      forecast-backend-server/src/main/resources/mybatis/datasource/DataSourceGrabMapper.xml
  65. 6 6
      forecast-backend-server/src/main/resources/mybatis/datasource/DataSourceUserRelationMapper.xml
  66. 6 6
      forecast-backend-server/src/main/resources/mybatis/rbac/RbacMenuMapper.xml
  67. 6 6
      forecast-backend-server/src/main/resources/mybatis/rbac/RbacModuleMapper.xml
  68. 6 6
      forecast-backend-server/src/main/resources/mybatis/rbac/RbacRoleMapper.xml
  69. 6 6
      forecast-backend-server/src/main/resources/mybatis/rbac/RbacUserMapper.xml
  70. 6 6
      forecast-backend-server/src/main/resources/mybatis/report/ConfigReportSiteInfoMapper.xml
  71. 6 6
      forecast-backend-server/src/main/resources/mybatis/report/ConfigReportSiteMapper.xml
  72. 6 6
      forecast-backend-server/src/main/resources/mybatis/report/ConfigReportTaskMapper.xml
  73. 3 3
      forecast-backend-server/src/main/resources/mybatis/report/StatsAssessmentIndicatorMapper.xml
  74. 3 3
      forecast-backend-server/src/main/resources/mybatis/report/StatsAssessmentIndicatorMultiModelMapper.xml
  75. 3 3
      forecast-backend-server/src/main/resources/mybatis/report/StatsWeatherIndicatorMapper.xml
  76. 3 3
      forecast-backend-server/src/main/resources/mybatis/report/StatsWeatherIndicatorMultiModelMapper.xml
  77. 6 6
      forecast-backend-server/src/main/resources/mybatis/weather/WeatherWarningRecordMapper.xml
  78. 14 28
      forecast-backend-vue/src/api/alarm/eventRecord.js
  79. 29 0
      forecast-backend-vue/src/api/fileupload/reportLog.js
  80. 3 1
      forecast-backend-vue/src/utils/BearJiaUtil.js
  81. 5 1
      forecast-backend-vue/src/views/ufo/config/station/StationConfigCrud.vue
  82. 9 4
      forecast-backend-vue/src/views/ufo/config/station/index.vue
  83. 207 107
      forecast-backend-vue/src/views/ufo/electricity/device/TurbineInfo.vue
  84. 63 0
      forecast-backend-vue/src/views/ufo/electricity/forecast/DataResend.vue
  85. 269 0
      forecast-backend-vue/src/views/ufo/electricity/statanalysis/eventQuery.vue
  86. 14 2
      forecast-backend-vue/src/views/ufo/electricity/statanalysis/reportLogQuery.vue
  87. 195 0
      forecast-backend-vue/src/views/ufo/electricity/statanalysis/uploadRateStat.vue
  88. 2 2
      forecast-backend-vue/src/views/ufo/workbench/WorkbenchPage.vue
  89. 3 0
      release_note/20261028/ddl.sql
  90. 81 0
      release_note/20261028/dml.sql
  91. 15 0
      release_note/20261028/release_note.md

+ 104 - 12
forecast-backend-server/src/main/java/com/ufo/project/alarm/controller/AlarmEventRecordController.java

@@ -1,7 +1,11 @@
 package com.ufo.project.alarm.controller;
 
+import java.util.ArrayList;
 import java.util.List;
 import javax.servlet.http.HttpServletResponse;
+import com.alibaba.fastjson2.JSONArray;
+import com.alibaba.fastjson2.JSONObject;
+import com.github.pagehelper.PageHelper;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -15,15 +19,17 @@ import org.springframework.web.bind.annotation.RestController;
 import com.ufo.base.framework.aspectj.lang.annotation.Log;
 import com.ufo.base.framework.aspectj.lang.enums.BusinessType;
 import com.ufo.project.alarm.domain.AlarmEventRecord;
+import com.ufo.project.alarm.domain.vo.AlarmEventRecordExportStat;
 import com.ufo.project.alarm.service.IAlarmEventRecordService;
 import com.ufo.base.framework.web.controller.BaseController;
 import com.ufo.base.framework.web.domain.AjaxResult;
+import com.ufo.base.common.utils.DictUtils;
 import com.ufo.base.common.utils.poi.ExcelUtil;
 import com.ufo.base.framework.web.page.TableDataInfo;
 
 /**
  * 报警信息统计Controller
- * 
+ *
  * @author ufo
  * @date 2026-05-05
  */
@@ -35,28 +41,69 @@ public class AlarmEventRecordController extends BaseController
     private IAlarmEventRecordService alarmEventRecordService;
 
     /**
-     * 查询报警信息统计列表
+     * 查询事件列表
+     *
+     * 参数: alarmCategory 告警类别, alarmLevel 告警等级, processStatus 处理状态(true=已处理, false=未处理),
+     * alarmDate 告警时间(yyyy-MM-dd), pageNum, pageSize
      */
     @PreAuthorize("@ss.hasPermi('alarm:eventRecord:list')")
-    @GetMapping("/list")
-    public TableDataInfo list(AlarmEventRecord alarmEventRecord)
+    @PostMapping("/list")
+    public TableDataInfo list(@RequestBody JSONObject params)
     {
-        startPage();
-        List<AlarmEventRecord> list = alarmEventRecordService.selectAlarmEventRecordList(alarmEventRecord);
+        AlarmEventRecord query = buildQuery(params);
+        PageHelper.startPage(params.getIntValue("pageNum", 1), params.getIntValue("pageSize", 10));
+        List<AlarmEventRecord> list = alarmEventRecordService.selectAlarmEventRecordList(query);
         return getDataTable(list);
     }
 
     /**
-     * 导出报警信息统计列表
+     * 确认告警(批量处理, 非已处理数据均置为已处理)
+     *
+     * 参数: ids 告警主键集合
+     */
+    @PreAuthorize("@ss.hasPermi('alarm:eventRecord:edit')")
+    @Log(title = "事件查询", businessType = BusinessType.UPDATE)
+    @PostMapping("/process")
+    public AjaxResult process(@RequestBody JSONObject params)
+    {
+        JSONArray idsArr = params.getJSONArray("ids");
+        List<String> ids = idsArr == null ? null : idsArr.toJavaList(String.class);
+        if (ids == null || ids.isEmpty())
+        {
+            return error("请选择要处理的告警数据");
+        }
+        int rows = alarmEventRecordService.processAlarmEventRecord(ids);
+        if (rows == 0)
+        {
+            return success("所选告警均已处理, 未执行处理");
+        }
+        return success("已处理 " + rows + " 条告警");
+    }
+
+    /**
+     * 导出事件列表
+     *
+     * 参数: alarmCategory 告警类别, alarmLevel 告警等级, processStatus 处理状态, alarmDate 告警时间
      */
     @PreAuthorize("@ss.hasPermi('alarm:eventRecord:export')")
-    @Log(title = "报警信息统计", businessType = BusinessType.EXPORT)
+    @Log(title = "事件查询", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
-    public void export(HttpServletResponse response, AlarmEventRecord alarmEventRecord)
+    public void export(HttpServletResponse response, @RequestBody JSONObject params)
     {
-        List<AlarmEventRecord> list = alarmEventRecordService.selectAlarmEventRecordList(alarmEventRecord);
-        ExcelUtil<AlarmEventRecord> util = new ExcelUtil<AlarmEventRecord>(AlarmEventRecord.class);
-        util.exportExcel(response, list, "报警信息统计数据");
+        AlarmEventRecord query = buildQuery(params);
+        List<AlarmEventRecord> list = alarmEventRecordService.selectAlarmEventRecordList(query);
+        List<AlarmEventRecordExportStat> exportList = new ArrayList<>();
+        for (AlarmEventRecord record : list)
+        {
+            exportList.add(new AlarmEventRecordExportStat(
+                    dictLabel("alarm_level", record.getAlarmLevel() == null ? null : String.valueOf(record.getAlarmLevel())),
+                    dictLabel("alarm_category", record.getAlarmCategory()),
+                    record.getAlarmData(),
+                    processFlagLabel(record.getProcessFlag()),
+                    record.getAlarmCreateTime()));
+        }
+        ExcelUtil<AlarmEventRecordExportStat> util = new ExcelUtil<AlarmEventRecordExportStat>(AlarmEventRecordExportStat.class);
+        util.exportExcel(response, exportList, "事件查询数据");
     }
 
     /**
@@ -101,4 +148,49 @@ public class AlarmEventRecordController extends BaseController
     {
         return toAjax(alarmEventRecordService.deleteAlarmEventRecordByIds(ids));
     }
+
+    /**
+     * 构建查询条件
+     */
+    private AlarmEventRecord buildQuery(JSONObject params)
+    {
+        AlarmEventRecord query = new AlarmEventRecord();
+        query.setAlarmCategory(params.getString("alarmCategory"));
+        query.setAlarmLevel(params.getLong("alarmLevel"));
+        query.setProcessStatus(params.getBoolean("processStatus"));
+        query.setAlarmDate(params.getString("alarmDate"));
+        return query;
+    }
+
+    /**
+     * 字典值转标签(无匹配时返回空串)
+     */
+    private String dictLabel(String dictType, String dictValue)
+    {
+        if (dictValue == null || dictValue.isEmpty())
+        {
+            return "";
+        }
+        String label = DictUtils.getDictLabel(dictType, dictValue);
+        return label == null ? "" : label;
+    }
+
+    /**
+     * 处理状态转文案: 0未处理/1处理中/2已处理/3误报
+     */
+    private String processFlagLabel(Long processFlag)
+    {
+        if (processFlag == null)
+        {
+            return "";
+        }
+        switch (processFlag.intValue())
+        {
+            case 0: return "未处理";
+            case 1: return "处理中";
+            case 2: return "已处理";
+            case 3: return "误报";
+            default: return String.valueOf(processFlag);
+        }
+    }
 }

+ 69 - 6
forecast-backend-server/src/main/java/com/ufo/project/alarm/domain/AlarmEventRecord.java

@@ -1,5 +1,6 @@
 package com.ufo.project.alarm.domain;
 
+import java.time.LocalDateTime;
 import com.ufo.base.framework.web.domain.BaseEntity;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
@@ -34,6 +35,10 @@ public class AlarmEventRecord extends BaseEntity
     @Excel(name = "监控端")
     private String monitoringTerminal;
 
+    /** 告警类别 */
+    @Excel(name = "告警类别")
+    private String alarmCategory;
+
     /** 报警级别 */
     @Excel(name = "报警级别")
     private Long alarmLevel;
@@ -62,7 +67,19 @@ public class AlarmEventRecord extends BaseEntity
     @Excel(name = "处理人ids")
     private String processPersonId;
 
-    public void setId(String id) 
+    /** 处理状态(查询条件: true=已处理即process_flag=2, false=未处理即process_flag!=2) */
+    private Boolean processStatus;
+
+    /** 告警日期(查询条件: yyyy-MM-dd, 按当天 00:00:00~23:59:59 过滤) */
+    private String alarmDate;
+
+    /** 告警时间下限(查询条件: service层由alarmDate计算, 当天00:00:00) */
+    private LocalDateTime alarmDateBegin;
+
+    /** 告警时间上限(查询条件: service层由alarmDate计算, 当天23:59:59) */
+    private LocalDateTime alarmDateEnd;
+
+    public void setId(String id)
     {
         this.id = id;
     }
@@ -98,16 +115,25 @@ public class AlarmEventRecord extends BaseEntity
     {
         return alarmData;
     }
-    public void setMonitoringTerminal(String monitoringTerminal) 
+    public void setMonitoringTerminal(String monitoringTerminal)
     {
         this.monitoringTerminal = monitoringTerminal;
     }
 
-    public String getMonitoringTerminal() 
+    public String getMonitoringTerminal()
     {
         return monitoringTerminal;
     }
-    public void setAlarmLevel(Long alarmLevel) 
+    public void setAlarmCategory(String alarmCategory)
+    {
+        this.alarmCategory = alarmCategory;
+    }
+
+    public String getAlarmCategory()
+    {
+        return alarmCategory;
+    }
+    public void setAlarmLevel(Long alarmLevel)
     {
         this.alarmLevel = alarmLevel;
     }
@@ -161,15 +187,51 @@ public class AlarmEventRecord extends BaseEntity
     {
         return sendStatus;
     }
-    public void setProcessPersonId(String processPersonId) 
+    public void setProcessPersonId(String processPersonId)
     {
         this.processPersonId = processPersonId;
     }
 
-    public String getProcessPersonId() 
+    public String getProcessPersonId()
     {
         return processPersonId;
     }
+    public void setProcessStatus(Boolean processStatus)
+    {
+        this.processStatus = processStatus;
+    }
+
+    public Boolean getProcessStatus()
+    {
+        return processStatus;
+    }
+    public void setAlarmDate(String alarmDate)
+    {
+        this.alarmDate = alarmDate;
+    }
+
+    public String getAlarmDate()
+    {
+        return alarmDate;
+    }
+    public void setAlarmDateBegin(LocalDateTime alarmDateBegin)
+    {
+        this.alarmDateBegin = alarmDateBegin;
+    }
+
+    public LocalDateTime getAlarmDateBegin()
+    {
+        return alarmDateBegin;
+    }
+    public void setAlarmDateEnd(LocalDateTime alarmDateEnd)
+    {
+        this.alarmDateEnd = alarmDateEnd;
+    }
+
+    public LocalDateTime getAlarmDateEnd()
+    {
+        return alarmDateEnd;
+    }
 
     @Override
     public String toString() {
@@ -179,6 +241,7 @@ public class AlarmEventRecord extends BaseEntity
             .append("alarmRuleId", getAlarmRuleId())
             .append("alarmData", getAlarmData())
             .append("monitoringTerminal", getMonitoringTerminal())
+            .append("alarmCategory", getAlarmCategory())
             .append("createTime", getCreateTime())
             .append("createBy", getCreateBy())
             .append("updateTime", getUpdateTime())

+ 95 - 0
forecast-backend-server/src/main/java/com/ufo/project/alarm/domain/vo/AlarmEventRecordExportStat.java

@@ -0,0 +1,95 @@
+package com.ufo.project.alarm.domain.vo;
+
+import com.ufo.base.framework.aspectj.lang.annotation.Excel;
+
+/**
+ * 事件查询导出行数据
+ *
+ * @author ufo
+ * @date 2026-11-02
+ */
+public class AlarmEventRecordExportStat
+{
+    /** 告警等级 */
+    @Excel(name = "告警等级")
+    private String alarmLevel;
+
+    /** 告警类别 */
+    @Excel(name = "告警类别")
+    private String alarmCategory;
+
+    /** 告警内容 */
+    @Excel(name = "告警内容")
+    private String alarmData;
+
+    /** 处理状态 */
+    @Excel(name = "处理状态")
+    private String processFlag;
+
+    /** 告警日期 */
+    @Excel(name = "告警日期")
+    private String alarmDate;
+
+    public AlarmEventRecordExportStat()
+    {
+    }
+
+    public AlarmEventRecordExportStat(String alarmLevel, String alarmCategory, String alarmData, String processFlag, String alarmDate)
+    {
+        this.alarmLevel = alarmLevel;
+        this.alarmCategory = alarmCategory;
+        this.alarmData = alarmData;
+        this.processFlag = processFlag;
+        this.alarmDate = alarmDate;
+    }
+
+    public String getAlarmLevel()
+    {
+        return alarmLevel;
+    }
+
+    public void setAlarmLevel(String alarmLevel)
+    {
+        this.alarmLevel = alarmLevel;
+    }
+
+    public String getAlarmCategory()
+    {
+        return alarmCategory;
+    }
+
+    public void setAlarmCategory(String alarmCategory)
+    {
+        this.alarmCategory = alarmCategory;
+    }
+
+    public String getAlarmData()
+    {
+        return alarmData;
+    }
+
+    public void setAlarmData(String alarmData)
+    {
+        this.alarmData = alarmData;
+    }
+
+    public String getProcessFlag()
+    {
+        return processFlag;
+    }
+
+    public void setProcessFlag(String processFlag)
+    {
+        this.processFlag = processFlag;
+    }
+
+    public String getAlarmDate()
+    {
+        return alarmDate;
+    }
+
+    public void setAlarmDate(String alarmDate)
+    {
+        this.alarmDate = alarmDate;
+    }
+}

+ 12 - 1
forecast-backend-server/src/main/java/com/ufo/project/alarm/mapper/AlarmEventRecordMapper.java

@@ -1,6 +1,8 @@
 package com.ufo.project.alarm.mapper;
 
+import java.util.Date;
 import java.util.List;
+import org.apache.ibatis.annotations.Param;
 import com.ufo.project.alarm.domain.AlarmEventRecord;
 
 /**
@@ -53,9 +55,18 @@ public interface AlarmEventRecordMapper
 
     /**
      * 批量删除报警信息统计
-     * 
+     *
      * @param ids 需要删除的数据主键集合
      * @return 结果
      */
     public int deleteAlarmEventRecordByIds(String[] ids);
+
+    /**
+     * 批量处理报警信息(仅未处理数据可处理, 置为已处理)
+     *
+     * @param ids 需要处理的报警信息主键集合
+     * @param updateTime 更新时间
+     * @return 结果
+     */
+    public int processAlarmEventRecord(@Param("ids") List<String> ids, @Param("updateTime") Date updateTime);
 }

+ 9 - 1
forecast-backend-server/src/main/java/com/ufo/project/alarm/service/IAlarmEventRecordService.java

@@ -53,9 +53,17 @@ public interface IAlarmEventRecordService
 
     /**
      * 删除报警信息统计信息
-     * 
+     *
      * @param id 报警信息统计主键
      * @return 结果
      */
     public int deleteAlarmEventRecordById(String id);
+
+    /**
+     * 批量处理报警信息(仅未处理数据可处理, 置为已处理)
+     *
+     * @param ids 需要处理的报警信息主键集合
+     * @return 结果
+     */
+    public int processAlarmEventRecord(List<String> ids);
 }

+ 21 - 1
forecast-backend-server/src/main/java/com/ufo/project/alarm/service/impl/AlarmEventRecordServiceImpl.java

@@ -1,7 +1,9 @@
 package com.ufo.project.alarm.service.impl;
 
+import java.time.LocalDate;
 import java.util.List;
 import com.ufo.base.common.utils.DateUtils;
+import com.ufo.base.common.utils.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.ufo.project.alarm.mapper.AlarmEventRecordMapper;
@@ -41,6 +43,12 @@ public class AlarmEventRecordServiceImpl implements IAlarmEventRecordService
     @Override
     public List<AlarmEventRecord> selectAlarmEventRecordList(AlarmEventRecord alarmEventRecord)
     {
+        if (StringUtils.isNotEmpty(alarmEventRecord.getAlarmDate()))
+        {
+            LocalDate day = LocalDate.parse(alarmEventRecord.getAlarmDate());
+            alarmEventRecord.setAlarmDateBegin(day.atStartOfDay());
+            alarmEventRecord.setAlarmDateEnd(day.atTime(23, 59, 59));
+        }
         return alarmEventRecordMapper.selectAlarmEventRecordList(alarmEventRecord);
     }
 
@@ -84,7 +92,7 @@ public class AlarmEventRecordServiceImpl implements IAlarmEventRecordService
 
     /**
      * 删除报警信息统计信息
-     * 
+     *
      * @param id 报警信息统计主键
      * @return 结果
      */
@@ -93,4 +101,16 @@ public class AlarmEventRecordServiceImpl implements IAlarmEventRecordService
     {
         return alarmEventRecordMapper.deleteAlarmEventRecordById(id);
     }
+
+    /**
+     * 批量处理报警信息(非已处理数据均置为已处理)
+     *
+     * @param ids 需要处理的报警信息主键集合
+     * @return 结果
+     */
+    @Override
+    public int processAlarmEventRecord(List<String> ids)
+    {
+        return alarmEventRecordMapper.processAlarmEventRecord(ids, DateUtils.getNowDate());
+    }
 }

+ 68 - 5
forecast-backend-server/src/main/java/com/ufo/project/config/controller/ConfigDeviceController.java

@@ -20,6 +20,7 @@ import com.ufo.base.framework.aspectj.lang.enums.BusinessType;
 import com.ufo.project.config.domain.ConfigChannel;
 import com.ufo.project.config.domain.ConfigDevice;
 import com.ufo.project.config.domain.ConfigStation;
+import com.ufo.project.config.domain.vo.ConfigDeviceExportVo;
 import com.ufo.project.config.service.IConfigChannelService;
 import com.ufo.project.config.service.IConfigDeviceService;
 import com.ufo.project.config.service.IConfigStationService;
@@ -64,8 +65,14 @@ public class ConfigDeviceController extends BaseController
     {
         List<ConfigDevice> list = configDeviceService.selectConfigDeviceList(configDevice);
         fillNames(list);
-        ExcelUtil<ConfigDevice> util = new ExcelUtil<ConfigDevice>(ConfigDevice.class);
-        util.exportExcel(response, list, "设备配置");
+        List<ConfigDeviceExportVo> exportList = new ArrayList<>();
+        long index = 1;
+        for (ConfigDevice device : list)
+        {
+            exportList.add(new ConfigDeviceExportVo(index++, device.getName(), device.getChannelName(), device.getStationName(), tpLabel(device.getTp()), device.getBrand(), device.getModel(), device.getSerialNumber(), statusLabel(device.getStatus()), formatDate(device.getInstallDate())));
+        }
+        ExcelUtil<ConfigDeviceExportVo> util = new ExcelUtil<ConfigDeviceExportVo>(ConfigDeviceExportVo.class);
+        util.exportExcel(response, exportList, "设备配置");
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationDevice:query')")
@@ -112,9 +119,10 @@ public class ConfigDeviceController extends BaseController
         List<Long> channelIds = new ArrayList<>();
         for (ConfigDevice device : list)
         {
-            if (device.getConfigStationId() != null && !stationIds.contains(device.getConfigStationId()))
+            Long stationId = parseStationId(device.getConfigStationId());
+            if (stationId != null && !stationIds.contains(stationId))
             {
-                stationIds.add(device.getConfigStationId());
+                stationIds.add(stationId);
             }
             if (device.getConfigChannelId() != null && !channelIds.contains(device.getConfigChannelId()))
             {
@@ -131,7 +139,7 @@ public class ConfigDeviceController extends BaseController
             }
             for (ConfigDevice device : list)
             {
-                device.setStationName(stationNameMap.get(device.getConfigStationId()));
+                device.setStationName(stationNameMap.get(parseStationId(device.getConfigStationId())));
             }
         }
         if (!channelIds.isEmpty())
@@ -148,4 +156,59 @@ public class ConfigDeviceController extends BaseController
             }
         }
     }
+
+    /**
+     * 状态字典转换: GOOD=在线, 否则离线
+     */
+    private String statusLabel(String status)
+    {
+        return "GOOD".equals(status) ? "在线" : "离线";
+    }
+
+    /**
+     * 设备类型字典转换: WEATHER=气象设备, AGVC=agvc设备, 其他原样
+     */
+    private String tpLabel(String tp)
+    {
+        if ("WEATHER".equals(tp))
+        {
+            return "气象设备";
+        }
+        if ("AGVC".equals(tp))
+        {
+            return "agvc设备";
+        }
+        return tp;
+    }
+
+    /**
+     * 投运日期格式化为yyyy-MM-dd
+     */
+    private String formatDate(java.util.Date date)
+    {
+        if (date == null)
+        {
+            return null;
+        }
+        return new java.text.SimpleDateFormat("yyyy-MM-dd").format(date);
+    }
+
+    /**
+     * config_device.config_station_id 是varchar列(存数字字符串), 转为Long用于关联config_station; 非数字值视为无场站
+     */
+    private Long parseStationId(String value)
+    {
+        if (value == null || value.trim().isEmpty())
+        {
+            return null;
+        }
+        try
+        {
+            return Long.parseLong(value.trim());
+        }
+        catch (NumberFormatException e)
+        {
+            return null;
+        }
+    }
 }

+ 6 - 6
forecast-backend-server/src/main/java/com/ufo/project/config/controller/ConfigStationChannelController.java

@@ -22,7 +22,7 @@ import com.ufo.base.common.utils.poi.ExcelUtil;
 import com.ufo.base.framework.web.page.TableDataInfo;
 
 /**
- * ????Controller
+ * 通道配置Controller
  *
  * @author ufo
  * @date 2026-07-21
@@ -44,13 +44,13 @@ public class ConfigStationChannelController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationChannel:export')")
-    @Log(title = "????", businessType = BusinessType.EXPORT)
+    @Log(title = "通道配置", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
     public void export(HttpServletResponse response, ConfigStationChannel configStationChannel)
     {
         List<ConfigStationChannel> list = configStationChannelService.selectConfigStationChannelList(configStationChannel);
         ExcelUtil<ConfigStationChannel> util = new ExcelUtil<ConfigStationChannel>(ConfigStationChannel.class);
-        util.exportExcel(response, list, "??????");
+        util.exportExcel(response, list, "通道配置数据");
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationChannel:query')")
@@ -61,7 +61,7 @@ public class ConfigStationChannelController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationChannel:add')")
-    @Log(title = "????", businessType = BusinessType.INSERT)
+    @Log(title = "通道配置", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody ConfigStationChannel configStationChannel)
     {
@@ -69,7 +69,7 @@ public class ConfigStationChannelController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationChannel:edit')")
-    @Log(title = "????", businessType = BusinessType.UPDATE)
+    @Log(title = "通道配置", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody ConfigStationChannel configStationChannel)
     {
@@ -77,7 +77,7 @@ public class ConfigStationChannelController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationChannel:remove')")
-    @Log(title = "????", businessType = BusinessType.DELETE)
+    @Log(title = "通道配置", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)
     {

+ 6 - 6
forecast-backend-server/src/main/java/com/ufo/project/config/controller/ConfigStationDeviceController.java

@@ -22,7 +22,7 @@ import com.ufo.base.common.utils.poi.ExcelUtil;
 import com.ufo.base.framework.web.page.TableDataInfo;
 
 /**
- * ????Controller
+ * 设备配置Controller
  *
  * @author ufo
  * @date 2026-07-21
@@ -44,13 +44,13 @@ public class ConfigStationDeviceController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationDevice:export')")
-    @Log(title = "????", businessType = BusinessType.EXPORT)
+    @Log(title = "设备配置", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
     public void export(HttpServletResponse response, ConfigStationDevice configStationDevice)
     {
         List<ConfigStationDevice> list = configStationDeviceService.selectConfigStationDeviceList(configStationDevice);
         ExcelUtil<ConfigStationDevice> util = new ExcelUtil<ConfigStationDevice>(ConfigStationDevice.class);
-        util.exportExcel(response, list, "??????");
+        util.exportExcel(response, list, "设备配置数据");
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationDevice:query')")
@@ -61,7 +61,7 @@ public class ConfigStationDeviceController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationDevice:add')")
-    @Log(title = "????", businessType = BusinessType.INSERT)
+    @Log(title = "设备配置", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody ConfigStationDevice configStationDevice)
     {
@@ -69,7 +69,7 @@ public class ConfigStationDeviceController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationDevice:edit')")
-    @Log(title = "????", businessType = BusinessType.UPDATE)
+    @Log(title = "设备配置", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody ConfigStationDevice configStationDevice)
     {
@@ -77,7 +77,7 @@ public class ConfigStationDeviceController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationDevice:remove')")
-    @Log(title = "????", businessType = BusinessType.DELETE)
+    @Log(title = "设备配置", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)
     {

+ 6 - 6
forecast-backend-server/src/main/java/com/ufo/project/config/controller/ConfigStationPointController.java

@@ -22,7 +22,7 @@ import com.ufo.base.common.utils.poi.ExcelUtil;
 import com.ufo.base.framework.web.page.TableDataInfo;
 
 /**
- * ????Controller
+ * 测点配置Controller
  *
  * @author ufo
  * @date 2026-07-21
@@ -44,13 +44,13 @@ public class ConfigStationPointController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationPoint:export')")
-    @Log(title = "????", businessType = BusinessType.EXPORT)
+    @Log(title = "测点配置", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
     public void export(HttpServletResponse response, ConfigStationPoint configStationPoint)
     {
         List<ConfigStationPoint> list = configStationPointService.selectConfigStationPointList(configStationPoint);
         ExcelUtil<ConfigStationPoint> util = new ExcelUtil<ConfigStationPoint>(ConfigStationPoint.class);
-        util.exportExcel(response, list, "??????");
+        util.exportExcel(response, list, "测点配置数据");
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationPoint:query')")
@@ -61,7 +61,7 @@ public class ConfigStationPointController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationPoint:add')")
-    @Log(title = "????", businessType = BusinessType.INSERT)
+    @Log(title = "测点配置", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody ConfigStationPoint configStationPoint)
     {
@@ -69,7 +69,7 @@ public class ConfigStationPointController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationPoint:edit')")
-    @Log(title = "????", businessType = BusinessType.UPDATE)
+    @Log(title = "测点配置", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody ConfigStationPoint configStationPoint)
     {
@@ -77,7 +77,7 @@ public class ConfigStationPointController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationPoint:remove')")
-    @Log(title = "????", businessType = BusinessType.DELETE)
+    @Log(title = "测点配置", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)
     {

+ 6 - 6
forecast-backend-server/src/main/java/com/ufo/project/config/controller/ConfigStationPowerLimitController.java

@@ -22,7 +22,7 @@ import com.ufo.base.common.utils.poi.ExcelUtil;
 import com.ufo.base.framework.web.page.TableDataInfo;
 
 /**
- * ????Controller
+ * 限电配置Controller
  *
  * @author ufo
  * @date 2026-07-21
@@ -44,13 +44,13 @@ public class ConfigStationPowerLimitController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationPowerLimit:export')")
-    @Log(title = "????", businessType = BusinessType.EXPORT)
+    @Log(title = "限电配置", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
     public void export(HttpServletResponse response, ConfigStationPowerLimit configStationPowerLimit)
     {
         List<ConfigStationPowerLimit> list = configStationPowerLimitService.selectConfigStationPowerLimitList(configStationPowerLimit);
         ExcelUtil<ConfigStationPowerLimit> util = new ExcelUtil<ConfigStationPowerLimit>(ConfigStationPowerLimit.class);
-        util.exportExcel(response, list, "??????");
+        util.exportExcel(response, list, "限电配置数据");
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationPowerLimit:query')")
@@ -61,7 +61,7 @@ public class ConfigStationPowerLimitController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationPowerLimit:add')")
-    @Log(title = "????", businessType = BusinessType.INSERT)
+    @Log(title = "限电配置", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody ConfigStationPowerLimit configStationPowerLimit)
     {
@@ -69,7 +69,7 @@ public class ConfigStationPowerLimitController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationPowerLimit:edit')")
-    @Log(title = "????", businessType = BusinessType.UPDATE)
+    @Log(title = "限电配置", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody ConfigStationPowerLimit configStationPowerLimit)
     {
@@ -77,7 +77,7 @@ public class ConfigStationPowerLimitController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationPowerLimit:remove')")
-    @Log(title = "????", businessType = BusinessType.DELETE)
+    @Log(title = "限电配置", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)
     {

+ 6 - 6
forecast-backend-server/src/main/java/com/ufo/project/config/controller/ConfigStationPvModuleController.java

@@ -22,7 +22,7 @@ import com.ufo.base.common.utils.poi.ExcelUtil;
 import com.ufo.base.framework.web.page.TableDataInfo;
 
 /**
- * ????Controller
+ * 光伏组件Controller
  *
  * @author ufo
  * @date 2026-07-21
@@ -44,13 +44,13 @@ public class ConfigStationPvModuleController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationModule:export')")
-    @Log(title = "????", businessType = BusinessType.EXPORT)
+    @Log(title = "光伏组件", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
     public void export(HttpServletResponse response, ConfigStationPvModule configStationPvModule)
     {
         List<ConfigStationPvModule> list = configStationPvModuleService.selectConfigStationPvModuleList(configStationPvModule);
         ExcelUtil<ConfigStationPvModule> util = new ExcelUtil<ConfigStationPvModule>(ConfigStationPvModule.class);
-        util.exportExcel(response, list, "??????");
+        util.exportExcel(response, list, "光伏组件数据");
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationModule:query')")
@@ -61,7 +61,7 @@ public class ConfigStationPvModuleController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationModule:add')")
-    @Log(title = "????", businessType = BusinessType.INSERT)
+    @Log(title = "光伏组件", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody ConfigStationPvModule configStationPvModule)
     {
@@ -69,7 +69,7 @@ public class ConfigStationPvModuleController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationModule:edit')")
-    @Log(title = "????", businessType = BusinessType.UPDATE)
+    @Log(title = "光伏组件", businessType = BusinessType.UPDATE)
     @PutMapping
     public AjaxResult edit(@RequestBody ConfigStationPvModule configStationPvModule)
     {
@@ -77,7 +77,7 @@ public class ConfigStationPvModuleController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('config:stationModule:remove')")
-    @Log(title = "????", businessType = BusinessType.DELETE)
+    @Log(title = "光伏组件", businessType = BusinessType.DELETE)
     @DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)
     {

+ 18 - 8
forecast-backend-server/src/main/java/com/ufo/project/config/domain/ConfigDevice.java

@@ -1,6 +1,7 @@
 package com.ufo.project.config.domain;
-import com.ufo.base.framework.aspectj.lang.annotation.Excel;
 
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 
 /**
@@ -9,7 +10,7 @@ import lombok.Data;
  * @author ufo
  * @date 2026-08-06
  */
-@Data 
+@Data
 public class ConfigDevice
 {
 
@@ -17,23 +18,32 @@ public class ConfigDevice
     private Long id;
 
     /** 名称 */
-    @Excel(name = "名称")
     private String name;
+    /** 设备类型 */
+    private String tp;
+    /** 品牌 */
+    private String brand;
+    /** 型号 */
+    private String model;
+    /** 序列号 */
+    private String serialNumber;
+    /** 状态 */
+    private String status;
+    /** 投运日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date installDate;
     /**
      * 配置通道ID
      */
-    @Excel(name = "通道")
     private Long configChannelId;
     /**
-     * 配置站点ID
+     * 配置站点ID(数据库列为varchar, 存数字字符串)
      */
-    @Excel(name = "场站")
-    private Long configStationId;
+    private String configStationId;
 
     /**
      * Parent配置设备ID
      */
-    @Excel(name = "父级设备")
     private Long configDeviceId;
 
     /**

+ 22 - 22
forecast-backend-server/src/main/java/com/ufo/project/config/domain/ConfigStationChannel.java

@@ -6,7 +6,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ufo.base.framework.aspectj.lang.annotation.Excel;
 
 /**
- * ?????? config_station_channel
+ * 通道配置对象 config_station_channel
  *
  * @author ufo
  * @date 2026-07-21
@@ -15,47 +15,47 @@ public class ConfigStationChannel extends BaseEntity
 {
     private static final long serialVersionUID = 1L;
 
-    /** ???? */
+    /** 唯一标识 */
     private Long id;
 
-    /** ??ID */
-    @Excel(name = "??ID")
+    /** 场站ID */
+    @Excel(name = "场站ID")
     private Long stationId;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 所属场站 */
+    @Excel(name = "所属场站")
     private String stationName;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 通道名称 */
+    @Excel(name = "通道名称")
     private String channelName;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 协议 */
+    @Excel(name = "协议")
     private String protocol;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 主机地址 */
+    @Excel(name = "主机地址")
     private String host;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 端口 */
+    @Excel(name = "端口")
     private Long port;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 采集间隔 */
+    @Excel(name = "采集间隔")
     private String collectInterval;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 数据类型 */
+    @Excel(name = "数据类型")
     private String dataType;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 状态 */
+    @Excel(name = "状态")
     private String status;
 
-    /** ?????? */
-    @Excel(name = "??????")
+    /** 最后同步时间 */
+    @Excel(name = "最后同步时间")
     private String lastSyncTime;
 
     public void setId(Long id) 

+ 20 - 20
forecast-backend-server/src/main/java/com/ufo/project/config/domain/ConfigStationDevice.java

@@ -6,7 +6,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ufo.base.framework.aspectj.lang.annotation.Excel;
 
 /**
- * ?????? config_station_device
+ * 设备配置对象 config_station_device
  *
  * @author ufo
  * @date 2026-07-21
@@ -15,43 +15,43 @@ public class ConfigStationDevice extends BaseEntity
 {
     private static final long serialVersionUID = 1L;
 
-    /** ???? */
+    /** 唯一标识 */
     private Long id;
 
-    /** ??ID */
-    @Excel(name = "??ID")
+    /** 场站ID */
+    @Excel(name = "场站ID")
     private Long stationId;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 所属场站 */
+    @Excel(name = "所属场站")
     private String stationName;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 设备名称 */
+    @Excel(name = "设备名称")
     private String deviceName;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 设备类型 */
+    @Excel(name = "设备类型")
     private String deviceType;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 品牌 */
+    @Excel(name = "品牌")
     private String brand;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 型号 */
+    @Excel(name = "型号")
     private String model;
 
-    /** ??? */
-    @Excel(name = "???")
+    /** 序列号 */
+    @Excel(name = "序列号")
     private String serialNumber;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 状态 */
+    @Excel(name = "状态")
     private String status;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 安装日期 */
+    @Excel(name = "安装日期")
     private String installDate;
 
     public void setId(Long id) 

+ 24 - 24
forecast-backend-server/src/main/java/com/ufo/project/config/domain/ConfigStationPoint.java

@@ -6,7 +6,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ufo.base.framework.aspectj.lang.annotation.Excel;
 
 /**
- * ?????? config_station_point
+ * 测点配置对象 config_station_point
  *
  * @author ufo
  * @date 2026-07-21
@@ -15,51 +15,51 @@ public class ConfigStationPoint extends BaseEntity
 {
     private static final long serialVersionUID = 1L;
 
-    /** ???? */
+    /** 唯一标识 */
     private Long id;
 
-    /** ??ID */
-    @Excel(name = "??ID")
+    /** 场站ID */
+    @Excel(name = "场站ID")
     private Long stationId;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 所属场站 */
+    @Excel(name = "所属场站")
     private String stationName;
 
-    /** ??ID */
-    @Excel(name = "??ID")
+    /** 设备ID */
+    @Excel(name = "设备ID")
     private Long deviceId;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 设备名称 */
+    @Excel(name = "设备名称")
     private String deviceName;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 测点名称 */
+    @Excel(name = "测点名称")
     private String pointName;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 单位 */
+    @Excel(name = "单位")
     private String unit;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 精度 */
+    @Excel(name = "精度")
     private Long precisionValue;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 量程 */
+    @Excel(name = "量程")
     private String measureRange;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 协议 */
+    @Excel(name = "协议")
     private String protocol;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 地址 */
+    @Excel(name = "地址")
     private String address;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 采集间隔 */
+    @Excel(name = "采集间隔")
     private String collectInterval;
 
     public void setId(Long id) 

+ 20 - 20
forecast-backend-server/src/main/java/com/ufo/project/config/domain/ConfigStationPowerLimit.java

@@ -6,7 +6,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ufo.base.framework.aspectj.lang.annotation.Excel;
 
 /**
- * ?????? config_station_power_limit
+ * 限电配置对象 config_station_power_limit
  *
  * @author ufo
  * @date 2026-07-21
@@ -15,43 +15,43 @@ public class ConfigStationPowerLimit extends BaseEntity
 {
     private static final long serialVersionUID = 1L;
 
-    /** ???? */
+    /** 唯一标识 */
     private Long id;
 
-    /** ??ID */
-    @Excel(name = "??ID")
+    /** 场站ID */
+    @Excel(name = "场站ID")
     private Long stationId;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 所属场站 */
+    @Excel(name = "所属场站")
     private String stationName;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 限电类型 */
+    @Excel(name = "限电类型")
     private String limitType;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 开始日期 */
+    @Excel(name = "开始日期")
     private String startDate;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 结束日期 */
+    @Excel(name = "结束日期")
     private String endDate;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 限电比例 */
+    @Excel(name = "限电比例")
     private String limitPercent;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 原因 */
+    @Excel(name = "原因")
     private String reason;
 
-    /** ??? */
-    @Excel(name = "???")
+    /** 审批方 */
+    @Excel(name = "审批方")
     private String approver;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 状态 */
+    @Excel(name = "状态")
     private String status;
 
     public void setId(Long id) 

+ 22 - 22
forecast-backend-server/src/main/java/com/ufo/project/config/domain/ConfigStationPvModule.java

@@ -7,7 +7,7 @@ import com.ufo.base.framework.aspectj.lang.annotation.Excel;
 import java.math.BigDecimal;
 
 /**
- * ?????? config_station_pv_module
+ * 光伏组件对象 config_station_pv_module
  *
  * @author ufo
  * @date 2026-07-21
@@ -16,47 +16,47 @@ public class ConfigStationPvModule extends BaseEntity
 {
     private static final long serialVersionUID = 1L;
 
-    /** ???? */
+    /** 唯一标识 */
     private Long id;
 
-    /** ??ID */
-    @Excel(name = "??ID")
+    /** 场站ID */
+    @Excel(name = "场站ID")
     private Long stationId;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 所属场站 */
+    @Excel(name = "所属场站")
     private String stationName;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 组件型号 */
+    @Excel(name = "组件型号")
     private String model;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 品牌 */
+    @Excel(name = "品牌")
     private String brand;
 
-    /** ????(W) */
-    @Excel(name = "????(W)")
+    /** 额定功率(W) */
+    @Excel(name = "额定功率(W)")
     private BigDecimal ratedPower;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 组件类型 */
+    @Excel(name = "组件类型")
     private String moduleType;
 
-    /** ?? */
-    @Excel(name = "??")
+    /** 数量 */
+    @Excel(name = "数量")
     private Long moduleCount;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 转换效率 */
+    @Excel(name = "转换效率")
     private String conversionEfficiency;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 温度系数 */
+    @Excel(name = "温度系数")
     private String temperatureCoefficient;
 
-    /** ???? */
-    @Excel(name = "????")
+    /** 安装日期 */
+    @Excel(name = "安装日期")
     private String installDate;
 
     public void setId(Long id) 

+ 171 - 0
forecast-backend-server/src/main/java/com/ufo/project/config/domain/vo/ConfigDeviceExportVo.java

@@ -0,0 +1,171 @@
+package com.ufo.project.config.domain.vo;
+
+import com.ufo.base.framework.aspectj.lang.annotation.Excel;
+import com.ufo.base.framework.aspectj.lang.annotation.Excel.ColumnType;
+
+/**
+ * 设备配置导出行数据(列名/值/顺序与设备管理页表格一致)
+ *
+ * @author ufo
+ * @date 2026-11-06
+ */
+public class ConfigDeviceExportVo
+{
+    /** 序号 */
+    @Excel(name = "序号", cellType = ColumnType.NUMERIC)
+    private Long serial;
+
+    /** 设备名称 */
+    @Excel(name = "设备名称")
+    private String name;
+
+    /** 通道 */
+    @Excel(name = "通道")
+    private String channelName;
+
+    /** 所属场站 */
+    @Excel(name = "所属场站")
+    private String stationName;
+
+    /** 设备类型 */
+    @Excel(name = "设备类型")
+    private String tp;
+
+    /** 品牌 */
+    @Excel(name = "品牌")
+    private String brand;
+
+    /** 型号 */
+    @Excel(name = "型号")
+    private String model;
+
+    /** 序列号 */
+    @Excel(name = "序列号")
+    private String serialNumber;
+
+    /** 状态 */
+    @Excel(name = "状态")
+    private String status;
+
+    /** 投运日期 */
+    @Excel(name = "投运日期")
+    private String installDate;
+
+    public ConfigDeviceExportVo()
+    {
+    }
+
+    public ConfigDeviceExportVo(Long serial, String name, String channelName, String stationName, String tp, String brand, String model, String serialNumber, String status, String installDate)
+    {
+        this.serial = serial;
+        this.name = name;
+        this.channelName = channelName;
+        this.stationName = stationName;
+        this.tp = tp;
+        this.brand = brand;
+        this.model = model;
+        this.serialNumber = serialNumber;
+        this.status = status;
+        this.installDate = installDate;
+    }
+
+    public Long getSerial()
+    {
+        return serial;
+    }
+
+    public void setSerial(Long serial)
+    {
+        this.serial = serial;
+    }
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public String getChannelName()
+    {
+        return channelName;
+    }
+
+    public void setChannelName(String channelName)
+    {
+        this.channelName = channelName;
+    }
+
+    public String getStationName()
+    {
+        return stationName;
+    }
+
+    public void setStationName(String stationName)
+    {
+        this.stationName = stationName;
+    }
+
+    public String getTp()
+    {
+        return tp;
+    }
+
+    public void setTp(String tp)
+    {
+        this.tp = tp;
+    }
+
+    public String getBrand()
+    {
+        return brand;
+    }
+
+    public void setBrand(String brand)
+    {
+        this.brand = brand;
+    }
+
+    public String getModel()
+    {
+        return model;
+    }
+
+    public void setModel(String model)
+    {
+        this.model = model;
+    }
+
+    public String getSerialNumber()
+    {
+        return serialNumber;
+    }
+
+    public void setSerialNumber(String serialNumber)
+    {
+        this.serialNumber = serialNumber;
+    }
+
+    public String getStatus()
+    {
+        return status;
+    }
+
+    public void setStatus(String status)
+    {
+        this.status = status;
+    }
+
+    public String getInstallDate()
+    {
+        return installDate;
+    }
+
+    public void setInstallDate(String installDate)
+    {
+        this.installDate = installDate;
+    }
+}

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/mapper/ConfigStationChannelMapper.java

@@ -4,7 +4,7 @@ import java.util.List;
 import com.ufo.project.config.domain.ConfigStationChannel;
 
 /**
- * ????Mapper??
+ * 通道配置Mapper接口
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/mapper/ConfigStationDeviceMapper.java

@@ -4,7 +4,7 @@ import java.util.List;
 import com.ufo.project.config.domain.ConfigStationDevice;
 
 /**
- * ????Mapper??
+ * 设备配置Mapper接口
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/mapper/ConfigStationPointMapper.java

@@ -4,7 +4,7 @@ import java.util.List;
 import com.ufo.project.config.domain.ConfigStationPoint;
 
 /**
- * ????Mapper??
+ * 测点配置Mapper接口
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/mapper/ConfigStationPowerLimitMapper.java

@@ -4,7 +4,7 @@ import java.util.List;
 import com.ufo.project.config.domain.ConfigStationPowerLimit;
 
 /**
- * ????Mapper??
+ * 限电配置Mapper接口
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/mapper/ConfigStationPvModuleMapper.java

@@ -4,7 +4,7 @@ import java.util.List;
 import com.ufo.project.config.domain.ConfigStationPvModule;
 
 /**
- * ????Mapper??
+ * 光伏组件Mapper接口
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/service/IConfigStationChannelService.java

@@ -4,7 +4,7 @@ import java.util.List;
 import com.ufo.project.config.domain.ConfigStationChannel;
 
 /**
- * ????Service??
+ * 通道配置Service接口
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/service/IConfigStationDeviceService.java

@@ -4,7 +4,7 @@ import java.util.List;
 import com.ufo.project.config.domain.ConfigStationDevice;
 
 /**
- * ????Service??
+ * 设备配置Service接口
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/service/IConfigStationPointService.java

@@ -4,7 +4,7 @@ import java.util.List;
 import com.ufo.project.config.domain.ConfigStationPoint;
 
 /**
- * ????Service??
+ * 测点配置Service接口
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/service/IConfigStationPowerLimitService.java

@@ -4,7 +4,7 @@ import java.util.List;
 import com.ufo.project.config.domain.ConfigStationPowerLimit;
 
 /**
- * ????Service??
+ * 限电配置Service接口
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/service/IConfigStationPvModuleService.java

@@ -4,7 +4,7 @@ import java.util.List;
 import com.ufo.project.config.domain.ConfigStationPvModule;
 
 /**
- * ????Service??
+ * 光伏组件Service接口
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/service/impl/ConfigStationChannelServiceImpl.java

@@ -9,7 +9,7 @@ import com.ufo.project.config.domain.ConfigStationChannel;
 import com.ufo.project.config.service.IConfigStationChannelService;
 
 /**
- * ????Service?????
+ * 通道配置Service业务层处理
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/service/impl/ConfigStationDeviceServiceImpl.java

@@ -9,7 +9,7 @@ import com.ufo.project.config.domain.ConfigStationDevice;
 import com.ufo.project.config.service.IConfigStationDeviceService;
 
 /**
- * ????Service?????
+ * 设备配置Service业务层处理
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/service/impl/ConfigStationPointServiceImpl.java

@@ -9,7 +9,7 @@ import com.ufo.project.config.domain.ConfigStationPoint;
 import com.ufo.project.config.service.IConfigStationPointService;
 
 /**
- * ????Service?????
+ * 测点配置Service业务层处理
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/service/impl/ConfigStationPowerLimitServiceImpl.java

@@ -9,7 +9,7 @@ import com.ufo.project.config.domain.ConfigStationPowerLimit;
 import com.ufo.project.config.service.IConfigStationPowerLimitService;
 
 /**
- * ????Service?????
+ * 限电配置Service业务层处理
  *
  * @author ufo
  * @date 2026-07-21

+ 1 - 1
forecast-backend-server/src/main/java/com/ufo/project/config/service/impl/ConfigStationPvModuleServiceImpl.java

@@ -9,7 +9,7 @@ import com.ufo.project.config.domain.ConfigStationPvModule;
 import com.ufo.project.config.service.IConfigStationPvModuleService;
 
 /**
- * ????Service?????
+ * 光伏组件Service业务层处理
  *
  * @author ufo
  * @date 2026-07-21

+ 16 - 16
forecast-backend-server/src/main/java/com/ufo/project/data/task/MockDataTask.java

@@ -29,8 +29,8 @@ import com.ufo.project.weather.mapper.DataWeatherLocalMapper;
 /**
  * 功率与气象mock数据生成任务
  *
- * 在 project.profile 目录下生成 超短期/短期/中期 功率预测mock文件(生产环境即 /home/power/upload/power/MOCK/BHWP01 下)
- * 及短期气象mock文件(生产环境即 /home/power/upload/weather/MOCK/BHWP01 下),
+ * 在 project.profile 目录下生成 超短期/短期/中期 功率预测mock文件(生产环境即 /home/power/upload/power/MOCK/YMPV03 下)
+ * 及短期气象mock文件(生产环境即 /home/power/upload/weather/MOCK/YMPV03 下),
  * 供数据文件同步任务(data_file_sync_config)消费.
  * 文件命名与init_time均使用UTC时间; time列为UTC, time_shanghai列为UTC+8.
  * 文件格式:
@@ -44,10 +44,10 @@ import com.ufo.project.weather.mapper.DataWeatherLocalMapper;
  *
  * 短期气象mock为SYSU格式38列CSV, 自起报时间(含)起961点(10天), 间隔15分钟,
  * 文件名时间为UTC, date_time列为GMT+8(即文件名时间+8h);
- * t2m/rh2/sp/云量/风/ghi/dni/dhi尽量符合物理规律, 按北纬38.9度定标, 同样按起报时间可复现.
+ * t2m/rh2/sp/云量/风/ghi/dni/dhi尽量符合物理规律, 按北纬35.1度定标, 同样按起报时间可复现.
  *
  * 另含采集数据mock任务(generateCollectedData): 每5分钟向 data_weather_local 与
- * data_electricity_local 各插1行, tm取当前最近5分钟整北京时间, config_station_id固定为2,
+ * data_electricity_local 各插1行, tm取当前最近5分钟整北京时间, config_station_id固定为4,
  * 两行共享同一时刻物理状态(云量/钟形/季节), 数据尽量接近真实, 按北京日期可复现.
  *
  * @author ufo
@@ -97,14 +97,14 @@ public class MockDataTask
     /** 中期数据点数(自起报时间(含)起960点) */
     private static final int MEDIUM_POINTS = 960;
 
-    /** 出力上限(kW, 与采集数据mock一致, 按150000kW装机容量定标) */
-    private static final double POWER_CAP = 150000.0;
+    /** 出力上限(kW, 与采集数据mock一致, 按80000kW装机容量定标) */
+    private static final double POWER_CAP = 80000.0;
 
     /** 采集数据mock固定场站ID(config_station_id) */
-    private static final long COLLECT_STATION_ID = 2L;
+    private static final long COLLECT_STATION_ID = 4L;
 
     /** 采集数据mock装机容量(kW) */
-    private static final double COLLECT_CAPACITY_KW = 150000.0;
+    private static final double COLLECT_CAPACITY_KW = 80000.0;
 
     /** 采集数据mock理论出力定标的晴空峰值辐照度(W/m², 夏季正午晴空) */
     private static final double CLEAR_PEAK_REF = 850.0;
@@ -113,13 +113,13 @@ public class MockDataTask
     private static final int WEATHER_POINTS = 961;
 
     /** 气象mock场站编号(farm_id, 同时是文件名的场站段) */
-    private static final String WEATHER_FARM_ID = "BHWP01";
+    private static final String WEATHER_FARM_ID = "YMPV03";
 
     /** 气象mock固定经度(文件名按经度-纬度顺序) */
-    private static final double WEATHER_LON = 121.4737;
+    private static final double WEATHER_LON = 118.3262;
 
     /** 气象mock固定纬度 */
-    private static final double WEATHER_LAT = 38.9087;
+    private static final double WEATHER_LAT = 35.0654;
 
     /** 气象文件名日期格式(yyyyMMdd, UTC即北京日期) */
     private static final DateTimeFormatter WEATHER_FILE_DATE_FMT = DateTimeFormatter.ofPattern("yyyyMMdd");
@@ -217,7 +217,7 @@ public class MockDataTask
 
     /**
      * 短期气象: 每天凌晨2点执行一次, 生成当日(北京日期)12:00 UTC起报的一个预报文件,
-     * 自起报时间(含)起961点, 间隔15分钟, 输出到 weather/MOCK/BHWP01/;
+     * 自起报时间(含)起961点, 间隔15分钟, 输出到 weather/MOCK/YMPV03/;
      * 文件名时间为UTC, date_time列为GMT+8(即文件名时间+8h)
      */
     @Scheduled(cron = "0 0 2 * * ?")
@@ -238,8 +238,8 @@ public class MockDataTask
     /**
      * 采集数据: 每5分钟执行一次, 向 data_weather_local 与 data_electricity_local 各插1行,
      * tm为当前时间就近取整到最近5分钟整北京时间(如00:05:34→00:05:00, 00:09:12→00:10:00),
-     * config_station_id固定为2; 两行共享同一时刻的物理状态(云量/钟形/季节), 功率与气象描述同一天气;
-     * 理论出力为按150000kW装机容量定标的晴空钟形(夏峰~150000/冬峰~112500, 夜间0),
+     * config_station_id固定为4; 两行共享同一时刻的物理状态(云量/钟形/季节), 功率与气象描述同一天气;
+     * 理论出力为按80000kW装机容量定标的晴空钟形(夏峰~80000/冬峰~61800, 夜间0),
      * 实发功率=理论×慢快云量衰减×可用率(恒≤理论), 随机参数按北京日期派生, 当日数据稳定可复现
      */
     @Scheduled(cron = "0 0/5 * * * ?")
@@ -476,7 +476,7 @@ public class MockDataTask
     private Path generateFile(String type, ZonedDateTime init, DateTimeFormatter fileFmt,
             DateTimeFormatter initFmt, int firstOffsetMin, int points) throws IOException
     {
-        Path dir = Paths.get(ProjectConfig.getProfile(), "power", "MOCK", "BHWP01", type,
+        Path dir = Paths.get(ProjectConfig.getProfile(), "power", "MOCK", "YMPV03", type,
                 String.format(Locale.ROOT, "%04d", init.getYear()),
                 String.format(Locale.ROOT, "%02d", init.getMonthValue()),
                 String.format(Locale.ROOT, "%02d", init.getDayOfMonth()));
@@ -512,7 +512,7 @@ public class MockDataTask
 
     /**
      * 计算mock光伏出力: 夜间为0, 白天钟形曲线, 峰值与日照时长随季节变化, 叠加缓慢云量波动;
-     * 峰值按150000kW装机容量对晴空辐照定标(与采集数据mock同量纲, 夏峰~150000/冬峰~112500),
+     * 峰值按80000kW装机容量对晴空辐照定标(与采集数据mock同量纲, 夏峰~80000/冬峰~61800),
      * 与采集数据在相近时刻的数值接近
      *
      * @param tUtc 数据时刻(UTC)

+ 56 - 0
forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/controller/DataReportLogController.java

@@ -2,21 +2,31 @@ package com.ufo.project.forecast.fileupload.controller;
 
 import java.io.File;
 import java.io.IOException;
+import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.util.List;
+import javax.servlet.http.HttpServletResponse;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.PathVariable;
+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.RestController;
+import com.alibaba.excel.EasyExcel;
 import com.ufo.base.common.utils.StringUtils;
+import com.ufo.base.framework.aspectj.lang.annotation.Log;
+import com.ufo.base.framework.aspectj.lang.enums.BusinessType;
 import com.ufo.base.framework.web.controller.BaseController;
 import com.ufo.base.framework.web.domain.AjaxResult;
 import com.ufo.base.framework.web.page.TableDataInfo;
 import com.ufo.project.forecast.fileupload.domain.DataReportLog;
+import com.ufo.project.forecast.fileupload.domain.vo.UploadRateStatQuery;
+import com.ufo.project.forecast.fileupload.domain.vo.UploadRateStatVO;
 import com.ufo.project.forecast.fileupload.service.IDataReportLogService;
 
 /**
@@ -79,4 +89,50 @@ public class DataReportLogController extends BaseController
             return AjaxResult.error("读取上报文件失败: " + path);
         }
     }
+
+    /**
+     * 光伏上传率统计查询(手动分页: total=范围内天数)
+     */
+    @PreAuthorize("@ss.hasPermi('fileupload:reportLog:uploadRate')")
+    @Log(title = "光伏上传率统计", businessType = BusinessType.OTHER)
+    @PostMapping("/uploadRate/list")
+    public TableDataInfo uploadRateList(@RequestBody UploadRateStatQuery query)
+    {
+        long total = dataReportLogService.uploadRateTotalDays(query);
+        List<UploadRateStatVO> rows = dataReportLogService.uploadRatePageList(query);
+        return new TableDataInfo(rows, (int) total);
+    }
+
+    /**
+     * 光伏上传率统计导出(全量日期范围)
+     */
+    @PreAuthorize("@ss.hasPermi('fileupload:reportLog:uploadRateExport')")
+    @Log(title = "光伏上传率统计", businessType = BusinessType.EXPORT)
+    @PostMapping("/uploadRate/export")
+    public void uploadRateExport(@RequestBody UploadRateStatQuery query, HttpServletResponse response) throws IOException
+    {
+        List<UploadRateStatVO> data = dataReportLogService.uploadRateExportList(query);
+        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+        response.setCharacterEncoding("utf-8");
+        String dateRange = StringUtils.equals(query.getStartTime(), query.getEndTime())
+                ? query.getStartTime()
+                : query.getStartTime() + "_" + query.getEndTime();
+        String fileName = URLEncoder.encode("光伏上传率统计_" + dateRange.replace("-", "") + ".xlsx",
+                "UTF-8").replaceAll("\\+", "%20");
+        response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName);
+        EasyExcel.write(response.getOutputStream(), UploadRateStatVO.class)
+                .sheet("光伏上传率统计")
+                .doWrite(data);
+    }
+
+    /**
+     * 数据补传: 对比当日上报成功条数与每日文件上传配置, 缺失类型插入对应条数上传成功记录
+     */
+    @PreAuthorize("@ss.hasPermi('fileupload:reportLog:reUpload')")
+    @Log(title = "数据补传", businessType = BusinessType.INSERT)
+    @GetMapping("/reUpload")
+    public AjaxResult reUpload(@RequestParam("stationCode") String stationCode)
+    {
+        return AjaxResult.success(dataReportLogService.reUpload(stationCode));
+    }
 }

+ 9 - 2
forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/domain/DataReportLog.java

@@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 
@@ -18,8 +20,13 @@ import org.apache.commons.lang3.builder.ToStringStyle;
 @TableName("data_report_log")
 public class DataReportLog
 {
-    /** 日志主键ID */
-    @TableId(value = "id", type = IdType.AUTO)
+    /**
+     * 日志主键ID
+     * 雪花ID客户端生成(ASSIGN_ID): 该表由外部服务写入显式ID, DB自增序列与max(id)失步, 依赖AUTO会主键冲突
+     * 序列化为字符串: 雪花ID超过JS Number安全整数(2^53), 前端回传file/content/{id}需保持精度
+     */
+    @TableId(value = "id", type = IdType.ASSIGN_ID)
+    @JsonSerialize(using = ToStringSerializer.class)
     private Long id;
 
     /** 文件类型:supershort|short|medium */

+ 71 - 0
forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/domain/vo/ReUploadCountRow.java

@@ -0,0 +1,71 @@
+package com.ufo.project.forecast.fileupload.domain.vo;
+
+import java.io.Serializable;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 数据补传聚合行(仅 Mapper 聚合结果映射用)
+ *
+ * @author ufo
+ * @date 2026-11-05
+ */
+public class ReUploadCountRow implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 文件类型 */
+    private String type;
+
+    /** 上报状态:0待发送,1上报成功 */
+    private Integer status;
+
+    /** 记录条数 */
+    private Long cnt;
+
+    /** 获取文件类型 */
+    public String getType()
+    {
+        return type;
+    }
+
+    /** 设置文件类型 */
+    public void setType(String type)
+    {
+        this.type = type;
+    }
+
+    /** 获取上报状态 */
+    public Integer getStatus()
+    {
+        return status;
+    }
+
+    /** 设置上报状态 */
+    public void setStatus(Integer status)
+    {
+        this.status = status;
+    }
+
+    /** 获取记录条数 */
+    public Long getCnt()
+    {
+        return cnt;
+    }
+
+    /** 设置记录条数 */
+    public void setCnt(Long cnt)
+    {
+        this.cnt = cnt;
+    }
+
+    @Override
+    public String toString()
+    {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("type", getType())
+            .append("status", getStatus())
+            .append("cnt", getCnt())
+            .toString();
+    }
+}

+ 71 - 0
forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/domain/vo/UploadRateCountRow.java

@@ -0,0 +1,71 @@
+package com.ufo.project.forecast.fileupload.domain.vo;
+
+import java.io.Serializable;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 光伏上传率统计聚合行(仅 Mapper 聚合结果映射用)
+ *
+ * @author ufo
+ * @date 2026-11-04
+ */
+public class UploadRateCountRow implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 上海时区日期 yyyy-MM-dd */
+    private String statDate;
+
+    /** 文件类型 */
+    private String type;
+
+    /** 成功条数 */
+    private Long cnt;
+
+    /** 获取上海时区日期 */
+    public String getStatDate()
+    {
+        return statDate;
+    }
+
+    /** 设置上海时区日期 */
+    public void setStatDate(String statDate)
+    {
+        this.statDate = statDate;
+    }
+
+    /** 获取文件类型 */
+    public String getType()
+    {
+        return type;
+    }
+
+    /** 设置文件类型 */
+    public void setType(String type)
+    {
+        this.type = type;
+    }
+
+    /** 获取成功条数 */
+    public Long getCnt()
+    {
+        return cnt;
+    }
+
+    /** 设置成功条数 */
+    public void setCnt(Long cnt)
+    {
+        this.cnt = cnt;
+    }
+
+    @Override
+    public String toString()
+    {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("statDate", getStatDate())
+            .append("type", getType())
+            .append("cnt", getCnt())
+            .toString();
+    }
+}

+ 103 - 0
forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/domain/vo/UploadRateStatQuery.java

@@ -0,0 +1,103 @@
+package com.ufo.project.forecast.fileupload.domain.vo;
+
+import java.io.Serializable;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 光伏上传率统计查询对象
+ *
+ * @author ufo
+ * @date 2026-11-04
+ */
+public class UploadRateStatQuery implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 场站编码(必填) */
+    private String stationCode;
+
+    /** 起始时间 yyyy-MM-dd(必填) */
+    private String startTime;
+
+    /** 截止时间 yyyy-MM-dd(必填) */
+    private String endTime;
+
+    /** 页码,缺省按 1 */
+    private Integer pageNum;
+
+    /** 每页行数,缺省按 10 */
+    private Integer pageSize;
+
+    /** 获取场站编码 */
+    public String getStationCode()
+    {
+        return stationCode;
+    }
+
+    /** 设置场站编码 */
+    public void setStationCode(String stationCode)
+    {
+        this.stationCode = stationCode;
+    }
+
+    /** 获取起始时间 */
+    public String getStartTime()
+    {
+        return startTime;
+    }
+
+    /** 设置起始时间 */
+    public void setStartTime(String startTime)
+    {
+        this.startTime = startTime;
+    }
+
+    /** 获取截止时间 */
+    public String getEndTime()
+    {
+        return endTime;
+    }
+
+    /** 设置截止时间 */
+    public void setEndTime(String endTime)
+    {
+        this.endTime = endTime;
+    }
+
+    /** 获取页码 */
+    public Integer getPageNum()
+    {
+        return pageNum;
+    }
+
+    /** 设置页码 */
+    public void setPageNum(Integer pageNum)
+    {
+        this.pageNum = pageNum;
+    }
+
+    /** 获取每页行数 */
+    public Integer getPageSize()
+    {
+        return pageSize;
+    }
+
+    /** 设置每页行数 */
+    public void setPageSize(Integer pageSize)
+    {
+        this.pageSize = pageSize;
+    }
+
+    @Override
+    public String toString()
+    {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("stationCode", getStationCode())
+            .append("startTime", getStartTime())
+            .append("endTime", getEndTime())
+            .append("pageNum", getPageNum())
+            .append("pageSize", getPageSize())
+            .toString();
+    }
+}

+ 194 - 0
forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/domain/vo/UploadRateStatVO.java

@@ -0,0 +1,194 @@
+package com.ufo.project.forecast.fileupload.domain.vo;
+
+import java.io.Serializable;
+import com.alibaba.excel.annotation.ExcelProperty;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * 光伏上传率统计结果行(一行一天)
+ *
+ * @author ufo
+ * @date 2026-11-04
+ */
+public class UploadRateStatVO implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 日期时间 yyyy-MM-dd */
+    @ExcelProperty(index = 0, value = "日期时间")
+    private String date;
+
+    /** 超短期文件上传率(%) */
+    @ExcelProperty(index = 1, value = "超短期文件上传率(%)")
+    private Double supershortRate;
+
+    /** 短期文件上传率(%) */
+    @ExcelProperty(index = 2, value = "短期文件上传率(%)")
+    private Double shortRate;
+
+    /** 中期文件上传率(%) */
+    @ExcelProperty(index = 3, value = "中期文件上传率(%)")
+    private Double mediumRate;
+
+    /** 环境检测仪数据上传率(%) */
+    @ExcelProperty(index = 4, value = "环境检测仪数据上传率(%)")
+    private Double envlDetectorRate;
+
+    /** 逆变器运行信息文件上传率(%) */
+    @ExcelProperty(index = 5, value = "逆变器运行信息文件上传率(%)")
+    private Double inverterOperationRate;
+
+    /** 升压变数据上传率(%) */
+    @ExcelProperty(index = 6, value = "升压变数据上传率(%)")
+    private Double boosterTransformerRate;
+
+    /** 理论功率文件上传率(%) */
+    @ExcelProperty(index = 7, value = "理论功率文件上传率(%)")
+    private Double theoreticalPowerRate;
+
+    /** 数值天气预报文件上传率(%) */
+    @ExcelProperty(index = 8, value = "数值天气预报文件上传率(%)")
+    private Double numericalWeatherRate;
+
+    /** 发电计划文件上传率(%) */
+    @ExcelProperty(index = 9, value = "发电计划文件上传率(%)")
+    private Double powerGenerationPlanRate;
+
+    /** 获取日期时间 */
+    public String getDate()
+    {
+        return date;
+    }
+
+    /** 设置日期时间 */
+    public void setDate(String date)
+    {
+        this.date = date;
+    }
+
+    /** 获取超短期文件上传率 */
+    public Double getSupershortRate()
+    {
+        return supershortRate;
+    }
+
+    /** 设置超短期文件上传率 */
+    public void setSupershortRate(Double supershortRate)
+    {
+        this.supershortRate = supershortRate;
+    }
+
+    /** 获取短期文件上传率 */
+    public Double getShortRate()
+    {
+        return shortRate;
+    }
+
+    /** 设置短期文件上传率 */
+    public void setShortRate(Double shortRate)
+    {
+        this.shortRate = shortRate;
+    }
+
+    /** 获取中期文件上传率 */
+    public Double getMediumRate()
+    {
+        return mediumRate;
+    }
+
+    /** 设置中期文件上传率 */
+    public void setMediumRate(Double mediumRate)
+    {
+        this.mediumRate = mediumRate;
+    }
+
+    /** 获取环境检测仪数据上传率 */
+    public Double getEnvlDetectorRate()
+    {
+        return envlDetectorRate;
+    }
+
+    /** 设置环境检测仪数据上传率 */
+    public void setEnvlDetectorRate(Double envlDetectorRate)
+    {
+        this.envlDetectorRate = envlDetectorRate;
+    }
+
+    /** 获取逆变器运行信息文件上传率 */
+    public Double getInverterOperationRate()
+    {
+        return inverterOperationRate;
+    }
+
+    /** 设置逆变器运行信息文件上传率 */
+    public void setInverterOperationRate(Double inverterOperationRate)
+    {
+        this.inverterOperationRate = inverterOperationRate;
+    }
+
+    /** 获取升压变数据上传率 */
+    public Double getBoosterTransformerRate()
+    {
+        return boosterTransformerRate;
+    }
+
+    /** 设置升压变数据上传率 */
+    public void setBoosterTransformerRate(Double boosterTransformerRate)
+    {
+        this.boosterTransformerRate = boosterTransformerRate;
+    }
+
+    /** 获取理论功率文件上传率 */
+    public Double getTheoreticalPowerRate()
+    {
+        return theoreticalPowerRate;
+    }
+
+    /** 设置理论功率文件上传率 */
+    public void setTheoreticalPowerRate(Double theoreticalPowerRate)
+    {
+        this.theoreticalPowerRate = theoreticalPowerRate;
+    }
+
+    /** 获取数值天气预报文件上传率 */
+    public Double getNumericalWeatherRate()
+    {
+        return numericalWeatherRate;
+    }
+
+    /** 设置数值天气预报文件上传率 */
+    public void setNumericalWeatherRate(Double numericalWeatherRate)
+    {
+        this.numericalWeatherRate = numericalWeatherRate;
+    }
+
+    /** 获取发电计划文件上传率 */
+    public Double getPowerGenerationPlanRate()
+    {
+        return powerGenerationPlanRate;
+    }
+
+    /** 设置发电计划文件上传率 */
+    public void setPowerGenerationPlanRate(Double powerGenerationPlanRate)
+    {
+        this.powerGenerationPlanRate = powerGenerationPlanRate;
+    }
+
+    @Override
+    public String toString()
+    {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("date", getDate())
+            .append("supershortRate", getSupershortRate())
+            .append("shortRate", getShortRate())
+            .append("mediumRate", getMediumRate())
+            .append("envlDetectorRate", getEnvlDetectorRate())
+            .append("inverterOperationRate", getInverterOperationRate())
+            .append("boosterTransformerRate", getBoosterTransformerRate())
+            .append("theoreticalPowerRate", getTheoreticalPowerRate())
+            .append("numericalWeatherRate", getNumericalWeatherRate())
+            .append("powerGenerationPlanRate", getPowerGenerationPlanRate())
+            .toString();
+    }
+}

+ 39 - 0
forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/mapper/DataReportLogMapper.java

@@ -1,7 +1,12 @@
 package com.ufo.project.forecast.fileupload.mapper;
 
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.ufo.project.forecast.fileupload.domain.DataReportLog;
+import com.ufo.project.forecast.fileupload.domain.vo.ReUploadCountRow;
+import com.ufo.project.forecast.fileupload.domain.vo.UploadRateCountRow;
 
 /**
  * 功率预测上报日志Mapper接口(MyBatis-Plus, 无需Mapper XML)
@@ -11,4 +16,38 @@ import com.ufo.project.forecast.fileupload.domain.DataReportLog;
  */
 public interface DataReportLogMapper extends BaseMapper<DataReportLog>
 {
+    /**
+     * 按"天×type"统计上报成功条数(只统计status=1, 上海时区分天, 时间范围左闭右开含截止日全天)
+     *
+     * @param stationCode 场站编码
+     * @param windowStart 窗口起始日 yyyy-MM-dd
+     * @param windowEnd 窗口截止日 yyyy-MM-dd
+     * @return statDate/type/cnt 聚合行
+     */
+    @Select("SELECT DATE(est_report_time AT TIME ZONE 'Asia/Shanghai') AS stat_date, "
+            + "type, COUNT(*) AS cnt "
+            + "FROM data_report_log "
+            + "WHERE station_code = #{stationCode} "
+            + "AND status = 1 "
+            + "AND est_report_time >= (#{windowStart}::timestamp AT TIME ZONE 'Asia/Shanghai') "
+            + "AND est_report_time < (#{windowEnd}::timestamp AT TIME ZONE 'Asia/Shanghai') + INTERVAL '1 day' "
+            + "GROUP BY 1, 2")
+    List<UploadRateCountRow> selectUploadRateCounts(@Param("stationCode") String stationCode,
+            @Param("windowStart") String windowStart, @Param("windowEnd") String windowEnd);
+
+    /**
+     * 按"type×status"统计当日上报表条数(上海时区分天, status只取0待发送/1上报成功)
+     *
+     * @param stationCode 场站编码
+     * @param day 当日日期(上海时区) yyyy-MM-dd
+     * @return type/status/cnt 聚合行
+     */
+    @Select("SELECT type, status, COUNT(*) AS cnt "
+            + "FROM data_report_log "
+            + "WHERE station_code = #{stationCode} "
+            + "AND status IN (0, 1) "
+            + "AND DATE(est_report_time AT TIME ZONE 'Asia/Shanghai') = #{day}::date "
+            + "GROUP BY 1, 2")
+    List<ReUploadCountRow> selectTodayCounts(@Param("stationCode") String stationCode,
+            @Param("day") String day);
 }

+ 25 - 0
forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/service/IDataReportLogService.java

@@ -2,6 +2,8 @@ package com.ufo.project.forecast.fileupload.service;
 
 import java.util.List;
 import com.ufo.project.forecast.fileupload.domain.DataReportLog;
+import com.ufo.project.forecast.fileupload.domain.vo.UploadRateStatQuery;
+import com.ufo.project.forecast.fileupload.domain.vo.UploadRateStatVO;
 
 /**
  * 功率预测上报日志Service接口
@@ -26,4 +28,27 @@ public interface IDataReportLogService
      * @return 功率预测上报日志集合
      */
     public List<DataReportLog> selectDataReportLogList(DataReportLog dataReportLog);
+
+    /**
+     * 光伏上传率统计: 日期范围天数(手动分页的total)
+     */
+    public long uploadRateTotalDays(UploadRateStatQuery query);
+
+    /**
+     * 光伏上传率统计: 查询当前页(每页每天一行, 无记录天为0.00)
+     */
+    public List<UploadRateStatVO> uploadRatePageList(UploadRateStatQuery query);
+
+    /**
+     * 光伏上传率统计: 查询全量日期(导出用)
+     */
+    public List<UploadRateStatVO> uploadRateExportList(UploadRateStatQuery query);
+
+    /**
+     * 数据补传: 按类型对比当日上报成功条数与每日文件上传配置, 缺失类型插入对应条数上传成功记录
+     *
+     * @param stationCode 场站编码
+     * @return 补传结果提示消息
+     */
+    public String reUpload(String stationCode);
 }

+ 324 - 0
forecast-backend-server/src/main/java/com/ufo/project/forecast/fileupload/service/impl/DataReportLogServiceImpl.java

@@ -1,20 +1,36 @@
 package com.ufo.project.forecast.fileupload.service.impl;
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.time.LocalDate;
 import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.UUID;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ufo.base.common.exception.ServiceException;
+import com.ufo.base.common.utils.SecurityUtils;
 import com.ufo.base.common.utils.StringUtils;
+import com.ufo.module.system.domain.SysDictData;
+import com.ufo.module.system.service.ISysDictTypeService;
 import com.ufo.project.config.domain.ConfigStation;
 import com.ufo.project.config.service.IConfigStationService;
 import com.ufo.project.forecast.fileupload.domain.DataReportLog;
+import com.ufo.project.forecast.fileupload.domain.vo.ReUploadCountRow;
+import com.ufo.project.forecast.fileupload.domain.vo.UploadRateCountRow;
+import com.ufo.project.forecast.fileupload.domain.vo.UploadRateStatQuery;
+import com.ufo.project.forecast.fileupload.domain.vo.UploadRateStatVO;
 import com.ufo.project.forecast.fileupload.mapper.DataReportLogMapper;
 import com.ufo.project.forecast.fileupload.service.IDataReportLogService;
 
@@ -33,6 +49,17 @@ public class DataReportLogServiceImpl implements IDataReportLogService
     @Autowired
     private IConfigStationService configStationService;
 
+    /** 字典类型: 每日文件上传配置 */
+    private static final String DICT_TYPE = "daily_file_upload_config";
+
+    /** 9 类上报文件 type(与字典及VO字段顺序一致) */
+    private static final List<String> ALL_TYPES = Arrays.asList("supershort", "short", "medium",
+            "envlDetec", "ivtOpr", "bstTrn",
+            "thrPower", "nmrWeather", "pwrGen");
+
+    @Autowired
+    private ISysDictTypeService dictTypeService;
+
     /**
      * 查询功率预测上报日志
      *
@@ -72,6 +99,172 @@ public class DataReportLogServiceImpl implements IDataReportLogService
         return list;
     }
 
+    @Override
+    public long uploadRateTotalDays(UploadRateStatQuery query)
+    {
+        LocalDate[] range = parseRange(query);
+        return ChronoUnit.DAYS.between(range[0], range[1]) + 1;
+    }
+
+    @Override
+    public List<UploadRateStatVO> uploadRatePageList(UploadRateStatQuery query)
+    {
+        LocalDate[] range = parseRange(query);
+        LocalDate start = range[0];
+        LocalDate end = range[1];
+        int pageNum = query.getPageNum() == null || query.getPageNum() < 1 ? 1 : query.getPageNum();
+        int pageSize = query.getPageSize() == null || query.getPageSize() < 1 ? 10 : query.getPageSize();
+        LocalDate windowStart = start.plusDays((long) (pageNum - 1) * pageSize);
+        if (windowStart.isAfter(end))
+        {
+            // 超出末页: 返回空
+            return new ArrayList<>();
+        }
+        LocalDate windowEnd = windowStart.plusDays(pageSize - 1L);
+        if (windowEnd.isAfter(end))
+        {
+            windowEnd = end;
+        }
+        return buildDayVos(windowStart, windowEnd, query.getStationCode());
+    }
+
+    @Override
+    public List<UploadRateStatVO> uploadRateExportList(UploadRateStatQuery query)
+    {
+        LocalDate[] range = parseRange(query);
+        return buildDayVos(range[0], range[1], query.getStationCode());
+    }
+
+    @Override
+    public String reUpload(String stationCode)
+    {
+        if (StringUtils.isEmpty(stationCode))
+        {
+            throw new ServiceException("stationCode不能为空");
+        }
+        Map<String, Integer> nums = loadNums();
+        Map<String, String> labels = loadLabels();
+        LocalDate today = LocalDate.now(ZoneId.of("Asia/Shanghai"));
+        List<ReUploadCountRow> rows = dataReportLogMapper.selectTodayCounts(stationCode, today.toString());
+        Map<String, Long> successCnt = new HashMap<>();
+        Map<String, Long> pendingCnt = new HashMap<>();
+        for (ReUploadCountRow row : rows)
+        {
+            if (row.getStatus() == null)
+            {
+                continue;
+            }
+            if (row.getStatus() == 1)
+            {
+                successCnt.merge(row.getType(), row.getCnt() == null ? 0L : row.getCnt(), Long::sum);
+            }
+            else
+            {
+                pendingCnt.merge(row.getType(), row.getCnt() == null ? 0L : row.getCnt(), Long::sum);
+            }
+        }
+
+        // 按ALL_TYPES固定顺序逐类决策: 成功数>=num视为已完成; 存在待发送(上传中/处理中)则跳过; 否则补插缺失条数
+        List<String> pendingLabels = new ArrayList<>();
+        int insertCount = 0;
+        Date now = new Date();
+        String username = SecurityUtils.getUsername();
+        for (String type : ALL_TYPES)
+        {
+            int num = nums.get(type);
+            long ok = successCnt.getOrDefault(type, 0L);
+            long pending = pendingCnt.getOrDefault(type, 0L);
+            if (ok >= num)
+            {
+                continue;
+            }
+            if (pending > 0)
+            {
+                pendingLabels.add(labels.getOrDefault(type, type));
+                continue;
+            }
+            for (int i = 0; i < num - ok; i++)
+            {
+                dataReportLogMapper.insert(buildReUploadLog(type, stationCode, now, username));
+                insertCount++;
+            }
+        }
+
+        if (insertCount == 0 && pendingLabels.isEmpty())
+        {
+            return "当日文件已全部上传,无需补传";
+        }
+        StringBuilder sb = new StringBuilder();
+        if (insertCount > 0)
+        {
+            sb.append("补传成功 ").append(insertCount).append(" 条");
+        }
+        if (!pendingLabels.isEmpty())
+        {
+            if (sb.length() > 0)
+            {
+                sb.append(",");
+            }
+            sb.append("以下类型正在上传/处理中,未补传: ").append(String.join("、", pendingLabels));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * 构造补传记录: 固定模拟字段值(curve_id=9999/省调/通道1/ACK:OK/模拟生成), message_no随机保证唯一
+     */
+    private DataReportLog buildReUploadLog(String type, String stationCode, Date now, String username)
+    {
+        DataReportLog log = new DataReportLog();
+        log.setType(type);
+        log.setStationCode(stationCode);
+        log.setCurveId(9999L);
+        log.setEstReportTime(now);
+        log.setReportTime(now);
+        log.setTargetDispatch("省调");
+        log.setChannel(1);
+        log.setMessageNo("SUP" + now.getTime() + type
+                + UUID.randomUUID().toString().replace("-", "").substring(0, 8).toUpperCase());
+        log.setStatus(1);
+        log.setResponseMsg("ACK:OK");
+        log.setRemark("模拟生成");
+        log.setCreateTime(now);
+        log.setUpdateTime(now);
+        log.setCreateBy(username);
+        log.setUpdateBy(username);
+        return log;
+    }
+
+    /**
+     * 读字典 daily_file_upload_config → type→中文label(用于提示消息), 解析失败回退type编码
+     */
+    private Map<String, String> loadLabels()
+    {
+        Map<String, String> labels = new HashMap<>();
+        List<SysDictData> dictDatas = dictTypeService.selectDictDataByType(DICT_TYPE);
+        if (dictDatas == null || dictDatas.isEmpty())
+        {
+            return labels;
+        }
+        for (SysDictData dict : dictDatas)
+        {
+            try
+            {
+                JSONObject obj = JSON.parseObject(dict.getDictValue());
+                String type = obj == null ? null : obj.getString("type");
+                if (StringUtils.isNotEmpty(type) && StringUtils.isNotEmpty(dict.getDictLabel()))
+                {
+                    labels.put(type, dict.getDictLabel());
+                }
+            }
+            catch (Exception e)
+            {
+                // label解析失败时该type回退为type编码
+            }
+        }
+        return labels;
+    }
+
     /**
      * 按计划上报时间过滤单日(queryDate, yyyy-MM-dd)
      */
@@ -135,4 +328,135 @@ public class DataReportLogServiceImpl implements IDataReportLogService
             }
         }
     }
+
+    /**
+     * 查窗口内聚合并按天生成VO(无记录天全0.00)
+     */
+    private List<UploadRateStatVO> buildDayVos(LocalDate windowStart, LocalDate windowEnd, String stationCode)
+    {
+        Map<String, Integer> nums = loadNums();
+        List<UploadRateCountRow> rows = dataReportLogMapper.selectUploadRateCounts(
+                stationCode, windowStart.toString(), windowEnd.toString());
+        Map<String, Map<String, Long>> byDay = new HashMap<>();
+        for (UploadRateCountRow row : rows)
+        {
+            byDay.computeIfAbsent(row.getStatDate(), k -> new HashMap<>()).put(row.getType(), row.getCnt());
+        }
+        List<UploadRateStatVO> result = new ArrayList<>();
+        for (LocalDate day = windowStart; !day.isAfter(windowEnd); day = day.plusDays(1))
+        {
+            Map<String, Long> typeCnt = byDay.get(day.toString());
+            UploadRateStatVO vo = new UploadRateStatVO();
+            vo.setDate(day.toString());
+            vo.setSupershortRate(calcRate("supershort", typeCnt, nums));
+            vo.setShortRate(calcRate("short", typeCnt, nums));
+            vo.setMediumRate(calcRate("medium", typeCnt, nums));
+            vo.setEnvlDetectorRate(calcRate("envlDetec", typeCnt, nums));
+            vo.setInverterOperationRate(calcRate("ivtOpr", typeCnt, nums));
+            vo.setBoosterTransformerRate(calcRate("bstTrn", typeCnt, nums));
+            vo.setTheoreticalPowerRate(calcRate("thrPower", typeCnt, nums));
+            vo.setNumericalWeatherRate(calcRate("nmrWeather", typeCnt, nums));
+            vo.setPowerGenerationPlanRate(calcRate("pwrGen", typeCnt, nums));
+            result.add(vo);
+        }
+        return result;
+    }
+
+    /**
+     * 比率 = 成功条数/num×100, 保留2位小数(HALF_UP), 超过100按100计; 无记录为0.0
+     */
+    private Double calcRate(String type, Map<String, Long> typeCnt, Map<String, Integer> nums)
+    {
+        long cnt = typeCnt == null ? 0L : typeCnt.getOrDefault(type, 0L);
+        if (cnt == 0)
+        {
+            return 0.0;
+        }
+        BigDecimal rate = new BigDecimal(cnt * 100).divide(new BigDecimal(nums.get(type)), 2, RoundingMode.HALF_UP);
+        if (rate.compareTo(new BigDecimal("100")) > 0)
+        {
+            rate = new BigDecimal("100");
+        }
+        return rate.doubleValue();
+    }
+
+    /**
+     * 参数校验: stationCode/startTime/endTime非空、格式yyyy-MM-dd、start<=end
+     */
+    private LocalDate[] parseRange(UploadRateStatQuery query)
+    {
+        if (StringUtils.isEmpty(query.getStationCode()))
+        {
+            throw new ServiceException("stationCode不能为空");
+        }
+        LocalDate start = parseDay(query.getStartTime(), "起始时间");
+        LocalDate end = parseDay(query.getEndTime(), "截止时间");
+        if (start.isAfter(end))
+        {
+            throw new ServiceException("起始时间不能晚于截止时间");
+        }
+        return new LocalDate[]{start, end};
+    }
+
+    private LocalDate parseDay(String value, String label)
+    {
+        if (StringUtils.isEmpty(value))
+        {
+            throw new ServiceException(label + "不能为空");
+        }
+        try
+        {
+            return LocalDate.parse(value);
+        }
+        catch (Exception e)
+        {
+            throw new ServiceException(label + "格式不正确, 应为yyyy-MM-dd: " + value);
+        }
+    }
+
+    /**
+     * 读字典 daily_file_upload_config → type→num; 未配置/JSON非法/缺项/num非法时fail fast
+     */
+    private Map<String, Integer> loadNums()
+    {
+        List<SysDictData> dictDatas = dictTypeService.selectDictDataByType(DICT_TYPE);
+        if (dictDatas == null || dictDatas.isEmpty())
+        {
+            throw new ServiceException("字典" + DICT_TYPE + "未配置, 请先在字典管理中添加每日文件上传配置");
+        }
+        Map<String, Integer> nums = new HashMap<>();
+        for (SysDictData dict : dictDatas)
+        {
+            String dictValue = dict.getDictValue();
+            try
+            {
+                JSONObject obj = JSON.parseObject(dictValue);
+                String type = obj == null ? null : obj.getString("type");
+                Integer num = obj == null ? null : obj.getInteger("num");
+                if (StringUtils.isEmpty(type) || num == null || num <= 0)
+                {
+                    throw new ServiceException("字典" + DICT_TYPE + "数据[" + dict.getDictLabel()
+                            + "]缺少type或num不合法: " + dictValue);
+                }
+                nums.put(type, num);
+            }
+            catch (ServiceException e)
+            {
+                throw e;
+            }
+            catch (Exception e)
+            {
+                throw new ServiceException("字典" + DICT_TYPE + "数据[" + dict.getDictLabel()
+                        + "]不是合法JSON: " + dictValue);
+            }
+        }
+        for (String type : ALL_TYPES)
+        {
+            if (!nums.containsKey(type))
+            {
+                throw new ServiceException("字典" + DICT_TYPE + "缺少" + type + "的配置");
+            }
+        }
+        return nums;
+    }
 }

+ 20 - 2
forecast-backend-server/src/main/resources/mybatis/alarm/AlarmEventRecordMapper.xml

@@ -10,6 +10,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="alarmRuleId"    column="alarm_rule_id"    />
         <result property="alarmData"    column="alarm_data"    />
         <result property="monitoringTerminal"    column="monitoring_terminal"    />
+        <result property="alarmCategory"    column="alarm_category"    />
         <result property="createTime"    column="create_time"    />
         <result property="createBy"    column="create_by"    />
         <result property="updateTime"    column="update_time"    />
@@ -24,7 +25,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </resultMap>
 
     <sql id="selectAlarmEventRecordVo">
-        select id, station_id, alarm_rule_id, alarm_data, monitoring_terminal, create_time, create_by, update_time, update_by, alarm_level, alarm_create_time, process_flag, process_person, process_result, send_status, process_person_id from alarm_event_record
+        select id, station_id, alarm_rule_id, alarm_data, monitoring_terminal, alarm_category, create_time, create_by, update_time, update_by, alarm_level, alarm_create_time, process_flag, process_person, process_result, send_status, process_person_id from alarm_event_record
     </sql>
 
     <select id="selectAlarmEventRecordList" parameterType="AlarmEventRecord" resultMap="AlarmEventRecordResult">
@@ -34,14 +35,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="alarmRuleId != null  and alarmRuleId != ''"> and alarm_rule_id = #{alarmRuleId}</if>
             <if test="alarmData != null  and alarmData != ''"> and alarm_data = #{alarmData}</if>
             <if test="monitoringTerminal != null  and monitoringTerminal != ''"> and monitoring_terminal = #{monitoringTerminal}</if>
+            <if test="alarmCategory != null  and alarmCategory != ''"> and alarm_category = #{alarmCategory}</if>
             <if test="alarmLevel != null "> and alarm_level = #{alarmLevel}</if>
             <if test="alarmCreateTime != null  and alarmCreateTime != ''"> and alarm_create_time = #{alarmCreateTime}</if>
+            <if test="processStatus != null and processStatus"> and process_flag = 2</if>
+            <if test="processStatus != null and !processStatus"> and process_flag != 2</if>
+            <if test="alarmDateBegin != null and alarmDateEnd != null"> and alarm_create_time &gt;= #{alarmDateBegin} and alarm_create_time &lt;= #{alarmDateEnd}</if>
             <if test="processFlag != null "> and process_flag = #{processFlag}</if>
             <if test="processPerson != null  and processPerson != ''"> and process_person = #{processPerson}</if>
             <if test="processResult != null  and processResult != ''"> and process_result = #{processResult}</if>
             <if test="sendStatus != null "> and send_status = #{sendStatus}</if>
             <if test="processPersonId != null  and processPersonId != ''"> and process_person_id = #{processPersonId}</if>
         </where>
+        order by alarm_create_time desc
     </select>
     
     <select id="selectAlarmEventRecordById" parameterType="String" resultMap="AlarmEventRecordResult">
@@ -57,6 +63,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="alarmRuleId != null and alarmRuleId != ''">alarm_rule_id,</if>
             <if test="alarmData != null">alarm_data,</if>
             <if test="monitoringTerminal != null">monitoring_terminal,</if>
+            <if test="alarmCategory != null and alarmCategory != ''">alarm_category,</if>
             <if test="createTime != null">create_time,</if>
             <if test="createBy != null">create_by,</if>
             <if test="updateTime != null">update_time,</if>
@@ -75,6 +82,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="alarmRuleId != null and alarmRuleId != ''">#{alarmRuleId},</if>
             <if test="alarmData != null">#{alarmData},</if>
             <if test="monitoringTerminal != null">#{monitoringTerminal},</if>
+            <if test="alarmCategory != null and alarmCategory != ''">#{alarmCategory},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="createBy != null">#{createBy},</if>
             <if test="updateTime != null">#{updateTime},</if>
@@ -96,6 +104,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="alarmRuleId != null and alarmRuleId != ''">alarm_rule_id = #{alarmRuleId},</if>
             <if test="alarmData != null">alarm_data = #{alarmData},</if>
             <if test="monitoringTerminal != null">monitoring_terminal = #{monitoringTerminal},</if>
+            <if test="alarmCategory != null and alarmCategory != ''">alarm_category = #{alarmCategory},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
@@ -116,9 +125,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </delete>
 
     <delete id="deleteAlarmEventRecordByIds" parameterType="String">
-        delete from alarm_event_record where id in 
+        delete from alarm_event_record where id in
         <foreach item="id" collection="array" open="(" separator="," close=")">
             #{id}
         </foreach>
     </delete>
+
+    <update id="processAlarmEventRecord">
+        update alarm_event_record
+        set process_flag = 2, update_time = #{updateTime}
+        where process_flag != 2 and id in
+        <foreach item="id" collection="ids" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </update>
 </mapper>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/alarm/AlarmEventRuleMapper.xml

@@ -54,9 +54,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="alarmTemplateId != null and alarmTemplateId != ''">alarm_template_id,</if>
             <if test="enableFlag != null">enable_flag,</if>
             <if test="delFlag != null">del_flag,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
             <if test="updateBy != null">update_by,</if>
             <if test="alarmWarnLevel != null">alarm_warn_level,</if>
             <if test="sendSet != null">send_set,</if>
@@ -71,9 +71,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="alarmTemplateId != null and alarmTemplateId != ''">#{alarmTemplateId},</if>
             <if test="enableFlag != null">#{enableFlag},</if>
             <if test="delFlag != null">#{delFlag},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
             <if test="alarmWarnLevel != null">#{alarmWarnLevel},</if>
             <if test="sendSet != null">#{sendSet},</if>
@@ -91,9 +91,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="alarmTemplateId != null and alarmTemplateId != ''">alarm_template_id = #{alarmTemplateId},</if>
             <if test="enableFlag != null">enable_flag = #{enableFlag},</if>
             <if test="delFlag != null">del_flag = #{delFlag},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
             <if test="alarmWarnLevel != null">alarm_warn_level = #{alarmWarnLevel},</if>
             <if test="sendSet != null">send_set = #{sendSet},</if>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/alarm/AlarmEventRuleTemplateMapper.xml

@@ -45,9 +45,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="alarmLevelUp != null and alarmLevelUp != ''">alarm_level_up,</if>
             <if test="alarmLevel != null">alarm_level,</if>
             <if test="alarmName != null">alarm_name,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
             <if test="updateBy != null">update_by,</if>
             <if test="delFlag != null">del_flag,</if>
             <if test="alarmKey != null">alarm_key,</if>
@@ -58,9 +58,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="alarmLevelUp != null and alarmLevelUp != ''">#{alarmLevelUp},</if>
             <if test="alarmLevel != null">#{alarmLevel},</if>
             <if test="alarmName != null">#{alarmName},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
             <if test="delFlag != null">#{delFlag},</if>
             <if test="alarmKey != null">#{alarmKey},</if>
@@ -74,9 +74,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="alarmLevelUp != null and alarmLevelUp != ''">alarm_level_up = #{alarmLevelUp},</if>
             <if test="alarmLevel != null">alarm_level = #{alarmLevel},</if>
             <if test="alarmName != null">alarm_name = #{alarmName},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
             <if test="delFlag != null">del_flag = #{delFlag},</if>
             <if test="alarmKey != null">alarm_key = #{alarmKey},</if>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/alarm/AlarmStatRecordMapper.xml

@@ -37,18 +37,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="id != null">id,</if>
             <if test="scheduleTime != null">schedule_time,</if>
             <if test="alarmData != null">alarm_data,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
             <if test="updateBy != null">update_by,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="id != null">#{id},</if>
             <if test="scheduleTime != null">#{scheduleTime},</if>
             <if test="alarmData != null">#{alarmData},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
          </trim>
     </insert>
@@ -58,9 +58,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <trim prefix="SET" suffixOverrides=",">
             <if test="scheduleTime != null">schedule_time = #{scheduleTime},</if>
             <if test="alarmData != null">alarm_data = #{alarmData},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
         </trim>
         where id = #{id}

+ 3 - 3
forecast-backend-server/src/main/resources/mybatis/alarm/MonitoringCheckRecordMapper.xml

@@ -53,7 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="div != null">div,</if>
             <if test="thresholdCount != null">threshold_count,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="dataSourcegrabId != null">#{dataSourcegrabId},</if>
@@ -65,7 +65,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="div != null">#{div},</if>
             <if test="thresholdCount != null">#{thresholdCount},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
          </trim>
     </insert>
 
@@ -81,7 +81,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="div != null">div = #{div},</if>
             <if test="thresholdCount != null">threshold_count = #{thresholdCount},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 3 - 3
forecast-backend-server/src/main/resources/mybatis/alarm/MonitoringSendRecordMapper.xml

@@ -41,7 +41,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="recipientEmail != null">recipient_email,</if>
             <if test="emailInfo != null">email_info,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="checkRecordId != null">#{checkRecordId},</if>
@@ -49,7 +49,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="recipientEmail != null">#{recipientEmail},</if>
             <if test="emailInfo != null">#{emailInfo},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
          </trim>
     </insert>
 
@@ -61,7 +61,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="recipientEmail != null">recipient_email = #{recipientEmail},</if>
             <if test="emailInfo != null">email_info = #{emailInfo},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/api/ApiAccessControlMapper.xml

@@ -49,9 +49,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="callNumber != null">call_number,</if>
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null and updateBy != ''">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="accessKey != null and accessKey != ''">#{accessKey},</if>
@@ -61,9 +61,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="callNumber != null">#{callNumber},</if>
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null and updateBy != ''">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -77,9 +77,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="callNumber != null">call_number = #{callNumber},</if>
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 3 - 3
forecast-backend-server/src/main/resources/mybatis/api/ApiAccessRecordMapper.xml

@@ -52,7 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="time != null">time,</if>
             <if test="accessKey != null and accessKey != ''">access_key,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="apiRecordId != null">api_record_id,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
@@ -64,7 +64,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="time != null">#{time},</if>
             <if test="accessKey != null and accessKey != ''">#{accessKey},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="apiRecordId != null">#{apiRecordId},</if>
          </trim>
     </insert>
@@ -80,7 +80,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="time != null">time = #{time},</if>
             <if test="accessKey != null and accessKey != ''">access_key = #{accessKey},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="apiRecordId != null">api_record_id = #{apiRecordId},</if>
         </trim>
         where id = #{id}

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/api/ApiRecordMapper.xml

@@ -43,9 +43,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="apiDescription != null">api_description,</if>
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null and updateBy != ''">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="apiPath != null and apiPath != ''">#{apiPath},</if>
@@ -53,9 +53,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="apiDescription != null">#{apiDescription},</if>
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null and updateBy != ''">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -67,9 +67,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="apiDescription != null">api_description = #{apiDescription},</if>
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/api/ApiVersionReleaseMapper.xml

@@ -42,9 +42,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="version != null and version != ''">version,</if>
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null and updateBy != ''">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
             <if test="apis != null and apis != ''">apis,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
@@ -52,9 +52,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="version != null and version != ''">#{version},</if>
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null and updateBy != ''">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
             <if test="apis != null and apis != ''">#{apis},</if>
          </trim>
     </insert>
@@ -66,9 +66,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="version != null and version != ''">version = #{version},</if>
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="apis != null and apis != ''">apis = #{apis},</if>
         </trim>
         where id = #{id}

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/assessment/ConfigAssessmentIndicatorMapper.xml

@@ -54,9 +54,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="enableFlag != null and enableFlag != ''">enable_flag,</if>
             <if test="delFlag != null and delFlag != ''">del_flag,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="id != null">#{id},</if>
@@ -69,9 +69,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="enableFlag != null and enableFlag != ''">#{enableFlag},</if>
             <if test="delFlag != null and delFlag != ''">#{delFlag},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -87,9 +87,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="enableFlag != null and enableFlag != ''">enable_flag = #{enableFlag},</if>
             <if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/assessment/ConfigAssessmentRuleMapper.xml

@@ -40,9 +40,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="provinceNumber != null">province_number,</if>
             <if test="delFlag != null and delFlag != ''">del_flag,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="id != null">#{id},</if>
@@ -50,9 +50,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="provinceNumber != null">#{provinceNumber},</if>
             <if test="delFlag != null and delFlag != ''">#{delFlag},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -63,9 +63,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="provinceNumber != null">province_number = #{provinceNumber},</if>
             <if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/assessment/ConfigAssessmentTaskMapper.xml

@@ -43,9 +43,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="enableFlag != null and enableFlag != ''">enable_flag,</if>
             <if test="delFlag != null and delFlag != ''">del_flag,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="id != null">#{id},</if>
@@ -54,9 +54,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="enableFlag != null and enableFlag != ''">#{enableFlag},</if>
             <if test="delFlag != null and delFlag != ''">#{delFlag},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -68,9 +68,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="enableFlag != null and enableFlag != ''">enable_flag = #{enableFlag},</if>
             <if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/config/ConfigAreaMapper.xml

@@ -44,9 +44,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="superiorAreaId != null">superior_area_id,</if>
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null and updateBy != ''">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="id != null and id != ''">#{id},</if>
@@ -55,9 +55,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="superiorAreaId != null">#{superiorAreaId},</if>
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null and updateBy != ''">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -69,9 +69,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="superiorAreaId != null">superior_area_id = #{superiorAreaId},</if>
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 25 - 4
forecast-backend-server/src/main/resources/mybatis/config/ConfigDeviceMapper.xml

@@ -7,6 +7,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <resultMap type="ConfigDevice" id="ConfigDeviceResult">
         <result property="id"    column="id"    />
         <result property="name"    column="name"    />
+        <result property="tp"    column="tp"    />
+        <result property="brand"    column="brand"    />
+        <result property="model"    column="model"    />
+        <result property="serialNumber"    column="serial_number"    />
+        <result property="status"    column="status"    />
+        <result property="installDate"    column="install_date"    />
         <result property="configChannelId"    column="config_channel_id"    />
         <result property="configStationId"    column="config_station_id"    />
         <result property="configDeviceId"    column="config_device_id"    />
@@ -14,7 +20,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </resultMap>
 
     <sql id="selectConfigDeviceVo">
-        select id, name, config_channel_id, config_station_id, config_device_id, remark from config_device
+        select id, name, tp, brand, model, serial_number, status, install_date, config_channel_id, config_station_id, config_device_id, remark from config_device
     </sql>
 
     <select id="selectConfigDeviceList" parameterType="ConfigDevice" resultMap="ConfigDeviceResult">
@@ -54,17 +60,27 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         insert into config_device
         <trim prefix="(" suffix=")" suffixOverrides=",">
             <if test="name != null and name != ''">name,</if>
+            <if test="tp != null and tp != ''">tp,</if>
+            <if test="brand != null and brand != ''">brand,</if>
+            <if test="model != null and model != ''">model,</if>
+            <if test="serialNumber != null and serialNumber != ''">serial_number,</if>
+            <if test="status != null and status != ''">status,</if>
+            <if test="installDate != null">install_date,</if>
             <if test="configChannelId != null">config_channel_id,</if>
             <if test="configStationId != null">config_station_id,</if>
-            <if test="configDeviceId != null and configDeviceId != ''">config_device_id,</if>
             <if test="configDeviceId != null">config_device_id,</if>
             <if test="remark != null and remark != ''">remark,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="name != null and name != ''">#{name},</if>
+            <if test="tp != null and tp != ''">#{tp},</if>
+            <if test="brand != null and brand != ''">#{brand},</if>
+            <if test="model != null and model != ''">#{model},</if>
+            <if test="serialNumber != null and serialNumber != ''">#{serialNumber},</if>
+            <if test="status != null and status != ''">#{status},</if>
+            <if test="installDate != null">#{installDate},</if>
             <if test="configChannelId != null">#{configChannelId},</if>
             <if test="configStationId != null">#{configStationId},</if>
-            <if test="configDeviceId != null and configDeviceId != ''">#{configDeviceId},</if>
             <if test="configDeviceId != null">#{configDeviceId},</if>
             <if test="remark != null and remark != ''">#{remark},</if>
         </trim>
@@ -74,9 +90,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         update config_device
         <trim prefix="SET" suffixOverrides=",">
             <if test="name != null and name != ''">name = #{name},</if>
+            <if test="tp != null and tp != ''">tp = #{tp},</if>
+            <if test="brand != null and brand != ''">brand = #{brand},</if>
+            <if test="model != null and model != ''">model = #{model},</if>
+            <if test="serialNumber != null and serialNumber != ''">serial_number = #{serialNumber},</if>
+            <if test="status != null and status != ''">status = #{status},</if>
+            <if test="installDate != null">install_date = #{installDate},</if>
             <if test="configChannelId != null">config_channel_id = #{configChannelId},</if>
             <if test="configStationId != null">config_station_id = #{configStationId},</if>
-            <if test="configDeviceId != null and configDeviceId != ''">config_device_id = #{configDeviceId},</if>
             <if test="configDeviceId != null">config_device_id = #{configDeviceId},</if>
             <if test="remark != null and remark != ''">remark = #{remark},</if>
             update_time = CURRENT_TIMESTAMP,

+ 10 - 8
forecast-backend-server/src/main/resources/mybatis/config/ConfigStationMapper.xml

@@ -45,7 +45,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <select id="selectConfigStationList" parameterType="ConfigStation" resultMap="ConfigStationResult">
         <include refid="selectConfigStationVo"/>
-        <where>  
+        <where>
+            and station_type = 1
             <if test="stationName != null  and stationName != ''"> and station_name like concat('%', #{stationName}, '%')</if>
             <if test="stationCode != null  and stationCode != ''"> and station_code = #{stationCode}</if>
             <if test="superiorAreaId != null  and superiorAreaId != ''"> and superior_area_id = #{superiorAreaId}</if>
@@ -78,7 +79,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     
     <select id="selectConfigStationById" parameterType="Long" resultMap="ConfigStationResult">
         <include refid="selectConfigStationVo"/>
-        where id = #{id}
+        where id = #{id} and station_type = 1
     </select>
 
     <select id="selectConfigStationByIds" parameterType="Long" resultMap="ConfigStationResult">
@@ -86,6 +87,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <foreach item="id" collection="array" open="(" separator="," close=")">
             #{id}
         </foreach>
+        and station_type = 1
     </select>
         
     <insert id="insertConfigStation" parameterType="ConfigStation">
@@ -117,9 +119,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isFullPower != null and isFullPower != ''">is_full_power,</if>
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
             <if test="stationType != null">station_type,</if>
             <if test="systemPowerUuid != null">system_power_uuid,</if>
             <if test="areaId != null">area_id,</if>
@@ -151,9 +153,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isFullPower != null and isFullPower != ''">#{isFullPower},</if>
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
             <if test="stationType != null">#{stationType},</if>
             <if test="systemPowerUuid != null">#{systemPowerUuid},</if>
             <if test="areaId != null">#{areaId},</if>
@@ -188,9 +190,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isFullPower != null and isFullPower != ''">is_full_power = #{isFullPower},</if>
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="stationType != null">station_type = #{stationType},</if>
             <if test="systemPowerUuid != null">system_power_uuid = #{systemPowerUuid},</if>
             <if test="areaId != null">area_id = #{areaId},</if>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/config/ConfigSupplierMapper.xml

@@ -40,18 +40,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="supplierName != null and supplierName != ''">supplier_name,</if>
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null and updateBy != ''">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="supplierCode != null and supplierCode != ''">#{supplierCode},</if>
             <if test="supplierName != null and supplierName != ''">#{supplierName},</if>
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null and updateBy != ''">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -62,9 +62,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="supplierName != null and supplierName != ''">supplier_name = #{supplierName},</if>
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/config/StopReducePowerRecordMapper.xml

@@ -51,9 +51,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="stationId != null">station_id,</if>
             <if test="startTime != null">start_time,</if>
             <if test="endTime != null">end_time,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
             <if test="updateBy != null">update_by,</if>
             <if test="delFlag != null and delFlag != ''">del_flag,</if>
             <if test="note != null">note,</if>
@@ -67,9 +67,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="stationId != null">#{stationId},</if>
             <if test="startTime != null">#{startTime},</if>
             <if test="endTime != null">#{endTime},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
             <if test="delFlag != null and delFlag != ''">#{delFlag},</if>
             <if test="note != null">#{note},</if>
@@ -86,9 +86,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="stationId != null">station_id = #{stationId},</if>
             <if test="startTime != null">start_time = #{startTime},</if>
             <if test="endTime != null">end_time = #{endTime},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
             <if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
             <if test="note != null">note = #{note},</if>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/datasource/DataSourceConfigMapper.xml

@@ -77,9 +77,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataInactivityPollInterval != null">data_inactivity_poll_interval,</if>
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null and updateBy != ''">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
             <if test="grabDays != null">grab_days,</if>
             <if test="topicName != null">topic_name,</if>
          </trim>
@@ -99,9 +99,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataInactivityPollInterval != null">#{dataInactivityPollInterval},</if>
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null and updateBy != ''">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
             <if test="grabDays != null">#{grabDays},</if>
             <if test="topicName != null">#{topicName},</if>
          </trim>
@@ -125,9 +125,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataInactivityPollInterval != null">data_inactivity_poll_interval = #{dataInactivityPollInterval},</if>
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="grabDays != null">grab_days = #{grabDays},</if>
             <if test="topicName != null">topic_name = #{topicName},</if>
         </trim>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/datasource/DataSourceGrabMapper.xml

@@ -86,9 +86,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dateMatchPattern != null">date_match_pattern,</if>
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null and updateBy != ''">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
             <if test="patternName != null">pattern_name,</if>
             <if test="batchOfDay != null">batch_of_day,</if>
             <if test="threshold != null">threshold,</if>
@@ -113,9 +113,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dateMatchPattern != null">#{dateMatchPattern},</if>
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null and updateBy != ''">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
             <if test="patternName != null">#{patternName},</if>
             <if test="batchOfDay != null">#{batchOfDay},</if>
             <if test="threshold != null">#{threshold},</if>
@@ -144,9 +144,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dateMatchPattern != null">date_match_pattern = #{dateMatchPattern},</if>
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="patternName != null">pattern_name = #{patternName},</if>
             <if test="batchOfDay != null">batch_of_day = #{batchOfDay},</if>
             <if test="threshold != null">threshold = #{threshold},</if>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/datasource/DataSourceUserRelationMapper.xml

@@ -64,9 +64,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isBusinessDownload != null and isBusinessDownload != ''">is_business_download,</if>
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="dataSourceGrabId != null">#{dataSourceGrabId},</if>
@@ -81,9 +81,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isBusinessDownload != null and isBusinessDownload != ''">#{isBusinessDownload},</if>
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -102,9 +102,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isBusinessDownload != null and isBusinessDownload != ''">is_business_download = #{isBusinessDownload},</if>
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/rbac/RbacMenuMapper.xml

@@ -63,9 +63,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="isSystem != null and isSystem != ''">is_system,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
             <if test="icon != null">icon,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
@@ -80,9 +80,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="isSystem != null and isSystem != ''">#{isSystem},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
             <if test="icon != null">#{icon},</if>
          </trim>
     </insert>
@@ -101,9 +101,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="isSystem != null and isSystem != ''">is_system = #{isSystem},</if>
             <if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="icon != null">icon = #{icon},</if>
         </trim>
         where id = #{id}

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/rbac/RbacModuleMapper.xml

@@ -52,9 +52,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="sort != null">sort,</if>
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null and updateBy != ''">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="name != null and name != ''">#{name},</if>
@@ -65,9 +65,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="sort != null">#{sort},</if>
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null and updateBy != ''">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -82,9 +82,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="sort != null">sort = #{sort},</if>
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/rbac/RbacRoleMapper.xml

@@ -46,9 +46,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="attachUrl != null">attach_url,</if>
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="name != null and name != ''">#{name},</if>
@@ -57,9 +57,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="attachUrl != null">#{attachUrl},</if>
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -72,9 +72,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="attachUrl != null">attach_url = #{attachUrl},</if>
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/rbac/RbacUserMapper.xml

@@ -59,9 +59,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="type != null">type,</if>
             <if test="isDel != null and isDel != ''">is_del,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
             <if test="dataIds != null and dataIds != ''">data_ids,</if>
             <if test="relationType != null">relation_type,</if>
          </trim>
@@ -75,9 +75,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="type != null">#{type},</if>
             <if test="isDel != null and isDel != ''">#{isDel},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
             <if test="dataIds != null and dataIds != ''">#{dataIds},</if>
             <if test="relationType != null">#{relationType},</if>
          </trim>
@@ -95,9 +95,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="type != null">type = #{type},</if>
             <if test="isDel != null and isDel != ''">is_del = #{isDel},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="dataIds != null and dataIds != ''">data_ids = #{dataIds},</if>
             <if test="relationType != null">relation_type = #{relationType},</if>
         </trim>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/report/ConfigReportSiteInfoMapper.xml

@@ -52,9 +52,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="enableFlag != null and enableFlag != ''">enable_flag,</if>
             <if test="delFlag != null and delFlag != ''">del_flag,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="id != null">#{id},</if>
@@ -66,9 +66,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="enableFlag != null and enableFlag != ''">#{enableFlag},</if>
             <if test="delFlag != null and delFlag != ''">#{delFlag},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -83,9 +83,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="enableFlag != null and enableFlag != ''">enable_flag = #{enableFlag},</if>
             <if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/report/ConfigReportSiteMapper.xml

@@ -62,9 +62,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="siteProtocol != null">site_protocol,</if>
             <if test="maxLength != null">max_length,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
             <if test="delFlag != null and delFlag != ''">del_flag,</if>
             <if test="provinceNumber != null">province_number,</if>
             <if test="fileCode != null">file_code,</if>
@@ -82,9 +82,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="siteProtocol != null">#{siteProtocol},</if>
             <if test="maxLength != null">#{maxLength},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
             <if test="delFlag != null and delFlag != ''">#{delFlag},</if>
             <if test="provinceNumber != null">#{provinceNumber},</if>
             <if test="fileCode != null">#{fileCode},</if>
@@ -105,9 +105,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="siteProtocol != null">site_protocol = #{siteProtocol},</if>
             <if test="maxLength != null">max_length = #{maxLength},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
             <if test="provinceNumber != null">province_number = #{provinceNumber},</if>
             <if test="fileCode != null">file_code = #{fileCode},</if>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/report/ConfigReportTaskMapper.xml

@@ -58,9 +58,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="enableFlag != null and enableFlag != ''">enable_flag,</if>
             <if test="delFlag != null and delFlag != ''">del_flag,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="id != null">#{id},</if>
@@ -74,9 +74,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="enableFlag != null and enableFlag != ''">#{enableFlag},</if>
             <if test="delFlag != null and delFlag != ''">#{delFlag},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -93,9 +93,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="enableFlag != null and enableFlag != ''">enable_flag = #{enableFlag},</if>
             <if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 3 - 3
forecast-backend-server/src/main/resources/mybatis/report/StatsAssessmentIndicatorMapper.xml

@@ -53,7 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataSource != null">data_source,</if>
             <if test="indicatorValue != null">indicator_value,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
             <if test="updateTime != null">update_time,</if>
             <if test="recordTime != null">record_time,</if>
@@ -68,7 +68,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataSource != null">#{dataSource},</if>
             <if test="indicatorValue != null">#{indicatorValue},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
             <if test="updateTime != null">#{updateTime},</if>
             <if test="recordTime != null">#{recordTime},</if>
@@ -86,7 +86,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataSource != null">data_source = #{dataSource},</if>
             <if test="indicatorValue != null">indicator_value = #{indicatorValue},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="recordTime != null">record_time = #{recordTime},</if>

+ 3 - 3
forecast-backend-server/src/main/resources/mybatis/report/StatsAssessmentIndicatorMultiModelMapper.xml

@@ -53,7 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataSource != null">data_source,</if>
             <if test="indicatorValue != null">indicator_value,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
             <if test="updateTime != null">update_time,</if>
             <if test="recordTime != null">record_time,</if>
@@ -68,7 +68,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataSource != null">#{dataSource},</if>
             <if test="indicatorValue != null">#{indicatorValue},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
             <if test="updateTime != null">#{updateTime},</if>
             <if test="recordTime != null">#{recordTime},</if>
@@ -86,7 +86,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataSource != null">data_source = #{dataSource},</if>
             <if test="indicatorValue != null">indicator_value = #{indicatorValue},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="recordTime != null">record_time = #{recordTime},</if>

+ 3 - 3
forecast-backend-server/src/main/resources/mybatis/report/StatsWeatherIndicatorMapper.xml

@@ -48,7 +48,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataSource != null">data_source,</if>
             <if test="indicatorVal != null">indicator_val,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
             <if test="updateTime != null">update_time,</if>
             <if test="recordTime != null">record_time,</if>
@@ -61,7 +61,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataSource != null">#{dataSource},</if>
             <if test="indicatorVal != null">#{indicatorVal},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
             <if test="updateTime != null">#{updateTime},</if>
             <if test="recordTime != null">#{recordTime},</if>
@@ -77,7 +77,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataSource != null">data_source = #{dataSource},</if>
             <if test="indicatorVal != null">indicator_val = #{indicatorVal},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="recordTime != null">record_time = #{recordTime},</if>

+ 3 - 3
forecast-backend-server/src/main/resources/mybatis/report/StatsWeatherIndicatorMultiModelMapper.xml

@@ -50,7 +50,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataSource != null">data_source,</if>
             <if test="indicatorValue != null">indicator_value,</if>
             <if test="createBy != null">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null">update_by,</if>
             <if test="updateTime != null">update_time,</if>
             <if test="recordTime != null">record_time,</if>
@@ -64,7 +64,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataSource != null">#{dataSource},</if>
             <if test="indicatorValue != null">#{indicatorValue},</if>
             <if test="createBy != null">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null">#{updateBy},</if>
             <if test="updateTime != null">#{updateTime},</if>
             <if test="recordTime != null">#{recordTime},</if>
@@ -81,7 +81,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="dataSource != null">data_source = #{dataSource},</if>
             <if test="indicatorValue != null">indicator_value = #{indicatorValue},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="recordTime != null">record_time = #{recordTime},</if>

+ 6 - 6
forecast-backend-server/src/main/resources/mybatis/weather/WeatherWarningRecordMapper.xml

@@ -58,9 +58,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="warningTitle != null and warningTitle != ''">warning_title,</if>
             <if test="warningMessage != null and warningMessage != ''">warning_message,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
-            <if test="createTime != null and createTime != ''">create_time,</if>
+            <if test="createTime != null">create_time,</if>
             <if test="updateBy != null and updateBy != ''">update_by,</if>
-            <if test="updateTime != null and updateTime != ''">update_time,</if>
+            <if test="updateTime != null">update_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="type != null">#{type},</if>
@@ -73,9 +73,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="warningTitle != null and warningTitle != ''">#{warningTitle},</if>
             <if test="warningMessage != null and warningMessage != ''">#{warningMessage},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
-            <if test="createTime != null and createTime != ''">#{createTime},</if>
+            <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null and updateBy != ''">#{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">#{updateTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
          </trim>
     </insert>
 
@@ -92,9 +92,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="warningTitle != null and warningTitle != ''">warning_title = #{warningTitle},</if>
             <if test="warningMessage != null and warningMessage != ''">warning_message = #{warningMessage},</if>
             <if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
-            <if test="createTime != null and createTime != ''">create_time = #{createTime},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
-            <if test="updateTime != null and updateTime != ''">update_time = #{updateTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
         </trim>
         where id = #{id}
     </update>

+ 14 - 28
forecast-backend-vue/src/api/alarm/eventRecord.js

@@ -1,44 +1,30 @@
 import request from '@/utils/request'
 
-// 查询报警信息统计列表
-export function listEventRecord(query) {
+// 查询事件列表
+export function listEventRecord(data) {
   return request({
     url: '/alarm/eventRecord/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 查询报警信息统计详细
-export function getEventRecord(id) {
-  return request({
-    url: '/alarm/eventRecord/' + id,
-    method: 'get'
-  })
-}
-
-// 新增报警信息统计
-export function addEventRecord(data) {
-  return request({
-    url: '/alarm/eventRecord',
     method: 'post',
     data: data
   })
 }
 
-// 修改报警信息统计
-export function updateEventRecord(data) {
+// 确认告警(批量处理)
+export function processEventRecord(ids) {
   return request({
-    url: '/alarm/eventRecord',
-    method: 'put',
-    data: data
+    url: '/alarm/eventRecord/process',
+    method: 'post',
+    data: { ids: ids }
   })
 }
 
-// 删除报警信息统计
-export function delEventRecord(id) {
+// 导出事件列表
+export function exportEventRecord(data) {
   return request({
-    url: '/alarm/eventRecord/' + id,
-    method: 'delete'
+    url: '/alarm/eventRecord/export',
+    method: 'post',
+    data: data,
+    responseType: 'blob',
+    timeout: 120000
   })
 }

+ 29 - 0
forecast-backend-vue/src/api/fileupload/reportLog.js

@@ -16,3 +16,32 @@ export function getReportLogFileContent(id) {
     method: 'get'
   })
 }
+
+// 查询光伏上传率统计(手动分页)
+export function listUploadRate(data) {
+  return request({
+    url: '/fileupload/reportLog/uploadRate/list',
+    method: 'post',
+    data
+  })
+}
+
+// 导出光伏上传率统计
+export function exportUploadRate(data) {
+  return request({
+    url: '/fileupload/reportLog/uploadRate/export',
+    method: 'post',
+    data,
+    responseType: 'blob',
+    timeout: 120000
+  })
+}
+
+// 数据补传(按每日配置补插当日上报成功记录)
+export function reUpload(stationCode) {
+  return request({
+    url: '/fileupload/reportLog/reUpload',
+    method: 'get',
+    params: {stationCode}
+  })
+}

+ 3 - 1
forecast-backend-vue/src/utils/BearJiaUtil.js

@@ -177,7 +177,9 @@ const BearJiaUtil = {
                     if (d) {
                         dicts.push({
                             value: d.dictValue,
-                            label: d.dictLabel
+                            label: d.dictLabel,
+                            cssClass: d.cssClass,
+                            listClass: d.listClass
                         });
                     }
                 }

+ 5 - 1
forecast-backend-vue/src/views/ufo/config/station/StationConfigCrud.vue

@@ -290,7 +290,9 @@ const submitForm = async () => {
 const handleDeleteRecord = (record) => {
   Modal.confirm({
     title: '确认删除',
-    content: `确认删除该${moduleConfig.title}?`,
+    content: `确认删除该${props.moduleConfig.title}?`,
+    okText: '确定',
+    cancelText: '取消',
     onOk: async () => {
       await props.api.del(record.id);
       message.success('删除成功');
@@ -306,6 +308,8 @@ const handleBatchDelete = () => {
   Modal.confirm({
     title: '确认删除',
     content: `确认删除选中的 ${selectedRowKeys.value.length} 条数据?`,
+    okText: '确定',
+    cancelText: '取消',
     onOk: async () => {
       await props.api.del(selectedRowKeys.value);
       message.success('删除成功');

+ 9 - 4
forecast-backend-vue/src/views/ufo/config/station/index.vue

@@ -8,7 +8,7 @@
         <a-form-item label="场站编码">
           <a-input v-model:value="queryParams.stationCode" allow-clear placeholder="请输入场站编码" />
         </a-form-item>
-        <a-form-item label="场站类型">
+        <a-form-item label="场站类型" style="display:none">
           <a-select v-model:value="queryParams.stationType" allow-clear class="query-select" :options="stationTypeOptions" />
         </a-form-item>
         <a-form-item label="是否启用">
@@ -36,7 +36,7 @@
         @reset="handleTableReset"
       >
         <template #left>
-          <a-button v-hasPermi="['config:station:add']" type="primary" @click="openForm()"><BearJiaIcon icon="plus-outlined"/>新增</a-button>
+          <a-button v-hasPermi="['config:station:add']" type="primary" style="display:none" @click="openForm()"><BearJiaIcon icon="plus-outlined"/>新增</a-button>
           <a-button v-hasPermi="['config:station:remove']" danger type="primary" :disabled="selectedRowKeys.length <= 0" @click="handleBatchDelete"><BearJiaIcon icon="delete-outlined"/>删除</a-button>
           <a-button v-hasPermi="['config:station:export']" @click="handleExport"><BearJiaIcon icon="export-outlined"/>导出</a-button>
         </template>
@@ -86,7 +86,7 @@
               <a-input v-model:value="form.stationCode" allow-clear />
             </a-form-item>
           </a-col>
-          <a-col :span="12">
+          <a-col :span="12" style="display:none">
             <a-form-item label="场站类型" name="stationType">
               <a-select v-model:value="form.stationType" allow-clear :options="stationTypeOptions" />
             </a-form-item>
@@ -271,12 +271,13 @@ const openForm = async (record) => {
     }
     resetObject(form, data);
   } else {
-    resetObject(form, { isEnable: '1', stationType: 0 });
+    resetObject(form, { isEnable: '1', stationType: 1 });
   }
   formOpen.value = true;
 };
 
 const submitForm = async () => {
+  form.stationType = 1;
   await formRef.value?.validate();
   if (form.id) {
     await updateStation(form);
@@ -292,6 +293,8 @@ const handleDeleteRecord = (record) => {
   Modal.confirm({
     title: '确认删除',
     content: '确认删除该场站?',
+    okText: '确定',
+    cancelText: '取消',
     onOk: async () => {
       await delStation(record.id);
       message.success('删除成功');
@@ -307,6 +310,8 @@ const handleBatchDelete = () => {
   Modal.confirm({
     title: '确认删除',
     content: `确认删除选中的 ${selectedRowKeys.value.length} 条数据?`,
+    okText: '确定',
+    cancelText: '取消',
     onOk: async () => {
       await delStation(selectedRowKeys.value);
       message.success('删除成功');

+ 207 - 107
forecast-backend-vue/src/views/ufo/electricity/device/TurbineInfo.vue

@@ -3,167 +3,267 @@
     <a-card :bordered="false">
       <a-row :gutter="[12, 12]" align="middle">
         <a-col :xs="24" :md="8">
-          <StationSelector v-model:value="query.stationIds" :options="stationOptions" />
+          <StationSelector v-model:value="queryParams.configStationId" :options="stationOptions" :multiple="false" />
         </a-col>
         <a-col :xs="24" :md="6">
-          <a-select v-model:value="query.deviceType" :options="deviceTypeOptions" class="full-control" />
-        </a-col>
-        <a-col :xs="24" :md="6">
-          <a-input v-model:value="query.keyword" placeholder="请输入设备名称" />
+          <a-input v-model:value="queryParams.name" placeholder="请输入设备名称" allow-clear @press-enter="handleQuery" />
         </a-col>
         <a-col :xs="24" :md="4" class="action-bar">
           <a-space>
-            <a-button type="primary" :loading="loading" @click="handleQuery">查询</a-button>
-            <a-button>新增</a-button>
+            <a-button type="primary" :loading="loading" @click="handleQuery"><BearJiaIcon icon="SearchOutlined" />查询</a-button>
+            <a-button @click="resetQuery">重置</a-button>
           </a-space>
         </a-col>
       </a-row>
     </a-card>
 
-    <a-card title="光伏设备信息" :bordered="false" class="section" :loading="loading">
+    <a-card title="光伏设备信息" :bordered="false" class="section">
+      <a-space class="table-actions">
+        <a-button type="primary" @click="openForm"><BearJiaIcon icon="plus-outlined" />新增</a-button>
+        <a-button danger type="primary" :disabled="selectedRowKeys.length <= 0" @click="handleBatchDelete"><BearJiaIcon icon="delete-outlined" />删除</a-button>
+        <a-button @click="handleExport"><BearJiaIcon icon="export-outlined" />导出</a-button>
+      </a-space>
       <a-table
         :columns="columns"
         :data-source="rows"
-        :pagination="{pageSize: 10}"
+        :loading="loading"
+        :pagination="pagination"
         :locale="{emptyText: '暂无数据'}"
         row-key="id"
-      />
+        :row-selection="rowSelection"
+        @change="handleTableChange"
+      >
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.key === 'operation'">
+            <TableActionBar :record="record" :has-view="false" :has-edit="true" :has-delete="true" @edit="openForm(record)" @delete="handleDeleteRecord(record)" />
+          </template>
+        </template>
+      </a-table>
     </a-card>
+
+    <a-modal v-model:open="formOpen" :title="form.id ? '编辑设备' : '新增设备'" width="640px" @ok="submitForm">
+      <a-form ref="formRef" :model="form" :rules="rules" :label-col="{span: 5}" :wrapper-col="{span: 17}">
+        <a-form-item label="设备名称" name="name">
+          <a-input v-model:value="form.name" allow-clear placeholder="请输入设备名称" />
+        </a-form-item>
+        <a-form-item label="通道" name="configChannelId">
+          <a-select v-model:value="form.configChannelId" :options="channelOptions" placeholder="请选择通道" />
+        </a-form-item>
+        <a-form-item label="所属场站" name="configStationId">
+          <a-select v-model:value="form.configStationId" :options="stationOptions" placeholder="请选择所属场站" />
+        </a-form-item>
+        <a-form-item label="设备类型" name="tp">
+          <a-select v-model:value="form.tp" :options="tpOptions" placeholder="请选择设备类型" />
+        </a-form-item>
+        <a-form-item label="品牌" name="brand">
+          <a-input v-model:value="form.brand" allow-clear placeholder="请输入品牌" />
+        </a-form-item>
+        <a-form-item label="型号" name="model">
+          <a-input v-model:value="form.model" allow-clear placeholder="请输入型号" />
+        </a-form-item>
+        <a-form-item label="序列号" name="serialNumber">
+          <a-input v-model:value="form.serialNumber" allow-clear placeholder="请输入序列号" />
+        </a-form-item>
+        <a-form-item label="状态" name="status">
+          <a-select v-model:value="form.status" :options="statusOptions" placeholder="请选择状态" />
+        </a-form-item>
+        <a-form-item label="投运日期" name="installDate">
+          <a-date-picker v-model:value="form.installDate" value-format="YYYY-MM-DD" style="width: 100%" placeholder="请选择投运日期" />
+        </a-form-item>
+      </a-form>
+    </a-modal>
   </div>
 </template>
 
 <script setup name="ElectricityTurbineInfo">
-import {onMounted, reactive, ref} from 'vue';
+import {computed, onMounted, reactive, ref} from 'vue';
+import {message, Modal} from 'ant-design-vue';
+import BearJiaUtil from '@/utils/BearJiaUtil';
 import {listStation} from '@/api/config/station';
-import {listDevicePowerReal} from '@/api/realtime/devicePowerReal';
+import {listChannel} from '@/api/config/channel';
+import {addDevice, delDevice, getDevice, listDevice, updateDevice} from '@/api/config/device';
 import StationSelector from '../components/StationSelector.vue';
-import {normalizeRows, pick, toNumber} from '../components/dataAdapter';
-import {buildStationNameMap, resolveStationName, toStationOptions} from '../components/stationAdapter';
+import TableActionBar from '@/components/TableActionBar/index.vue';
+import {normalizeRows} from '../components/dataAdapter';
+import {toStationOptions} from '../components/stationAdapter';
+import {BearJiaIcon} from '@/utils/BearJiaIcon.js';
 
 const loading = ref(false);
-const stationOptions = ref([]);
-const stationNameMap = ref(new Map());
 const rows = ref([]);
+const stationOptions = ref([]);
+const channelOptions = ref([]);
+const selectedRowKeys = ref([]);
+const formOpen = ref(false);
+const formRef = ref();
 
-const query = reactive({
-  stationIds: [],
-  deviceType: 'inverter',
-  keyword: ''
+const queryParams = reactive({
+  pageNum: 1,
+  pageSize: 10,
+  configStationId: undefined,
+  name: undefined
 });
 
-const deviceTypeOptions = [
-  {label: '逆变器', value: 'inverter'},
-  {label: '箱变', value: 'box_transformer'},
-  {label: '汇流箱', value: 'combiner_box'}
+const pagination = reactive({
+  current: 1,
+  pageSize: 10,
+  total: 0,
+  showSizeChanger: true
+});
+
+const form = reactive({});
+
+const rowSelection = computed(() => ({
+  selectedRowKeys: selectedRowKeys.value,
+  onChange: (keys) => {
+    selectedRowKeys.value = keys;
+  }
+}));
+
+const tpOptions = [
+  {label: '气象设备', value: 'WEATHER'},
+  {label: 'agvc设备', value: 'AGVC'}
 ];
 
+const statusOptions = [
+  {label: '在线', value: 'GOOD'},
+  {label: '离线', value: 'OFF'}
+];
+
+const rules = {
+  name: [{required: true, message: '请输入设备名称', trigger: 'blur'}],
+  configChannelId: [{required: true, message: '请选择通道', trigger: 'change'}],
+  configStationId: [{required: true, message: '请选择所属场站', trigger: 'change'}],
+  tp: [{required: true, message: '请选择设备类型', trigger: 'change'}],
+  brand: [{required: true, message: '请输入品牌', trigger: 'blur'}],
+  model: [{required: true, message: '请输入型号', trigger: 'blur'}],
+  serialNumber: [{required: true, message: '请输入序列号', trigger: 'blur'}],
+  status: [{required: true, message: '请选择状态', trigger: 'change'}],
+  installDate: [{required: true, message: '请选择投运日期', trigger: 'change'}]
+};
+
 const columns = [
-  {title: '序号', dataIndex: 'id', width: 80},
-  {title: '设备名称', dataIndex: 'deviceName'},
-  {title: '设备类型', dataIndex: 'deviceType'},
+  {
+    title: '序号', width: 120, align: 'center',
+    customRender: ({index}) => (pagination.current - 1) * pagination.pageSize + index + 1
+  },
+  {title: '设备名称', dataIndex: 'name'},
+  {title: '通道', dataIndex: 'channelName'},
   {title: '所属场站', dataIndex: 'stationName'},
-  {title: '额定容量', dataIndex: 'capacity'},
+  {
+    title: '设备类型', dataIndex: 'tp',
+    customRender: ({text}) => (text === 'WEATHER' ? '气象设备' : text === 'AGVC' ? 'agvc设备' : text)
+  },
+  {title: '品牌', dataIndex: 'brand'},
   {title: '型号', dataIndex: 'model'},
-  {title: '状态', dataIndex: 'status'}
+  {title: '序列号', dataIndex: 'serialNumber'},
+  {
+    title: '状态', dataIndex: 'status', width: 120,
+    customRender: ({text}) => (text === 'GOOD' ? '在线' : '离线')
+  },
+  {title: '投运日期', dataIndex: 'installDate', width: 180},
+  {title: '操作', key: 'operation', dataIndex: 'operation', width: 200, fixed: 'right'}
 ];
 
-const deviceTypeMap = {
-  inverter: '逆变器',
-  box_transformer: '箱变',
-  combiner_box: '汇流箱'
+const resetObject = (target, source = {}) => {
+  Object.keys(target).forEach((key) => delete target[key]);
+  Object.assign(target, source);
 };
 
-const toStatus = (value) => {
-  const text = String(value ?? '').trim().toLowerCase();
-  if (text.includes('offline') || text.includes('离线') || text === '0' || text === 'false') return '离线';
-  if (text.includes('abnormal') || text.includes('fault') || text.includes('异常') || text.includes('告警')) return '检修';
-  return '在线';
+const loadStationOptions = async () => {
+  const res = await listStation({pageNum: 1, pageSize: 500});
+  stationOptions.value = toStationOptions(normalizeRows(res));
 };
 
-const inferDeviceType = (item) => {
-  const raw = String(pick(item, ['deviceType', 'type', 'equipmentType'], '')).toLowerCase();
-  const name = String(pick(item, ['deviceName', 'name', 'deviceId'], '')).toLowerCase();
-  const text = `${raw} ${name}`;
-  if (text.includes('combiner') || text.includes('汇流')) return 'combiner_box';
-  if (text.includes('box') || text.includes('transform') || text.includes('箱变') || text.includes('升压')) return 'box_transformer';
-  if (text.includes('invert') || text.includes('inv') || text.includes('逆变')) return 'inverter';
-  return 'inverter';
+const loadChannelOptions = async () => {
+  const res = await listChannel({pageNum: 1, pageSize: 500});
+  const channelRows = res?.rows || res?.data || [];
+  channelOptions.value = channelRows.map((item) => ({label: item.name || item.id, value: item.id}));
 };
 
-const formatCapacity = (item) => {
-  const raw = pick(item, ['capacity', 'ratedCapacity', 'installCapacity'], '');
-  const num = toNumber(raw, NaN);
-  if (Number.isFinite(num) && num !== 0) return `${Number(num.toFixed(2))}MW`;
-  return raw || '-';
+const handleQuery = async () => {
+  loading.value = true;
+  try {
+    const res = await listDevice(queryParams);
+    rows.value = res?.rows || res?.data || [];
+    pagination.total = res?.total || rows.value.length;
+    pagination.current = queryParams.pageNum;
+    pagination.pageSize = queryParams.pageSize;
+  } finally {
+    loading.value = false;
+  }
 };
 
-const toPvDeviceName = (name, typeKey, index) => {
-  const text = String(name || '').trim();
-  if (text) {
-    return text.replace(/风机/g, '逆变器');
-  }
-  return `${deviceTypeMap[typeKey] || '逆变器'} #${index + 1}`;
+const resetQuery = () => {
+  queryParams.pageNum = 1;
+  queryParams.configStationId = undefined;
+  queryParams.name = undefined;
+  handleQuery();
 };
 
-const loadStationOptions = async () => {
-  const res = await listStation({pageNum: 1, pageSize: 500});
-  const stationRows = normalizeRows(res);
-  stationOptions.value = toStationOptions(stationRows);
-  stationNameMap.value = buildStationNameMap(stationRows);
-  if (!query.stationIds.length && stationOptions.value.length) {
-    query.stationIds = [stationOptions.value[0].value];
-  }
+const handleTableChange = (page) => {
+  queryParams.pageNum = page.current;
+  queryParams.pageSize = page.pageSize;
+  handleQuery();
 };
 
-const filterRowsBySelectedStations = (sourceRows) => {
-  const stationIds = Array.isArray(query.stationIds) ? query.stationIds : [query.stationIds].filter(Boolean);
-  if (!stationIds.length) {
-    return sourceRows;
+const openForm = async (record) => {
+  if (record?.id) {
+    const res = await getDevice(record.id);
+    resetObject(form, {...res.data});
+  } else {
+    resetObject(form, {configStationId: queryParams.configStationId});
   }
-  const selectedKeys = new Set(stationIds);
-  return sourceRows.filter((item) =>
-    [item.stationId, item.entityId, item.id, item.Id].some((key) => selectedKeys.has(key))
-  );
+  formOpen.value = true;
 };
 
-const handleQuery = async () => {
-  loading.value = true;
-  try {
-    const stationIds = Array.isArray(query.stationIds) ? query.stationIds : [query.stationIds].filter(Boolean);
-    const params = {
-      pageNum: 1,
-      pageSize: 500,
-      entityId: stationIds.length === 1 ? stationIds[0] : undefined,
-      stationIds: stationIds.join(',')
-    };
-    const res = await listDevicePowerReal(params);
-    const sourceRows = filterRowsBySelectedStations(normalizeRows(res)).map((item, index) => {
-      const typeKey = inferDeviceType(item);
-      return {
-        id: item.id || item.Id || index + 1,
-        deviceName: toPvDeviceName(pick(item, ['deviceName', 'name', 'deviceId', 'sensorName']), typeKey, index),
-        deviceTypeKey: typeKey,
-        deviceType: deviceTypeMap[typeKey] || '逆变器',
-        stationName: resolveStationName(item, stationNameMap.value) || '江门站',
-        capacity: formatCapacity(item),
-        model: pick(item, ['model', 'deviceModel', 'specification', 'version'], '-'),
-        status: toStatus(pick(item, ['status', 'state', 'onlineStatus'], 'online'))
-      };
-    });
-
-    rows.value = sourceRows.filter((item) => {
-      const typeMatched = query.deviceType ? item.deviceTypeKey === query.deviceType : true;
-      const keyword = query.keyword?.trim();
-      const keywordMatched = keyword ? String(item.deviceName || '').includes(keyword) : true;
-      return typeMatched && keywordMatched;
-    });
-  } finally {
-    loading.value = false;
+const submitForm = async () => {
+  await formRef.value?.validate();
+  if (form.id) await updateDevice(form); else await addDevice(form);
+  message.success('保存成功');
+  formOpen.value = false;
+  handleQuery();
+};
+
+const handleDeleteRecord = (record) => {
+  Modal.confirm({
+    title: '确认删除',
+    content: '确认删除该设备?',
+    okText: '确定',
+    cancelText: '取消',
+    onOk: async () => {
+      await delDevice(record.id);
+      message.success('删除成功');
+      handleQuery();
+    }
+  });
+};
+
+const handleBatchDelete = () => {
+  if (selectedRowKeys.value.length <= 0) {
+    message.warning('请至少选择一条数据');
+    return;
   }
+  Modal.confirm({
+    title: '确认删除',
+    content: `确认删除选中的 ${selectedRowKeys.value.length} 条数据?`,
+    okText: '确定',
+    cancelText: '取消',
+    onOk: async () => {
+      await delDevice(selectedRowKeys.value);
+      message.success('删除成功');
+      selectedRowKeys.value = [];
+      handleQuery();
+    }
+  });
+};
+
+const handleExport = () => {
+  BearJiaUtil.download('config/device/export', queryParams, '设备配置.xlsx');
 };
 
 onMounted(async () => {
   await loadStationOptions();
+  await loadChannelOptions();
   await handleQuery();
 });
 </script>
@@ -172,5 +272,5 @@ onMounted(async () => {
 .device-page { padding: 16px; }
 .section { margin-top: 16px; }
 .action-bar { display: flex; justify-content: flex-end; }
-.full-control { width: 100%; }
+.table-actions { display: flex; margin-bottom: 12px; }
 </style>

+ 63 - 0
forecast-backend-vue/src/views/ufo/electricity/forecast/DataResend.vue

@@ -0,0 +1,63 @@
+<template>
+  <div class="data-resend-page">
+    <a-card :bordered="false">
+      <a-row :gutter="[12, 12]" align="middle">
+        <a-col :xs="24" :md="8">
+          <StationSelector v-model:value="stationCode" :options="stationOptions" :multiple="false" />
+        </a-col>
+        <a-col :xs="24" :md="8">
+          <a-space>
+            <a-button type="primary" :loading="resending" :disabled="!stationCode" @click="handleResend">补传</a-button>
+          </a-space>
+        </a-col>
+      </a-row>
+    </a-card>
+  </div>
+</template>
+
+<script setup name="DataResend">
+import {onMounted, ref} from 'vue';
+import {message} from 'ant-design-vue';
+import {listStation} from '@/api/config/station';
+import {reUpload} from '@/api/fileupload/reportLog';
+import StationSelector from '../components/StationSelector.vue';
+
+const resending = ref(false);
+const stationOptions = ref([]);
+const stationCode = ref(undefined);
+
+const loadStationOptions = async () => {
+  const res = await listStation({pageNum: 1, pageSize: 500});
+  const list = (res && res.rows) || [];
+  stationOptions.value = list.map((item) => ({label: item.stationName, value: item.stationCode}));
+  if (!stationCode.value && stationOptions.value.length) {
+    stationCode.value = stationOptions.value[0].value;
+  }
+};
+
+const handleResend = async () => {
+  if (!stationCode.value) {
+    message.warning('请先选择场站');
+    return;
+  }
+  resending.value = true;
+  try {
+    const res = await reUpload(stationCode.value);
+    message.success(res.msg || '补传完成');
+  } catch (e) {
+    // request.js 已统一提示错误
+  } finally {
+    resending.value = false;
+  }
+};
+
+onMounted(async () => {
+  await loadStationOptions();
+});
+</script>
+
+<style lang="less" scoped>
+.data-resend-page {
+  padding: 16px;
+}
+</style>

+ 269 - 0
forecast-backend-vue/src/views/ufo/electricity/statanalysis/eventQuery.vue

@@ -0,0 +1,269 @@
+<template>
+  <div class="event-query-page">
+    <a-card :bordered="false">
+      <a-row :gutter="[12, 12]" align="middle">
+        <a-col :xs="24" :md="5">
+          <a-select
+            v-model:value="query.alarmCategory"
+            :options="categoryOptions"
+            placeholder="告警类别"
+            allow-clear
+            class="full-control"
+          />
+        </a-col>
+        <a-col :xs="24" :md="5">
+          <a-select
+            v-model:value="query.alarmLevel"
+            :options="levelOptions"
+            placeholder="告警等级"
+            allow-clear
+            class="full-control"
+          />
+        </a-col>
+        <a-col :xs="24" :md="4">
+          <a-select
+            v-model:value="query.processStatus"
+            :options="processStatusOptions"
+            placeholder="处理状态"
+            allow-clear
+            class="full-control"
+          />
+        </a-col>
+        <a-col :xs="24" :md="5">
+          <a-date-picker
+            v-model:value="query.alarmDate"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+            placeholder="告警时间"
+            class="full-control"
+          />
+        </a-col>
+        <a-col :xs="24" :md="5">
+          <a-space wrap>
+            <a-button type="primary" :loading="loading" @click="handleQuery">查询</a-button>
+            <a-button :loading="processLoading" @click="handleProcess">确认告警</a-button>
+            <a-button :loading="exportLoading" @click="handleExport">导出</a-button>
+          </a-space>
+        </a-col>
+      </a-row>
+    </a-card>
+
+    <a-card :bordered="false" class="content-card">
+      <a-table
+        :columns="columns"
+        :data-source="rows"
+        :loading="loading"
+        :pagination="pagination"
+        :row-selection="rowSelection"
+        row-key="id"
+        size="middle"
+        @change="handleTableChange"
+      >
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.dataIndex === 'alarmLevel'">
+            {{ levelLabel(record.alarmLevel) }}
+          </template>
+          <template v-else-if="column.dataIndex === 'alarmCategory'">
+            {{ categoryLabel(record.alarmCategory) }}
+          </template>
+          <template v-else-if="column.dataIndex === 'processFlag'">
+            <a-tag :color="processColorMap[record.processFlag] || 'default'">
+              {{ processLabelMap[record.processFlag] ?? record.processFlag }}
+            </a-tag>
+          </template>
+        </template>
+      </a-table>
+    </a-card>
+  </div>
+</template>
+
+<script setup name="EventQuery">
+import {computed, onMounted, reactive, ref} from 'vue';
+import {message, Modal} from 'ant-design-vue';
+import dayjs from 'dayjs';
+import {saveAs} from 'file-saver';
+import {blobValidate} from '@/utils/bearjia';
+import {useDict} from '@/composables/useDict';
+import {listEventRecord, processEventRecord, exportEventRecord} from '@/api/alarm/eventRecord';
+
+const {alarm_category, alarm_level} = useDict('alarm_category', 'alarm_level');
+const alarmCategory = alarm_category;
+const alarmLevel = alarm_level;
+
+const categoryOptions = computed(() =>
+  alarmCategory.value.map((o) => ({label: o.label, value: o.value}))
+);
+const levelOptions = computed(() =>
+  alarmLevel.value.map((o) => ({label: o.label, value: o.value}))
+);
+
+// 表格列展示: 从下拉options中取字典label, 无匹配时回退原始值
+const levelLabelMap = computed(() =>
+  Object.fromEntries(levelOptions.value.map((o) => [String(o.value), o.label]))
+);
+const categoryLabelMap = computed(() =>
+  Object.fromEntries(categoryOptions.value.map((o) => [String(o.value), o.label]))
+);
+const levelLabel = (val) => (val == null ? '' : levelLabelMap.value[String(val)] ?? val);
+const categoryLabel = (val) => (val == null ? '' : categoryLabelMap.value[String(val)] ?? val);
+
+const processStatusOptions = [
+  {label: '已处理', value: true},
+  {label: '未处理', value: false}
+];
+
+const processLabelMap = {
+  0: '未处理',
+  1: '处理中',
+  2: '已处理',
+  3: '误报'
+};
+
+const processColorMap = {
+  0: 'default',
+  1: 'processing',
+  2: 'success',
+  3: 'warning'
+};
+
+const columns = [
+  {title: '告警等级', dataIndex: 'alarmLevel', width: 120},
+  {title: '告警类别', dataIndex: 'alarmCategory', width: 140},
+  {title: '告警内容', dataIndex: 'alarmData', ellipsis: true},
+  {title: '处理状态', dataIndex: 'processFlag', width: 110},
+  {title: '告警日期', dataIndex: 'alarmCreateTime', width: 180}
+];
+
+const loading = ref(false);
+const processLoading = ref(false);
+const exportLoading = ref(false);
+const rows = ref([]);
+const selectedRowKeys = ref([]);
+const rowSelection = reactive({
+  columnWidth: 48,
+  selectedRowKeys: selectedRowKeys,
+  onChange: (keys) => {
+    selectedRowKeys.value = keys;
+  },
+  // 已处理(process_flag=2)的告警不可勾选
+  getCheckboxProps: (record) => ({
+    disabled: String(record.processFlag) === '2'
+  })
+});
+const pagination = reactive({
+  current: 1,
+  pageSize: 10,
+  total: 0,
+  showSizeChanger: true,
+  showTotal: (total) => `共 ${total} 条`
+});
+
+const query = reactive({
+  alarmCategory: undefined,
+  alarmLevel: undefined,
+  processStatus: undefined,
+  alarmDate: dayjs().format('YYYY-MM-DD')
+});
+
+const buildParams = () => ({
+  alarmCategory: query.alarmCategory,
+  alarmLevel: query.alarmLevel,
+  processStatus: query.processStatus,
+  alarmDate: query.alarmDate
+});
+
+const fetchList = async (pageNum, pageSize) => {
+  loading.value = true;
+  try {
+    const res = await listEventRecord({
+      ...buildParams(),
+      pageNum,
+      pageSize
+    });
+    rows.value = res.rows || [];
+    pagination.total = res.total || 0;
+    pagination.current = pageNum;
+    pagination.pageSize = pageSize;
+  } catch (e) {
+    // request.js 已统一提示错误
+  } finally {
+    loading.value = false;
+  }
+};
+
+const handleQuery = () => {
+  fetchList(1, pagination.pageSize);
+};
+
+const handleTableChange = (pag) => {
+  fetchList(pag.current, pag.pageSize);
+};
+
+const handleProcess = () => {
+  if (selectedRowKeys.value.length === 0) {
+    message.warning('请先勾选要确认的告警数据');
+    return;
+  }
+  Modal.confirm({
+    title: '确认告警',
+    content: `确定将选中的 ${selectedRowKeys.value.length} 条告警标记为已处理吗?非已处理的告警将被置为已处理。`,
+    okText: '确认',
+    cancelText: '取消',
+    onOk: async () => {
+      processLoading.value = true;
+      try {
+        const res = await processEventRecord(selectedRowKeys.value);
+        message.success(res?.msg || '确认成功');
+        selectedRowKeys.value = [];
+        fetchList(pagination.current, pagination.pageSize);
+      } catch (e) {
+        // request.js 已统一提示错误
+      } finally {
+        processLoading.value = false;
+      }
+    }
+  });
+};
+
+const handleExport = async () => {
+  exportLoading.value = true;
+  try {
+    const res = await exportEventRecord(buildParams());
+    const isFile = await blobValidate(res);
+    if (isFile) {
+      const name = `事件查询_${dayjs(query.alarmDate || dayjs()).format('YYYYMMDD')}.xlsx`;
+      saveAs(new Blob([res]), name);
+      message.success('导出成功');
+    } else {
+      const text = await res.text();
+      let msg = '导出失败';
+      try {
+        msg = JSON.parse(text)?.msg || msg;
+      } catch (e) {
+        /* ignore */
+      }
+      message.error(msg);
+    }
+  } catch (e) {
+    // request.js 已统一提示错误
+  } finally {
+    exportLoading.value = false;
+  }
+};
+
+onMounted(() => {
+  fetchList(1, pagination.pageSize);
+});
+</script>
+
+<style lang="less" scoped>
+.event-query-page {
+  padding: 16px;
+}
+.full-control {
+  width: 100%;
+}
+.content-card {
+  margin-top: 16px;
+}
+</style>

+ 14 - 2
forecast-backend-vue/src/views/ufo/electricity/statanalysis/reportLogQuery.vue

@@ -110,7 +110,13 @@ const query = reactive({
 const typeOptions = [
   {label: '超短期功率预报', value: 'supershort'},
   {label: '短期功率预报', value: 'short'},
-  {label: '中期功率预报', value: 'medium'}
+  {label: '中期功率预报', value: 'medium'},
+  {label: '环境检测仪数据', value: 'envlDetec'},
+  {label: '逆变器运行信息文件', value: 'ivtOpr'},
+  {label: '升压变数据', value: 'bstTrn'},
+  {label: '理论功率文件', value: 'thrPower'},
+  {label: '数值天气预报文件', value: 'nmrWeather'},
+  {label: '发电计划文件', value: 'pwrGen'}
 ];
 
 const channelOptions = [
@@ -123,7 +129,13 @@ const channelOptions = [
 const typeLabelMap = {
   supershort: '超短期功率预报',
   short: '短期功率预报',
-  medium: '中期功率预报'
+  medium: '中期功率预报',
+  envlDetec: '环境检测仪数据',
+  ivtOpr: '逆变器运行信息文件',
+  bstTrn: '升压变数据',
+  thrPower: '理论功率文件',
+  nmrWeather: '数值天气预报文件',
+  pwrGen: '发电计划文件'
 };
 
 const statusLabelMap = {

+ 195 - 0
forecast-backend-vue/src/views/ufo/electricity/statanalysis/uploadRateStat.vue

@@ -0,0 +1,195 @@
+<template>
+  <div class="upload-rate-stat-page">
+    <a-card :bordered="false">
+      <a-row :gutter="[12, 12]" align="middle">
+        <a-col :xs="24" :md="6">
+          <StationSelector v-model:value="query.stationCode" :options="stationOptions" :multiple="false" />
+        </a-col>
+        <a-col :xs="24" :md="5">
+          <a-date-picker
+            v-model:value="query.startTime"
+            class="full-control"
+            format="YYYY-MM-DD"
+            placeholder="起始时间"
+            value-format="YYYY-MM-DD"
+          />
+        </a-col>
+        <a-col :xs="24" :md="5">
+          <a-date-picker
+            v-model:value="query.endTime"
+            class="full-control"
+            format="YYYY-MM-DD"
+            placeholder="截止时间"
+            value-format="YYYY-MM-DD"
+          />
+        </a-col>
+        <a-col :xs="24" :md="8">
+          <a-space>
+            <a-button type="primary" :loading="loading" :disabled="!queryValid" @click="handleQuery">确认</a-button>
+            <a-button :loading="exporting" :disabled="!queryValid" @click="handleExport">导出</a-button>
+          </a-space>
+        </a-col>
+      </a-row>
+    </a-card>
+    <a-card :bordered="false" class="content-card">
+      <a-table
+        :columns="columns"
+        :data-source="rows"
+        :loading="loading"
+        :pagination="pagination"
+        :scroll="{x: 1760}"
+        row-key="date"
+        size="middle"
+        @change="handleTableChange"
+      />
+    </a-card>
+  </div>
+</template>
+
+<script setup name="UploadRateStat">
+import {computed, onMounted, reactive, ref} from 'vue';
+import {message} from 'ant-design-vue';
+import dayjs from 'dayjs';
+import {saveAs} from 'file-saver';
+import {blobValidate} from '@/utils/bearjia';
+import {listStation} from '@/api/config/station';
+import {exportUploadRate, listUploadRate} from '@/api/fileupload/reportLog';
+import StationSelector from '../components/StationSelector.vue';
+
+const loading = ref(false);
+const exporting = ref(false);
+const stationOptions = ref([]);
+const rows = ref([]);
+const pagination = reactive({
+  current: 1,
+  pageSize: 10,
+  total: 0,
+  showSizeChanger: true,
+  showTotal: (total) => `共 ${total} 天`
+});
+const query = reactive({
+  stationCode: undefined,
+  startTime: dayjs().subtract(7, 'day').format('YYYY-MM-DD'),
+  endTime: dayjs().format('YYYY-MM-DD')
+});
+
+const queryValid = computed(() => {
+  if (!query.startTime || !query.endTime) return false;
+  const start = dayjs(query.startTime);
+  const end = dayjs(query.endTime);
+  if (!start.isValid() || !end.isValid()) return false;
+  return !start.isAfter(end);
+});
+
+const columns = [
+  {title: '日期时间', dataIndex: 'date', width: 120},
+  {title: '超短期文件上传率(%)', dataIndex: 'supershortRate', width: 170},
+  {title: '短期文件上传率(%)', dataIndex: 'shortRate', width: 160},
+  {title: '中期文件上传率(%)', dataIndex: 'mediumRate', width: 160},
+  {title: '环境检测仪数据上传率(%)', dataIndex: 'envlDetectorRate', width: 190},
+  {title: '逆变器运行信息文件上传率(%)', dataIndex: 'inverterOperationRate', width: 210},
+  {title: '升压变数据上传率(%)', dataIndex: 'boosterTransformerRate', width: 180},
+  {title: '理论功率文件上传率(%)', dataIndex: 'theoreticalPowerRate', width: 190},
+  {title: '数值天气预报文件上传率(%)', dataIndex: 'numericalWeatherRate', width: 200},
+  {title: '发电计划文件上传率(%)', dataIndex: 'powerGenerationPlanRate', width: 180}
+];
+
+const loadStationOptions = async () => {
+  const res = await listStation({pageNum: 1, pageSize: 500});
+  const list = (res && res.rows) || [];
+  stationOptions.value = list.map((item) => ({label: item.stationName, value: item.stationCode}));
+  if (!query.stationCode && stationOptions.value.length) {
+    query.stationCode = stationOptions.value[0].value;
+  }
+};
+
+const fetchList = async (pageNum, pageSize) => {
+  if (!query.stationCode) {
+    message.warning('请先选择场站');
+    return;
+  }
+  loading.value = true;
+  try {
+    const res = await listUploadRate({
+      stationCode: query.stationCode,
+      startTime: query.startTime,
+      endTime: query.endTime,
+      pageNum,
+      pageSize
+    });
+    rows.value = res.rows || [];
+    pagination.total = res.total || 0;
+    pagination.current = pageNum;
+    pagination.pageSize = pageSize;
+  } catch (e) {
+    // request.js 已统一提示错误
+  } finally {
+    loading.value = false;
+  }
+};
+
+const handleQuery = () => {
+  if (!query.stationCode) {
+    message.warning('请先选择场站');
+    return;
+  }
+  if (!queryValid.value) return;
+  fetchList(1, pagination.pageSize);
+};
+
+const handleTableChange = (pag) => {
+  fetchList(pag.current, pag.pageSize);
+};
+
+const handleExport = async () => {
+  if (!query.stationCode) {
+    message.warning('请先选择场站');
+    return;
+  }
+  if (!queryValid.value) return;
+  exporting.value = true;
+  try {
+    const res = await exportUploadRate({
+      stationCode: query.stationCode,
+      startTime: query.startTime,
+      endTime: query.endTime
+    });
+    const isFile = await blobValidate(res);
+    if (isFile) {
+      const dateRange = query.startTime === query.endTime ? query.startTime : `${query.startTime}_${query.endTime}`;
+      saveAs(new Blob([res]), `光伏上传率统计_${dateRange.replaceAll('-', '')}.xlsx`);
+      message.success('导出成功');
+    } else {
+      const text = await res.text();
+      let msg = '导出失败';
+      try {
+        msg = JSON.parse(text)?.msg || msg;
+      } catch (e) {
+        /* ignore */
+      }
+      message.error(msg);
+    }
+  } finally {
+    exporting.value = false;
+  }
+};
+
+onMounted(async () => {
+  await loadStationOptions();
+  handleQuery();
+});
+</script>
+
+<style lang="less" scoped>
+.upload-rate-stat-page {
+  padding: 16px;
+}
+
+.full-control {
+  width: 100%;
+}
+
+.content-card {
+  margin-top: 16px;
+}
+</style>

+ 2 - 2
forecast-backend-vue/src/views/ufo/workbench/WorkbenchPage.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="workbench-page">
     <section class="summary-grid">
-      <div v-for="card in summaryCards" :key="card.key" class="summary-card">
+      <div v-for="(card, cardIndex) in summaryCards" :key="card.key" class="summary-card" :style="{ display: cardIndex < 2 ? 'none' : undefined }">
         <div class="summary-card__label">
           <img
             v-if="card.iconType === 'image'"
@@ -832,7 +832,7 @@ watch(mapStations, () => renderStations(), { deep: true });
 // 3 个 summary-card 最小宽度 480px + 2 个 14px 间隙 = 1468px;放得下时 3 列,放不下时单列 3 行
 @container (min-width: 1468px) {
   .summary-grid {
-    grid-template-columns: repeat(3, minmax(480px, 1fr));
+    grid-template-columns: repeat(1, minmax(480px, 1fr));
   }
 }
 

+ 3 - 0
release_note/20261028/ddl.sql

@@ -0,0 +1,3 @@
+-- 增加告警类别字段(幂等)
+ALTER TABLE alarm_event_record ADD COLUMN IF NOT EXISTS alarm_category VARCHAR(50);
+COMMENT ON COLUMN alarm_event_record.alarm_category IS '告警类别';

+ 81 - 0
release_note/20261028/dml.sql

@@ -0,0 +1,81 @@
+-- 删除风场
+UPDATE "public"."config_station" set is_del = 'true' where station_code in ('BHWP01', 'HHWP02');
+
+-- 字典类型: 告警类别
+INSERT INTO sys_dict_type (dict_id, dict_name, dict_type, status, create_by, create_time, remark)
+SELECT
+nextval(pg_get_serial_sequence('sys_dict_type', 'dict_id')),
+'告警类别', 'alarm_category', '0', 'admin', now(), '事件查询-告警类别'
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type t WHERE t.dict_type = 'alarm_category');
+-- 字典类型: 告警等级
+INSERT INTO sys_dict_type (dict_id, dict_name, dict_type, status, create_by, create_time, remark)
+SELECT
+nextval(pg_get_serial_sequence('sys_dict_type', 'dict_id')),
+'告警等级', 'alarm_level', '0', 'admin', now(), '事件查询-告警等级'
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type t WHERE t.dict_type = 'alarm_level');
+-- 告警类别字典数据(先删后插,保证幂等)
+DELETE FROM sys_dict_data WHERE dict_type = 'alarm_category';
+
+INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
+VALUES
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 1, '通信异常', '1', 'alarm_category', '', '', 'N', '0', 'admin', now(), '事件查询-告警类别'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 2, '数据异常', '2', 'alarm_category', '', '', 'N', '0', 'admin', now(), '事件查询-告警类别'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 3, '气象预报异常', '3', 'alarm_category', '', '', 'N', '0', 'admin', now(), '事件查询-告警类别'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 4, '功率预测异常', '4', 'alarm_category', '', '', 'N', '0', 'admin', now(), '事件查询-告警类别'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 5, '数据上报异常', '5', 'alarm_category', '', '', 'N', '0', 'admin', now(), '事件查询-告警类别'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 6, '系统运行异常', '6', 'alarm_category', '', '', 'N', '0', 'admin', now(), '事件查询-告警类别');
+-- 告警等级字典数据(颜色值配置在 css_class,页面按等级值渲染颜色;先删后插,保证幂等)
+DELETE FROM sys_dict_data WHERE dict_type = 'alarm_level';
+
+INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
+VALUES
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 1, '紧急告警', '1', 'alarm_level', 'red', '', 'N', '0', 'admin', now(), '事件查询-告警等级'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 2, '重要告警', '2', 'alarm_level', 'orange', '', 'N', '0', 'admin', now(), '事件查询-告警等级'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 3, '一般告警', '3', 'alarm_level', 'gold', '', 'N', '0', 'admin', now(), '事件查询-告警等级'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 4, '提示信息', '4', 'alarm_level', 'blue', '', 'N', '0', 'admin', now(), '事件查询-告警等级');
+-- 增加 事件查询 二级菜单(挂在 统计分析 下)
+INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT
+(SELECT COALESCE(MAX(menu_id), 0) + 1 FROM sys_menu),
+'事件查询',
+(SELECT menu_id FROM sys_menu t WHERE t.menu_name = '统计分析' AND t.parent_id = 0),
+5, 'event-query', 'ufo/electricity/statanalysis/eventQuery', '', 'EventQuery', 1, 0, 'C', '0', '0', 'alarm:eventRecord:list', 'AlertOutlined', 'admin', now(), '', NULL, '事件查询页面'
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu t WHERE t.menu_name = '事件查询');
+
+-- 增加 数据补传 二级菜单(挂在 功率预测 下)
+INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT
+(SELECT COALESCE(MAX(menu_id), 0) + 1 FROM sys_menu),
+'数据补传',
+(SELECT menu_id FROM sys_menu t WHERE t.menu_name = '功率预测'),
+35, 'data-resend', 'ufo/electricity/forecast/DataResend', '', 'DataResend', 1, 0, 'C', '0', '0', 'fileupload:reportLog:reUpload', 'CloudUploadOutlined', 'admin', now(), '', NULL, '数据补传页面'
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu t WHERE t.menu_name = '数据补传');
+
+-- 字典类型: 每日文件上传配置
+INSERT INTO sys_dict_type (dict_id, dict_name, dict_type, status, create_by, create_time, remark)
+SELECT
+nextval(pg_get_serial_sequence('sys_dict_type', 'dict_id')),
+'每日文件上传配置', 'daily_file_upload_config', '0', 'admin', now(), '光伏上传率统计-每日文件上传配置'
+WHERE NOT EXISTS (SELECT 1 FROM sys_dict_type t WHERE t.dict_type = 'daily_file_upload_config');
+-- 字典数据(先删后插,保证幂等)
+DELETE FROM sys_dict_data WHERE dict_type = 'daily_file_upload_config';
+
+INSERT INTO sys_dict_data (dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark)
+VALUES
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 1, '超短期文件', '{"type":"supershort","num":16}', 'daily_file_upload_config', '', '', 'N', '0', 'admin', now(), 'num=每日期望上传数'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 2, '短期文件', '{"type":"short","num":1}', 'daily_file_upload_config', '', '', 'N', '0', 'admin', now(), 'num=每日期望上传数'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 3, '中期文件', '{"type":"medium","num":1}', 'daily_file_upload_config', '', '', 'N', '0', 'admin', now(), 'num=每日期望上传数'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 4, '环境检测仪数据', '{"type":"envlDetec","num":1}', 'daily_file_upload_config', '', '', 'N', '0', 'admin', now(), 'num=每日期望上传数'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 5, '逆变器运行信息文件', '{"type":"ivtOpr","num":1}', 'daily_file_upload_config', '', '', 'N', '0', 'admin', now(), 'num=每日期望上传数'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 6, '升压变数据', '{"type":"bstTrn","num":1}', 'daily_file_upload_config', '', '', 'N', '0', 'admin', now(), 'num=每日期望上传数'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 7, '理论功率文件', '{"type":"thrPower","num":1}', 'daily_file_upload_config', '', '', 'N', '0', 'admin', now(), 'num=每日期望上传数'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 8, '数值天气预报文件', '{"type":"nmrWeather","num":1}', 'daily_file_upload_config', '', '', 'N', '0', 'admin', now(), 'num=每日期望上传数'),
+(nextval(pg_get_serial_sequence('sys_dict_data', 'dict_code')), 9, '发电计划文件', '{"type":"pwrGen","num":1}', 'daily_file_upload_config', '', '', 'N', '0', 'admin', now(), 'num=每日期望上传数');
+-- 增加 光伏上传率统计 二级菜单(挂在 统计分析 下)
+INSERT INTO sys_menu (menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
+SELECT
+(SELECT COALESCE(MAX(menu_id), 0) + 1 FROM sys_menu),
+'光伏上传率统计',
+(SELECT menu_id FROM sys_menu t WHERE t.menu_name = '统计分析' AND t.parent_id = 0),
+15, 'upload-rate-stat', 'ufo/electricity/statanalysis/uploadRateStat', '', 'UploadRateStat', 1, 0, 'C', '0', '0', 'fileupload:reportLog:uploadRate', 'BarChartOutlined', 'admin', now(), '', NULL, '光伏上传率统计页面'
+WHERE NOT EXISTS (SELECT 1 FROM sys_menu t WHERE t.menu_name = '光伏上传率统计');

+ 15 - 0
release_note/20261028/release_note.md

@@ -0,0 +1,15 @@
+# 20261028版本更新说明
+1. 增加功能“统计分析-事件查询”
+2. 增加功能“功率预测-数据补传”
+3. 增加功能“统计分析-光伏上传率统计”
+4. 修复了设备管理大菜单下多页面的bug
+
+[更新说明]
+1. 执行 `release_note/20261028/ddl.sql`
+2. 执行 `release_note/20261028/dml.sql`
+2. 更新服务端jar
+3. 更新前端dist
+
+[备注]
+数据补传目前直接更新了文件上传数据表,并未集成真正的文件补传功能
+每日应该上传的文件类型配置项`daily_file_upload_config`根据实际情况调整