Browse Source

电量核算代码提交

fuyuchuan 3 days ago
parent
commit
024965afcc

+ 23 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/controller/web/SettlementController.java

@@ -7,6 +7,8 @@ import com.usky.vpp.service.VppSettlementService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import javax.servlet.http.HttpServletResponse;
+
 /**
  * 虚拟电厂 - Settlement 接口
  * 网关前缀: /prod-api/service-vpp
@@ -38,6 +40,27 @@ public class SettlementController {
         return ApiResult.success(vppSettlementService.pageReading(id, customerName, calcCycle, current, size));
     }
 
+    /**
+     * 导出月度电量核算数据(Excel)
+     * <ul>
+     *   <li>scope = "current":导出当前页,需传 current + size</li>
+     *   <li>scope = "all":导出当前租户全部未删除月度电量核算记录</li>
+     *   <li>scope = "filtered":导出筛选结果,需至少传一个筛选条件</li>
+     * </ul>
+     */
+    @GetMapping(value = "/reading/export")
+    public void exportReading(
+            @RequestParam("scope") String scope,
+            @RequestParam("fileName") String fileName,
+            @RequestParam(value = "id", required = false) Long id,
+            @RequestParam(value = "customerName", required = false) String customerName,
+            @RequestParam(value = "calcCycle", required = false) String calcCycle,
+            @RequestParam(value = "current", required = false) Integer current,
+            @RequestParam(value = "size", required = false) Integer size,
+            HttpServletResponse response) {
+        vppSettlementService.exportReading(scope, fileName, id, customerName, calcCycle, current, size, response);
+    }
+
     @PostMapping(value = "/reading/calculate")
     public ApiResult<Void> calculateReading(@RequestBody(required = false) Object body) {
         return ApiResult.success();

+ 1 - 1
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/domain/VppCustomer.java

@@ -32,7 +32,7 @@ public class VppCustomer implements Serializable {
     private String customerName;
 
     /**
-     * 1中高压 2低压商用 3居民充电桩 4自有资产
+     * 客户管理1.虚拟电厂2.售电客户
      */
     @TableField("customer_type")
     private Integer customerType;

+ 4 - 0
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/domain/VppEnergyReadingMonthly.java

@@ -31,6 +31,10 @@ public class VppEnergyReadingMonthly implements Serializable {
 
     @TableField("resource_id")
     private Long resourceId;
+
+    @TableField(exist = false)
+    private String resourceName;
+
     @TableField("settle_year")
     private String settleYear;
     @TableField("settle_month")

+ 1 - 1
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/job/VppUnPollScheduler.java

@@ -22,7 +22,7 @@ public class VppUnPollScheduler {
     @Autowired
     private VppUnIntegrationService integrationService;
 
-    //@Scheduled(fixedDelayString = "#{${vpp.un.poll-interval-sec:10} * 1000}")
+    @Scheduled(fixedDelayString = "#{${vpp.un.poll-interval-sec:10} * 1000}")
     public void pollUn() {
         if (!properties.isPollActive()) {
             return;

+ 17 - 1
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/VppSettlementService.java

@@ -3,6 +3,7 @@ package com.usky.vpp.service;
 import com.usky.common.core.bean.CommonPage;
 import com.usky.vpp.domain.VppEnergyReadingMonthly;
 
+import javax.servlet.http.HttpServletResponse;
 import java.util.Map;
 
 /**
@@ -14,5 +15,20 @@ public interface VppSettlementService {
         return null;
     }
 
-    CommonPage<VppEnergyReadingMonthly> pageReading(Long id, String customerName, String calcCycle, Integer current, Integer size);
+    CommonPage<VppEnergyReadingMonthly> pageReading(Long id, String customerName, String calcCycle,
+                                                    Integer current, Integer size);
+
+    /**
+     * 导出月度电量核算数据(Excel)
+     *
+     * @param scope        导出范围:current / all / filtered
+     * @param fileName     文件名(不含扩展名)
+     * @param id           客户ID精确匹配(filtered 模式可用)
+     * @param customerName 客户名称模糊匹配(filtered 模式可用)
+     * @param calcCycle    核算周期,格式 yyyy-MM(filtered 模式可用)
+     * @param current      当前页码(current 模式必传)
+     * @param size         页大小(current 模式必传)
+     */
+    void exportReading(String scope, String fileName, Long id, String customerName, String calcCycle,
+                       Integer current, Integer size, HttpServletResponse response);
 }

+ 4 - 4
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppCustomerServiceImpl.java

@@ -398,9 +398,9 @@ public class VppCustomerServiceImpl implements VppCustomerService {
     @Transactional(rollbackFor = Exception.class)
     public Boolean createCustomer(CustomerRequestVO vo) {
         // 必填校验
-        if (StringUtils.isBlank(vo.getAccountNo())) {
-            throw new BusinessException("电力户号不能为空!");
-        }
+        // if (StringUtils.isBlank(vo.getAccountNo())) {
+        //     throw new BusinessException("电力户号不能为空!");
+        // }
         if (StringUtils.isBlank(vo.getCustomerName())) {
             throw new BusinessException("客户名称不能为空!");
         }
@@ -836,7 +836,7 @@ public class VppCustomerServiceImpl implements VppCustomerService {
                 setCell(row, 0, rowIdx - 1);                       // 序号
                 setCell(row, 1, c.getAccountNo());
                 setCell(row, 2, c.getCustomerName());
-                setCell(row, 3, typeLabel(c.getCustomerType(), "中高压", "低压商用", "居民充电桩", "自有资产"));
+                setCell(row, 3, typeLabel(c.getCustomerType(), "虚拟电厂", "售电客户"));
                 setCell(row, 4, c.getPowerCompany());
                 setCell(row, 5, numberStr(c.getContractCapacity()));
                 setCell(row, 6, typeLabel(c.getCreditStatus(), "正常", "关注", "不良"));

+ 243 - 29
service-vpp/service-vpp-biz/src/main/java/com/usky/vpp/service/impl/VppSettlementServiceImpl.java

@@ -8,21 +8,29 @@ import com.usky.common.core.exception.BusinessException;
 import com.usky.common.security.utils.SecurityUtils;
 import com.usky.vpp.domain.VppCustomer;
 import com.usky.vpp.domain.VppEnergyReadingMonthly;
+import com.usky.vpp.domain.VppResourcePoint;
 import com.usky.vpp.mapper.VppCustomerMapper;
 import com.usky.vpp.mapper.VppEnergyReadingMonthlyMapper;
+import com.usky.vpp.mapper.VppResourcePointMapper;
 import com.usky.vpp.service.VppSettlementService;
 import com.usky.vpp.util.VppAuditHelper;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
- * VppSettlementService 默认实现(待按业务完善)
+ * VppSettlementService 默认实现
  */
 @Service
 public class VppSettlementServiceImpl implements VppSettlementService {
@@ -31,52 +39,258 @@ public class VppSettlementServiceImpl implements VppSettlementService {
     private VppCustomerMapper vppCustomerMapper;
     @Autowired
     private VppEnergyReadingMonthlyMapper energyReadingMonthlyMapper;
-
+    @Autowired
+    private VppResourcePointMapper resourcePointMapper;
 
     @Override
-    public CommonPage<VppEnergyReadingMonthly> pageReading(Long id, String customerName, String calcCycle, Integer current, Integer size) {
+    public CommonPage<VppEnergyReadingMonthly> pageReading(Long id, String customerName, String calcCycle,
+                                                           Integer current, Integer size) {
         IPage<VppEnergyReadingMonthly> page = new Page<>(current, size);
-        Integer tenantId = SecurityUtils.getTenantId();
+        LambdaQueryWrapper<VppEnergyReadingMonthly> wrapper = buildReadingQueryWrapper(id, customerName, calcCycle);
+        if (wrapper == null) {
+            return new CommonPage<>(Collections.emptyList(), 0L, size, current);
+        }
+        page = energyReadingMonthlyMapper.selectPage(page, wrapper);
+        List<VppEnergyReadingMonthly> records = page.getRecords();
+        fillCustomerNames(records);
+        fillResourceNames(records);
+        return new CommonPage<>(records, page.getTotal(), size, current);
+    }
+
+    @Override
+    public void exportReading(String scope, String fileName, Long id, String customerName, String calcCycle,
+                              Integer current, Integer size, HttpServletResponse response) {
+        List<VppEnergyReadingMonthly> readingList;
+
+        switch (scope) {
+            case "current":
+                if (current == null || size == null) {
+                    throw new BusinessException("current 模式下分页参数 current 和 size 不能为空!");
+                }
+                IPage<VppEnergyReadingMonthly> page = new Page<>(current, size);
+                LambdaQueryWrapper<VppEnergyReadingMonthly> currWrapper = baseReadingQueryWrapper();
+                currWrapper.orderByDesc(VppEnergyReadingMonthly::getSettleYear)
+                        .orderByDesc(VppEnergyReadingMonthly::getSettleMonth);
+                page = energyReadingMonthlyMapper.selectPage(page, currWrapper);
+                readingList = page.getRecords();
+                break;
 
-        LambdaQueryWrapper<VppCustomer> customerQueryWrapper = new LambdaQueryWrapper<VppCustomer>();
-        customerQueryWrapper.eq(VppCustomer::getTenantId, tenantId)
+            case "all":
+                LambdaQueryWrapper<VppEnergyReadingMonthly> allWrapper = baseReadingQueryWrapper();
+                allWrapper.orderByDesc(VppEnergyReadingMonthly::getSettleYear)
+                        .orderByDesc(VppEnergyReadingMonthly::getSettleMonth);
+                readingList = energyReadingMonthlyMapper.selectList(allWrapper);
+                break;
+
+            case "filtered":
+                boolean hasFilter = id != null || StringUtils.isNotBlank(customerName)
+                        || StringUtils.isNotBlank(calcCycle);
+                if (!hasFilter) {
+                    throw new BusinessException("filtered 模式下请至少提供一个筛选条件(id / customerName / calcCycle)!");
+                }
+                LambdaQueryWrapper<VppEnergyReadingMonthly> filteredWrapper = buildReadingQueryWrapper(
+                        id, customerName, calcCycle);
+                if (filteredWrapper == null) {
+                    readingList = Collections.emptyList();
+                    break;
+                }
+                readingList = energyReadingMonthlyMapper.selectList(filteredWrapper);
+                break;
+
+            default:
+                throw new BusinessException("不支持的导出范围:" + scope + ",请使用 current / all / filtered");
+        }
+
+        if (readingList == null) {
+            readingList = Collections.emptyList();
+        }
+        fillCustomerNames(readingList);
+        fillResourceNames(readingList);
+        writeReadingExcel(fileName, readingList, response);
+    }
+
+    private LambdaQueryWrapper<VppEnergyReadingMonthly> baseReadingQueryWrapper() {
+        return new LambdaQueryWrapper<VppEnergyReadingMonthly>()
+                .eq(VppEnergyReadingMonthly::getTenantId, SecurityUtils.getTenantId())
+                .eq(VppEnergyReadingMonthly::getDeleteFlag, VppAuditHelper.NOT_DELETED);
+    }
+
+    private LambdaQueryWrapper<VppEnergyReadingMonthly> buildReadingQueryWrapper(Long customerId,
+                                                                                 String customerName,
+                                                                                 String calcCycle) {
+        Map<Long, String> customerNameMap = resolveCustomerNameMap(customerName);
+        if (StringUtils.isNotBlank(customerName) && customerNameMap.isEmpty()) {
+            return null;
+        }
+
+        Map<String, Object> cycleMap = null;
+        if (StringUtils.isNotBlank(calcCycle)) {
+            cycleMap = resolveCalcCycle(calcCycle);
+        }
+
+        return baseReadingQueryWrapper()
+                .eq(customerId != null, VppEnergyReadingMonthly::getCustomerId, customerId)
+                .in(StringUtils.isNotBlank(customerName), VppEnergyReadingMonthly::getCustomerId, customerNameMap.keySet())
+                .eq(cycleMap != null, VppEnergyReadingMonthly::getSettleYear,
+                        cycleMap != null ? cycleMap.get("settleYear") : null)
+                .eq(cycleMap != null, VppEnergyReadingMonthly::getSettleMonth,
+                        cycleMap != null ? cycleMap.get("settleMonth") : null)
+                .orderByDesc(VppEnergyReadingMonthly::getSettleYear)
+                .orderByDesc(VppEnergyReadingMonthly::getSettleMonth);
+    }
+
+    private Map<Long, String> resolveCustomerNameMap(String customerName) {
+        LambdaQueryWrapper<VppCustomer> customerQueryWrapper = new LambdaQueryWrapper<VppCustomer>()
+                .eq(VppCustomer::getTenantId, SecurityUtils.getTenantId())
                 .eq(VppCustomer::getDeleteFlag, VppAuditHelper.NOT_DELETED)
                 .like(StringUtils.isNotBlank(customerName), VppCustomer::getCustomerName, customerName);
-        Map<Long, String> customerNameMap = vppCustomerMapper.selectList(customerQueryWrapper)
+        return vppCustomerMapper.selectList(customerQueryWrapper)
                 .stream()
-                .collect(Collectors.toMap(VppCustomer::getId, VppCustomer::getCustomerName));
-
-        LambdaQueryWrapper<VppEnergyReadingMonthly> energyReadingQueryWrapper = new LambdaQueryWrapper<VppEnergyReadingMonthly>();
-        energyReadingQueryWrapper.eq(VppEnergyReadingMonthly::getTenantId, tenantId)
-                .eq(VppEnergyReadingMonthly::getDeleteFlag, VppAuditHelper.NOT_DELETED)
-                .eq(id != null, VppEnergyReadingMonthly::getCustomerId, id)
-                .eq(StringUtils.isNotBlank(calcCycle), VppEnergyReadingMonthly::getSettleYear, resolveCalcCycle(calcCycle).get("settleYear"))
-                .eq(StringUtils.isNotBlank(calcCycle), VppEnergyReadingMonthly::getSettleMonth, resolveCalcCycle(calcCycle).get("settleMonth"))
-                .orderByDesc(VppEnergyReadingMonthly::getSettleYear);
-        page = energyReadingMonthlyMapper.selectPage(page, energyReadingQueryWrapper);
-
-        List<VppEnergyReadingMonthly> energyReadingMonthlyList = page.getRecords();
-        energyReadingMonthlyList.forEach(energyReadingMonthly -> {
-            energyReadingMonthly.setCustomerName(customerNameMap.get(energyReadingMonthly.getCustomerId()));
-        });
+                .collect(Collectors.toMap(VppCustomer::getId, VppCustomer::getCustomerName, (a, b) -> a));
+    }
 
-        return new CommonPage<>(energyReadingMonthlyList, page.getTotal(), size, current);
+    private void fillCustomerNames(List<VppEnergyReadingMonthly> records) {
+        if (records == null || records.isEmpty()) {
+            return;
+        }
+        Set<Long> customerIds = records.stream()
+                .map(VppEnergyReadingMonthly::getCustomerId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        if (customerIds.isEmpty()) {
+            return;
+        }
+        Map<Long, String> customerNameMap = vppCustomerMapper.selectBatchIds(customerIds).stream()
+                .collect(Collectors.toMap(VppCustomer::getId, VppCustomer::getCustomerName, (a, b) -> a));
+        records.forEach(reading -> reading.setCustomerName(customerNameMap.get(reading.getCustomerId())));
+    }
+
+    private void fillResourceNames(List<VppEnergyReadingMonthly> records) {
+        if (records == null || records.isEmpty()) {
+            return;
+        }
+        Set<Long> resourceIds = records.stream()
+                .map(VppEnergyReadingMonthly::getResourceId)
+                .filter(Objects::nonNull)
+                .collect(Collectors.toSet());
+        Map<Long, String> resourceNameMap = resourceIds.isEmpty()
+                ? Collections.emptyMap()
+                : resourcePointMapper.selectBatchIds(resourceIds).stream()
+                        .collect(Collectors.toMap(VppResourcePoint::getId, VppResourcePoint::getResourceName, (a, b) -> a));
+        records.forEach(reading -> {
+            if (reading.getResourceId() == null) {
+                reading.setResourceName("户号级");
+            } else {
+                reading.setResourceName(resourceNameMap.get(reading.getResourceId()));
+            }
+        });
     }
 
-    /**
-     * 解析核算周期,年-月
-     * @param calcCycle
-     * @return
-     */
     private Map<String, Object> resolveCalcCycle(String calcCycle) {
         Map<String, Object> calcCycleMap = new HashMap<>();
         String[] split = calcCycle.split("-");
         try {
             calcCycleMap.put("settleYear", split[0]);
-            calcCycleMap.put("settleMonth", split[1]);
+            calcCycleMap.put("settleMonth", Integer.valueOf(split[1]));
         } catch (Exception e) {
             throw new BusinessException("解析核算周期失败!请核对参数!");
         }
         return calcCycleMap;
     }
+
+    private void writeReadingExcel(String fileName, List<VppEnergyReadingMonthly> readingList,
+                                   HttpServletResponse response) {
+        try (org.apache.poi.xssf.usermodel.XSSFWorkbook workbook = new org.apache.poi.xssf.usermodel.XSSFWorkbook()) {
+            org.apache.poi.xssf.usermodel.XSSFSheet sheet = workbook.createSheet("月度电量核算");
+
+            org.apache.poi.xssf.usermodel.XSSFCellStyle headerStyle = workbook.createCellStyle();
+            headerStyle.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.GREY_25_PERCENT.getIndex());
+            headerStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
+            org.apache.poi.xssf.usermodel.XSSFFont headerFont = workbook.createFont();
+            headerFont.setBold(true);
+            headerStyle.setFont(headerFont);
+
+            String[] headers = {
+                    "序号", "客户名称", "资源点", "结算年", "结算月",
+                    "总用电量(kWh)", "峰电量(kWh)", "平电量(kWh)", "谷电量(kWh)", "尖电量(kWh)",
+                    "发电量(kWh)", "核算状态", "核算时间", "创建时间"
+            };
+            org.apache.poi.xssf.usermodel.XSSFRow headerRow = sheet.createRow(0);
+            for (int i = 0; i < headers.length; i++) {
+                org.apache.poi.xssf.usermodel.XSSFCell cell = headerRow.createCell(i);
+                cell.setCellValue(headers[i]);
+                cell.setCellStyle(headerStyle);
+            }
+
+            int rowIdx = 1;
+            for (VppEnergyReadingMonthly reading : readingList) {
+                org.apache.poi.xssf.usermodel.XSSFRow row = sheet.createRow(rowIdx++);
+                setCell(row, 0, rowIdx - 1);
+                setCell(row, 1, reading.getCustomerName());
+                setCell(row, 2, reading.getResourceName());
+                setCell(row, 3, reading.getSettleYear());
+                setCell(row, 4, reading.getSettleMonth());
+                setCell(row, 5, numberStr(reading.getTotalEnergyKwh()));
+                setCell(row, 6, numberStr(reading.getPeakEnergyKwh()));
+                setCell(row, 7, numberStr(reading.getFlatEnergyKwh()));
+                setCell(row, 8, numberStr(reading.getValleyEnergyKwh()));
+                setCell(row, 9, numberStr(reading.getSharpEnergyKwh()));
+                setCell(row, 10, numberStr(reading.getGenEnergyKwh()));
+                setCell(row, 11, calcStatusLabel(reading.getCalcStatus()));
+                setCell(row, 12, dateTimeStr(reading.getCalcAt()));
+                setCell(row, 13, dateTimeStr(reading.getCreateTime()));
+            }
+
+            for (int i = 0; i < headers.length; i++) {
+                sheet.autoSizeColumn(i);
+                int width = sheet.getColumnWidth(i);
+                if (width > 15000) {
+                    sheet.setColumnWidth(i, 15000);
+                }
+            }
+
+            String encodedFileName = java.net.URLEncoder.encode(fileName + ".xlsx", StandardCharsets.UTF_8.name())
+                    .replace("+", "%20");
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
+            response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName
+                    + "; filename*=UTF-8''" + encodedFileName);
+            workbook.write(response.getOutputStream());
+            response.getOutputStream().flush();
+        } catch (IOException e) {
+            throw new BusinessException("导出月度电量核算数据失败: " + e.getMessage());
+        }
+    }
+
+    private static void setCell(org.apache.poi.xssf.usermodel.XSSFRow row, int col, Object value) {
+        if (value != null) {
+            row.createCell(col).setCellValue(String.valueOf(value));
+        } else {
+            row.createCell(col).setCellValue("");
+        }
+    }
+
+    private static String numberStr(Object value) {
+        return value == null ? "" : value.toString();
+    }
+
+    private static String dateTimeStr(Object value) {
+        return value == null ? "" : value.toString();
+    }
+
+    private static String calcStatusLabel(Integer calcStatus) {
+        if (calcStatus == null) {
+            return "";
+        }
+        switch (calcStatus) {
+            case 0:
+                return "待核算";
+            case 1:
+                return "已核算";
+            case 2:
+                return "异常";
+            default:
+                return String.valueOf(calcStatus);
+        }
+    }
 }