From b40f521c037aba80bb964eaf1d0747840a01ba3e Mon Sep 17 00:00:00 2001 From: kkfluous Date: Mon, 16 Mar 2026 13:22:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(energy):=20Energy=20=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端优化: - 创建加氢站表,删除 energy_station_config - 简化事件驱动(7个→3个) - 合并导入流程(自动匹配+生成明细) - 优化审核流程(审核+扣款合并+批量审核) - 修复跨模块依赖(创建 Asset API 接口层) 前端优化: - 简化导入交互(3步→1步) - 批量审核功能 - 快速生成账单(本月/上月) - 批量价格配置(前端完成) 技术改进: - 微服务架构规范(API 优先) - 事务一致性保证 - 用户体验优化 --- .../asset/api/contract/ContractApi.java | 19 +++ .../api/contract/dto/ContractRespDTO.java | 45 ++++++ .../asset/api/customer/CustomerApi.java | 19 +++ .../api/customer/dto/CustomerRespDTO.java | 39 +++++ .../asset/api/station/HydrogenStationApi.java | 19 +++ .../station/dto/HydrogenStationRespDTO.java | 39 +++++ .../module/asset/api/vehicle/VehicleApi.java | 19 +++ .../asset/api/vehicle/dto/VehicleRespDTO.java | 39 +++++ .../module/asset/api/ContractApiImpl.java | 37 +++++ .../module/asset/api/CustomerApiImpl.java | 32 ++++ .../asset/api/HydrogenStationApiImpl.java | 27 ++++ .../module/asset/api/VehicleApiImpl.java | 27 ++++ .../convert/contract/ContractConvert.java | 6 + .../convert/customer/CustomerConvert.java | 6 + .../station/HydrogenStationConvert.java | 6 + .../asset/convert/vehicle/VehicleConvert.java | 8 + yudao-module-energy/IMPLEMENTATION_SUMMARY.md | 140 ++++++++++++++++++ .../yudao-module-energy-server/pom.xml | 7 + .../detail/HydrogenDetailController.java | 13 +- .../vo/HydrogenDetailBatchAuditReqVO.java | 30 ++++ .../detail/EnergyHydrogenDetailDO.java | 11 ++ .../energy/listener/AccountEventListener.java | 33 ++--- .../energy/listener/DetailEventListener.java | 9 +- .../service/detail/HydrogenDetailService.java | 3 +- .../detail/HydrogenDetailServiceImpl.java | 84 +++++++++-- .../detail/dto/BatchAuditResultDTO.java | 33 +++++ .../match/HydrogenMatchServiceImpl.java | 38 ++--- 27 files changed, 722 insertions(+), 66 deletions(-) create mode 100644 yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/contract/ContractApi.java create mode 100644 yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/contract/dto/ContractRespDTO.java create mode 100644 yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/customer/CustomerApi.java create mode 100644 yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/customer/dto/CustomerRespDTO.java create mode 100644 yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/station/HydrogenStationApi.java create mode 100644 yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/station/dto/HydrogenStationRespDTO.java create mode 100644 yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/vehicle/VehicleApi.java create mode 100644 yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/vehicle/dto/VehicleRespDTO.java create mode 100644 yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/ContractApiImpl.java create mode 100644 yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/CustomerApiImpl.java create mode 100644 yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/HydrogenStationApiImpl.java create mode 100644 yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/VehicleApiImpl.java create mode 100644 yudao-module-energy/IMPLEMENTATION_SUMMARY.md create mode 100644 yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/detail/vo/HydrogenDetailBatchAuditReqVO.java create mode 100644 yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/detail/dto/BatchAuditResultDTO.java diff --git a/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/contract/ContractApi.java b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/contract/ContractApi.java new file mode 100644 index 0000000..af130c5 --- /dev/null +++ b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/contract/ContractApi.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.asset.api.contract; + +import cn.iocoder.yudao.module.asset.api.contract.dto.ContractRespDTO; + +/** + * 合同 API 接口 + * + * @author 芋道源码 + */ +public interface ContractApi { + + /** + * 查询客户的生效中合同 + * + * @param customerId 客户ID + * @return 合同信息 + */ + ContractRespDTO getActiveByCustomerId(Long customerId); +} diff --git a/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/contract/dto/ContractRespDTO.java b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/contract/dto/ContractRespDTO.java new file mode 100644 index 0000000..97dd28f --- /dev/null +++ b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/contract/dto/ContractRespDTO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.asset.api.contract.dto; + +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDateTime; + +/** + * 合同响应 DTO + * + * @author 芋道源码 + */ +@Data +public class ContractRespDTO implements Serializable { + + /** + * 合同ID + */ + private Long id; + + /** + * 合同编号 + */ + private String contractNo; + + /** + * 客户ID + */ + private Long customerId; + + /** + * 合同状态 + */ + private Integer contractStatus; + + /** + * 开始日期 + */ + private LocalDateTime startDate; + + /** + * 结束日期 + */ + private LocalDateTime endDate; +} diff --git a/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/customer/CustomerApi.java b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/customer/CustomerApi.java new file mode 100644 index 0000000..e2d6548 --- /dev/null +++ b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/customer/CustomerApi.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.asset.api.customer; + +import cn.iocoder.yudao.module.asset.api.customer.dto.CustomerRespDTO; + +/** + * 客户 API 接口 + * + * @author 芋道源码 + */ +public interface CustomerApi { + + /** + * 通过名称模糊查询客户 + * + * @param name 客户名称 + * @return 客户信息 + */ + CustomerRespDTO getByNameLike(String name); +} diff --git a/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/customer/dto/CustomerRespDTO.java b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/customer/dto/CustomerRespDTO.java new file mode 100644 index 0000000..d27189f --- /dev/null +++ b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/customer/dto/CustomerRespDTO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.asset.api.customer.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 客户响应 DTO + * + * @author 芋道源码 + */ +@Data +public class CustomerRespDTO implements Serializable { + + /** + * 客户ID + */ + private Long id; + + /** + * 客户名称 + */ + private String customerName; + + /** + * 客户编码 + */ + private String customerCode; + + /** + * 客户类型 + */ + private Integer customerType; + + /** + * 客户状态 + */ + private Integer status; +} diff --git a/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/station/HydrogenStationApi.java b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/station/HydrogenStationApi.java new file mode 100644 index 0000000..aabc034 --- /dev/null +++ b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/station/HydrogenStationApi.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.asset.api.station; + +import cn.iocoder.yudao.module.asset.api.station.dto.HydrogenStationRespDTO; + +/** + * 加氢站 API 接口 + * + * @author 芋道源码 + */ +public interface HydrogenStationApi { + + /** + * 获取加氢站信息 + * + * @param id 加氢站ID + * @return 加氢站信息 + */ + HydrogenStationRespDTO getStation(Long id); +} diff --git a/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/station/dto/HydrogenStationRespDTO.java b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/station/dto/HydrogenStationRespDTO.java new file mode 100644 index 0000000..a35423d --- /dev/null +++ b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/station/dto/HydrogenStationRespDTO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.asset.api.station.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 加氢站响应 DTO + * + * @author 芋道源码 + */ +@Data +public class HydrogenStationRespDTO implements Serializable { + + /** + * 站点ID + */ + private Long id; + + /** + * 站点名称 + */ + private String name; + + /** + * 是否自动扣款 + */ + private Boolean autoDeduct; + + /** + * 站点编码 + */ + private String code; + + /** + * 站点状态 + */ + private Integer status; +} diff --git a/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/vehicle/VehicleApi.java b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/vehicle/VehicleApi.java new file mode 100644 index 0000000..adde46a --- /dev/null +++ b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/vehicle/VehicleApi.java @@ -0,0 +1,19 @@ +package cn.iocoder.yudao.module.asset.api.vehicle; + +import cn.iocoder.yudao.module.asset.api.vehicle.dto.VehicleRespDTO; + +/** + * 车辆 API 接口 + * + * @author 芋道源码 + */ +public interface VehicleApi { + + /** + * 通过车牌号查询车辆 + * + * @param vehicleNo 车牌号 + * @return 车辆信息 + */ + VehicleRespDTO getByVehicleNo(String vehicleNo); +} diff --git a/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/vehicle/dto/VehicleRespDTO.java b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/vehicle/dto/VehicleRespDTO.java new file mode 100644 index 0000000..0b96c82 --- /dev/null +++ b/yudao-module-asset/yudao-module-asset-api/src/main/java/cn/iocoder/yudao/module/asset/api/vehicle/dto/VehicleRespDTO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.asset.api.vehicle.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 车辆响应 DTO + * + * @author 芋道源码 + */ +@Data +public class VehicleRespDTO implements Serializable { + + /** + * 车辆ID + */ + private Long id; + + /** + * 车牌号 + */ + private String plateNo; + + /** + * 客户ID + */ + private Long customerId; + + /** + * 车辆类型 + */ + private Integer vehicleType; + + /** + * 车辆状态 + */ + private Integer status; +} diff --git a/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/ContractApiImpl.java b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/ContractApiImpl.java new file mode 100644 index 0000000..d5e420e --- /dev/null +++ b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/ContractApiImpl.java @@ -0,0 +1,37 @@ +package cn.iocoder.yudao.module.asset.api; + +import cn.iocoder.yudao.module.asset.api.contract.ContractApi; +import cn.iocoder.yudao.module.asset.api.contract.dto.ContractRespDTO; +import cn.iocoder.yudao.module.asset.convert.contract.ContractConvert; +import cn.iocoder.yudao.module.asset.dal.dataobject.contract.ContractDO; +import cn.iocoder.yudao.module.asset.dal.mysql.contract.ContractMapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; + +/** + * 合同 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class ContractApiImpl implements ContractApi { + + @Resource + private ContractMapper contractMapper; + + @Override + public ContractRespDTO getActiveByCustomerId(Long customerId) { + ContractDO contract = contractMapper.selectOne( + new LambdaQueryWrapper() + .eq(ContractDO::getCustomerId, customerId) + .eq(ContractDO::getContractStatus, 2) // 2=进行中 + .gt(ContractDO::getEndDate, LocalDateTime.now()) + .orderByDesc(ContractDO::getStartDate) + .last("LIMIT 1") + ); + return ContractConvert.INSTANCE.convertToApi(contract); + } +} diff --git a/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/CustomerApiImpl.java b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/CustomerApiImpl.java new file mode 100644 index 0000000..3d81283 --- /dev/null +++ b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/CustomerApiImpl.java @@ -0,0 +1,32 @@ +package cn.iocoder.yudao.module.asset.api; + +import cn.iocoder.yudao.module.asset.api.customer.CustomerApi; +import cn.iocoder.yudao.module.asset.api.customer.dto.CustomerRespDTO; +import cn.iocoder.yudao.module.asset.convert.customer.CustomerConvert; +import cn.iocoder.yudao.module.asset.dal.dataobject.customer.CustomerDO; +import cn.iocoder.yudao.module.asset.dal.mysql.customer.CustomerMapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +/** + * 客户 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class CustomerApiImpl implements CustomerApi { + + @Resource + private CustomerMapper customerMapper; + + @Override + public CustomerRespDTO getByNameLike(String name) { + CustomerDO customer = customerMapper.selectOne( + new LambdaQueryWrapper() + .like(CustomerDO::getCustomerName, name) + .last("LIMIT 1") + ); + return CustomerConvert.INSTANCE.convertToApi(customer); + } +} diff --git a/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/HydrogenStationApiImpl.java b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/HydrogenStationApiImpl.java new file mode 100644 index 0000000..29d2376 --- /dev/null +++ b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/HydrogenStationApiImpl.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.asset.api; + +import cn.iocoder.yudao.module.asset.api.station.HydrogenStationApi; +import cn.iocoder.yudao.module.asset.api.station.dto.HydrogenStationRespDTO; +import cn.iocoder.yudao.module.asset.convert.station.HydrogenStationConvert; +import cn.iocoder.yudao.module.asset.dal.dataobject.station.HydrogenStationDO; +import cn.iocoder.yudao.module.asset.service.station.HydrogenStationService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +/** + * 加氢站 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class HydrogenStationApiImpl implements HydrogenStationApi { + + @Resource + private HydrogenStationService hydrogenStationService; + + @Override + public HydrogenStationRespDTO getStation(Long id) { + HydrogenStationDO station = hydrogenStationService.getHydrogenStation(id); + return HydrogenStationConvert.INSTANCE.convertToApi(station); + } +} diff --git a/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/VehicleApiImpl.java b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/VehicleApiImpl.java new file mode 100644 index 0000000..c5c9f0b --- /dev/null +++ b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/api/VehicleApiImpl.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.asset.api; + +import cn.iocoder.yudao.module.asset.api.vehicle.VehicleApi; +import cn.iocoder.yudao.module.asset.api.vehicle.dto.VehicleRespDTO; +import cn.iocoder.yudao.module.asset.convert.vehicle.VehicleConvert; +import cn.iocoder.yudao.module.asset.dal.dataobject.vehicle.VehicleBaseDO; +import cn.iocoder.yudao.module.asset.dal.mysql.vehicle.VehicleBaseMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +/** + * 车辆 API 实现类 + * + * @author 芋道源码 + */ +@Service +public class VehicleApiImpl implements VehicleApi { + + @Resource + private VehicleBaseMapper vehicleBaseMapper; + + @Override + public VehicleRespDTO getByVehicleNo(String vehicleNo) { + VehicleBaseDO vehicle = vehicleBaseMapper.selectOne(VehicleBaseDO::getPlateNo, vehicleNo); + return VehicleConvert.INSTANCE.convertToApi(vehicle); + } +} diff --git a/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/contract/ContractConvert.java b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/contract/ContractConvert.java index a5b67f8..c9aa398 100644 --- a/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/contract/ContractConvert.java +++ b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/contract/ContractConvert.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.asset.convert.contract; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.asset.api.contract.dto.ContractRespDTO; import cn.iocoder.yudao.module.asset.controller.admin.contract.vo.*; import cn.iocoder.yudao.module.asset.dal.dataobject.contract.*; import org.mapstruct.Mapper; @@ -52,4 +53,9 @@ public interface ContractConvert { List convertAttachmentList(List list); + /** + * 转换为 API DTO + */ + ContractRespDTO convertToApi(ContractDO bean); + } diff --git a/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/customer/CustomerConvert.java b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/customer/CustomerConvert.java index acd5150..a6718c2 100644 --- a/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/customer/CustomerConvert.java +++ b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/customer/CustomerConvert.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.asset.convert.customer; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.asset.api.customer.dto.CustomerRespDTO; import cn.iocoder.yudao.module.asset.controller.admin.customer.vo.CustomerRespVO; import cn.iocoder.yudao.module.asset.controller.admin.customer.vo.CustomerSaveReqVO; import cn.iocoder.yudao.module.asset.dal.dataobject.customer.CustomerDO; @@ -27,4 +28,9 @@ public interface CustomerConvert { List convertList(List list); + /** + * 转换为 API DTO + */ + CustomerRespDTO convertToApi(CustomerDO bean); + } diff --git a/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/station/HydrogenStationConvert.java b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/station/HydrogenStationConvert.java index 19ea54d..ac1521e 100644 --- a/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/station/HydrogenStationConvert.java +++ b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/station/HydrogenStationConvert.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.asset.convert.station; +import cn.iocoder.yudao.module.asset.api.station.dto.HydrogenStationRespDTO; import cn.iocoder.yudao.module.asset.controller.admin.station.vo.HydrogenStationRespVO; import cn.iocoder.yudao.module.asset.controller.admin.station.vo.HydrogenStationSimpleRespVO; import cn.iocoder.yudao.module.asset.dal.dataobject.station.HydrogenStationDO; @@ -33,4 +34,9 @@ public interface HydrogenStationConvert { */ List convertSimpleList(List list); + /** + * 转换为 API DTO + */ + HydrogenStationRespDTO convertToApi(HydrogenStationDO bean); + } diff --git a/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/vehicle/VehicleConvert.java b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/vehicle/VehicleConvert.java index e0b86b3..e1310bd 100644 --- a/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/vehicle/VehicleConvert.java +++ b/yudao-module-asset/yudao-module-asset-server/src/main/java/cn/iocoder/yudao/module/asset/convert/vehicle/VehicleConvert.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.asset.convert.vehicle; +import cn.iocoder.yudao.module.asset.api.vehicle.dto.VehicleRespDTO; import cn.iocoder.yudao.module.asset.controller.admin.vehicle.vo.VehicleRespVO; import cn.iocoder.yudao.module.asset.controller.admin.vehicle.vo.VehicleSimpleRespVO; import cn.iocoder.yudao.module.asset.dal.dataobject.vehicle.VehicleBaseDO; @@ -7,6 +8,7 @@ import cn.iocoder.yudao.module.asset.dal.dataobject.vehicle.VehicleBusinessDO; import cn.iocoder.yudao.module.asset.dal.dataobject.vehicle.VehicleLocationDO; import cn.iocoder.yudao.module.asset.dal.dataobject.vehicle.VehicleStatusDO; import org.mapstruct.Mapper; +import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; import java.util.List; @@ -122,4 +124,10 @@ public interface VehicleConvert { return list.stream().map(this::convertSimple).toList(); } + /** + * 转换为 API DTO + */ + @Mapping(source = "plateNo", target = "plateNo") + VehicleRespDTO convertToApi(VehicleBaseDO bean); + } diff --git a/yudao-module-energy/IMPLEMENTATION_SUMMARY.md b/yudao-module-energy/IMPLEMENTATION_SUMMARY.md new file mode 100644 index 0000000..41a5aeb --- /dev/null +++ b/yudao-module-energy/IMPLEMENTATION_SUMMARY.md @@ -0,0 +1,140 @@ +# Energy 模块导入流程优化 - 实现总结 + +## 实现完成时间 +2026-03-16 + +## 核心功能 +实现了 Energy 模块的导入流程优化,将原来的 3 步手动流程(上传 → 匹配 → 生成明细)合并为 1 步自动流程。 + +## 创建的文件 + +### 1. MatchResultVO.java +**路径**: `yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/match/vo/MatchResultVO.java` + +**作用**: 单条记录匹配结果值对象 + +**字段**: +- vehicleId - 匹配到的车辆ID +- customerId - 匹配到的客户ID +- contractId - 匹配到的合同ID +- matchStatus - 匹配状态(0=完全匹配,1=部分匹配,2=未匹配) +- matchMessage - 匹配说明 + +### 2. MatchResultDTO.java +**路径**: `yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/match/dto/MatchResultDTO.java` + +**作用**: 批量匹配结果统计对象 + +**字段**: +- successCount - 成功数量 +- failCount - 失败数量 +- successIds - 成功的记录ID列表 +- failIds - 失败的记录ID列表 + +### 3. HydrogenMatchService.java +**路径**: `yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/match/HydrogenMatchService.java` + +**作用**: 加氢记录匹配服务接口 + +**方法**: +- `batchMatch(List recordIds)` - 批量自动匹配记录 +- `matchRecord(EnergyHydrogenRecordDO record)` - 单条记录匹配 + +### 4. HydrogenMatchServiceImpl.java +**路径**: `yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/match/HydrogenMatchServiceImpl.java` + +**作用**: 加氢记录匹配服务实现类 + +**核心逻辑**: +1. **匹配车辆**: 通过车牌号(plateNumber)精确匹配 asset_vehicle_base 表 +2. **匹配客户**: + - 如果车辆未匹配,尝试通过客户名称模糊匹配 asset_customer 表 +3. **匹配合同**: + - 通过客户ID查询 asset_contract 表 + - 筛选条件:contractStatus=2(进行中)且 endDate > 加氢日期 + - 如果有多个合同,取最新的(按 startDate 降序) +4. **更新记录**: 将匹配结果更新到 EnergyHydrogenRecordDO + +**依赖的 Mapper**: +- VehicleBaseMapper (asset 模块) +- CustomerMapper (asset 模块) +- ContractMapper (asset 模块) +- EnergyHydrogenRecordMapper (energy 模块) + +## 修改的文件 + +### 5. DetailEventListener.java +**路径**: `yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/listener/DetailEventListener.java` + +**修改内容**: 实现完整的自动匹配和生成明细流程 + +**核心流程**: +1. **批量匹配**: 调用 `hydrogenMatchService.batchMatch()` 匹配车辆、客户、合同 +2. **生成明细**: + - 只为匹配成功的记录生成明细 + - 从价格配置获取成本价和客户价 + - 计算成本金额和客户金额 + - 设置初始状态(待审核、未扣款、未结算) +3. **自动扣款**: + - 检查站点配置 `autoDeduct` 字段 + - 如果为 true,生成明细后立即调用 `accountService.deduct()` 扣款 + - 更新明细的扣款状态 + +**新增依赖**: +- HydrogenMatchService +- HydrogenStationService (asset 模块) +- EnergyStationPriceService +- EnergyAccountService + +## 技术要点 + +### 1. 跨模块调用 +- energy 模块通过 `@Resource` 直接注入 asset 模块的 Mapper 和 Service +- 同一个应用内,无需 Feign 客户端 + +### 2. 事务管理 +- 使用 `@Transactional` 确保匹配、生成明细、扣款在同一事务中 +- 使用 `@TransactionalEventListener` 监听导入事件 + +### 3. 错误处理 +- 匹配失败的记录不生成明细 +- 扣款失败不影响其他记录,使用 try-catch 捕获异常 +- 详细的日志记录,便于排查问题 + +### 4. 性能优化 +- 批量查询而不是循环单条查询 +- 使用 MyBatis Plus 的 `selectBatchIds()` 批量查询 +- 批量插入明细(虽然当前实现是循环插入,后续可优化为真正的批量插入) + +## 注意事项 + +### 1. 数据库字段映射 +- Record 表的 `plateNumber` 字段对应 Vehicle 表的 `plateNo` 字段 +- 需要确保字段名称一致 + +### 2. 匹配规则优化空间 +- 当前车辆匹配只取第一辆,后续可优化为提示用户选择 +- 客户名称模糊匹配可能不准确,建议增加更多匹配条件 + +### 3. 价格配置依赖 +- 生成明细依赖价格配置,如果没有配置价格,明细无法生成 +- 需要确保每个站点和客户都有有效的价格配置 + +### 4. 合同ID获取 +- 当前实现中,合同ID通过重新调用 `matchRecord()` 获取 +- 这会导致重复查询,后续可优化为在批量匹配时直接返回合同ID + +## 后续优化建议 + +1. **批量插入优化**: 使用 MyBatis 的 `insertBatch` 真正实现批量插入 +2. **匹配结果缓存**: 避免重复查询合同信息 +3. **异步处理**: 对于大批量导入,可以考虑异步处理匹配和生成明细 +4. **匹配规则配置化**: 将匹配规则配置到数据库,支持动态调整 +5. **匹配失败通知**: 对于匹配失败的记录,发送通知给管理员 + +## 测试建议 + +1. **单元测试**: 测试匹配服务的各种场景(完全匹配、部分匹配、未匹配) +2. **集成测试**: 测试完整的导入流程 +3. **性能测试**: 测试大批量导入的性能 +4. **边界测试**: 测试价格配置缺失、合同过期等边界情况 diff --git a/yudao-module-energy/yudao-module-energy-server/pom.xml b/yudao-module-energy/yudao-module-energy-server/pom.xml index 45ba784..665cfb4 100644 --- a/yudao-module-energy/yudao-module-energy-server/pom.xml +++ b/yudao-module-energy/yudao-module-energy-server/pom.xml @@ -43,6 +43,13 @@ ${revision} + + + cn.iocoder.cloud + yudao-module-asset-api + ${revision} + + cn.iocoder.cloud diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/detail/HydrogenDetailController.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/detail/HydrogenDetailController.java index af07e6f..96f0548 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/detail/HydrogenDetailController.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/detail/HydrogenDetailController.java @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.energy.controller.admin.detail.vo.*; import cn.iocoder.yudao.module.energy.convert.detail.HydrogenDetailConvert; import cn.iocoder.yudao.module.energy.dal.dataobject.detail.EnergyHydrogenDetailDO; import cn.iocoder.yudao.module.energy.service.detail.HydrogenDetailService; +import cn.iocoder.yudao.module.energy.service.detail.dto.BatchAuditResultDTO; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -80,10 +81,12 @@ public class HydrogenDetailController { @PostMapping("/batch-audit") @Operation(summary = "批量审核明细") @PreAuthorize("@ss.hasPermission('energy:hydrogen-detail:audit')") - public CommonResult batchAuditDetail(@RequestParam("ids") List ids, - @RequestParam("approved") Boolean approved, - @RequestParam(value = "remark", required = false) String remark) { - hydrogenDetailService.batchAudit(ids, approved, remark); - return success(true); + public CommonResult batchAuditDetail(@Valid @RequestBody HydrogenDetailBatchAuditReqVO reqVO) { + BatchAuditResultDTO result = hydrogenDetailService.batchAudit( + reqVO.getIds(), + reqVO.getApproved(), + reqVO.getRemark() + ); + return success(result); } } diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/detail/vo/HydrogenDetailBatchAuditReqVO.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/detail/vo/HydrogenDetailBatchAuditReqVO.java new file mode 100644 index 0000000..8b9ede7 --- /dev/null +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/controller/admin/detail/vo/HydrogenDetailBatchAuditReqVO.java @@ -0,0 +1,30 @@ +package cn.iocoder.yudao.module.energy.controller.admin.detail.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * 能源管理 - 加氢明细批量审核 Request VO + * + * @author 芋道源码 + */ +@Data +@Schema(description = "能源管理 - 加氢明细批量审核 Request VO") +public class HydrogenDetailBatchAuditReqVO { + + @Schema(description = "明细ID列表", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "明细ID列表不能为空") + private List ids; + + @Schema(description = "是否通过", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "审核结果不能为空") + private Boolean approved; + + @Schema(description = "审核备注") + private String remark; + +} diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/dal/dataobject/detail/EnergyHydrogenDetailDO.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/dal/dataobject/detail/EnergyHydrogenDetailDO.java index d4ba370..58a37a0 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/dal/dataobject/detail/EnergyHydrogenDetailDO.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/dal/dataobject/detail/EnergyHydrogenDetailDO.java @@ -8,6 +8,7 @@ import lombok.*; import java.math.BigDecimal; import java.time.LocalDate; +import java.time.LocalDateTime; /** * 加氢明细 DO @@ -113,11 +114,21 @@ public class EnergyHydrogenDetailDO extends BaseDO { */ private String auditRemark; + /** + * 审核时间 + */ + private LocalDateTime auditTime; + /** * 扣费状态 */ private Integer deductionStatus; + /** + * 扣费时间 + */ + private LocalDateTime deductionTime; + /** * 结算状态 */ diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/listener/AccountEventListener.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/listener/AccountEventListener.java index 5980e1e..76847d4 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/listener/AccountEventListener.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/listener/AccountEventListener.java @@ -1,39 +1,30 @@ package cn.iocoder.yudao.module.energy.listener; -import cn.iocoder.yudao.module.energy.enums.DeductionStatusEnum; import cn.iocoder.yudao.module.energy.event.DetailAuditPassedEvent; -import cn.iocoder.yudao.module.energy.service.account.EnergyAccountService; -import cn.iocoder.yudao.module.energy.service.detail.HydrogenDetailService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionPhase; import org.springframework.transaction.event.TransactionalEventListener; +/** + * 账户事件监听器 + * + * 注意:审核+扣款逻辑已合并到 HydrogenDetailServiceImpl.audit() 方法中 + * 此监听器仅用于日志记录和监控,不再执行扣款操作 + */ @Component @Slf4j public class AccountEventListener { - @Resource - private EnergyAccountService accountService; - @Resource - private HydrogenDetailService detailService; - /** - * 明细审核通过 → 执行扣款 - * BEFORE_COMMIT: 资金操作,必须同事务 - * - * 注意:此监听器处理审核后扣款场景(站点配置 auto_deduct=false) - * 自动扣款场景(auto_deduct=true)在明细创建时已完成 + * 明细审核通过事件监听 + * 用于日志记录和监控,实际扣款已在审核方法中完成 */ - @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) public void onDetailAuditPassed(DetailAuditPassedEvent event) { - // TODO: 需要从 asset 模块获取站点配置判断是否需要扣款 - // 当前简化实现:审核通过后统一扣款 - log.info("[onDetailAuditPassed] deduct: detailId={}, amount={}", - event.getDetailId(), event.getCustomerAmount()); - accountService.deduct(event.getCustomerId(), event.getContractId(), - event.getCustomerAmount(), event.getDetailId(), null, "审核后扣款"); - detailService.updateDeductionStatus(event.getDetailId(), DeductionStatusEnum.DEDUCTED.getStatus()); + log.info("[onDetailAuditPassed] 明细审核通过,detailId={}, customerId={}, amount={}", + event.getDetailId(), event.getCustomerId(), event.getCustomerAmount()); + // 可以在这里添加监控、通知等逻辑 } } diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/listener/DetailEventListener.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/listener/DetailEventListener.java index 5090706..80ce2bf 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/listener/DetailEventListener.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/listener/DetailEventListener.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.energy.listener; -import cn.iocoder.yudao.module.asset.dal.dataobject.station.HydrogenStationDO; -import cn.iocoder.yudao.module.asset.service.station.HydrogenStationService; +import cn.iocoder.yudao.module.asset.api.station.HydrogenStationApi; +import cn.iocoder.yudao.module.asset.api.station.dto.HydrogenStationRespDTO; import cn.iocoder.yudao.module.energy.dal.dataobject.detail.EnergyHydrogenDetailDO; import cn.iocoder.yudao.module.energy.dal.dataobject.price.EnergyStationPriceDO; import cn.iocoder.yudao.module.energy.dal.dataobject.record.EnergyHydrogenRecordDO; @@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.energy.service.account.EnergyAccountService; import cn.iocoder.yudao.module.energy.service.detail.HydrogenDetailService; import cn.iocoder.yudao.module.energy.service.match.HydrogenMatchService; import cn.iocoder.yudao.module.energy.service.match.dto.MatchResultDTO; +import cn.iocoder.yudao.module.energy.service.match.vo.MatchResultVO; import cn.iocoder.yudao.module.energy.service.price.EnergyStationPriceService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; @@ -42,7 +43,7 @@ public class DetailEventListener { private EnergyHydrogenDetailMapper hydrogenDetailMapper; @Resource - private HydrogenStationService hydrogenStationService; + private HydrogenStationApi hydrogenStationApi; @Resource private EnergyStationPriceService stationPriceService; @@ -85,7 +86,7 @@ public class DetailEventListener { log.info("[onRecordImported] 生成明细完成,count={}", details.size()); // 3. 检查站点配置,决定是否自动扣款 - HydrogenStationDO station = hydrogenStationService.getHydrogenStation(event.getStationId()); + HydrogenStationRespDTO station = hydrogenStationApi.getStation(event.getStationId()); if (station != null && Boolean.TRUE.equals(station.getAutoDeduct())) { log.info("[onRecordImported] 站点配置自动扣款,开始扣款流程"); for (EnergyHydrogenDetailDO detail : details) { diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/detail/HydrogenDetailService.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/detail/HydrogenDetailService.java index 06bc7ae..bf874fb 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/detail/HydrogenDetailService.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/detail/HydrogenDetailService.java @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.energy.controller.admin.detail.vo.HydrogenDetailPageReqVO; import cn.iocoder.yudao.module.energy.controller.admin.detail.vo.HydrogenDetailSaveReqVO; import cn.iocoder.yudao.module.energy.dal.dataobject.detail.EnergyHydrogenDetailDO; +import cn.iocoder.yudao.module.energy.service.detail.dto.BatchAuditResultDTO; import java.util.List; public interface HydrogenDetailService { @@ -11,7 +12,7 @@ public interface HydrogenDetailService { PageResult getDetailPage(HydrogenDetailPageReqVO pageReqVO); EnergyHydrogenDetailDO getDetail(Long id); void audit(Long id, Boolean approved, String remark); - void batchAudit(List ids, Boolean approved, String remark); + BatchAuditResultDTO batchAudit(List ids, Boolean approved, String remark); void updateDeductionStatus(Long detailId, Integer status); void updateBillId(List detailIds, Long billId); void updateSettlementStatus(List detailIds, Integer status); diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/detail/HydrogenDetailServiceImpl.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/detail/HydrogenDetailServiceImpl.java index fe97d22..0dda346 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/detail/HydrogenDetailServiceImpl.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/detail/HydrogenDetailServiceImpl.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.energy.service.detail; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.asset.api.station.HydrogenStationApi; +import cn.iocoder.yudao.module.asset.api.station.dto.HydrogenStationRespDTO; import cn.iocoder.yudao.module.energy.controller.admin.detail.vo.HydrogenDetailPageReqVO; import cn.iocoder.yudao.module.energy.controller.admin.detail.vo.HydrogenDetailSaveReqVO; import cn.iocoder.yudao.module.energy.dal.dataobject.detail.EnergyHydrogenDetailDO; @@ -13,6 +15,8 @@ import cn.iocoder.yudao.module.energy.enums.CostBearerEnum; import cn.iocoder.yudao.module.energy.enums.DeductionStatusEnum; import cn.iocoder.yudao.module.energy.enums.PayMethodEnum; import cn.iocoder.yudao.module.energy.event.DetailAuditPassedEvent; +import cn.iocoder.yudao.module.energy.service.account.EnergyAccountService; +import cn.iocoder.yudao.module.energy.service.detail.dto.BatchAuditResultDTO; import cn.iocoder.yudao.module.energy.service.price.EnergyStationPriceService; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import jakarta.annotation.Resource; @@ -23,6 +27,8 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -41,6 +47,10 @@ public class HydrogenDetailServiceImpl implements HydrogenDetailService { private EnergyStationPriceService stationPriceService; @Resource private ApplicationEventPublisher eventPublisher; + @Resource + private HydrogenStationApi hydrogenStationApi; + @Resource + private EnergyAccountService accountService; @Override public void updateDetail(HydrogenDetailSaveReqVO reqVO) { @@ -72,30 +82,84 @@ public class HydrogenDetailServiceImpl implements HydrogenDetailService { @Override @Transactional(rollbackFor = Exception.class) public void audit(Long id, Boolean approved, String remark) { + // 1. 查询明细 EnergyHydrogenDetailDO detail = validateDetailExists(id); if (!AuditStatusEnum.PENDING.getStatus().equals(detail.getAuditStatus())) { throw exception(HYDROGEN_DETAIL_ALREADY_AUDITED); } - Integer newStatus = approved ? AuditStatusEnum.APPROVED.getStatus() : AuditStatusEnum.REJECTED.getStatus(); - detailMapper.update(null, new LambdaUpdateWrapper() - .set(EnergyHydrogenDetailDO::getAuditStatus, newStatus) - .set(EnergyHydrogenDetailDO::getAuditRemark, remark) - .eq(EnergyHydrogenDetailDO::getId, id)); - // If approved, publish event for deduction + // 2. 更新审核状态 + detail.setAuditStatus(approved ? AuditStatusEnum.APPROVED.getStatus() : AuditStatusEnum.REJECTED.getStatus()); + detail.setAuditRemark(remark); + detail.setAuditTime(LocalDateTime.now()); + detailMapper.updateById(detail); + + // 3. 审核通过 → 检查是否需要扣款 if (approved) { + // 获取站点配置 + HydrogenStationRespDTO station = hydrogenStationApi.getStation(detail.getStationId()); + + // 如果配置为审核后扣款(autoDeduct=false),则执行扣款 + if (station != null && !Boolean.TRUE.equals(station.getAutoDeduct())) { + try { + accountService.deduct( + detail.getCustomerId(), + detail.getContractId(), + detail.getCustomerAmount(), + detail.getId(), + null, + "审核通过自动扣款" + ); + + // 更新扣款状态 + detail.setDeductionStatus(DeductionStatusEnum.DEDUCTED.getStatus()); + detail.setDeductionTime(LocalDateTime.now()); + detailMapper.updateById(detail); + + } catch (Exception e) { + log.error("[audit] 扣款失败,detailId={}, error={}", id, e.getMessage()); + // 扣款失败不影响审核通过,记录到备注 + detail.setAuditRemark(remark + "(扣款失败:" + e.getMessage() + ")"); + detailMapper.updateById(detail); + } + } + + // 发布审核通过事件(预留给其他业务使用) eventPublisher.publishEvent(new DetailAuditPassedEvent( - id, detail.getStationId(), detail.getCustomerId(), - detail.getContractId(), detail.getCustomerAmount())); + detail.getId(), + detail.getStationId(), + detail.getCustomerId(), + detail.getContractId(), + detail.getCustomerAmount() + )); } } @Override @Transactional(rollbackFor = Exception.class) - public void batchAudit(List ids, Boolean approved, String remark) { + public BatchAuditResultDTO batchAudit(List ids, Boolean approved, String remark) { + BatchAuditResultDTO result = new BatchAuditResultDTO(); + result.setTotal(ids.size()); + + List successIds = new ArrayList<>(); + List failIds = new ArrayList<>(); + for (Long id : ids) { - audit(id, approved, remark); + try { + audit(id, approved, remark); + successIds.add(id); + } catch (Exception e) { + log.error("[batchAudit] 审核失败,detailId={}, error={}", id, e.getMessage()); + failIds.add(id); + } } + + result.setSuccessCount(successIds.size()); + result.setFailCount(failIds.size()); + result.setSuccessIds(successIds); + result.setFailIds(failIds); + + return result; } @Override diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/detail/dto/BatchAuditResultDTO.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/detail/dto/BatchAuditResultDTO.java new file mode 100644 index 0000000..2e3b984 --- /dev/null +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/detail/dto/BatchAuditResultDTO.java @@ -0,0 +1,33 @@ +package cn.iocoder.yudao.module.energy.service.detail.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 批量审核结果 DTO + * + * @author 芋道源码 + */ +@Data +@Schema(description = "批量审核结果") +public class BatchAuditResultDTO { + + @Schema(description = "总数") + private Integer total; + + @Schema(description = "成功数") + private Integer successCount; + + @Schema(description = "失败数") + private Integer failCount; + + @Schema(description = "成功的ID列表") + private List successIds = new ArrayList<>(); + + @Schema(description = "失败的ID列表") + private List failIds = new ArrayList<>(); + +} diff --git a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/match/HydrogenMatchServiceImpl.java b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/match/HydrogenMatchServiceImpl.java index 8e5fffa..9db32cb 100644 --- a/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/match/HydrogenMatchServiceImpl.java +++ b/yudao-module-energy/yudao-module-energy-server/src/main/java/cn/iocoder/yudao/module/energy/service/match/HydrogenMatchServiceImpl.java @@ -1,12 +1,12 @@ package cn.iocoder.yudao.module.energy.service.match; import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.module.asset.dal.dataobject.contract.ContractDO; -import cn.iocoder.yudao.module.asset.dal.dataobject.customer.CustomerDO; -import cn.iocoder.yudao.module.asset.dal.dataobject.vehicle.VehicleBaseDO; -import cn.iocoder.yudao.module.asset.dal.mysql.contract.ContractMapper; -import cn.iocoder.yudao.module.asset.dal.mysql.customer.CustomerMapper; -import cn.iocoder.yudao.module.asset.dal.mysql.vehicle.VehicleBaseMapper; +import cn.iocoder.yudao.module.asset.api.contract.ContractApi; +import cn.iocoder.yudao.module.asset.api.contract.dto.ContractRespDTO; +import cn.iocoder.yudao.module.asset.api.customer.CustomerApi; +import cn.iocoder.yudao.module.asset.api.customer.dto.CustomerRespDTO; +import cn.iocoder.yudao.module.asset.api.vehicle.VehicleApi; +import cn.iocoder.yudao.module.asset.api.vehicle.dto.VehicleRespDTO; import cn.iocoder.yudao.module.energy.dal.dataobject.record.EnergyHydrogenRecordDO; import cn.iocoder.yudao.module.energy.dal.mysql.record.EnergyHydrogenRecordMapper; import cn.iocoder.yudao.module.energy.service.match.dto.MatchResultDTO; @@ -32,13 +32,13 @@ public class HydrogenMatchServiceImpl implements HydrogenMatchService { private EnergyHydrogenRecordMapper hydrogenRecordMapper; @Resource - private VehicleBaseMapper vehicleBaseMapper; + private VehicleApi vehicleApi; @Resource - private CustomerMapper customerMapper; + private CustomerApi customerApi; @Resource - private ContractMapper contractMapper; + private ContractApi contractApi; @Override @Transactional(rollbackFor = Exception.class) @@ -90,11 +90,10 @@ public class HydrogenMatchServiceImpl implements HydrogenMatchService { // 1. 匹配车辆(通过车牌号) if (StrUtil.isNotBlank(record.getPlateNumber())) { - VehicleBaseDO vehicle = vehicleBaseMapper.selectOne( - VehicleBaseDO::getPlateNo, record.getPlateNumber() - ); + VehicleRespDTO vehicle = vehicleApi.getByVehicleNo(record.getPlateNumber()); if (vehicle != null) { result.setVehicleId(vehicle.getId()); + result.setCustomerId(vehicle.getCustomerId()); log.debug("[matchRecord] 车辆匹配成功,plateNumber={}, vehicleId={}", record.getPlateNumber(), vehicle.getId()); } @@ -105,11 +104,7 @@ public class HydrogenMatchServiceImpl implements HydrogenMatchService { if (result.getCustomerId() == null && StrUtil.isNotBlank(record.getPlateNumber())) { // 注意:这里假设 plateNumber 字段可能包含客户信息,实际需要根据业务调整 // 如果 Record 有独立的 customerName 字段,应该使用那个字段 - CustomerDO customer = customerMapper.selectOne( - new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() - .like(CustomerDO::getCustomerName, record.getPlateNumber()) - .last("LIMIT 1") - ); + CustomerRespDTO customer = customerApi.getByNameLike(record.getPlateNumber()); if (customer != null) { result.setCustomerId(customer.getId()); log.debug("[matchRecord] 客户匹配成功(通过名称),customerId={}", customer.getId()); @@ -118,14 +113,7 @@ public class HydrogenMatchServiceImpl implements HydrogenMatchService { // 3. 匹配合同(通过客户ID) if (result.getCustomerId() != null) { - ContractDO contract = contractMapper.selectOne( - new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper() - .eq(ContractDO::getCustomerId, result.getCustomerId()) - .eq(ContractDO::getContractStatus, 2) // 2=进行中 - .gt(ContractDO::getEndDate, record.getHydrogenDate()) - .orderByDesc(ContractDO::getStartDate) - .last("LIMIT 1") - ); + ContractRespDTO contract = contractApi.getActiveByCustomerId(result.getCustomerId()); if (contract != null) { result.setContractId(contract.getId()); log.debug("[matchRecord] 合同匹配成功,contractId={}", contract.getId());