refactor(energy): 简化事件驱动系统(7个→3个)
- 删除旧事件:BillApprovedEvent, BillCreatedEvent, DeductionCompletedEvent, DetailAuditedEvent, DetailCreatedEvent, RecordMatchedEvent - 新增事件:BillAuditPassedEvent, DetailAuditPassedEvent - 保留事件:RecordImportedEvent - 更新监听器:AccountEventListener, BillEventListener, DetailEventListener - 清理代码中的旧事件引用和注释 优化原则:前端简单,后端健壮 事件流程:导入→匹配→生成明细→审核→扣款→生成账单→结算
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.yudao.module.energy;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
/**
|
||||
* 能源管理服务启动类
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients(basePackages = {"cn.iocoder.yudao.module.infra.api"})
|
||||
public class EnergyServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(EnergyServerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.controller.admin.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.energy.controller.admin.config.vo.EnergyStationConfigPageReqVO;
|
||||
import cn.iocoder.yudao.module.energy.controller.admin.config.vo.EnergyStationConfigRespVO;
|
||||
import cn.iocoder.yudao.module.energy.controller.admin.config.vo.EnergyStationConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.energy.controller.admin.config.vo.EnergyStationConfigSimpleVO;
|
||||
import cn.iocoder.yudao.module.energy.convert.config.EnergyStationConfigConvert;
|
||||
import cn.iocoder.yudao.module.energy.dal.dataobject.config.EnergyStationConfigDO;
|
||||
import cn.iocoder.yudao.module.energy.service.config.EnergyStationConfigService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 加氢站配置")
|
||||
@RestController
|
||||
@RequestMapping("/energy/station-config")
|
||||
@Validated
|
||||
public class EnergyStationConfigController {
|
||||
|
||||
@Resource
|
||||
private EnergyStationConfigService stationConfigService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建站点配置")
|
||||
@PreAuthorize("@ss.hasPermission('energy:station-config:create')")
|
||||
public CommonResult<Long> createConfig(@Valid @RequestBody EnergyStationConfigSaveReqVO createReqVO) {
|
||||
return success(stationConfigService.createConfig(createReqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新站点配置")
|
||||
@PreAuthorize("@ss.hasPermission('energy:station-config:update')")
|
||||
public CommonResult<Boolean> updateConfig(@Valid @RequestBody EnergyStationConfigSaveReqVO updateReqVO) {
|
||||
stationConfigService.updateConfig(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得站点配置")
|
||||
@Parameter(name = "id", description = "编号", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('energy:station-config:query')")
|
||||
public CommonResult<EnergyStationConfigRespVO> getConfig(@RequestParam("id") Long id) {
|
||||
EnergyStationConfigDO config = stationConfigService.getConfig(id);
|
||||
return success(EnergyStationConfigConvert.INSTANCE.convert(config));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得站点配置分页")
|
||||
@PreAuthorize("@ss.hasPermission('energy:station-config:query')")
|
||||
public CommonResult<PageResult<EnergyStationConfigRespVO>> getConfigPage(@Valid EnergyStationConfigPageReqVO pageReqVO) {
|
||||
PageResult<EnergyStationConfigDO> pageResult = stationConfigService.getConfigPage(pageReqVO);
|
||||
return success(EnergyStationConfigConvert.INSTANCE.convertPage(pageResult));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得站点配置简单列表")
|
||||
public CommonResult<List<EnergyStationConfigSimpleVO>> getSimpleList() {
|
||||
List<EnergyStationConfigDO> list = stationConfigService.getConfigList();
|
||||
return success(EnergyStationConfigConvert.INSTANCE.convertSimpleList(list));
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.controller.admin.config.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
@Schema(description = "管理后台 - 加氢站配置分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class EnergyStationConfigPageReqVO extends PageParam {
|
||||
@Schema(description = "加氢站ID")
|
||||
private Long stationId;
|
||||
@Schema(description = "是否自动扣款")
|
||||
private Boolean autoDeduct;
|
||||
@Schema(description = "合作类型")
|
||||
private Integer cooperationType;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.controller.admin.config.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 加氢站配置 Response VO")
|
||||
@Data
|
||||
public class EnergyStationConfigRespVO {
|
||||
@Schema(description = "主键ID")
|
||||
private Long id;
|
||||
@Schema(description = "加氢站ID")
|
||||
private Long stationId;
|
||||
@Schema(description = "是否自动扣款")
|
||||
private Boolean autoDeduct;
|
||||
@Schema(description = "合作类型")
|
||||
private Integer cooperationType;
|
||||
@Schema(description = "站点名称")
|
||||
private String stationName;
|
||||
@Schema(description = "自动匹配开关")
|
||||
private Boolean autoMatch;
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.controller.admin.config.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 加氢站配置创建/更新 Request VO")
|
||||
@Data
|
||||
public class EnergyStationConfigSaveReqVO {
|
||||
@Schema(description = "主键ID")
|
||||
private Long id;
|
||||
@Schema(description = "加氢站ID", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "加氢站不能为空")
|
||||
private Long stationId;
|
||||
@Schema(description = "是否自动扣款", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@NotNull(message = "是否自动扣款不能为空")
|
||||
private Boolean autoDeduct;
|
||||
@Schema(description = "合作类型")
|
||||
private Integer cooperationType;
|
||||
@Schema(description = "自动匹配开关")
|
||||
private Boolean autoMatch;
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.controller.admin.config.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 加氢站配置简单 Response VO")
|
||||
@Data
|
||||
public class EnergyStationConfigSimpleVO {
|
||||
@Schema(description = "配置ID")
|
||||
private Long id;
|
||||
@Schema(description = "站点ID")
|
||||
private Long stationId;
|
||||
@Schema(description = "站点名称")
|
||||
private String stationName;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.convert.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.energy.controller.admin.config.vo.EnergyStationConfigRespVO;
|
||||
import cn.iocoder.yudao.module.energy.controller.admin.config.vo.EnergyStationConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.energy.controller.admin.config.vo.EnergyStationConfigSimpleVO;
|
||||
import cn.iocoder.yudao.module.energy.dal.dataobject.config.EnergyStationConfigDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface EnergyStationConfigConvert {
|
||||
EnergyStationConfigConvert INSTANCE = Mappers.getMapper(EnergyStationConfigConvert.class);
|
||||
EnergyStationConfigDO convert(EnergyStationConfigSaveReqVO bean);
|
||||
EnergyStationConfigRespVO convert(EnergyStationConfigDO bean);
|
||||
PageResult<EnergyStationConfigRespVO> convertPage(PageResult<EnergyStationConfigDO> page);
|
||||
List<EnergyStationConfigSimpleVO> convertSimpleList(List<EnergyStationConfigDO> list);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.dal.dataobject.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 站点配置 DO
|
||||
*/
|
||||
@TableName("energy_station_config")
|
||||
@KeySequence("energy_station_config_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class EnergyStationConfigDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 主键
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 站点ID
|
||||
*/
|
||||
private Long stationId;
|
||||
|
||||
/**
|
||||
* 是否自动扣费
|
||||
*/
|
||||
private Boolean autoDeduct;
|
||||
|
||||
/**
|
||||
* 合作类型
|
||||
*/
|
||||
private Integer cooperationType;
|
||||
|
||||
/**
|
||||
* 自动匹配开关
|
||||
*/
|
||||
private Boolean autoMatch;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.dal.mysql.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.yudao.module.energy.controller.admin.config.vo.EnergyStationConfigPageReqVO;
|
||||
import cn.iocoder.yudao.module.energy.dal.dataobject.config.EnergyStationConfigDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface EnergyStationConfigMapper extends BaseMapperX<EnergyStationConfigDO> {
|
||||
|
||||
default EnergyStationConfigDO selectByStationId(Long stationId) {
|
||||
return selectOne(EnergyStationConfigDO::getStationId, stationId);
|
||||
}
|
||||
|
||||
default PageResult<EnergyStationConfigDO> selectPage(EnergyStationConfigPageReqVO reqVO) {
|
||||
return selectPage(reqVO, new LambdaQueryWrapperX<EnergyStationConfigDO>()
|
||||
.eqIfPresent(EnergyStationConfigDO::getStationId, reqVO.getStationId())
|
||||
.eqIfPresent(EnergyStationConfigDO::getAutoDeduct, reqVO.getAutoDeduct())
|
||||
.eqIfPresent(EnergyStationConfigDO::getCooperationType, reqVO.getCooperationType())
|
||||
.orderByDesc(EnergyStationConfigDO::getId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,9 +5,12 @@ import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 账单审核通过事件
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class BillApprovedEvent {
|
||||
public class BillAuditPassedEvent {
|
||||
private final Long billId;
|
||||
private final List<Long> detailIds;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class BillCreatedEvent {
|
||||
private final Long billId;
|
||||
private final List<Long> detailIds;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class DeductionCompletedEvent {
|
||||
private final Long detailId;
|
||||
}
|
||||
@@ -5,9 +5,12 @@ import lombok.Getter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 明细审核通过事件
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class DetailCreatedEvent {
|
||||
public class DetailAuditPassedEvent {
|
||||
private final Long detailId;
|
||||
private final Long stationId;
|
||||
private final Long customerId;
|
||||
@@ -1,16 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class DetailAuditedEvent {
|
||||
private final Long detailId;
|
||||
private final Long stationId;
|
||||
private final Long customerId;
|
||||
private final Long contractId;
|
||||
private final BigDecimal customerAmount;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.event;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class RecordMatchedEvent {
|
||||
private final Long recordId;
|
||||
private final Long stationId;
|
||||
private final Long vehicleId;
|
||||
private final Long customerId;
|
||||
private final String plateNumber;
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
package cn.iocoder.yudao.module.energy.listener;
|
||||
|
||||
import cn.iocoder.yudao.module.energy.dal.dataobject.config.EnergyStationConfigDO;
|
||||
import cn.iocoder.yudao.module.energy.dal.mysql.config.EnergyStationConfigMapper;
|
||||
import cn.iocoder.yudao.module.energy.enums.DeductionStatusEnum;
|
||||
import cn.iocoder.yudao.module.energy.event.DetailAuditedEvent;
|
||||
import cn.iocoder.yudao.module.energy.event.DetailCreatedEvent;
|
||||
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;
|
||||
@@ -20,37 +17,23 @@ public class AccountEventListener {
|
||||
@Resource
|
||||
private EnergyAccountService accountService;
|
||||
@Resource
|
||||
private EnergyStationConfigMapper stationConfigMapper;
|
||||
@Resource
|
||||
private HydrogenDetailService detailService;
|
||||
|
||||
/**
|
||||
* 明细创建完成 → 若配置自动扣款则扣款
|
||||
* 明细审核通过 → 执行扣款
|
||||
* BEFORE_COMMIT: 资金操作,必须同事务
|
||||
*
|
||||
* 注意:此监听器处理审核后扣款场景(站点配置 auto_deduct=false)
|
||||
* 自动扣款场景(auto_deduct=true)在明细创建时已完成
|
||||
*/
|
||||
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
|
||||
public void onDetailCreated(DetailCreatedEvent event) {
|
||||
EnergyStationConfigDO config = stationConfigMapper.selectByStationId(event.getStationId());
|
||||
if (config != null && Boolean.TRUE.equals(config.getAutoDeduct())) {
|
||||
log.info("[onDetailCreated] auto 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());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 明细审核通过 → 若配置审核后扣款则扣款
|
||||
* BEFORE_COMMIT: 资金操作,必须同事务
|
||||
*/
|
||||
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
|
||||
public void onDetailAudited(DetailAuditedEvent event) {
|
||||
EnergyStationConfigDO config = stationConfigMapper.selectByStationId(event.getStationId());
|
||||
if (config != null && !Boolean.TRUE.equals(config.getAutoDeduct())) {
|
||||
log.info("[onDetailAudited] audit-then-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());
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
package cn.iocoder.yudao.module.energy.listener;
|
||||
|
||||
import cn.iocoder.yudao.module.energy.enums.SettlementStatusEnum;
|
||||
import cn.iocoder.yudao.module.energy.event.BillApprovedEvent;
|
||||
import cn.iocoder.yudao.module.energy.event.BillCreatedEvent;
|
||||
import cn.iocoder.yudao.module.energy.event.BillAuditPassedEvent;
|
||||
import cn.iocoder.yudao.module.energy.service.detail.HydrogenDetailService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -16,23 +15,15 @@ public class BillEventListener {
|
||||
@Resource
|
||||
private HydrogenDetailService detailService;
|
||||
|
||||
/**
|
||||
* 账单生成完成 → 更新明细的 billId
|
||||
* AFTER_COMMIT: 非资金操作
|
||||
*/
|
||||
@TransactionalEventListener
|
||||
public void onBillCreated(BillCreatedEvent event) {
|
||||
log.info("[onBillCreated] billId={}, detailCount={}", event.getBillId(), event.getDetailIds().size());
|
||||
detailService.updateBillId(event.getDetailIds(), event.getBillId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 账单审核通过 → 更新明细结算状态
|
||||
* AFTER_COMMIT: 非资金操作
|
||||
*
|
||||
* 预留二期财务对接
|
||||
*/
|
||||
@TransactionalEventListener
|
||||
public void onBillApproved(BillApprovedEvent event) {
|
||||
log.info("[onBillApproved] billId={}", event.getBillId());
|
||||
public void onBillAuditPassed(BillAuditPassedEvent event) {
|
||||
log.info("[onBillAuditPassed] billId={}", event.getBillId());
|
||||
detailService.updateSettlementStatus(event.getDetailIds(), SettlementStatusEnum.SETTLED.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,30 @@
|
||||
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.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;
|
||||
import cn.iocoder.yudao.module.energy.dal.mysql.detail.EnergyHydrogenDetailMapper;
|
||||
import cn.iocoder.yudao.module.energy.dal.mysql.record.EnergyHydrogenRecordMapper;
|
||||
import cn.iocoder.yudao.module.energy.enums.DeductionStatusEnum;
|
||||
import cn.iocoder.yudao.module.energy.event.RecordImportedEvent;
|
||||
import cn.iocoder.yudao.module.energy.event.RecordMatchedEvent;
|
||||
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.price.EnergyStationPriceService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.event.TransactionalEventListener;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
public class DetailEventListener {
|
||||
@@ -15,26 +32,136 @@ public class DetailEventListener {
|
||||
@Resource
|
||||
private HydrogenDetailService hydrogenDetailService;
|
||||
|
||||
@Resource
|
||||
private HydrogenMatchService hydrogenMatchService;
|
||||
|
||||
@Resource
|
||||
private EnergyHydrogenRecordMapper hydrogenRecordMapper;
|
||||
|
||||
@Resource
|
||||
private EnergyHydrogenDetailMapper hydrogenDetailMapper;
|
||||
|
||||
@Resource
|
||||
private HydrogenStationService hydrogenStationService;
|
||||
|
||||
@Resource
|
||||
private EnergyStationPriceService stationPriceService;
|
||||
|
||||
@Resource
|
||||
private EnergyAccountService accountService;
|
||||
|
||||
/**
|
||||
* 记录导入完成 → 触发自动匹配(占位,后续实现批量自动匹配)
|
||||
* 记录导入完成 → 触发自动匹配+生成明细
|
||||
* AFTER_COMMIT: 非资金操作
|
||||
*/
|
||||
@TransactionalEventListener
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void onRecordImported(RecordImportedEvent event) {
|
||||
log.info("[onRecordImported] stationId={}, recordCount={}, batchNo={}",
|
||||
log.info("[onRecordImported] 开始处理导入记录,stationId={}, recordCount={}, batchNo={}",
|
||||
event.getStationId(), event.getRecordIds().size(), event.getBatchNo());
|
||||
// TODO: 后续接入 asset 模块实现批量自动匹配
|
||||
// hydrogenRecordService.batchMatch(event.getRecordIds());
|
||||
|
||||
// 1. 批量匹配
|
||||
MatchResultDTO matchResult = hydrogenMatchService.batchMatch(event.getRecordIds());
|
||||
log.info("[onRecordImported] 匹配完成,成功={}, 失败={}",
|
||||
matchResult.getSuccessCount(), matchResult.getFailCount());
|
||||
|
||||
// 2. 批量生成明细(只为匹配成功的记录生成)
|
||||
if (matchResult.getSuccessCount() > 0) {
|
||||
List<EnergyHydrogenRecordDO> matchedRecords = hydrogenRecordMapper.selectBatchIds(matchResult.getSuccessIds());
|
||||
List<EnergyHydrogenDetailDO> details = new ArrayList<>();
|
||||
|
||||
for (EnergyHydrogenRecordDO record : matchedRecords) {
|
||||
EnergyHydrogenDetailDO detail = createDetailFromRecord(record, event.getStationId());
|
||||
if (detail != null) {
|
||||
details.add(detail);
|
||||
}
|
||||
}
|
||||
|
||||
if (!details.isEmpty()) {
|
||||
// 批量插入明细
|
||||
for (EnergyHydrogenDetailDO detail : details) {
|
||||
hydrogenDetailMapper.insert(detail);
|
||||
}
|
||||
log.info("[onRecordImported] 生成明细完成,count={}", details.size());
|
||||
|
||||
// 3. 检查站点配置,决定是否自动扣款
|
||||
HydrogenStationDO station = hydrogenStationService.getHydrogenStation(event.getStationId());
|
||||
if (station != null && Boolean.TRUE.equals(station.getAutoDeduct())) {
|
||||
log.info("[onRecordImported] 站点配置自动扣款,开始扣款流程");
|
||||
for (EnergyHydrogenDetailDO detail : details) {
|
||||
try {
|
||||
accountService.deduct(
|
||||
detail.getCustomerId(),
|
||||
detail.getContractId(),
|
||||
detail.getCustomerAmount(),
|
||||
detail.getId(),
|
||||
null,
|
||||
"加氢自动扣款"
|
||||
);
|
||||
hydrogenDetailService.updateDeductionStatus(
|
||||
detail.getId(),
|
||||
DeductionStatusEnum.DEDUCTED.getStatus()
|
||||
);
|
||||
log.info("[onRecordImported] 扣款成功,detailId={}, amount={}",
|
||||
detail.getId(), detail.getCustomerAmount());
|
||||
} catch (Exception e) {
|
||||
log.error("[onRecordImported] 扣款失败,detailId={}, error={}",
|
||||
detail.getId(), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("[onRecordImported] 处理完成");
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录匹配完成 → 创建明细
|
||||
* AFTER_COMMIT: 明细创建在独立事务中
|
||||
* 从记录创建明细
|
||||
*/
|
||||
@TransactionalEventListener
|
||||
public void onRecordMatched(RecordMatchedEvent event) {
|
||||
log.info("[onRecordMatched] recordId={}, vehicleId={}, customerId={}",
|
||||
event.getRecordId(), event.getVehicleId(), event.getCustomerId());
|
||||
hydrogenDetailService.createFromRecord(event);
|
||||
private EnergyHydrogenDetailDO createDetailFromRecord(EnergyHydrogenRecordDO record, Long stationId) {
|
||||
// 获取价格配置(使用客户ID和日期)
|
||||
EnergyStationPriceDO price = stationPriceService.getEffectivePrice(
|
||||
stationId,
|
||||
record.getCustomerId(),
|
||||
record.getHydrogenDate()
|
||||
);
|
||||
if (price == null) {
|
||||
log.warn("[createDetailFromRecord] 未找到有效价格配置,recordId={}, stationId={}, customerId={}, date={}",
|
||||
record.getId(), stationId, record.getCustomerId(), record.getHydrogenDate());
|
||||
return null;
|
||||
}
|
||||
|
||||
// 重新匹配获取合同ID(因为批量匹配时已经更新了record,但需要获取contractId)
|
||||
MatchResultVO matchResult = hydrogenMatchService.matchRecord(record);
|
||||
|
||||
// 计算金额
|
||||
BigDecimal costAmount = record.getHydrogenQuantity()
|
||||
.multiply(price.getCostPrice())
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal customerAmount = record.getHydrogenQuantity()
|
||||
.multiply(price.getCustomerPrice())
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
|
||||
return EnergyHydrogenDetailDO.builder()
|
||||
.recordId(record.getId())
|
||||
.stationId(stationId)
|
||||
.vehicleId(record.getVehicleId())
|
||||
.plateNumber(record.getPlateNumber())
|
||||
.hydrogenDate(record.getHydrogenDate())
|
||||
.hydrogenQuantity(record.getHydrogenQuantity())
|
||||
.costPrice(price.getCostPrice())
|
||||
.costAmount(costAmount)
|
||||
.customerPrice(price.getCustomerPrice())
|
||||
.customerAmount(customerAmount)
|
||||
.contractId(matchResult.getContractId()) // 从匹配结果获取合同ID
|
||||
.customerId(record.getCustomerId())
|
||||
.projectName(null) // 可以从合同获取,暂时为空
|
||||
.costBearer(1) // 默认客户承担,可以从合同配置获取
|
||||
.payMethod(1) // 默认预付,可以从合同配置获取
|
||||
.auditStatus(0) // 待审核
|
||||
.deductionStatus(0) // 未扣款
|
||||
.settlementStatus(0) // 未结算
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,8 +12,7 @@ import cn.iocoder.yudao.module.energy.dal.mysql.bill.EnergyBillAdjustmentMapper;
|
||||
import cn.iocoder.yudao.module.energy.dal.mysql.bill.EnergyBillMapper;
|
||||
import cn.iocoder.yudao.module.energy.dal.mysql.detail.EnergyHydrogenDetailMapper;
|
||||
import cn.iocoder.yudao.module.energy.enums.*;
|
||||
import cn.iocoder.yudao.module.energy.event.BillApprovedEvent;
|
||||
import cn.iocoder.yudao.module.energy.event.BillCreatedEvent;
|
||||
import cn.iocoder.yudao.module.energy.event.BillAuditPassedEvent;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
@@ -91,9 +90,11 @@ public class EnergyBillServiceImpl implements EnergyBillService {
|
||||
.build();
|
||||
billMapper.insert(bill);
|
||||
|
||||
// 4. Publish event to update detail billIds
|
||||
// 4. Update detail billIds directly (no event needed)
|
||||
List<Long> detailIds = details.stream().map(EnergyHydrogenDetailDO::getId).toList();
|
||||
eventPublisher.publishEvent(new BillCreatedEvent(bill.getId(), detailIds));
|
||||
detailMapper.update(null, new com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<EnergyHydrogenDetailDO>()
|
||||
.set(EnergyHydrogenDetailDO::getBillId, bill.getId())
|
||||
.in(EnergyHydrogenDetailDO::getId, detailIds));
|
||||
|
||||
return bill.getId();
|
||||
}
|
||||
@@ -160,7 +161,7 @@ public class EnergyBillServiceImpl implements EnergyBillService {
|
||||
if (approved) {
|
||||
List<EnergyHydrogenDetailDO> details = detailMapper.selectListByBillId(id);
|
||||
List<Long> detailIds = details.stream().map(EnergyHydrogenDetailDO::getId).toList();
|
||||
eventPublisher.publishEvent(new BillApprovedEvent(id, detailIds));
|
||||
eventPublisher.publishEvent(new BillAuditPassedEvent(id, detailIds));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.service.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.energy.controller.admin.config.vo.EnergyStationConfigPageReqVO;
|
||||
import cn.iocoder.yudao.module.energy.controller.admin.config.vo.EnergyStationConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.energy.dal.dataobject.config.EnergyStationConfigDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface EnergyStationConfigService {
|
||||
Long createConfig(EnergyStationConfigSaveReqVO createReqVO);
|
||||
void updateConfig(EnergyStationConfigSaveReqVO updateReqVO);
|
||||
EnergyStationConfigDO getConfig(Long id);
|
||||
PageResult<EnergyStationConfigDO> getConfigPage(EnergyStationConfigPageReqVO pageReqVO);
|
||||
EnergyStationConfigDO getByStationId(Long stationId);
|
||||
List<EnergyStationConfigDO> getConfigList();
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package cn.iocoder.yudao.module.energy.service.config;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.module.energy.controller.admin.config.vo.EnergyStationConfigPageReqVO;
|
||||
import cn.iocoder.yudao.module.energy.controller.admin.config.vo.EnergyStationConfigSaveReqVO;
|
||||
import cn.iocoder.yudao.module.energy.convert.config.EnergyStationConfigConvert;
|
||||
import cn.iocoder.yudao.module.energy.dal.dataobject.config.EnergyStationConfigDO;
|
||||
import cn.iocoder.yudao.module.energy.dal.mysql.config.EnergyStationConfigMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.module.energy.enums.ErrorCodeConstants.*;
|
||||
|
||||
@Service
|
||||
@Validated
|
||||
public class EnergyStationConfigServiceImpl implements EnergyStationConfigService {
|
||||
|
||||
@Resource
|
||||
private EnergyStationConfigMapper stationConfigMapper;
|
||||
|
||||
@Override
|
||||
public Long createConfig(EnergyStationConfigSaveReqVO createReqVO) {
|
||||
// 校验站点唯一
|
||||
EnergyStationConfigDO existing = stationConfigMapper.selectByStationId(createReqVO.getStationId());
|
||||
if (existing != null) {
|
||||
throw exception(STATION_CONFIG_DUPLICATE);
|
||||
}
|
||||
EnergyStationConfigDO config = EnergyStationConfigConvert.INSTANCE.convert(createReqVO);
|
||||
stationConfigMapper.insert(config);
|
||||
return config.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateConfig(EnergyStationConfigSaveReqVO updateReqVO) {
|
||||
validateConfigExists(updateReqVO.getId());
|
||||
EnergyStationConfigDO updateObj = EnergyStationConfigConvert.INSTANCE.convert(updateReqVO);
|
||||
stationConfigMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnergyStationConfigDO getConfig(Long id) {
|
||||
return stationConfigMapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<EnergyStationConfigDO> getConfigPage(EnergyStationConfigPageReqVO pageReqVO) {
|
||||
return stationConfigMapper.selectPage(pageReqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnergyStationConfigDO getByStationId(Long stationId) {
|
||||
return stationConfigMapper.selectByStationId(stationId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EnergyStationConfigDO> getConfigList() {
|
||||
return stationConfigMapper.selectList();
|
||||
}
|
||||
|
||||
private void validateConfigExists(Long id) {
|
||||
if (stationConfigMapper.selectById(id) == null) {
|
||||
throw exception(STATION_CONFIG_NOT_EXISTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,9 @@ 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.event.RecordMatchedEvent;
|
||||
import java.util.List;
|
||||
|
||||
public interface HydrogenDetailService {
|
||||
void createFromRecord(RecordMatchedEvent event);
|
||||
void updateDetail(HydrogenDetailSaveReqVO reqVO);
|
||||
PageResult<EnergyHydrogenDetailDO> getDetailPage(HydrogenDetailPageReqVO pageReqVO);
|
||||
EnergyHydrogenDetailDO getDetail(Long id);
|
||||
|
||||
@@ -12,9 +12,7 @@ import cn.iocoder.yudao.module.energy.enums.AuditStatusEnum;
|
||||
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.DetailAuditedEvent;
|
||||
import cn.iocoder.yudao.module.energy.event.DetailCreatedEvent;
|
||||
import cn.iocoder.yudao.module.energy.event.RecordMatchedEvent;
|
||||
import cn.iocoder.yudao.module.energy.event.DetailAuditPassedEvent;
|
||||
import cn.iocoder.yudao.module.energy.service.price.EnergyStationPriceService;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import jakarta.annotation.Resource;
|
||||
@@ -44,54 +42,6 @@ public class HydrogenDetailServiceImpl implements HydrogenDetailService {
|
||||
@Resource
|
||||
private ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void createFromRecord(RecordMatchedEvent event) {
|
||||
// 1. Get original record
|
||||
EnergyHydrogenRecordDO record = recordMapper.selectById(event.getRecordId());
|
||||
if (record == null) {
|
||||
log.warn("[createFromRecord] record not found: {}", event.getRecordId());
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Get price
|
||||
EnergyStationPriceDO price = stationPriceService.getEffectivePrice(
|
||||
event.getStationId(), event.getCustomerId(), record.getHydrogenDate());
|
||||
BigDecimal costPrice = price != null ? price.getCostPrice() : record.getUnitPrice();
|
||||
BigDecimal customerPrice = price != null ? price.getCustomerPrice() : record.getUnitPrice();
|
||||
|
||||
// 3. Calculate amounts
|
||||
BigDecimal costAmount = record.getHydrogenQuantity().multiply(costPrice).setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal customerAmount = record.getHydrogenQuantity().multiply(customerPrice).setScale(2, RoundingMode.HALF_UP);
|
||||
|
||||
// 4. Build detail - NOTE: contractId, costBearer, payMethod would normally come from asset/rental API
|
||||
// For now, use defaults. TODO: integrate with asset module for contract lookup
|
||||
EnergyHydrogenDetailDO detail = EnergyHydrogenDetailDO.builder()
|
||||
.recordId(event.getRecordId())
|
||||
.stationId(event.getStationId())
|
||||
.vehicleId(event.getVehicleId())
|
||||
.plateNumber(event.getPlateNumber())
|
||||
.hydrogenDate(record.getHydrogenDate())
|
||||
.hydrogenQuantity(record.getHydrogenQuantity())
|
||||
.costPrice(costPrice)
|
||||
.costAmount(costAmount)
|
||||
.customerPrice(customerPrice)
|
||||
.customerAmount(customerAmount)
|
||||
.customerId(event.getCustomerId())
|
||||
.costBearer(CostBearerEnum.CUSTOMER.getType())
|
||||
.payMethod(PayMethodEnum.PREPAID.getType())
|
||||
.auditStatus(AuditStatusEnum.PENDING.getStatus())
|
||||
.deductionStatus(DeductionStatusEnum.NOT_DEDUCTED.getStatus())
|
||||
.settlementStatus(0) // NOT_SETTLED
|
||||
.build();
|
||||
detailMapper.insert(detail);
|
||||
|
||||
// 5. Publish event (BEFORE_COMMIT listener will handle auto-deduction if configured)
|
||||
eventPublisher.publishEvent(new DetailCreatedEvent(
|
||||
detail.getId(), event.getStationId(), event.getCustomerId(),
|
||||
detail.getContractId(), customerAmount));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDetail(HydrogenDetailSaveReqVO reqVO) {
|
||||
validateDetailExists(reqVO.getId());
|
||||
@@ -134,7 +84,7 @@ public class HydrogenDetailServiceImpl implements HydrogenDetailService {
|
||||
|
||||
// If approved, publish event for deduction
|
||||
if (approved) {
|
||||
eventPublisher.publishEvent(new DetailAuditedEvent(
|
||||
eventPublisher.publishEvent(new DetailAuditPassedEvent(
|
||||
id, detail.getStationId(), detail.getCustomerId(),
|
||||
detail.getContractId(), detail.getCustomerAmount()));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.iocoder.yudao.module.energy.service.match;
|
||||
|
||||
import cn.iocoder.yudao.module.energy.dal.dataobject.record.EnergyHydrogenRecordDO;
|
||||
import cn.iocoder.yudao.module.energy.service.match.dto.MatchResultDTO;
|
||||
import cn.iocoder.yudao.module.energy.service.match.vo.MatchResultVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 加氢记录匹配服务
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public interface HydrogenMatchService {
|
||||
|
||||
/**
|
||||
* 批量自动匹配记录
|
||||
*
|
||||
* @param recordIds 记录ID列表
|
||||
* @return 匹配结果统计(成功数、失败数)
|
||||
*/
|
||||
MatchResultDTO batchMatch(List<Long> recordIds);
|
||||
|
||||
/**
|
||||
* 单条记录匹配
|
||||
*
|
||||
* @param record 原始记录
|
||||
* @return 匹配结果(vehicleId, customerId, contractId)
|
||||
*/
|
||||
MatchResultVO matchRecord(EnergyHydrogenRecordDO record);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
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.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;
|
||||
import cn.iocoder.yudao.module.energy.service.match.vo.MatchResultVO;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 加氢记录匹配服务实现
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class HydrogenMatchServiceImpl implements HydrogenMatchService {
|
||||
|
||||
@Resource
|
||||
private EnergyHydrogenRecordMapper hydrogenRecordMapper;
|
||||
|
||||
@Resource
|
||||
private VehicleBaseMapper vehicleBaseMapper;
|
||||
|
||||
@Resource
|
||||
private CustomerMapper customerMapper;
|
||||
|
||||
@Resource
|
||||
private ContractMapper contractMapper;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public MatchResultDTO batchMatch(List<Long> recordIds) {
|
||||
MatchResultDTO result = new MatchResultDTO();
|
||||
|
||||
for (Long recordId : recordIds) {
|
||||
EnergyHydrogenRecordDO record = hydrogenRecordMapper.selectById(recordId);
|
||||
if (record == null) {
|
||||
log.warn("[batchMatch] 记录不存在,recordId={}", recordId);
|
||||
result.setFailCount(result.getFailCount() + 1);
|
||||
result.getFailIds().add(recordId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 执行匹配
|
||||
MatchResultVO matchResult = matchRecord(record);
|
||||
|
||||
// 更新记录
|
||||
EnergyHydrogenRecordDO updateRecord = new EnergyHydrogenRecordDO();
|
||||
updateRecord.setId(recordId);
|
||||
updateRecord.setVehicleId(matchResult.getVehicleId());
|
||||
updateRecord.setCustomerId(matchResult.getCustomerId());
|
||||
updateRecord.setMatchStatus(matchResult.getMatchStatus());
|
||||
updateRecord.setUpdateTime(LocalDateTime.now());
|
||||
|
||||
hydrogenRecordMapper.updateById(updateRecord);
|
||||
|
||||
// 统计结果
|
||||
if (matchResult.getMatchStatus() == 0) {
|
||||
result.setSuccessCount(result.getSuccessCount() + 1);
|
||||
result.getSuccessIds().add(recordId);
|
||||
log.info("[batchMatch] 匹配成功,recordId={}, vehicleId={}, customerId={}, contractId={}",
|
||||
recordId, matchResult.getVehicleId(), matchResult.getCustomerId(), matchResult.getContractId());
|
||||
} else {
|
||||
result.setFailCount(result.getFailCount() + 1);
|
||||
result.getFailIds().add(recordId);
|
||||
log.warn("[batchMatch] 匹配失败,recordId={}, status={}, message={}",
|
||||
recordId, matchResult.getMatchStatus(), matchResult.getMatchMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MatchResultVO matchRecord(EnergyHydrogenRecordDO record) {
|
||||
MatchResultVO result = new MatchResultVO();
|
||||
|
||||
// 1. 匹配车辆(通过车牌号)
|
||||
if (StrUtil.isNotBlank(record.getPlateNumber())) {
|
||||
VehicleBaseDO vehicle = vehicleBaseMapper.selectOne(
|
||||
VehicleBaseDO::getPlateNo, record.getPlateNumber()
|
||||
);
|
||||
if (vehicle != null) {
|
||||
result.setVehicleId(vehicle.getId());
|
||||
log.debug("[matchRecord] 车辆匹配成功,plateNumber={}, vehicleId={}",
|
||||
record.getPlateNumber(), vehicle.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 匹配客户
|
||||
// 2.1 如果车辆未匹配,尝试通过客户名称模糊匹配
|
||||
if (result.getCustomerId() == null && StrUtil.isNotBlank(record.getPlateNumber())) {
|
||||
// 注意:这里假设 plateNumber 字段可能包含客户信息,实际需要根据业务调整
|
||||
// 如果 Record 有独立的 customerName 字段,应该使用那个字段
|
||||
CustomerDO customer = customerMapper.selectOne(
|
||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<CustomerDO>()
|
||||
.like(CustomerDO::getCustomerName, record.getPlateNumber())
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
if (customer != null) {
|
||||
result.setCustomerId(customer.getId());
|
||||
log.debug("[matchRecord] 客户匹配成功(通过名称),customerId={}", customer.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 匹配合同(通过客户ID)
|
||||
if (result.getCustomerId() != null) {
|
||||
ContractDO contract = contractMapper.selectOne(
|
||||
new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<ContractDO>()
|
||||
.eq(ContractDO::getCustomerId, result.getCustomerId())
|
||||
.eq(ContractDO::getContractStatus, 2) // 2=进行中
|
||||
.gt(ContractDO::getEndDate, record.getHydrogenDate())
|
||||
.orderByDesc(ContractDO::getStartDate)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
if (contract != null) {
|
||||
result.setContractId(contract.getId());
|
||||
log.debug("[matchRecord] 合同匹配成功,contractId={}", contract.getId());
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 判断匹配状态
|
||||
if (result.getVehicleId() != null && result.getCustomerId() != null && result.getContractId() != null) {
|
||||
result.setMatchStatus(0); // 完全匹配
|
||||
result.setMatchMessage("自动匹配成功");
|
||||
} else if (result.getCustomerId() != null) {
|
||||
result.setMatchStatus(1); // 部分匹配
|
||||
result.setMatchMessage("客户匹配成功,但缺少车辆或合同信息");
|
||||
} else {
|
||||
result.setMatchStatus(2); // 未匹配
|
||||
result.setMatchMessage("未找到匹配的客户");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package cn.iocoder.yudao.module.energy.service.match.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 批量匹配结果 DTO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class MatchResultDTO {
|
||||
|
||||
/**
|
||||
* 成功数量
|
||||
*/
|
||||
private Integer successCount = 0;
|
||||
|
||||
/**
|
||||
* 失败数量
|
||||
*/
|
||||
private Integer failCount = 0;
|
||||
|
||||
/**
|
||||
* 成功的记录ID列表
|
||||
*/
|
||||
private List<Long> successIds = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* 失败的记录ID列表
|
||||
*/
|
||||
private List<Long> failIds = new ArrayList<>();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package cn.iocoder.yudao.module.energy.service.match.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 匹配结果 VO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public class MatchResultVO {
|
||||
|
||||
/**
|
||||
* 车辆ID
|
||||
*/
|
||||
private Long vehicleId;
|
||||
|
||||
/**
|
||||
* 客户ID
|
||||
*/
|
||||
private Long customerId;
|
||||
|
||||
/**
|
||||
* 合同ID
|
||||
*/
|
||||
private Long contractId;
|
||||
|
||||
/**
|
||||
* 匹配状态(0=完全匹配,1=部分匹配,2=未匹配)
|
||||
*/
|
||||
private Integer matchStatus;
|
||||
|
||||
/**
|
||||
* 匹配说明
|
||||
*/
|
||||
private String matchMessage;
|
||||
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import cn.iocoder.yudao.module.energy.dal.mysql.record.EnergyHydrogenRecordMappe
|
||||
import cn.iocoder.yudao.module.energy.enums.MatchStatusEnum;
|
||||
import cn.iocoder.yudao.module.energy.enums.SourceTypeEnum;
|
||||
import cn.iocoder.yudao.module.energy.event.RecordImportedEvent;
|
||||
import cn.iocoder.yudao.module.energy.event.RecordMatchedEvent;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
@@ -132,8 +131,7 @@ public class HydrogenRecordServiceImpl implements HydrogenRecordService {
|
||||
.customerId(customerId)
|
||||
.matchStatus(MatchStatusEnum.MATCHED.getStatus())
|
||||
.build());
|
||||
// Publish event
|
||||
eventPublisher.publishEvent(new RecordMatchedEvent(id, record.getStationId(), vehicleId, customerId, record.getPlateNumber()));
|
||||
// 手动匹配功能保留,批量匹配和明细生成由 RecordImportedEvent 统一处理
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
server:
|
||||
port: 48085
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: energy-server
|
||||
|
||||
# 允许 Bean 覆盖
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
|
||||
# 禁用 Nacos 配置中心
|
||||
cloud:
|
||||
nacos:
|
||||
config:
|
||||
enabled: false
|
||||
import-check:
|
||||
enabled: false
|
||||
|
||||
# 数据源配置
|
||||
datasource:
|
||||
druid:
|
||||
url: jdbc:mysql://47.103.115.36:3306/oneos_energy?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
|
||||
username: root
|
||||
password: Passw0rd2026
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
initial-size: 5
|
||||
min-idle: 5
|
||||
max-active: 20
|
||||
max-wait: 60000
|
||||
test-while-idle: true
|
||||
test-on-borrow: false
|
||||
test-on-return: false
|
||||
|
||||
# Redis 配置
|
||||
data:
|
||||
redis:
|
||||
host: 47.103.115.36
|
||||
port: 6379
|
||||
database: 0
|
||||
password: Passw0rd2026
|
||||
timeout: 10s
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 8
|
||||
max-idle: 8
|
||||
min-idle: 0
|
||||
max-wait: -1ms
|
||||
|
||||
# MyBatis Plus 配置
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: auto
|
||||
logic-delete-field: deleted
|
||||
logic-delete-value: 1
|
||||
logic-not-delete-value: 0
|
||||
type-aliases-package: cn.iocoder.yudao.module.energy.dal.dataobject
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
root: INFO
|
||||
cn.iocoder.yudao.module.energy: DEBUG
|
||||
cn.iocoder.yudao.framework: INFO
|
||||
pattern:
|
||||
console: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n'
|
||||
|
||||
# 芋道配置
|
||||
yudao:
|
||||
info:
|
||||
version: 1.0.0
|
||||
base-package: cn.iocoder.yudao.module.energy
|
||||
web:
|
||||
admin-api:
|
||||
prefix: /admin-api
|
||||
controller: '**.controller.admin.**'
|
||||
admin-ui:
|
||||
url: http://localhost:3000
|
||||
security:
|
||||
permit-all_urls: []
|
||||
xss:
|
||||
enable: false
|
||||
access-log:
|
||||
enable: true
|
||||
error-code:
|
||||
enable: true
|
||||
demo: false
|
||||
tenant:
|
||||
enable: false
|
||||
|
||||
# Swagger 配置
|
||||
springdoc:
|
||||
api-docs:
|
||||
enabled: true
|
||||
path: /v3/api-docs
|
||||
swagger-ui:
|
||||
enabled: true
|
||||
path: /swagger-ui.html
|
||||
|
||||
knife4j:
|
||||
enable: true
|
||||
setting:
|
||||
language: zh_cn
|
||||
@@ -0,0 +1,27 @@
|
||||
spring:
|
||||
application:
|
||||
name: energy-server
|
||||
|
||||
profiles:
|
||||
active: dev
|
||||
|
||||
# 允许 Bean 覆盖
|
||||
main:
|
||||
allow-bean-definition-overriding: true
|
||||
|
||||
config:
|
||||
import:
|
||||
- optional:nacos:common-dev.yaml
|
||||
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml
|
||||
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: ${NACOS_ADDR:localhost:8848}
|
||||
namespace: ${NACOS_NAMESPACE:dev}
|
||||
username: ${NACOS_USERNAME:nacos}
|
||||
password: ${NACOS_PASSWORD:nacos}
|
||||
discovery:
|
||||
namespace: ${NACOS_NAMESPACE:dev}
|
||||
config:
|
||||
namespace: ${NACOS_NAMESPACE:dev}
|
||||
file-extension: yaml
|
||||
Reference in New Issue
Block a user