feat(energy): 新增 cooperation_type 和 auto_match 字段,标记旧导入接口废弃

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kkfluous
2026-03-16 00:37:09 +08:00
parent 57ecce2649
commit 7792c5157f
102 changed files with 4426 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
-- energy_alter_frontend.sql
ALTER TABLE energy_bill ADD COLUMN cooperation_type TINYINT DEFAULT NULL COMMENT '合作模式1=预充值 2=月结算)' AFTER station_name;
ALTER TABLE energy_station_config ADD COLUMN auto_match TINYINT DEFAULT 1 COMMENT '自动匹配开关0=关闭 1=开启)' AFTER cooperation_type;

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-module-energy</artifactId>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>能源账单模块,包含加氢记录、费用明细、账单管理、账户管理等功能</description>
<url>https://github.com/YunaiV/yudao-cloud</url>
<modules>
<module>yudao-module-energy-api</module>
<module>yudao-module-energy-server</module>
</modules>
</project>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>yudao-module-energy</artifactId>
<groupId>cn.iocoder.cloud</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-module-energy-api</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>能源账单 API定义枚举、错误码等</description>
<url>https://github.com/YunaiV/yudao-cloud</url>
<dependencies>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-common</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum AccountStatusEnum {
NORMAL(0, "正常"),
WARNING(1, "预警"),
OVERDUE(2, "欠费");
private final Integer status;
private final String name;
public static AccountStatusEnum valueOf(Integer status) {
return Arrays.stream(values())
.filter(item -> item.getStatus().equals(status))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum AdjustmentTypeEnum {
INCREASE(0, "调增"),
DECREASE(1, "调减");
private final Integer type;
private final String name;
public static AdjustmentTypeEnum valueOf(Integer type) {
return Arrays.stream(values())
.filter(item -> item.getType().equals(type))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum AuditStatusEnum {
PENDING(0, "待审核"),
APPROVED(1, "已通过"),
REJECTED(2, "已驳回");
private final Integer status;
private final String name;
public static AuditStatusEnum valueOf(Integer status) {
return Arrays.stream(values())
.filter(item -> item.getStatus().equals(status))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum BillAuditStatusEnum {
PENDING(0, "待审核"),
APPROVED(1, "已通过"),
REJECTED(2, "已驳回");
private final Integer status;
private final String name;
public static BillAuditStatusEnum valueOf(Integer status) {
return Arrays.stream(values())
.filter(item -> item.getStatus().equals(status))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum BillStatusEnum {
DRAFT(0, "草稿"),
GENERATED(1, "已生成"),
VOID(2, "已作废");
private final Integer status;
private final String name;
public static BillStatusEnum valueOf(Integer status) {
return Arrays.stream(values())
.filter(item -> item.getStatus().equals(status))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum BillSubmitStatusEnum {
NOT_SUBMITTED(0, "未提交"),
SUBMITTED(1, "已提交"),
REJECTED(2, "已驳回");
private final Integer status;
private final String name;
public static BillSubmitStatusEnum valueOf(Integer status) {
return Arrays.stream(values())
.filter(item -> item.getStatus().equals(status))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum BizTypeEnum {
HYDROGEN_DEDUCTION(0, "加氢扣款"),
BILL_SETTLEMENT(1, "账单结算"),
MANUAL_ADJUST(2, "手动调整"),
RECHARGE(3, "充值"),
REMIT(4, "汇款");
private final Integer type;
private final String name;
public static BizTypeEnum valueOf(Integer type) {
return Arrays.stream(values())
.filter(item -> item.getType().equals(type))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum CooperationTypeEnum {
COOPERATED(0, "合作"),
NON_COOPERATED(1, "非合作");
private final Integer type;
private final String name;
public static CooperationTypeEnum valueOf(Integer type) {
return Arrays.stream(values())
.filter(item -> item.getType().equals(type))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum CostBearerEnum {
CUSTOMER(0, "客户承担"),
COMPANY(1, "羚牛承担"),
SELF_SETTLE(2, "自行结算");
private final Integer type;
private final String name;
public static CostBearerEnum valueOf(Integer type) {
return Arrays.stream(values())
.filter(item -> item.getType().equals(type))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum DeductionStatusEnum {
NOT_DEDUCTED(0, "未扣款"),
DEDUCTED(1, "已扣款");
private final Integer status;
private final String name;
public static DeductionStatusEnum valueOf(Integer status) {
return Arrays.stream(values())
.filter(item -> item.getStatus().equals(status))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum EnergyTypeEnum {
HYDROGEN(0, "氢费"),
ELECTRIC(1, "电费"),
ETC(2, "ETC");
private final Integer type;
private final String name;
public static EnergyTypeEnum valueOf(Integer type) {
return Arrays.stream(values())
.filter(item -> item.getType().equals(type))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.energy.enums;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
public interface ErrorCodeConstants {
// ========== 加氢记录 1-050-000-000 ==========
ErrorCode HYDROGEN_RECORD_NOT_EXISTS = new ErrorCode(1_050_000_000, "加氢记录不存在");
ErrorCode HYDROGEN_RECORD_IMPORT_EMPTY = new ErrorCode(1_050_000_001, "导入数据为空");
ErrorCode HYDROGEN_RECORD_DUPLICATE = new ErrorCode(1_050_000_002, "存在重复导入记录");
// ========== 加氢明细 1-050-001-000 ==========
ErrorCode HYDROGEN_DETAIL_NOT_EXISTS = new ErrorCode(1_050_001_000, "加氢明细不存在");
ErrorCode HYDROGEN_DETAIL_ALREADY_AUDITED = new ErrorCode(1_050_001_001, "明细已审核,不可重复审核");
// ========== 能源账单 1-050-002-000 ==========
ErrorCode ENERGY_BILL_NOT_EXISTS = new ErrorCode(1_050_002_000, "能源账单不存在");
ErrorCode ENERGY_BILL_NOT_DRAFT = new ErrorCode(1_050_002_001, "账单非草稿状态,不可删除");
ErrorCode ENERGY_BILL_ALREADY_AUDITED = new ErrorCode(1_050_002_002, "账单已审核");
ErrorCode ENERGY_BILL_NO_DETAILS = new ErrorCode(1_050_002_003, "无可生成账单的明细");
// ========== 能源账户 1-050-003-000 ==========
ErrorCode ENERGY_ACCOUNT_NOT_EXISTS = new ErrorCode(1_050_003_000, "能源账户不存在");
ErrorCode ENERGY_ACCOUNT_VERSION_CONFLICT = new ErrorCode(1_050_003_001, "账户余额更新冲突,请重试");
// ========== 价格管理 1-050-004-000 ==========
ErrorCode STATION_PRICE_NOT_EXISTS = new ErrorCode(1_050_004_000, "站点价格不存在");
ErrorCode STATION_PRICE_DUPLICATE = new ErrorCode(1_050_004_001, "同一站点同一客户同一生效日期已存在价格");
// ========== 站点配置 1-050-005-000 ==========
ErrorCode STATION_CONFIG_NOT_EXISTS = new ErrorCode(1_050_005_000, "站点配置不存在");
ErrorCode STATION_CONFIG_DUPLICATE = new ErrorCode(1_050_005_001, "该站点配置已存在");
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum FlowTypeEnum {
RECHARGE(0, "充值"),
DEDUCTION(1, "扣款"),
REVERSAL(2, "冲正"),
REMIT(3, "汇款"),
REFUND(4, "退款");
private final Integer type;
private final String name;
public static FlowTypeEnum valueOf(Integer type) {
return Arrays.stream(values())
.filter(item -> item.getType().equals(type))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum MatchStatusEnum {
UNMATCHED(0, "未匹配"),
MATCHED(1, "已匹配"),
UNMATCHABLE(2, "无法匹配");
private final Integer status;
private final String name;
public static MatchStatusEnum valueOf(Integer status) {
return Arrays.stream(values())
.filter(item -> item.getStatus().equals(status))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum PayMethodEnum {
PREPAID(0, "预充值"),
MONTHLY(1, "月结算");
private final Integer type;
private final String name;
public static PayMethodEnum valueOf(Integer type) {
return Arrays.stream(values())
.filter(item -> item.getType().equals(type))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum PaymentStatusEnum {
UNPAID(0, "未支付"),
PARTIAL(1, "部分支付"),
PAID(2, "已支付");
private final Integer status;
private final String name;
public static PaymentStatusEnum valueOf(Integer status) {
return Arrays.stream(values())
.filter(item -> item.getStatus().equals(status))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum SettlementStatusEnum {
NOT_SETTLED(0, "未结算"),
SETTLED(1, "已结算");
private final Integer status;
private final String name;
public static SettlementStatusEnum valueOf(Integer status) {
return Arrays.stream(values())
.filter(item -> item.getStatus().equals(status))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.energy.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum SourceTypeEnum {
EXCEL(0, "Excel导入"),
WEB(1, "Web录入"),
API(2, "API对接"),
OCR(3, "OCR识别");
private final Integer type;
private final String name;
public static SourceTypeEnum valueOf(Integer type) {
return Arrays.stream(values())
.filter(item -> item.getType().equals(type))
.findFirst().orElse(null);
}
}

View File

@@ -0,0 +1,4 @@
/**
* 能源账单 API 模块,定义枚举、错误码等
*/
package cn.iocoder.yudao.module.energy;

View File

@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>yudao-module-energy</artifactId>
<groupId>cn.iocoder.cloud</groupId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-module-energy-server</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>能源账单业务实现</description>
<url>https://github.com/YunaiV/yudao-cloud</url>
<dependencies>
<!-- 环境配置 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-env</artifactId>
</dependency>
<!-- 能源账单 API -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-energy-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 系统模块 API -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 基础设施模块 API -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-infra-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- Spring Cloud 基础 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-rpc</artifactId>
</dependency>
<!-- Registry 注册中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Job 定时任务相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-job</artifactId>
</dependency>
<!-- 消息队列相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-mq</artifactId>
</dependency>
<!-- Test 测试相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-excel</artifactId>
</dependency>
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- 打包 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,89 @@
package cn.iocoder.yudao.module.energy.controller.admin.account;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.account.vo.*;
import cn.iocoder.yudao.module.energy.convert.account.EnergyAccountConvert;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyAccountDO;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyAccountFlowDO;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyProjectAccountDO;
import cn.iocoder.yudao.module.energy.service.account.EnergyAccountService;
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.math.BigDecimal;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 能源账户管理")
@RestController
@RequestMapping("/energy/account")
@Validated
public class EnergyAccountController {
@Resource
private EnergyAccountService accountService;
@GetMapping("/page")
@Operation(summary = "获得账户分页")
@PreAuthorize("@ss.hasPermission('energy:account:query')")
public CommonResult<PageResult<EnergyAccountRespVO>> getAccountPage(@Valid EnergyAccountPageReqVO pageReqVO) {
PageResult<EnergyAccountDO> pageResult = accountService.getAccountPage(pageReqVO);
return success(EnergyAccountConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/get")
@Operation(summary = "获得账户详情")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('energy:account:query')")
public CommonResult<EnergyAccountRespVO> getAccount(@RequestParam("id") Long id) {
EnergyAccountDO account = accountService.getAccount(id);
return success(EnergyAccountConvert.INSTANCE.convert(account));
}
@PostMapping("/recharge")
@Operation(summary = "账户充值")
@PreAuthorize("@ss.hasPermission('energy:account:recharge')")
public CommonResult<Boolean> recharge(@RequestParam("customerId") Long customerId,
@RequestParam("amount") BigDecimal amount,
@RequestParam(value = "remark", required = false) String remark) {
accountService.recharge(customerId, amount, null, null, remark);
return success(true);
}
@PutMapping("/update-threshold")
@Operation(summary = "更新预警阈值")
@PreAuthorize("@ss.hasPermission('energy:account:update')")
public CommonResult<Boolean> updateThreshold(@RequestParam("id") Long id,
@RequestParam("threshold") BigDecimal threshold) {
accountService.updateThreshold(id, threshold);
return success(true);
}
// ===== 项目账户 =====
@GetMapping("/project/list")
@Operation(summary = "获得项目账户列表")
@Parameter(name = "accountId", description = "总账户ID", required = true)
@PreAuthorize("@ss.hasPermission('energy:account:query')")
public CommonResult<List<EnergyProjectAccountRespVO>> getProjectAccountList(@RequestParam("accountId") Long accountId) {
List<EnergyProjectAccountDO> list = accountService.getProjectAccountsByAccountId(accountId);
return success(cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList(list, EnergyAccountConvert.INSTANCE::convert));
}
// ===== 流水查询 =====
@GetMapping("/flow/page")
@Operation(summary = "获得流水分页")
@PreAuthorize("@ss.hasPermission('energy:account:query')")
public CommonResult<PageResult<EnergyAccountFlowDO>> getFlowPage(@Valid EnergyAccountFlowPageReqVO pageReqVO) {
return success(accountService.getFlowPage(pageReqVO));
}
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.energy.controller.admin.account.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;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 账户流水分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class EnergyAccountFlowPageReqVO extends PageParam {
@Schema(description = "总账户ID")
private Long accountId;
@Schema(description = "项目账户ID")
private Long projectAccountId;
@Schema(description = "流水类型")
private Integer flowType;
@Schema(description = "业务类型")
private Integer bizType;
@Schema(description = "开始时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.energy.controller.admin.account.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 EnergyAccountPageReqVO extends PageParam {
@Schema(description = "客户ID")
private Long customerId;
@Schema(description = "账户状态")
private Integer accountStatus;
}

View File

@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.energy.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 能源账户 Response VO")
@Data
public class EnergyAccountRespVO {
@Schema(description = "主键ID")
private Long id;
@Schema(description = "客户ID")
private Long customerId;
@Schema(description = "当前余额")
private BigDecimal balance;
@Schema(description = "初始余额")
private BigDecimal initBalance;
@Schema(description = "累计充值")
private BigDecimal accumulatedRecharge;
@Schema(description = "累计氢费")
private BigDecimal accumulatedHydrogen;
@Schema(description = "累计电费")
private BigDecimal accumulatedElectric;
@Schema(description = "累计消费")
private BigDecimal accumulatedConsume;
@Schema(description = "提醒阈值")
private BigDecimal reminderThreshold;
@Schema(description = "账户状态")
private Integer accountStatus;
@Schema(description = "最后充值日期")
private LocalDate lastRechargeDate;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.energy.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 能源账户创建 Request VO")
@Data
public class EnergyAccountSaveReqVO {
@Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "客户不能为空")
private Long customerId;
@Schema(description = "初始余额")
private BigDecimal initBalance;
@Schema(description = "提醒阈值")
private BigDecimal reminderThreshold;
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.energy.controller.admin.account.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 项目账户 Response VO")
@Data
public class EnergyProjectAccountRespVO {
@Schema(description = "主键ID")
private Long id;
@Schema(description = "关联总账户ID")
private Long accountId;
@Schema(description = "合同ID")
private Long contractId;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "项目余额")
private BigDecimal projectBalance;
@Schema(description = "累计划账金额")
private BigDecimal projectRemitAmount;
@Schema(description = "累计氢费")
private BigDecimal projectHydrogenAmount;
@Schema(description = "累计电费")
private BigDecimal projectElectricAmount;
@Schema(description = "累计消费")
private BigDecimal projectConsumeAmount;
@Schema(description = "提醒阈值")
private BigDecimal reminderThreshold;
@Schema(description = "账户状态")
private Integer accountStatus;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,104 @@
package cn.iocoder.yudao.module.energy.controller.admin.bill;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.bill.vo.*;
import cn.iocoder.yudao.module.energy.convert.bill.EnergyBillConvert;
import cn.iocoder.yudao.module.energy.dal.dataobject.bill.EnergyBillDO;
import cn.iocoder.yudao.module.energy.service.bill.EnergyBillService;
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/bill")
@Validated
public class EnergyBillController {
@Resource
private EnergyBillService billService;
@GetMapping("/page")
@Operation(summary = "获得账单分页")
@PreAuthorize("@ss.hasPermission('energy:bill:query')")
public CommonResult<PageResult<EnergyBillRespVO>> getBillPage(@Valid EnergyBillPageReqVO pageReqVO) {
PageResult<EnergyBillDO> pageResult = billService.getBillPage(pageReqVO);
return success(EnergyBillConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/get")
@Operation(summary = "获得账单详情")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('energy:bill:query')")
public CommonResult<EnergyBillRespVO> getBill(@RequestParam("id") Long id) {
EnergyBillDO bill = billService.getBill(id);
return success(EnergyBillConvert.INSTANCE.convert(bill));
}
@PostMapping("/generate")
@Operation(summary = "生成账单")
@PreAuthorize("@ss.hasPermission('energy:bill:create')")
public CommonResult<Long> generateBill(@Valid @RequestBody EnergyBillGenerateReqVO reqVO) {
return success(billService.generateBill(reqVO));
}
@PostMapping("/batch-generate")
@Operation(summary = "批量生成账单")
@PreAuthorize("@ss.hasPermission('energy:bill:create')")
public CommonResult<List<Long>> batchGenerateBills(@Valid @RequestBody List<EnergyBillGenerateReqVO> reqVOs) {
return success(billService.batchGenerateBills(reqVOs));
}
@PutMapping("/update")
@Operation(summary = "更新账单")
@PreAuthorize("@ss.hasPermission('energy:bill:update')")
public CommonResult<Boolean> updateBill(@Valid @RequestBody EnergyBillSaveReqVO reqVO) {
billService.updateBill(reqVO);
return success(true);
}
@PostMapping("/audit")
@Operation(summary = "审核账单")
@PreAuthorize("@ss.hasPermission('energy:bill:audit')")
public CommonResult<Boolean> auditBill(@RequestParam("id") Long id,
@RequestParam("approved") Boolean approved,
@RequestParam(value = "remark", required = false) String remark) {
billService.auditBill(id, approved, remark);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除账单")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('energy:bill:delete')")
public CommonResult<Boolean> deleteBill(@RequestParam("id") Long id) {
billService.deleteBill(id);
return success(true);
}
@PostMapping("/adjustment/create")
@Operation(summary = "创建调整行")
@PreAuthorize("@ss.hasPermission('energy:bill:update')")
public CommonResult<Long> createAdjustment(@Valid @RequestBody EnergyBillAdjustmentSaveReqVO reqVO) {
return success(billService.createAdjustment(reqVO));
}
@DeleteMapping("/adjustment/delete")
@Operation(summary = "删除调整行")
@Parameter(name = "id", description = "调整行ID", required = true)
@PreAuthorize("@ss.hasPermission('energy:bill:update')")
public CommonResult<Boolean> deleteAdjustment(@RequestParam("id") Long id) {
billService.deleteAdjustment(id);
return success(true);
}
}

View File

@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.energy.controller.admin.bill.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 账单调整行创建 Request VO")
@Data
public class EnergyBillAdjustmentSaveReqVO {
@Schema(description = "账单ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "账单不能为空")
private Long billId;
@Schema(description = "关联明细ID")
private Long detailId;
@Schema(description = "调整类型", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "调整类型不能为空")
private Integer adjustmentType;
@Schema(description = "调整金额", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "调整金额不能为空")
private BigDecimal amount;
@Schema(description = "调整原因")
private String reason;
@Schema(description = "附件URL")
private String attachmentUrls;
}

View File

@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.energy.controller.admin.bill.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.time.LocalDate;
@Schema(description = "管理后台 - 能源账单生成 Request VO")
@Data
public class EnergyBillGenerateReqVO {
@Schema(description = "能源类型", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "能源类型不能为空")
private Integer energyType;
@Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "客户不能为空")
private Long customerId;
@Schema(description = "合同ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "合同不能为空")
private Long contractId;
@Schema(description = "加氢站ID")
private Long stationId;
@Schema(description = "账单周期开始", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "账单周期开始不能为空")
private LocalDate billPeriodStart;
@Schema(description = "账单周期结束", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "账单周期结束不能为空")
private LocalDate billPeriodEnd;
}

View File

@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.energy.controller.admin.bill.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;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
@Schema(description = "管理后台 - 能源账单分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class EnergyBillPageReqVO extends PageParam {
@Schema(description = "能源类型")
private Integer energyType;
@Schema(description = "客户ID")
private Long customerId;
@Schema(description = "加氢站ID")
private Long stationId;
@Schema(description = "账单状态")
private Integer status;
@Schema(description = "审核状态")
private Integer auditStatus;
@Schema(description = "账单周期")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate[] billPeriod;
@Schema(description = "合作模式")
private Integer cooperationType;
}

View File

@@ -0,0 +1,64 @@
package cn.iocoder.yudao.module.energy.controller.admin.bill.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 能源账单 Response VO")
@Data
public class EnergyBillRespVO {
@Schema(description = "主键ID")
private Long id;
@Schema(description = "账单编号")
private String billCode;
@Schema(description = "能源类型")
private Integer energyType;
@Schema(description = "客户ID")
private Long customerId;
@Schema(description = "客户名称")
private String customerName;
@Schema(description = "合同ID")
private Long contractId;
@Schema(description = "加氢站ID")
private Long stationId;
@Schema(description = "站点名称")
private String stationName;
@Schema(description = "合作模式1=预充值 2=月结算)")
private Integer cooperationType;
@Schema(description = "账单周期开始")
private LocalDate billPeriodStart;
@Schema(description = "账单周期结束")
private LocalDate billPeriodEnd;
@Schema(description = "应收总额")
private BigDecimal receivableAmount;
@Schema(description = "实收总额")
private BigDecimal actualAmount;
@Schema(description = "调整总额")
private BigDecimal adjustmentAmount;
@Schema(description = "已收金额")
private BigDecimal paidAmount;
@Schema(description = "总加氢量")
private BigDecimal totalQuantity;
@Schema(description = "明细条数")
private Integer detailCount;
@Schema(description = "账单状态")
private Integer status;
@Schema(description = "审核状态")
private Integer auditStatus;
@Schema(description = "提交状态")
private Integer submitStatus;
@Schema(description = "支付状态")
private Integer paymentStatus;
@Schema(description = "审核人ID")
private Long auditorId;
@Schema(description = "审核时间")
private LocalDateTime auditTime;
@Schema(description = "审核备注")
private String auditRemark;
@Schema(description = "生成时间")
private LocalDateTime generateTime;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,15 @@
package cn.iocoder.yudao.module.energy.controller.admin.bill.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - 能源账单更新 Request VO")
@Data
public class EnergyBillSaveReqVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "账单ID不能为空")
private Long id;
@Schema(description = "审核备注")
private String auditRemark;
}

View File

@@ -0,0 +1,62 @@
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.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 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));
}
}

View File

@@ -0,0 +1,20 @@
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;
}

View File

@@ -0,0 +1,26 @@
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;
}

View File

@@ -0,0 +1,24 @@
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;
}

View File

@@ -0,0 +1,75 @@
package cn.iocoder.yudao.module.energy.controller.admin.detail;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
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 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/hydrogen-detail")
@Validated
public class HydrogenDetailController {
@Resource
private HydrogenDetailService hydrogenDetailService;
@GetMapping("/page")
@Operation(summary = "获得明细分页")
@PreAuthorize("@ss.hasPermission('energy:hydrogen-detail:query')")
public CommonResult<PageResult<HydrogenDetailRespVO>> getDetailPage(@Valid HydrogenDetailPageReqVO pageReqVO) {
PageResult<EnergyHydrogenDetailDO> pageResult = hydrogenDetailService.getDetailPage(pageReqVO);
return success(HydrogenDetailConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/get")
@Operation(summary = "获得明细详情")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('energy:hydrogen-detail:query')")
public CommonResult<HydrogenDetailRespVO> getDetail(@RequestParam("id") Long id) {
EnergyHydrogenDetailDO detail = hydrogenDetailService.getDetail(id);
return success(HydrogenDetailConvert.INSTANCE.convert(detail));
}
@PutMapping("/update")
@Operation(summary = "更新明细")
@PreAuthorize("@ss.hasPermission('energy:hydrogen-detail:update')")
public CommonResult<Boolean> updateDetail(@Valid @RequestBody HydrogenDetailSaveReqVO reqVO) {
hydrogenDetailService.updateDetail(reqVO);
return success(true);
}
@PostMapping("/audit")
@Operation(summary = "审核明细")
@PreAuthorize("@ss.hasPermission('energy:hydrogen-detail:audit')")
public CommonResult<Boolean> auditDetail(@RequestParam("id") Long id,
@RequestParam("approved") Boolean approved,
@RequestParam(value = "remark", required = false) String remark) {
hydrogenDetailService.audit(id, approved, remark);
return success(true);
}
@PostMapping("/batch-audit")
@Operation(summary = "批量审核明细")
@PreAuthorize("@ss.hasPermission('energy:hydrogen-detail:audit')")
public CommonResult<Boolean> batchAuditDetail(@RequestParam("ids") List<Long> ids,
@RequestParam("approved") Boolean approved,
@RequestParam(value = "remark", required = false) String remark) {
hydrogenDetailService.batchAudit(ids, approved, remark);
return success(true);
}
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.energy.controller.admin.detail.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;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
@Schema(description = "管理后台 - 加氢明细分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class HydrogenDetailPageReqVO extends PageParam {
@Schema(description = "加氢站ID")
private Long stationId;
@Schema(description = "客户ID")
private Long customerId;
@Schema(description = "车牌号")
private String plateNumber;
@Schema(description = "审核状态")
private Integer auditStatus;
@Schema(description = "扣款状态")
private Integer deductionStatus;
@Schema(description = "结算状态")
private Integer settlementStatus;
@Schema(description = "账单ID")
private Long billId;
@Schema(description = "加氢日期范围")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate[] hydrogenDate;
}

View File

@@ -0,0 +1,58 @@
package cn.iocoder.yudao.module.energy.controller.admin.detail.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 加氢明细 Response VO")
@Data
public class HydrogenDetailRespVO {
@Schema(description = "主键ID")
private Long id;
@Schema(description = "关联原始记录ID")
private Long recordId;
@Schema(description = "加氢站ID")
private Long stationId;
@Schema(description = "车辆ID")
private Long vehicleId;
@Schema(description = "车牌号")
private String plateNumber;
@Schema(description = "加氢日期")
private LocalDate hydrogenDate;
@Schema(description = "加氢量(KG)")
private BigDecimal hydrogenQuantity;
@Schema(description = "成本单价")
private BigDecimal costPrice;
@Schema(description = "成本金额")
private BigDecimal costAmount;
@Schema(description = "对客单价")
private BigDecimal customerPrice;
@Schema(description = "对客金额")
private BigDecimal customerAmount;
@Schema(description = "合同ID")
private Long contractId;
@Schema(description = "客户ID")
private Long customerId;
@Schema(description = "项目名称")
private String projectName;
@Schema(description = "费用承担方")
private Integer costBearer;
@Schema(description = "支付方式")
private Integer payMethod;
@Schema(description = "审核状态")
private Integer auditStatus;
@Schema(description = "审核备注")
private String auditRemark;
@Schema(description = "扣款状态")
private Integer deductionStatus;
@Schema(description = "结算状态")
private Integer settlementStatus;
@Schema(description = "账单ID")
private Long billId;
@Schema(description = "备注")
private String remark;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.energy.controller.admin.detail.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - 加氢明细编辑 Request VO")
@Data
public class HydrogenDetailSaveReqVO {
@Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "明细ID不能为空")
private Long id;
@Schema(description = "成本单价")
private BigDecimal costPrice;
@Schema(description = "对客单价")
private BigDecimal customerPrice;
@Schema(description = "备注")
private String remark;
}

View File

@@ -0,0 +1,82 @@
package cn.iocoder.yudao.module.energy.controller.admin.price;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.price.vo.*;
import cn.iocoder.yudao.module.energy.convert.price.EnergyStationPriceConvert;
import cn.iocoder.yudao.module.energy.dal.dataobject.price.EnergyStationPriceDO;
import cn.iocoder.yudao.module.energy.service.price.EnergyStationPriceService;
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.time.LocalDate;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 加氢站价格管理")
@RestController
@RequestMapping("/energy/station-price")
@Validated
public class EnergyStationPriceController {
@Resource
private EnergyStationPriceService stationPriceService;
@PostMapping("/create")
@Operation(summary = "创建价格")
@PreAuthorize("@ss.hasPermission('energy:station-price:create')")
public CommonResult<Long> createPrice(@Valid @RequestBody EnergyStationPriceSaveReqVO createReqVO) {
return success(stationPriceService.createPrice(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新价格")
@PreAuthorize("@ss.hasPermission('energy:station-price:update')")
public CommonResult<Boolean> updatePrice(@Valid @RequestBody EnergyStationPriceSaveReqVO updateReqVO) {
stationPriceService.updatePrice(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除价格")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('energy:station-price:delete')")
public CommonResult<Boolean> deletePrice(@RequestParam("id") Long id) {
stationPriceService.deletePrice(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得价格")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('energy:station-price:query')")
public CommonResult<EnergyStationPriceRespVO> getPrice(@RequestParam("id") Long id) {
EnergyStationPriceDO price = stationPriceService.getPrice(id);
return success(EnergyStationPriceConvert.INSTANCE.convert(price));
}
@GetMapping("/page")
@Operation(summary = "获得价格分页")
@PreAuthorize("@ss.hasPermission('energy:station-price:query')")
public CommonResult<PageResult<EnergyStationPriceRespVO>> getPricePage(@Valid EnergyStationPricePageReqVO pageReqVO) {
PageResult<EnergyStationPriceDO> pageResult = stationPriceService.getPricePage(pageReqVO);
return success(EnergyStationPriceConvert.INSTANCE.convertPage(pageResult));
}
@GetMapping("/get-effective")
@Operation(summary = "获取生效价格")
@PreAuthorize("@ss.hasPermission('energy:station-price:query')")
public CommonResult<EnergyStationPriceRespVO> getEffectivePrice(
@RequestParam("stationId") Long stationId,
@RequestParam("customerId") Long customerId,
@RequestParam("date") LocalDate date) {
EnergyStationPriceDO price = stationPriceService.getEffectivePrice(stationId, customerId, date);
return success(EnergyStationPriceConvert.INSTANCE.convert(price));
}
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.energy.controller.admin.price.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 EnergyStationPricePageReqVO extends PageParam {
@Schema(description = "加氢站ID")
private Long stationId;
@Schema(description = "客户ID")
private Long customerId;
@Schema(description = "状态")
private Integer status;
}

View File

@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.energy.controller.admin.price.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 加氢站价格 Response VO")
@Data
public class EnergyStationPriceRespVO {
@Schema(description = "主键ID")
private Long id;
@Schema(description = "加氢站ID")
private Long stationId;
@Schema(description = "客户ID")
private Long customerId;
@Schema(description = "成本价")
private BigDecimal costPrice;
@Schema(description = "对客价")
private BigDecimal customerPrice;
@Schema(description = "生效日期")
private LocalDate effectiveDate;
@Schema(description = "失效日期")
private LocalDate expiryDate;
@Schema(description = "状态")
private Integer status;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.energy.controller.admin.price.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
@Schema(description = "管理后台 - 加氢站价格创建/更新 Request VO")
@Data
public class EnergyStationPriceSaveReqVO {
@Schema(description = "主键ID")
private Long id;
@Schema(description = "加氢站ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "加氢站不能为空")
private Long stationId;
@Schema(description = "客户ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "客户不能为空")
private Long customerId;
@Schema(description = "成本价", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "成本价不能为空")
private BigDecimal costPrice;
@Schema(description = "对客价", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "对客价不能为空")
private BigDecimal customerPrice;
@Schema(description = "生效日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "生效日期不能为空")
private LocalDate effectiveDate;
@Schema(description = "失效日期")
private LocalDate expiryDate;
}

View File

@@ -0,0 +1,105 @@
package cn.iocoder.yudao.module.energy.controller.admin.record;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.energy.controller.admin.record.vo.*;
import cn.iocoder.yudao.module.energy.convert.record.HydrogenRecordConvert;
import cn.iocoder.yudao.module.energy.dal.dataobject.record.EnergyHydrogenRecordDO;
import cn.iocoder.yudao.module.energy.service.record.HydrogenRecordService;
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 org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - 加氢记录管理")
@RestController
@RequestMapping("/energy/hydrogen-record")
@Validated
public class HydrogenRecordController {
@Resource
private HydrogenRecordService hydrogenRecordService;
@PostMapping("/create")
@Operation(summary = "创建加氢记录")
@PreAuthorize("@ss.hasPermission('energy:hydrogen-record:create')")
public CommonResult<Long> createRecord(@Valid @RequestBody HydrogenRecordSaveReqVO createReqVO) {
return success(hydrogenRecordService.createRecord(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新加氢记录")
@PreAuthorize("@ss.hasPermission('energy:hydrogen-record:update')")
public CommonResult<Boolean> updateRecord(@Valid @RequestBody HydrogenRecordSaveReqVO updateReqVO) {
hydrogenRecordService.updateRecord(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除加氢记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('energy:hydrogen-record:delete')")
public CommonResult<Boolean> deleteRecord(@RequestParam("id") Long id) {
hydrogenRecordService.deleteRecord(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得加氢记录")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('energy:hydrogen-record:query')")
public CommonResult<HydrogenRecordRespVO> getRecord(@RequestParam("id") Long id) {
EnergyHydrogenRecordDO record = hydrogenRecordService.getRecord(id);
return success(HydrogenRecordConvert.INSTANCE.convert(record));
}
@GetMapping("/page")
@Operation(summary = "获得加氢记录分页")
@PreAuthorize("@ss.hasPermission('energy:hydrogen-record:query')")
public CommonResult<PageResult<HydrogenRecordRespVO>> getRecordPage(@Valid HydrogenRecordPageReqVO pageReqVO) {
PageResult<EnergyHydrogenRecordDO> pageResult = hydrogenRecordService.getRecordPage(pageReqVO);
return success(HydrogenRecordConvert.INSTANCE.convertPage(pageResult));
}
@Deprecated // 被 import-preview + import-confirm + import-progress 三步流程替代
@PostMapping("/import")
@Operation(summary = "Excel 批量导入")
@PreAuthorize("@ss.hasPermission('energy:hydrogen-record:import')")
public CommonResult<Map<String, Integer>> importExcel(
@RequestParam("stationId") Long stationId,
@RequestParam("file") MultipartFile file) throws Exception {
List<HydrogenRecordImportVO> list = ExcelUtils.read(file, HydrogenRecordImportVO.class);
String batchNo = generateBatchNo();
Map<String, Integer> result = hydrogenRecordService.importRecords(stationId, list, batchNo);
return success(result);
}
@PutMapping("/match")
@Operation(summary = "手动匹配车辆")
@PreAuthorize("@ss.hasPermission('energy:hydrogen-record:update')")
public CommonResult<Boolean> matchRecord(@RequestParam("id") Long id,
@RequestParam("vehicleId") Long vehicleId,
@RequestParam("customerId") Long customerId) {
hydrogenRecordService.matchRecord(id, vehicleId, customerId);
return success(true);
}
private String generateBatchNo() {
return "IMP" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
+ UUID.randomUUID().toString().substring(0, 4).toUpperCase();
}
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.energy.controller.admin.record.vo;
import cn.idev.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Data
public class HydrogenRecordExcelVO {
@ExcelProperty("车牌号")
private String plateNumber;
@ExcelProperty("加氢日期")
private LocalDate hydrogenDate;
@ExcelProperty("加氢量(KG)")
private BigDecimal hydrogenQuantity;
@ExcelProperty("单价")
private BigDecimal unitPrice;
@ExcelProperty("金额")
private BigDecimal amount;
@ExcelProperty("里程数")
private BigDecimal mileage;
@ExcelProperty("数据来源")
private String sourceTypeName;
@ExcelProperty("匹配状态")
private String matchStatusName;
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.controller.admin.record.vo;
import cn.idev.excel.annotation.ExcelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
public class HydrogenRecordImportVO {
@ExcelProperty("车牌号")
private String plateNumber;
@ExcelProperty("加氢日期")
private LocalDate hydrogenDate;
@ExcelProperty("加氢量(KG)")
private BigDecimal hydrogenQuantity;
@ExcelProperty("单价")
private BigDecimal unitPrice;
@ExcelProperty("金额")
private BigDecimal amount;
@ExcelProperty("里程数")
private BigDecimal mileage;
}

View File

@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.energy.controller.admin.record.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;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
@Schema(description = "管理后台 - 加氢记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class HydrogenRecordPageReqVO extends PageParam {
@Schema(description = "加氢站ID")
private Long stationId;
@Schema(description = "车牌号")
private String plateNumber;
@Schema(description = "匹配状态")
private Integer matchStatus;
@Schema(description = "数据来源")
private Integer sourceType;
@Schema(description = "加氢日期范围")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate[] hydrogenDate;
}

View File

@@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.energy.controller.admin.record.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 加氢记录 Response VO")
@Data
public class HydrogenRecordRespVO {
@Schema(description = "主键ID")
private Long id;
@Schema(description = "加氢站ID")
private Long stationId;
@Schema(description = "车牌号")
private String plateNumber;
@Schema(description = "加氢日期")
private LocalDate hydrogenDate;
@Schema(description = "加氢量(KG)")
private BigDecimal hydrogenQuantity;
@Schema(description = "单价")
private BigDecimal unitPrice;
@Schema(description = "金额")
private BigDecimal amount;
@Schema(description = "里程数")
private BigDecimal mileage;
@Schema(description = "数据来源")
private Integer sourceType;
@Schema(description = "匹配状态")
private Integer matchStatus;
@Schema(description = "车辆ID")
private Long vehicleId;
@Schema(description = "客户ID")
private Long customerId;
@Schema(description = "导入批次号")
private String uploadBatchNo;
@Schema(description = "创建时间")
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.energy.controller.admin.record.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDate;
@Schema(description = "管理后台 - 加氢记录创建/更新 Request VO")
@Data
public class HydrogenRecordSaveReqVO {
@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 String plateNumber;
@Schema(description = "加氢日期", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "加氢日期不能为空")
private LocalDate hydrogenDate;
@Schema(description = "加氢量(KG)", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "加氢量不能为空")
private BigDecimal hydrogenQuantity;
@Schema(description = "单价", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "单价不能为空")
private BigDecimal unitPrice;
@Schema(description = "金额", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "金额不能为空")
private BigDecimal amount;
@Schema(description = "里程数")
private BigDecimal mileage;
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.energy.convert.account;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.account.vo.EnergyAccountRespVO;
import cn.iocoder.yudao.module.energy.controller.admin.account.vo.EnergyProjectAccountRespVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyAccountDO;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyProjectAccountDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface EnergyAccountConvert {
EnergyAccountConvert INSTANCE = Mappers.getMapper(EnergyAccountConvert.class);
EnergyAccountRespVO convert(EnergyAccountDO bean);
PageResult<EnergyAccountRespVO> convertPage(PageResult<EnergyAccountDO> page);
EnergyProjectAccountRespVO convert(EnergyProjectAccountDO bean);
PageResult<EnergyProjectAccountRespVO> convertProjectPage(PageResult<EnergyProjectAccountDO> page);
}

View File

@@ -0,0 +1,14 @@
package cn.iocoder.yudao.module.energy.convert.bill;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.bill.vo.EnergyBillRespVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.bill.EnergyBillDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface EnergyBillConvert {
EnergyBillConvert INSTANCE = Mappers.getMapper(EnergyBillConvert.class);
EnergyBillRespVO convert(EnergyBillDO bean);
PageResult<EnergyBillRespVO> convertPage(PageResult<EnergyBillDO> page);
}

View File

@@ -0,0 +1,16 @@
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.dal.dataobject.config.EnergyStationConfigDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface EnergyStationConfigConvert {
EnergyStationConfigConvert INSTANCE = Mappers.getMapper(EnergyStationConfigConvert.class);
EnergyStationConfigDO convert(EnergyStationConfigSaveReqVO bean);
EnergyStationConfigRespVO convert(EnergyStationConfigDO bean);
PageResult<EnergyStationConfigRespVO> convertPage(PageResult<EnergyStationConfigDO> page);
}

View File

@@ -0,0 +1,14 @@
package cn.iocoder.yudao.module.energy.convert.detail;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.detail.vo.HydrogenDetailRespVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.detail.EnergyHydrogenDetailDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface HydrogenDetailConvert {
HydrogenDetailConvert INSTANCE = Mappers.getMapper(HydrogenDetailConvert.class);
HydrogenDetailRespVO convert(EnergyHydrogenDetailDO bean);
PageResult<HydrogenDetailRespVO> convertPage(PageResult<EnergyHydrogenDetailDO> page);
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.energy.convert.price;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.price.vo.EnergyStationPriceRespVO;
import cn.iocoder.yudao.module.energy.controller.admin.price.vo.EnergyStationPriceSaveReqVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.price.EnergyStationPriceDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface EnergyStationPriceConvert {
EnergyStationPriceConvert INSTANCE = Mappers.getMapper(EnergyStationPriceConvert.class);
EnergyStationPriceDO convert(EnergyStationPriceSaveReqVO bean);
EnergyStationPriceRespVO convert(EnergyStationPriceDO bean);
PageResult<EnergyStationPriceRespVO> convertPage(PageResult<EnergyStationPriceDO> page);
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.energy.convert.record;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.record.vo.HydrogenRecordRespVO;
import cn.iocoder.yudao.module.energy.controller.admin.record.vo.HydrogenRecordSaveReqVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.record.EnergyHydrogenRecordDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface HydrogenRecordConvert {
HydrogenRecordConvert INSTANCE = Mappers.getMapper(HydrogenRecordConvert.class);
EnergyHydrogenRecordDO convert(HydrogenRecordSaveReqVO bean);
HydrogenRecordRespVO convert(EnergyHydrogenRecordDO bean);
PageResult<HydrogenRecordRespVO> convertPage(PageResult<EnergyHydrogenRecordDO> page);
}

View File

@@ -0,0 +1,86 @@
package cn.iocoder.yudao.module.energy.dal.dataobject.account;
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.*;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 能源账户 DO
*/
@TableName("energy_account")
@KeySequence("energy_account_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EnergyAccountDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 客户ID
*/
private Long customerId;
/**
* 余额
*/
private BigDecimal balance;
/**
* 初始余额
*/
private BigDecimal initBalance;
/**
* 累计充值金额
*/
private BigDecimal accumulatedRecharge;
/**
* 累计加氢金额
*/
private BigDecimal accumulatedHydrogen;
/**
* 累计充电金额
*/
private BigDecimal accumulatedElectric;
/**
* 累计消费金额
*/
private BigDecimal accumulatedConsume;
/**
* 余额预警阈值
*/
private BigDecimal reminderThreshold;
/**
* 账户状态
*/
private Integer accountStatus;
/**
* 最后充值日期
*/
private LocalDate lastRechargeDate;
/**
* 乐观锁版本号(手动控制,不使用 @Version 注解)
*/
private Integer version;
}

View File

@@ -0,0 +1,95 @@
package cn.iocoder.yudao.module.energy.dal.dataobject.account;
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.*;
import java.math.BigDecimal;
/**
* 账户流水 DO
*/
@TableName("energy_account_flow")
@KeySequence("energy_account_flow_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EnergyAccountFlowDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 账户ID
*/
private Long accountId;
/**
* 项目账户ID
*/
private Long projectAccountId;
/**
* 流水类型
*/
private Integer flowType;
/**
* 金额
*/
private BigDecimal amount;
/**
* 变更前余额
*/
private BigDecimal balanceBefore;
/**
* 变更后余额
*/
private BigDecimal balanceAfter;
/**
* 变更前项目余额
*/
private BigDecimal projectBalanceBefore;
/**
* 变更后项目余额
*/
private BigDecimal projectBalanceAfter;
/**
* 业务类型
*/
private Integer bizType;
/**
* 业务ID
*/
private Long bizId;
/**
* 业务编号
*/
private String bizCode;
/**
* 备注
*/
private String remark;
/**
* 操作人ID
*/
private Long operatorId;
}

View File

@@ -0,0 +1,85 @@
package cn.iocoder.yudao.module.energy.dal.dataobject.account;
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.*;
import java.math.BigDecimal;
/**
* 能源项目账户 DO
*/
@TableName("energy_project_account")
@KeySequence("energy_project_account_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EnergyProjectAccountDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 账户ID
*/
private Long accountId;
/**
* 合同ID
*/
private Long contractId;
/**
* 项目名称
*/
private String projectName;
/**
* 项目余额
*/
private BigDecimal projectBalance;
/**
* 项目汇款总额
*/
private BigDecimal projectRemitAmount;
/**
* 项目加氢金额
*/
private BigDecimal projectHydrogenAmount;
/**
* 项目充电金额
*/
private BigDecimal projectElectricAmount;
/**
* 项目消费总额
*/
private BigDecimal projectConsumeAmount;
/**
* 余额预警阈值
*/
private BigDecimal reminderThreshold;
/**
* 账户状态
*/
private Integer accountStatus;
/**
* 乐观锁版本号(手动控制,不使用 @Version 注解)
*/
private Integer version;
}

View File

@@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.energy.dal.dataobject.bill;
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.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 账单调整记录 DO
*/
@TableName("energy_bill_adjustment")
@KeySequence("energy_bill_adjustment_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EnergyBillAdjustmentDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 账单ID
*/
private Long billId;
/**
* 明细ID
*/
private Long detailId;
/**
* 调整类型
*/
private Integer adjustmentType;
/**
* 调整金额
*/
private BigDecimal amount;
/**
* 调整原因
*/
private String reason;
/**
* 附件URLs
*/
private String attachmentUrls;
/**
* 操作人ID
*/
private Long operatorId;
/**
* 操作时间
*/
private LocalDateTime operateTime;
}

View File

@@ -0,0 +1,162 @@
package cn.iocoder.yudao.module.energy.dal.dataobject.bill;
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.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* 能源账单 DO
*/
@TableName("energy_bill")
@KeySequence("energy_bill_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EnergyBillDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 账单编号
*/
private String billCode;
/**
* 能源类型
*/
private Integer energyType;
/**
* 客户ID
*/
private Long customerId;
/**
* 客户名称
*/
private String customerName;
/**
* 合同ID
*/
private Long contractId;
/**
* 站点ID
*/
private Long stationId;
/**
* 站点名称
*/
private String stationName;
/**
* 合作模式1=预充值 2=月结算)
*/
private Integer cooperationType;
/**
* 账单周期开始
*/
private LocalDate billPeriodStart;
/**
* 账单周期结束
*/
private LocalDate billPeriodEnd;
/**
* 应收金额
*/
private BigDecimal receivableAmount;
/**
* 实收金额
*/
private BigDecimal actualAmount;
/**
* 调整金额
*/
private BigDecimal adjustmentAmount;
/**
* 已付金额
*/
private BigDecimal paidAmount;
/**
* 总用量
*/
private BigDecimal totalQuantity;
/**
* 明细条数
*/
private Integer detailCount;
/**
* 账单状态
*/
private Integer status;
/**
* 审核状态
*/
private Integer auditStatus;
/**
* 提交状态
*/
private Integer submitStatus;
/**
* 付款状态
*/
private Integer paymentStatus;
/**
* 审核人ID
*/
private Long auditorId;
/**
* 审核时间
*/
private LocalDateTime auditTime;
/**
* 审核备注
*/
private String auditRemark;
/**
* 提交时间
*/
private LocalDateTime submitTime;
/**
* 生成时间
*/
private LocalDateTime generateTime;
/**
* YOS账单编号
*/
private String yosBillCode;
}

View File

@@ -0,0 +1,53 @@
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;
}

View File

@@ -0,0 +1,136 @@
package cn.iocoder.yudao.module.energy.dal.dataobject.detail;
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.*;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 加氢明细 DO
*/
@TableName("energy_hydrogen_detail")
@KeySequence("energy_hydrogen_detail_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EnergyHydrogenDetailDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 加氢记录ID
*/
private Long recordId;
/**
* 加氢站ID
*/
private Long stationId;
/**
* 车辆ID
*/
private Long vehicleId;
/**
* 车牌号
*/
private String plateNumber;
/**
* 加氢日期
*/
private LocalDate hydrogenDate;
/**
* 加氢量kg
*/
private BigDecimal hydrogenQuantity;
/**
* 成本价
*/
private BigDecimal costPrice;
/**
* 成本金额
*/
private BigDecimal costAmount;
/**
* 客户价
*/
private BigDecimal customerPrice;
/**
* 客户金额
*/
private BigDecimal customerAmount;
/**
* 合同ID
*/
private Long contractId;
/**
* 客户ID
*/
private Long customerId;
/**
* 项目名称
*/
private String projectName;
/**
* 费用承担方
*/
private Integer costBearer;
/**
* 支付方式
*/
private Integer payMethod;
/**
* 审核状态
*/
private Integer auditStatus;
/**
* 审核备注
*/
private String auditRemark;
/**
* 扣费状态
*/
private Integer deductionStatus;
/**
* 结算状态
*/
private Integer settlementStatus;
/**
* 账单ID
*/
private Long billId;
/**
* 备注
*/
private String remark;
}

View File

@@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.energy.dal.dataobject.price;
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.*;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 站点价格 DO
*/
@TableName("energy_station_price")
@KeySequence("energy_station_price_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EnergyStationPriceDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 站点ID
*/
private Long stationId;
/**
* 客户ID
*/
private Long customerId;
/**
* 成本价
*/
private BigDecimal costPrice;
/**
* 客户价
*/
private BigDecimal customerPrice;
/**
* 生效日期
*/
private LocalDate effectiveDate;
/**
* 失效日期
*/
private LocalDate expiryDate;
/**
* 状态
*/
private Integer status;
}

View File

@@ -0,0 +1,91 @@
package cn.iocoder.yudao.module.energy.dal.dataobject.record;
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.*;
import java.math.BigDecimal;
import java.time.LocalDate;
/**
* 加氢记录 DO
*/
@TableName("energy_hydrogen_record")
@KeySequence("energy_hydrogen_record_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EnergyHydrogenRecordDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 加氢站ID
*/
private Long stationId;
/**
* 车牌号
*/
private String plateNumber;
/**
* 加氢日期
*/
private LocalDate hydrogenDate;
/**
* 加氢量kg
*/
private BigDecimal hydrogenQuantity;
/**
* 单价
*/
private BigDecimal unitPrice;
/**
* 金额
*/
private BigDecimal amount;
/**
* 里程km
*/
private BigDecimal mileage;
/**
* 来源类型
*/
private Integer sourceType;
/**
* 匹配状态
*/
private Integer matchStatus;
/**
* 车辆ID
*/
private Long vehicleId;
/**
* 客户ID
*/
private Long customerId;
/**
* 上传批次号
*/
private String uploadBatchNo;
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.energy.dal.mysql.account;
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.account.vo.EnergyAccountFlowPageReqVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyAccountFlowDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EnergyAccountFlowMapper extends BaseMapperX<EnergyAccountFlowDO> {
default PageResult<EnergyAccountFlowDO> selectPage(EnergyAccountFlowPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<EnergyAccountFlowDO>()
.eqIfPresent(EnergyAccountFlowDO::getAccountId, reqVO.getAccountId())
.eqIfPresent(EnergyAccountFlowDO::getProjectAccountId, reqVO.getProjectAccountId())
.eqIfPresent(EnergyAccountFlowDO::getFlowType, reqVO.getFlowType())
.eqIfPresent(EnergyAccountFlowDO::getBizType, reqVO.getBizType())
.betweenIfPresent(EnergyAccountFlowDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(EnergyAccountFlowDO::getId));
}
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.energy.dal.mysql.account;
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.account.vo.EnergyAccountPageReqVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyAccountDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EnergyAccountMapper extends BaseMapperX<EnergyAccountDO> {
default EnergyAccountDO selectByCustomerId(Long customerId) {
return selectOne(EnergyAccountDO::getCustomerId, customerId);
}
default PageResult<EnergyAccountDO> selectPage(EnergyAccountPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<EnergyAccountDO>()
.eqIfPresent(EnergyAccountDO::getCustomerId, reqVO.getCustomerId())
.eqIfPresent(EnergyAccountDO::getAccountStatus, reqVO.getAccountStatus())
.orderByDesc(EnergyAccountDO::getId));
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.dal.mysql.account;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyProjectAccountDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface EnergyProjectAccountMapper extends BaseMapperX<EnergyProjectAccountDO> {
default EnergyProjectAccountDO selectByContractId(Long contractId) {
return selectOne(EnergyProjectAccountDO::getContractId, contractId);
}
default List<EnergyProjectAccountDO> selectListByAccountId(Long accountId) {
return selectList(new LambdaQueryWrapperX<EnergyProjectAccountDO>()
.eq(EnergyProjectAccountDO::getAccountId, accountId));
}
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.energy.dal.mysql.bill;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.energy.dal.dataobject.bill.EnergyBillAdjustmentDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface EnergyBillAdjustmentMapper extends BaseMapperX<EnergyBillAdjustmentDO> {
default List<EnergyBillAdjustmentDO> selectListByBillId(Long billId) {
return selectList(new LambdaQueryWrapperX<EnergyBillAdjustmentDO>()
.eq(EnergyBillAdjustmentDO::getBillId, billId));
}
}

View File

@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.energy.dal.mysql.bill;
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.bill.vo.EnergyBillPageReqVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.bill.EnergyBillDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EnergyBillMapper extends BaseMapperX<EnergyBillDO> {
default PageResult<EnergyBillDO> selectPage(EnergyBillPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<EnergyBillDO>()
.eqIfPresent(EnergyBillDO::getEnergyType, reqVO.getEnergyType())
.eqIfPresent(EnergyBillDO::getCustomerId, reqVO.getCustomerId())
.eqIfPresent(EnergyBillDO::getStationId, reqVO.getStationId())
.eqIfPresent(EnergyBillDO::getStatus, reqVO.getStatus())
.eqIfPresent(EnergyBillDO::getAuditStatus, reqVO.getAuditStatus())
.betweenIfPresent(EnergyBillDO::getBillPeriodStart, reqVO.getBillPeriod())
.orderByDesc(EnergyBillDO::getId));
}
}

View File

@@ -0,0 +1,25 @@
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));
}
}

View File

@@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.energy.dal.mysql.detail;
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.detail.vo.HydrogenDetailPageReqVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.detail.EnergyHydrogenDetailDO;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDate;
import java.util.List;
@Mapper
public interface EnergyHydrogenDetailMapper extends BaseMapperX<EnergyHydrogenDetailDO> {
default PageResult<EnergyHydrogenDetailDO> selectPage(HydrogenDetailPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<EnergyHydrogenDetailDO>()
.eqIfPresent(EnergyHydrogenDetailDO::getStationId, reqVO.getStationId())
.eqIfPresent(EnergyHydrogenDetailDO::getCustomerId, reqVO.getCustomerId())
.likeIfPresent(EnergyHydrogenDetailDO::getPlateNumber, reqVO.getPlateNumber())
.eqIfPresent(EnergyHydrogenDetailDO::getAuditStatus, reqVO.getAuditStatus())
.eqIfPresent(EnergyHydrogenDetailDO::getDeductionStatus, reqVO.getDeductionStatus())
.eqIfPresent(EnergyHydrogenDetailDO::getSettlementStatus, reqVO.getSettlementStatus())
.eqIfPresent(EnergyHydrogenDetailDO::getBillId, reqVO.getBillId())
.betweenIfPresent(EnergyHydrogenDetailDO::getHydrogenDate, reqVO.getHydrogenDate())
.orderByDesc(EnergyHydrogenDetailDO::getId));
}
default List<EnergyHydrogenDetailDO> selectListByBillId(Long billId) {
return selectList(new LambdaQueryWrapperX<EnergyHydrogenDetailDO>()
.eq(EnergyHydrogenDetailDO::getBillId, billId));
}
default List<EnergyHydrogenDetailDO> selectUnbilledByCondition(Long customerId, Long contractId, Long stationId, LocalDate startDate, LocalDate endDate) {
return selectList(new LambdaQueryWrapperX<EnergyHydrogenDetailDO>()
.eq(EnergyHydrogenDetailDO::getCustomerId, customerId)
.eq(EnergyHydrogenDetailDO::getContractId, contractId)
.eqIfPresent(EnergyHydrogenDetailDO::getStationId, stationId)
.ge(EnergyHydrogenDetailDO::getHydrogenDate, startDate)
.le(EnergyHydrogenDetailDO::getHydrogenDate, endDate)
.eq(EnergyHydrogenDetailDO::getAuditStatus, 1)
.eq(EnergyHydrogenDetailDO::getDeductionStatus, 1)
.isNull(EnergyHydrogenDetailDO::getBillId));
}
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.energy.dal.mysql.price;
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.price.vo.EnergyStationPricePageReqVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.price.EnergyStationPriceDO;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDate;
@Mapper
public interface EnergyStationPriceMapper extends BaseMapperX<EnergyStationPriceDO> {
default PageResult<EnergyStationPriceDO> selectPage(EnergyStationPricePageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<EnergyStationPriceDO>()
.eqIfPresent(EnergyStationPriceDO::getStationId, reqVO.getStationId())
.eqIfPresent(EnergyStationPriceDO::getCustomerId, reqVO.getCustomerId())
.eqIfPresent(EnergyStationPriceDO::getStatus, reqVO.getStatus())
.orderByDesc(EnergyStationPriceDO::getId));
}
default EnergyStationPriceDO selectEffective(Long stationId, Long customerId, LocalDate date) {
return selectOne(new LambdaQueryWrapperX<EnergyStationPriceDO>()
.eq(EnergyStationPriceDO::getStationId, stationId)
.eq(EnergyStationPriceDO::getCustomerId, customerId)
.le(EnergyStationPriceDO::getEffectiveDate, date)
.and(w -> w.ge(EnergyStationPriceDO::getExpiryDate, date)
.or().isNull(EnergyStationPriceDO::getExpiryDate))
.eq(EnergyStationPriceDO::getStatus, 0)
.orderByDesc(EnergyStationPriceDO::getEffectiveDate)
.last("LIMIT 1"));
}
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.energy.dal.mysql.record;
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.record.vo.HydrogenRecordPageReqVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.record.EnergyHydrogenRecordDO;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EnergyHydrogenRecordMapper extends BaseMapperX<EnergyHydrogenRecordDO> {
default PageResult<EnergyHydrogenRecordDO> selectPage(HydrogenRecordPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<EnergyHydrogenRecordDO>()
.eqIfPresent(EnergyHydrogenRecordDO::getStationId, reqVO.getStationId())
.likeIfPresent(EnergyHydrogenRecordDO::getPlateNumber, reqVO.getPlateNumber())
.eqIfPresent(EnergyHydrogenRecordDO::getMatchStatus, reqVO.getMatchStatus())
.eqIfPresent(EnergyHydrogenRecordDO::getSourceType, reqVO.getSourceType())
.betweenIfPresent(EnergyHydrogenRecordDO::getHydrogenDate, reqVO.getHydrogenDate())
.orderByDesc(EnergyHydrogenRecordDO::getId));
}
}

View File

@@ -0,0 +1,13 @@
package cn.iocoder.yudao.module.energy.event;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@Getter
@AllArgsConstructor
public class BillApprovedEvent {
private final Long billId;
private final List<Long> detailIds;
}

View File

@@ -0,0 +1,13 @@
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;
}

View File

@@ -0,0 +1,10 @@
package cn.iocoder.yudao.module.energy.event;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class DeductionCompletedEvent {
private final Long detailId;
}

View File

@@ -0,0 +1,16 @@
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;
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.energy.event;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.math.BigDecimal;
@Getter
@AllArgsConstructor
public class DetailCreatedEvent {
private final Long detailId;
private final Long stationId;
private final Long customerId;
private final Long contractId;
private final BigDecimal customerAmount;
}

View File

@@ -0,0 +1,13 @@
package cn.iocoder.yudao.module.energy.event;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@Getter
@AllArgsConstructor
public class RecordImportedEvent {
private final Long stationId;
private final List<Long> recordIds;
private final String batchNo;
}

View File

@@ -0,0 +1,14 @@
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;
}

View File

@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.energy.framework.security.config;
import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
/**
* Energy 模块的 Security 配置
*/
@Configuration("energySecurityConfiguration")
public class SecurityConfiguration {
@Bean("energyAuthorizeRequestsCustomizer")
public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
return new AuthorizeRequestsCustomizer() {
@Override
public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
// Swagger 接口文档
registry.requestMatchers("/v3/api-docs/**").permitAll()
.requestMatchers("/webjars/**").permitAll()
.requestMatchers("/swagger-ui").permitAll()
.requestMatchers("/swagger-ui/**").permitAll();
}
};
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.framework.web.config;
import cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Energy 模块的 Web 配置
*/
@Configuration(proxyBeanMethods = false)
public class EnergyWebConfiguration {
/**
* energy-server 模块的 API 分组
*/
@Bean
public GroupedOpenApi energyGroupedOpenApi() {
return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("energy");
}
}

View File

@@ -0,0 +1,85 @@
package cn.iocoder.yudao.module.energy.job;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyAccountDO;
import cn.iocoder.yudao.module.energy.dal.mysql.account.EnergyAccountMapper;
import cn.iocoder.yudao.module.energy.enums.AccountStatusEnum;
import com.xxl.job.core.handler.annotation.XxlJob;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.List;
/**
* 账户余额预警定时任务
*
* 功能:
* 1. 检查正常账户,余额低于预警阈值则更新为预警状态
* 2. 检查正常账户余额小于0则更新为欠费状态
* 3. 检查预警/欠费账户,余额恢复正常则更新为正常状态
*/
@Component
@Slf4j
public class AccountWarningJob {
@Resource
private EnergyAccountMapper accountMapper;
@XxlJob("accountWarningJob")
@TenantJob
public void execute() {
log.info("[accountWarningJob] 开始执行账户余额预警检查");
try {
int warningCount = 0;
int overdueCount = 0;
int normalCount = 0;
// 1. 检查正常账户,余额低于阈值则更新为预警/欠费
List<EnergyAccountDO> normalAccounts = accountMapper.selectList(new LambdaQueryWrapperX<EnergyAccountDO>()
.eq(EnergyAccountDO::getAccountStatus, AccountStatusEnum.NORMAL.getStatus())
.isNotNull(EnergyAccountDO::getReminderThreshold));
for (EnergyAccountDO account : normalAccounts) {
if (account.getBalance().compareTo(BigDecimal.ZERO) < 0) {
updateStatus(account.getId(), AccountStatusEnum.OVERDUE.getStatus());
overdueCount++;
log.info("[accountWarningJob] 账户 {} 余额为负,更新为欠费状态", account.getId());
} else if (account.getBalance().compareTo(account.getReminderThreshold()) < 0) {
updateStatus(account.getId(), AccountStatusEnum.WARNING.getStatus());
warningCount++;
log.info("[accountWarningJob] 账户 {} 余额低于预警阈值,更新为预警状态", account.getId());
}
}
// 2. 检查预警/欠费账户,余额恢复则更新为正常
List<EnergyAccountDO> abnormalAccounts = accountMapper.selectList(new LambdaQueryWrapperX<EnergyAccountDO>()
.in(EnergyAccountDO::getAccountStatus,
AccountStatusEnum.WARNING.getStatus(),
AccountStatusEnum.OVERDUE.getStatus())
.isNotNull(EnergyAccountDO::getReminderThreshold));
for (EnergyAccountDO account : abnormalAccounts) {
if (account.getBalance().compareTo(account.getReminderThreshold()) >= 0
&& account.getBalance().compareTo(BigDecimal.ZERO) >= 0) {
updateStatus(account.getId(), AccountStatusEnum.NORMAL.getStatus());
normalCount++;
log.info("[accountWarningJob] 账户 {} 余额已恢复,更新为正常状态", account.getId());
}
}
log.info("[accountWarningJob] 执行完成: 新增预警={}, 新增欠费={}, 恢复正常={}", warningCount, overdueCount, normalCount);
} catch (Exception e) {
log.error("[accountWarningJob] 执行失败", e);
}
}
private void updateStatus(Long accountId, Integer status) {
EnergyAccountDO update = new EnergyAccountDO();
update.setId(accountId);
update.setAccountStatus(status);
accountMapper.updateById(update);
}
}

View File

@@ -0,0 +1,56 @@
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.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;
@Component
@Slf4j
public class AccountEventListener {
@Resource
private EnergyAccountService accountService;
@Resource
private EnergyStationConfigMapper stationConfigMapper;
@Resource
private HydrogenDetailService detailService;
/**
* 明细创建完成 → 若配置自动扣款则扣款
* BEFORE_COMMIT: 资金操作,必须同事务
*/
@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());
}
}
}

View File

@@ -0,0 +1,38 @@
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.service.detail.HydrogenDetailService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionalEventListener;
@Component
@Slf4j
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());
detailService.updateSettlementStatus(event.getDetailIds(), SettlementStatusEnum.SETTLED.getStatus());
}
}

View File

@@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.energy.listener;
import cn.iocoder.yudao.module.energy.event.RecordImportedEvent;
import cn.iocoder.yudao.module.energy.event.RecordMatchedEvent;
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.TransactionalEventListener;
@Component
@Slf4j
public class DetailEventListener {
@Resource
private HydrogenDetailService hydrogenDetailService;
/**
* 记录导入完成 → 触发自动匹配(占位,后续实现批量自动匹配)
* AFTER_COMMIT: 非资金操作
*/
@TransactionalEventListener
public void onRecordImported(RecordImportedEvent event) {
log.info("[onRecordImported] stationId={}, recordCount={}, batchNo={}",
event.getStationId(), event.getRecordIds().size(), event.getBatchNo());
// TODO: 后续接入 asset 模块实现批量自动匹配
// hydrogenRecordService.batchMatch(event.getRecordIds());
}
/**
* 记录匹配完成 → 创建明细
* AFTER_COMMIT: 明细创建在独立事务中
*/
@TransactionalEventListener
public void onRecordMatched(RecordMatchedEvent event) {
log.info("[onRecordMatched] recordId={}, vehicleId={}, customerId={}",
event.getRecordId(), event.getVehicleId(), event.getCustomerId());
hydrogenDetailService.createFromRecord(event);
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.energy.service.account;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.account.vo.EnergyAccountFlowPageReqVO;
import cn.iocoder.yudao.module.energy.controller.admin.account.vo.EnergyAccountPageReqVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyAccountDO;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyAccountFlowDO;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyProjectAccountDO;
import java.math.BigDecimal;
import java.util.List;
public interface EnergyAccountService {
PageResult<EnergyAccountDO> getAccountPage(EnergyAccountPageReqVO reqVO);
EnergyAccountDO getAccount(Long id);
EnergyAccountDO getOrCreateByCustomerId(Long customerId);
EnergyProjectAccountDO getOrCreateByContractId(Long accountId, Long contractId, String projectName);
List<EnergyProjectAccountDO> getProjectAccountsByAccountId(Long accountId);
void deduct(Long customerId, Long contractId, BigDecimal amount, Long bizId, String bizCode, String remark);
void recharge(Long customerId, BigDecimal amount, Long bizId, String bizCode, String remark);
void updateThreshold(Long id, BigDecimal threshold);
PageResult<EnergyAccountFlowDO> getFlowPage(EnergyAccountFlowPageReqVO reqVO);
}

View File

@@ -0,0 +1,273 @@
package cn.iocoder.yudao.module.energy.service.account;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.account.vo.EnergyAccountFlowPageReqVO;
import cn.iocoder.yudao.module.energy.controller.admin.account.vo.EnergyAccountPageReqVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyAccountDO;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyAccountFlowDO;
import cn.iocoder.yudao.module.energy.dal.dataobject.account.EnergyProjectAccountDO;
import cn.iocoder.yudao.module.energy.dal.mysql.account.EnergyAccountFlowMapper;
import cn.iocoder.yudao.module.energy.dal.mysql.account.EnergyAccountMapper;
import cn.iocoder.yudao.module.energy.dal.mysql.account.EnergyProjectAccountMapper;
import cn.iocoder.yudao.module.energy.enums.AccountStatusEnum;
import cn.iocoder.yudao.module.energy.enums.BizTypeEnum;
import cn.iocoder.yudao.module.energy.enums.FlowTypeEnum;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
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
@Slf4j
public class EnergyAccountServiceImpl implements EnergyAccountService {
@Resource
private EnergyAccountMapper accountMapper;
@Resource
private EnergyProjectAccountMapper projectAccountMapper;
@Resource
private EnergyAccountFlowMapper accountFlowMapper;
@Override
public PageResult<EnergyAccountDO> getAccountPage(EnergyAccountPageReqVO reqVO) {
return accountMapper.selectPage(reqVO);
}
@Override
public EnergyAccountDO getAccount(Long id) {
return accountMapper.selectById(id);
}
@Override
public EnergyAccountDO getOrCreateByCustomerId(Long customerId) {
EnergyAccountDO account = accountMapper.selectByCustomerId(customerId);
if (account != null) {
return account;
}
// Auto-create
account = EnergyAccountDO.builder()
.customerId(customerId)
.balance(BigDecimal.ZERO)
.initBalance(BigDecimal.ZERO)
.accumulatedRecharge(BigDecimal.ZERO)
.accumulatedHydrogen(BigDecimal.ZERO)
.accumulatedElectric(BigDecimal.ZERO)
.accumulatedConsume(BigDecimal.ZERO)
.accountStatus(AccountStatusEnum.NORMAL.getStatus())
.version(0)
.build();
accountMapper.insert(account);
return account;
}
@Override
public EnergyProjectAccountDO getOrCreateByContractId(Long accountId, Long contractId, String projectName) {
EnergyProjectAccountDO projectAccount = projectAccountMapper.selectByContractId(contractId);
if (projectAccount != null) {
return projectAccount;
}
projectAccount = EnergyProjectAccountDO.builder()
.accountId(accountId)
.contractId(contractId)
.projectName(projectName)
.projectBalance(BigDecimal.ZERO)
.projectRemitAmount(BigDecimal.ZERO)
.projectHydrogenAmount(BigDecimal.ZERO)
.projectElectricAmount(BigDecimal.ZERO)
.projectConsumeAmount(BigDecimal.ZERO)
.accountStatus(AccountStatusEnum.NORMAL.getStatus())
.version(0)
.build();
projectAccountMapper.insert(projectAccount);
return projectAccount;
}
@Override
public List<EnergyProjectAccountDO> getProjectAccountsByAccountId(Long accountId) {
return projectAccountMapper.selectListByAccountId(accountId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deduct(Long customerId, Long contractId, BigDecimal amount, Long bizId, String bizCode, String remark) {
// 1. getOrCreate 总账户
EnergyAccountDO account = getOrCreateByCustomerId(customerId);
BigDecimal balanceBefore = account.getBalance();
BigDecimal balanceAfter = balanceBefore.subtract(amount).setScale(2, RoundingMode.HALF_UP);
// 2. 乐观锁更新总账户余额重试3次
boolean updated = updateAccountWithRetry(account, balanceAfter, amount, true);
if (!updated) {
throw exception(ENERGY_ACCOUNT_VERSION_CONFLICT);
}
// Re-read for correct balanceBefore after retry
if (!balanceBefore.equals(account.getBalance())) {
balanceBefore = account.getBalance();
balanceAfter = balanceBefore.subtract(amount).setScale(2, RoundingMode.HALF_UP);
}
// 3. 更新项目账户
BigDecimal projectBalanceBefore = null;
BigDecimal projectBalanceAfter = null;
Long projectAccountId = null;
if (contractId != null) {
EnergyProjectAccountDO projectAccount = getOrCreateByContractId(account.getId(), contractId, null);
projectBalanceBefore = projectAccount.getProjectBalance();
projectBalanceAfter = projectBalanceBefore.subtract(amount).setScale(2, RoundingMode.HALF_UP);
boolean projectUpdated = updateProjectAccountWithRetry(projectAccount, projectBalanceAfter, amount, true);
if (!projectUpdated) {
throw exception(ENERGY_ACCOUNT_VERSION_CONFLICT);
}
projectAccountId = projectAccount.getId();
}
// 4. 写流水
accountFlowMapper.insert(EnergyAccountFlowDO.builder()
.accountId(account.getId())
.projectAccountId(projectAccountId)
.flowType(FlowTypeEnum.DEDUCTION.getType())
.amount(amount)
.balanceBefore(balanceBefore)
.balanceAfter(balanceAfter)
.projectBalanceBefore(projectBalanceBefore)
.projectBalanceAfter(projectBalanceAfter)
.bizType(BizTypeEnum.HYDROGEN_DEDUCTION.getType())
.bizId(bizId)
.bizCode(bizCode)
.remark(remark)
.build());
// 5. 更新账户状态
updateAccountStatus(account.getId(), balanceAfter);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void recharge(Long customerId, BigDecimal amount, Long bizId, String bizCode, String remark) {
EnergyAccountDO account = getOrCreateByCustomerId(customerId);
BigDecimal balanceBefore = account.getBalance();
BigDecimal balanceAfter = balanceBefore.add(amount).setScale(2, RoundingMode.HALF_UP);
// Optimistic lock update
int retryCount = 0;
while (retryCount < 3) {
int rows = accountMapper.update(null, new LambdaUpdateWrapper<EnergyAccountDO>()
.set(EnergyAccountDO::getBalance, balanceAfter)
.set(EnergyAccountDO::getAccumulatedRecharge, account.getAccumulatedRecharge().add(amount))
.set(EnergyAccountDO::getLastRechargeDate, LocalDate.now())
.set(EnergyAccountDO::getVersion, account.getVersion() + 1)
.eq(EnergyAccountDO::getId, account.getId())
.eq(EnergyAccountDO::getVersion, account.getVersion()));
if (rows > 0) break;
retryCount++;
account = accountMapper.selectById(account.getId());
balanceBefore = account.getBalance();
balanceAfter = balanceBefore.add(amount).setScale(2, RoundingMode.HALF_UP);
}
if (retryCount >= 3) {
throw exception(ENERGY_ACCOUNT_VERSION_CONFLICT);
}
// Write flow
accountFlowMapper.insert(EnergyAccountFlowDO.builder()
.accountId(account.getId())
.flowType(FlowTypeEnum.RECHARGE.getType())
.amount(amount)
.balanceBefore(balanceBefore)
.balanceAfter(balanceAfter)
.bizType(BizTypeEnum.RECHARGE.getType())
.bizId(bizId)
.bizCode(bizCode)
.remark(remark)
.build());
// Update status
updateAccountStatus(account.getId(), balanceAfter);
}
@Override
public void updateThreshold(Long id, BigDecimal threshold) {
EnergyAccountDO account = accountMapper.selectById(id);
if (account == null) {
throw exception(ENERGY_ACCOUNT_NOT_EXISTS);
}
accountMapper.update(null, new LambdaUpdateWrapper<EnergyAccountDO>()
.set(EnergyAccountDO::getReminderThreshold, threshold)
.eq(EnergyAccountDO::getId, id));
}
@Override
public PageResult<EnergyAccountFlowDO> getFlowPage(EnergyAccountFlowPageReqVO reqVO) {
return accountFlowMapper.selectPage(reqVO);
}
// ===== Private helpers =====
private boolean updateAccountWithRetry(EnergyAccountDO account, BigDecimal balanceAfter, BigDecimal amount, boolean isDeduction) {
int retryCount = 0;
while (retryCount < 3) {
LambdaUpdateWrapper<EnergyAccountDO> wrapper = new LambdaUpdateWrapper<EnergyAccountDO>()
.set(EnergyAccountDO::getBalance, balanceAfter)
.set(EnergyAccountDO::getAccumulatedHydrogen, account.getAccumulatedHydrogen().add(amount))
.set(EnergyAccountDO::getAccumulatedConsume, account.getAccumulatedConsume().add(amount))
.set(EnergyAccountDO::getVersion, account.getVersion() + 1)
.eq(EnergyAccountDO::getId, account.getId())
.eq(EnergyAccountDO::getVersion, account.getVersion());
int rows = accountMapper.update(null, wrapper);
if (rows > 0) return true;
retryCount++;
account = accountMapper.selectById(account.getId());
BigDecimal newBalance = account.getBalance().subtract(amount).setScale(2, RoundingMode.HALF_UP);
// Update for next iteration
balanceAfter = newBalance;
}
return false;
}
private boolean updateProjectAccountWithRetry(EnergyProjectAccountDO projectAccount, BigDecimal balanceAfter, BigDecimal amount, boolean isDeduction) {
int retryCount = 0;
while (retryCount < 3) {
LambdaUpdateWrapper<EnergyProjectAccountDO> wrapper = new LambdaUpdateWrapper<EnergyProjectAccountDO>()
.set(EnergyProjectAccountDO::getProjectBalance, balanceAfter)
.set(EnergyProjectAccountDO::getProjectHydrogenAmount, projectAccount.getProjectHydrogenAmount().add(amount))
.set(EnergyProjectAccountDO::getProjectConsumeAmount, projectAccount.getProjectConsumeAmount().add(amount))
.set(EnergyProjectAccountDO::getVersion, projectAccount.getVersion() + 1)
.eq(EnergyProjectAccountDO::getId, projectAccount.getId())
.eq(EnergyProjectAccountDO::getVersion, projectAccount.getVersion());
int rows = projectAccountMapper.update(null, wrapper);
if (rows > 0) return true;
retryCount++;
projectAccount = projectAccountMapper.selectById(projectAccount.getId());
balanceAfter = projectAccount.getProjectBalance().subtract(amount).setScale(2, RoundingMode.HALF_UP);
}
return false;
}
private void updateAccountStatus(Long accountId, BigDecimal currentBalance) {
EnergyAccountDO account = accountMapper.selectById(accountId);
Integer newStatus;
if (currentBalance.compareTo(BigDecimal.ZERO) < 0) {
newStatus = AccountStatusEnum.OVERDUE.getStatus();
} else if (account.getReminderThreshold() != null
&& currentBalance.compareTo(account.getReminderThreshold()) <= 0) {
newStatus = AccountStatusEnum.WARNING.getStatus();
} else {
newStatus = AccountStatusEnum.NORMAL.getStatus();
}
if (!newStatus.equals(account.getAccountStatus())) {
accountMapper.update(null, new LambdaUpdateWrapper<EnergyAccountDO>()
.set(EnergyAccountDO::getAccountStatus, newStatus)
.eq(EnergyAccountDO::getId, accountId));
}
}
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.energy.service.bill;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.bill.vo.*;
import cn.iocoder.yudao.module.energy.dal.dataobject.bill.EnergyBillDO;
import java.util.List;
public interface EnergyBillService {
Long generateBill(EnergyBillGenerateReqVO reqVO);
List<Long> batchGenerateBills(List<EnergyBillGenerateReqVO> reqVOs);
PageResult<EnergyBillDO> getBillPage(EnergyBillPageReqVO reqVO);
EnergyBillDO getBill(Long id);
void updateBill(EnergyBillSaveReqVO reqVO);
void deleteBill(Long id);
void auditBill(Long id, Boolean approved, String remark);
Long createAdjustment(EnergyBillAdjustmentSaveReqVO reqVO);
void deleteAdjustment(Long adjustmentId);
}

View File

@@ -0,0 +1,215 @@
package cn.iocoder.yudao.module.energy.service.bill;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.bill.vo.*;
import cn.iocoder.yudao.module.energy.dal.dataobject.bill.EnergyBillAdjustmentDO;
import cn.iocoder.yudao.module.energy.dal.dataobject.bill.EnergyBillDO;
import cn.iocoder.yudao.module.energy.dal.dataobject.detail.EnergyHydrogenDetailDO;
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 jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.energy.enums.ErrorCodeConstants.*;
@Service
@Validated
@Slf4j
public class EnergyBillServiceImpl implements EnergyBillService {
@Resource
private EnergyBillMapper billMapper;
@Resource
private EnergyBillAdjustmentMapper adjustmentMapper;
@Resource
private EnergyHydrogenDetailMapper detailMapper;
@Resource
private ApplicationEventPublisher eventPublisher;
@Override
@Transactional(rollbackFor = Exception.class)
public Long generateBill(EnergyBillGenerateReqVO reqVO) {
// 1. Query unbilled, audited, deducted details
List<EnergyHydrogenDetailDO> details = detailMapper.selectUnbilledByCondition(
reqVO.getCustomerId(), reqVO.getContractId(), reqVO.getStationId(),
reqVO.getBillPeriodStart(), reqVO.getBillPeriodEnd());
if (details.isEmpty()) {
throw exception(ENERGY_BILL_NO_DETAILS);
}
// 2. Calculate totals
BigDecimal receivableAmount = details.stream()
.map(EnergyHydrogenDetailDO::getCustomerAmount)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal totalQuantity = details.stream()
.map(EnergyHydrogenDetailDO::getHydrogenQuantity)
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 3. Create bill (draft status)
EnergyBillDO bill = EnergyBillDO.builder()
.billCode(generateBillCode())
.energyType(reqVO.getEnergyType())
.customerId(reqVO.getCustomerId())
.contractId(reqVO.getContractId())
.stationId(reqVO.getStationId())
.billPeriodStart(reqVO.getBillPeriodStart())
.billPeriodEnd(reqVO.getBillPeriodEnd())
.receivableAmount(receivableAmount)
.actualAmount(receivableAmount)
.adjustmentAmount(BigDecimal.ZERO)
.paidAmount(BigDecimal.ZERO)
.totalQuantity(totalQuantity)
.detailCount(details.size())
.status(BillStatusEnum.DRAFT.getStatus())
.auditStatus(BillAuditStatusEnum.PENDING.getStatus())
.submitStatus(BillSubmitStatusEnum.NOT_SUBMITTED.getStatus())
.paymentStatus(PaymentStatusEnum.UNPAID.getStatus())
.generateTime(LocalDateTime.now())
.build();
billMapper.insert(bill);
// 4. Publish event to update detail billIds
List<Long> detailIds = details.stream().map(EnergyHydrogenDetailDO::getId).toList();
eventPublisher.publishEvent(new BillCreatedEvent(bill.getId(), detailIds));
return bill.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public List<Long> batchGenerateBills(List<EnergyBillGenerateReqVO> reqVOs) {
List<Long> billIds = new ArrayList<>();
for (EnergyBillGenerateReqVO reqVO : reqVOs) {
try {
billIds.add(generateBill(reqVO));
} catch (Exception e) {
log.warn("[batchGenerateBills] Failed for customer={}, contract={}",
reqVO.getCustomerId(), reqVO.getContractId(), e);
}
}
return billIds;
}
@Override
public PageResult<EnergyBillDO> getBillPage(EnergyBillPageReqVO reqVO) {
return billMapper.selectPage(reqVO);
}
@Override
public EnergyBillDO getBill(Long id) {
return billMapper.selectById(id);
}
@Override
public void updateBill(EnergyBillSaveReqVO reqVO) {
EnergyBillDO bill = validateBillExists(reqVO.getId());
bill.setAuditRemark(reqVO.getAuditRemark());
billMapper.updateById(bill);
}
@Override
public void deleteBill(Long id) {
EnergyBillDO bill = validateBillExists(id);
if (!BillStatusEnum.DRAFT.getStatus().equals(bill.getStatus())) {
throw exception(ENERGY_BILL_NOT_DRAFT);
}
billMapper.deleteById(id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void auditBill(Long id, Boolean approved, String remark) {
EnergyBillDO bill = validateBillExists(id);
if (!BillAuditStatusEnum.PENDING.getStatus().equals(bill.getAuditStatus())) {
throw exception(ENERGY_BILL_ALREADY_AUDITED);
}
Integer newAuditStatus = approved ? BillAuditStatusEnum.APPROVED.getStatus() : BillAuditStatusEnum.REJECTED.getStatus();
Integer newStatus = approved ? BillStatusEnum.GENERATED.getStatus() : bill.getStatus();
bill.setAuditStatus(newAuditStatus);
bill.setStatus(newStatus);
bill.setAuditRemark(remark);
bill.setAuditTime(LocalDateTime.now());
billMapper.updateById(bill);
// If approved, publish event to update detail settlement status
if (approved) {
List<EnergyHydrogenDetailDO> details = detailMapper.selectListByBillId(id);
List<Long> detailIds = details.stream().map(EnergyHydrogenDetailDO::getId).toList();
eventPublisher.publishEvent(new BillApprovedEvent(id, detailIds));
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long createAdjustment(EnergyBillAdjustmentSaveReqVO reqVO) {
EnergyBillDO bill = validateBillExists(reqVO.getBillId());
EnergyBillAdjustmentDO adjustment = EnergyBillAdjustmentDO.builder()
.billId(reqVO.getBillId())
.detailId(reqVO.getDetailId())
.adjustmentType(reqVO.getAdjustmentType())
.amount(reqVO.getAmount())
.reason(reqVO.getReason())
.attachmentUrls(reqVO.getAttachmentUrls())
.operateTime(LocalDateTime.now())
.build();
adjustmentMapper.insert(adjustment);
// Update bill amounts
BigDecimal delta = AdjustmentTypeEnum.INCREASE.getType().equals(reqVO.getAdjustmentType())
? reqVO.getAmount() : reqVO.getAmount().negate();
bill.setAdjustmentAmount(bill.getAdjustmentAmount().add(delta));
bill.setActualAmount(bill.getReceivableAmount().add(bill.getAdjustmentAmount()));
billMapper.updateById(bill);
return adjustment.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteAdjustment(Long adjustmentId) {
EnergyBillAdjustmentDO adjustment = adjustmentMapper.selectById(adjustmentId);
if (adjustment == null) return;
EnergyBillDO bill = billMapper.selectById(adjustment.getBillId());
if (bill != null) {
// Reverse the adjustment
BigDecimal delta = AdjustmentTypeEnum.INCREASE.getType().equals(adjustment.getAdjustmentType())
? adjustment.getAmount().negate() : adjustment.getAmount();
bill.setAdjustmentAmount(bill.getAdjustmentAmount().add(delta));
bill.setActualAmount(bill.getReceivableAmount().add(bill.getAdjustmentAmount()));
billMapper.updateById(bill);
}
adjustmentMapper.deleteById(adjustmentId);
}
private EnergyBillDO validateBillExists(Long id) {
EnergyBillDO bill = billMapper.selectById(id);
if (bill == null) {
throw exception(ENERGY_BILL_NOT_EXISTS);
}
return bill;
}
private String generateBillCode() {
return "EB" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
+ UUID.randomUUID().toString().substring(0, 4).toUpperCase();
}
}

View File

@@ -0,0 +1,14 @@
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;
public interface EnergyStationConfigService {
Long createConfig(EnergyStationConfigSaveReqVO createReqVO);
void updateConfig(EnergyStationConfigSaveReqVO updateReqVO);
EnergyStationConfigDO getConfig(Long id);
PageResult<EnergyStationConfigDO> getConfigPage(EnergyStationConfigPageReqVO pageReqVO);
EnergyStationConfigDO getByStationId(Long stationId);
}

View File

@@ -0,0 +1,62 @@
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 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);
}
private void validateConfigExists(Long id) {
if (stationConfigMapper.selectById(id) == null) {
throw exception(STATION_CONFIG_NOT_EXISTS);
}
}
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.energy.service.detail;
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);
void audit(Long id, Boolean approved, String remark);
void batchAudit(List<Long> ids, Boolean approved, String remark);
void updateDeductionStatus(Long detailId, Integer status);
void updateBillId(List<Long> detailIds, Long billId);
void updateSettlementStatus(List<Long> detailIds, Integer status);
}

View File

@@ -0,0 +1,181 @@
package cn.iocoder.yudao.module.energy.service.detail;
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.dal.dataobject.record.EnergyHydrogenRecordDO;
import cn.iocoder.yudao.module.energy.dal.dataobject.price.EnergyStationPriceDO;
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.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.service.price.EnergyStationPriceService;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.math.RoundingMode;
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
@Slf4j
public class HydrogenDetailServiceImpl implements HydrogenDetailService {
@Resource
private EnergyHydrogenDetailMapper detailMapper;
@Resource
private EnergyHydrogenRecordMapper recordMapper;
@Resource
private EnergyStationPriceService stationPriceService;
@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());
EnergyHydrogenDetailDO updateObj = new EnergyHydrogenDetailDO();
updateObj.setId(reqVO.getId());
if (reqVO.getCostPrice() != null) {
updateObj.setCostPrice(reqVO.getCostPrice());
}
if (reqVO.getCustomerPrice() != null) {
updateObj.setCustomerPrice(reqVO.getCustomerPrice());
}
if (reqVO.getRemark() != null) {
updateObj.setRemark(reqVO.getRemark());
}
detailMapper.updateById(updateObj);
}
@Override
public PageResult<EnergyHydrogenDetailDO> getDetailPage(HydrogenDetailPageReqVO pageReqVO) {
return detailMapper.selectPage(pageReqVO);
}
@Override
public EnergyHydrogenDetailDO getDetail(Long id) {
return detailMapper.selectById(id);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void audit(Long id, Boolean approved, String remark) {
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<EnergyHydrogenDetailDO>()
.set(EnergyHydrogenDetailDO::getAuditStatus, newStatus)
.set(EnergyHydrogenDetailDO::getAuditRemark, remark)
.eq(EnergyHydrogenDetailDO::getId, id));
// If approved, publish event for deduction
if (approved) {
eventPublisher.publishEvent(new DetailAuditedEvent(
id, detail.getStationId(), detail.getCustomerId(),
detail.getContractId(), detail.getCustomerAmount()));
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void batchAudit(List<Long> ids, Boolean approved, String remark) {
for (Long id : ids) {
audit(id, approved, remark);
}
}
@Override
public void updateDeductionStatus(Long detailId, Integer status) {
detailMapper.update(null, new LambdaUpdateWrapper<EnergyHydrogenDetailDO>()
.set(EnergyHydrogenDetailDO::getDeductionStatus, status)
.eq(EnergyHydrogenDetailDO::getId, detailId));
}
@Override
public void updateBillId(List<Long> detailIds, Long billId) {
if (detailIds == null || detailIds.isEmpty()) return;
detailMapper.update(null, new LambdaUpdateWrapper<EnergyHydrogenDetailDO>()
.set(EnergyHydrogenDetailDO::getBillId, billId)
.in(EnergyHydrogenDetailDO::getId, detailIds));
}
@Override
public void updateSettlementStatus(List<Long> detailIds, Integer status) {
if (detailIds == null || detailIds.isEmpty()) return;
detailMapper.update(null, new LambdaUpdateWrapper<EnergyHydrogenDetailDO>()
.set(EnergyHydrogenDetailDO::getSettlementStatus, status)
.in(EnergyHydrogenDetailDO::getId, detailIds));
}
private EnergyHydrogenDetailDO validateDetailExists(Long id) {
EnergyHydrogenDetailDO detail = detailMapper.selectById(id);
if (detail == null) {
throw exception(HYDROGEN_DETAIL_NOT_EXISTS);
}
return detail;
}
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.energy.service.price;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.price.vo.EnergyStationPricePageReqVO;
import cn.iocoder.yudao.module.energy.controller.admin.price.vo.EnergyStationPriceSaveReqVO;
import cn.iocoder.yudao.module.energy.dal.dataobject.price.EnergyStationPriceDO;
import java.time.LocalDate;
public interface EnergyStationPriceService {
Long createPrice(EnergyStationPriceSaveReqVO createReqVO);
void updatePrice(EnergyStationPriceSaveReqVO updateReqVO);
void deletePrice(Long id);
EnergyStationPriceDO getPrice(Long id);
PageResult<EnergyStationPriceDO> getPricePage(EnergyStationPricePageReqVO pageReqVO);
EnergyStationPriceDO getEffectivePrice(Long stationId, Long customerId, LocalDate date);
}

View File

@@ -0,0 +1,75 @@
package cn.iocoder.yudao.module.energy.service.price;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.energy.controller.admin.price.vo.EnergyStationPricePageReqVO;
import cn.iocoder.yudao.module.energy.controller.admin.price.vo.EnergyStationPriceSaveReqVO;
import cn.iocoder.yudao.module.energy.convert.price.EnergyStationPriceConvert;
import cn.iocoder.yudao.module.energy.dal.dataobject.price.EnergyStationPriceDO;
import cn.iocoder.yudao.module.energy.dal.mysql.price.EnergyStationPriceMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDate;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.energy.enums.ErrorCodeConstants.*;
@Service
@Validated
public class EnergyStationPriceServiceImpl implements EnergyStationPriceService {
@Resource
private EnergyStationPriceMapper stationPriceMapper;
@Override
public Long createPrice(EnergyStationPriceSaveReqVO createReqVO) {
// Validate unique constraint: station + customer + effectiveDate
validatePriceUnique(createReqVO.getStationId(), createReqVO.getCustomerId(), createReqVO.getEffectiveDate(), null);
EnergyStationPriceDO price = EnergyStationPriceConvert.INSTANCE.convert(createReqVO);
price.setStatus(0); // 生效中
stationPriceMapper.insert(price);
return price.getId();
}
@Override
public void updatePrice(EnergyStationPriceSaveReqVO updateReqVO) {
validatePriceExists(updateReqVO.getId());
validatePriceUnique(updateReqVO.getStationId(), updateReqVO.getCustomerId(), updateReqVO.getEffectiveDate(), updateReqVO.getId());
EnergyStationPriceDO updateObj = EnergyStationPriceConvert.INSTANCE.convert(updateReqVO);
stationPriceMapper.updateById(updateObj);
}
@Override
public void deletePrice(Long id) {
validatePriceExists(id);
stationPriceMapper.deleteById(id);
}
@Override
public EnergyStationPriceDO getPrice(Long id) {
return stationPriceMapper.selectById(id);
}
@Override
public PageResult<EnergyStationPriceDO> getPricePage(EnergyStationPricePageReqVO pageReqVO) {
return stationPriceMapper.selectPage(pageReqVO);
}
@Override
public EnergyStationPriceDO getEffectivePrice(Long stationId, Long customerId, LocalDate date) {
return stationPriceMapper.selectEffective(stationId, customerId, date);
}
private void validatePriceExists(Long id) {
if (stationPriceMapper.selectById(id) == null) {
throw exception(STATION_PRICE_NOT_EXISTS);
}
}
private void validatePriceUnique(Long stationId, Long customerId, LocalDate effectiveDate, Long excludeId) {
EnergyStationPriceDO existing = stationPriceMapper.selectEffective(stationId, customerId, effectiveDate);
if (existing != null && !existing.getId().equals(excludeId)) {
throw exception(STATION_PRICE_DUPLICATE);
}
}
}

Some files were not shown because too many files have changed in this diff Show More