diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/bill/EnergyBillController.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/bill/EnergyBillController.java index 48a2545..29a7cf9 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/bill/EnergyBillController.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/bill/EnergyBillController.java @@ -21,6 +21,7 @@ import org.springframework.web.bind.annotation.*; import java.io.IOException; import java.util.List; +import java.util.Map; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -74,6 +75,13 @@ public class EnergyBillController { return success(billService.batchGenerateBills(reqVOs)); } + @PostMapping("/batch-generate-by-period") + @Operation(summary = "按月批量生成账单") + @PreAuthorize("@ss.hasPermission('energy:bill:create')") + public CommonResult> batchGenerateByPeriod(@RequestParam("billPeriod") String billPeriod) { + return success(billService.batchGenerateByPeriod(billPeriod)); + } + @PutMapping("/update") @Operation(summary = "更新账单") @PreAuthorize("@ss.hasPermission('energy:bill:update')") diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/record/HydrogenRecordController.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/record/HydrogenRecordController.java index 58f591b..6064bfc 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/record/HydrogenRecordController.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/record/HydrogenRecordController.java @@ -112,6 +112,13 @@ public class HydrogenRecordController { return success(true); } + @PostMapping("/batch-match") + @Operation(summary = "批量重新匹配") + @PreAuthorize("@ss.hasPermission('energy:hydrogen-record:update')") + public CommonResult> batchMatch() { + return success(hydrogenRecordService.batchMatch()); + } + private String generateBatchNo() { return "IMP" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")) + UUID.randomUUID().toString().substring(0, 4).toUpperCase(); diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/dal/mysql/detail/EnergyHydrogenDetailMapper.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/dal/mysql/detail/EnergyHydrogenDetailMapper.java index 001a5fd..2f22341 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/dal/mysql/detail/EnergyHydrogenDetailMapper.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/dal/mysql/detail/EnergyHydrogenDetailMapper.java @@ -43,4 +43,11 @@ public interface EnergyHydrogenDetailMapper extends BaseMapperX selectUnbilledByPeriod(LocalDate start, LocalDate end) { + return selectList(new LambdaQueryWrapperX() + .eq(EnergyHydrogenDetailDO::getAuditStatus, 1) // APPROVED + .isNull(EnergyHydrogenDetailDO::getBillId) + .between(EnergyHydrogenDetailDO::getHydrogenDate, start, end)); + } + } diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/bill/EnergyBillService.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/bill/EnergyBillService.java index 68a5c2c..7a8118c 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/bill/EnergyBillService.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/bill/EnergyBillService.java @@ -5,10 +5,12 @@ import cn.iocoder.yudao.module.energy.controller.admin.bill.vo.*; import cn.iocoder.yudao.module.energy.controller.admin.detail.vo.HydrogenDetailRespVO; import cn.iocoder.yudao.module.energy.dal.dataobject.bill.EnergyBillDO; import java.util.List; +import java.util.Map; public interface EnergyBillService { Long generateBill(EnergyBillGenerateReqVO reqVO); List batchGenerateBills(List reqVOs); + Map batchGenerateByPeriod(String billPeriod); PageResult getBillPage(EnergyBillPageReqVO reqVO); EnergyBillDO getBill(Long id); void updateBill(EnergyBillSaveReqVO reqVO); diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/bill/EnergyBillServiceImpl.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/bill/EnergyBillServiceImpl.java index a9c57b4..33cdd04 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/bill/EnergyBillServiceImpl.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/bill/EnergyBillServiceImpl.java @@ -21,11 +21,16 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; +import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.energy.enums.ErrorCodeConstants.*; @@ -215,6 +220,46 @@ public class EnergyBillServiceImpl implements EnergyBillService { return EnergyBillConvert.INSTANCE.convertAdjustmentList(adjustments); } + @Override + @Transactional(rollbackFor = Exception.class) + public Map batchGenerateByPeriod(String billPeriod) { + YearMonth ym = YearMonth.parse(billPeriod); + LocalDate start = ym.atDay(1); + LocalDate end = ym.atEndOfMonth(); + + // Query all unbilled, approved details for this period + List allDetails = detailMapper.selectUnbilledByPeriod(start, end); + // Group by customerId + contractId + stationId + Map> grouped = allDetails.stream() + .collect(Collectors.groupingBy(d -> + d.getCustomerId() + "_" + d.getContractId() + "_" + d.getStationId())); + + int generatedCount = 0; + int skippedCount = 0; + for (Map.Entry> entry : grouped.entrySet()) { + List details = entry.getValue(); + EnergyHydrogenDetailDO first = details.get(0); + try { + EnergyBillGenerateReqVO reqVO = new EnergyBillGenerateReqVO(); + reqVO.setCustomerId(first.getCustomerId()); + reqVO.setContractId(first.getContractId()); + reqVO.setStationId(first.getStationId()); + reqVO.setBillPeriodStart(start); + reqVO.setBillPeriodEnd(end); + reqVO.setEnergyType(1); // HYDROGEN = 1 + generateBill(reqVO); + generatedCount++; + } catch (Exception e) { + log.warn("[batchGenerateByPeriod] Skipped group: {}", entry.getKey(), e); + skippedCount++; + } + } + Map result = new HashMap<>(); + result.put("generatedCount", generatedCount); + result.put("skippedCount", skippedCount); + return result; + } + private EnergyBillDO validateBillExists(Long id) { EnergyBillDO bill = billMapper.selectById(id); if (bill == null) { diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/record/HydrogenRecordService.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/record/HydrogenRecordService.java index 75a359f..a996dd1 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/record/HydrogenRecordService.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/record/HydrogenRecordService.java @@ -16,4 +16,5 @@ public interface HydrogenRecordService { PageResult getRecordPage(HydrogenRecordPageReqVO pageReqVO); Map importRecords(Long stationId, List list, String batchNo); void matchRecord(Long id, Long vehicleId, Long customerId); + Map batchMatch(); } diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/record/HydrogenRecordServiceImpl.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/record/HydrogenRecordServiceImpl.java index 2ee0328..f1bccba 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/record/HydrogenRecordServiceImpl.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/record/HydrogenRecordServiceImpl.java @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.energy.controller.admin.record.vo.HydrogenRecordP import cn.iocoder.yudao.module.energy.controller.admin.record.vo.HydrogenRecordSaveReqVO; import cn.iocoder.yudao.module.energy.convert.record.HydrogenRecordConvert; import cn.iocoder.yudao.module.energy.dal.dataobject.record.EnergyHydrogenRecordDO; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.energy.dal.mysql.record.EnergyHydrogenRecordMapper; import cn.iocoder.yudao.module.energy.enums.MatchStatusEnum; import cn.iocoder.yudao.module.energy.enums.SourceTypeEnum; @@ -119,6 +120,30 @@ public class HydrogenRecordServiceImpl implements HydrogenRecordService { eventPublisher.publishEvent(new RecordMatchedEvent(id, record.getStationId(), vehicleId, customerId, record.getPlateNumber())); } + @Override + @Transactional(rollbackFor = Exception.class) + public Map batchMatch() { + // Query all match-failed records (matchStatus = 2) + List failedRecords = hydrogenRecordMapper.selectList( + new LambdaQueryWrapperX() + .eq(EnergyHydrogenRecordDO::getMatchStatus, 2)); // MATCH_FAILED + int successCount = 0; + int failCount = 0; + for (EnergyHydrogenRecordDO record : failedRecords) { + try { + // TODO: Look up vehicle by plateNumber from asset module + // For now, mark all as still failed + failCount++; + } catch (Exception e) { + failCount++; + } + } + Map result = new HashMap<>(); + result.put("successCount", successCount); + result.put("failCount", failCount); + return result; + } + private EnergyHydrogenRecordDO validateRecordExists(Long id) { EnergyHydrogenRecordDO record = hydrogenRecordMapper.selectById(id); if (record == null) {