chore: 添加输入输出文件 + .claude记忆和计划
输入文件:
- 租赁任务考核_2026年{1,2,3}月.xlsx (考核源数据)
- {1,2}月.xlsx (客户盈亏表)
- 车辆里程考核与奖金发放规则(V.1.2).docx
输出文件:
- 里程任务考核_{1,2,3}月核算.xlsx (月度核算结果)
- 里程任务考核_Q1汇总.xlsx (含车辆台账)
- 3月客户盈亏表(待填写).xlsx (模版)
.claude_memory: 项目记忆(规则/偏好/架构/测试车辆)
.claude_plans: 历次计划文件
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
701
.claude_plans/stateful-waddling-elephant.md
Normal file
701
.claude_plans/stateful-waddling-elephant.md
Normal file
@@ -0,0 +1,701 @@
|
||||
# Phase 2 ETC 模块完成计划(Controller + API Client + 前端)
|
||||
|
||||
## Context
|
||||
|
||||
Phase 1 基础设施层(FeeType 枚举、DeductionRequest、IDeductionService 重构、ContractMatchService、ReviewConfig 扩展)已完成并编译通过。
|
||||
Phase 2 ETC 模块后端核心(Entity/Mapper/Service/Event/Listener)已完成并编译通过。
|
||||
|
||||
**本次任务:** 完成 Phase 2 剩余部分:
|
||||
1. ETC 后端 Controller 层(3 个 Controller)
|
||||
2. EtczjApiClient(etczj.com HTTP 客户端 + OCR 验证码)
|
||||
3. 前端 ETC 配置页面 + ETC 账单查询页面
|
||||
|
||||
**用户决策:**
|
||||
- 账户结构:共用一个能源账户(三种费用从同一余额扣款)
|
||||
- 账单模式:各费用类型独立账单
|
||||
- 业务流程:ETC/电费与氢费完全一致(合同匹配 → 审核 → 扣款)
|
||||
- 数据源:先设计通用框架,具体 API/Excel 格式后续对接
|
||||
|
||||
---
|
||||
|
||||
## 架构方案:独立明细表 + 共享账户层
|
||||
|
||||
```
|
||||
modules/
|
||||
station/ ← 已有:氢费数据源(不动)
|
||||
etc/ ← 新增:ETC 数据源
|
||||
electricity/ ← 新增:电费数据源
|
||||
energy/ ← 已有:共享结算层(小幅扩展)
|
||||
payment/ ← 已有:款项管理(小幅扩展)
|
||||
```
|
||||
|
||||
**核心原则:** 三种费用的明细/账单各自独立(字段差异大),但扣款引擎、账户、流水、充值、款项管理完全共享。
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: 基础设施层改造(energy 域)
|
||||
|
||||
### 1.1 新增 `FeeType` 枚举
|
||||
|
||||
**文件:** `modules/energy/enums/FeeType.java`(新建)
|
||||
|
||||
```java
|
||||
public enum FeeType {
|
||||
HYDROGEN(1, "氢费"),
|
||||
ETC(2, "高速通行费"),
|
||||
ELECTRICITY(3, "电费");
|
||||
|
||||
private final int code;
|
||||
private final String label;
|
||||
}
|
||||
```
|
||||
|
||||
### 1.2 重构 `IDeductionService` → 接受通用扣款请求
|
||||
|
||||
**改动文件:**
|
||||
- `modules/energy/service/IDeductionService.java`
|
||||
- `modules/energy/service/impl/DeductionServiceImpl.java`
|
||||
|
||||
**改动内容:**
|
||||
|
||||
新增通用 DTO `DeductionRequest`:
|
||||
```java
|
||||
@Data @Builder
|
||||
public class DeductionRequest {
|
||||
private FeeType feeType;
|
||||
private Long detailId; // 明细ID(在各自的明细表中)
|
||||
private Long customerId;
|
||||
private String customerName;
|
||||
private Long contractId;
|
||||
private BigDecimal amount; // 扣款金额
|
||||
private Integer paymentMode; // 1-预充值 2-月结 3-自行结算
|
||||
private Integer deductionStatus; // 当前扣款状态(幂等守卫用)
|
||||
}
|
||||
```
|
||||
|
||||
接口签名变更:
|
||||
```java
|
||||
// 原:DeductionResult deduct(EnergyHydrogenDetail detail);
|
||||
// 新:
|
||||
DeductionResult deduct(DeductionRequest request);
|
||||
void reDeduct(DeductionRequest request, BigDecimal newAmount);
|
||||
```
|
||||
|
||||
`DeductionServiceImpl` 改动点:
|
||||
- `deduct()` 方法参数从 `EnergyHydrogenDetail` 改为 `DeductionRequest`,逻辑不变(读 customerId/amount/contractId/paymentMode)
|
||||
- **移除** `detailMapper.updateById(detail)` —— 扣款服务不再直接更新明细表,改为返回 `DeductionResult`,由调用方负责更新各自的明细表扣款状态
|
||||
- 流水 description 加入 `feeType.getLabel()`(如 "ETC扣款" / "电费扣款")
|
||||
- `reDeduct()` 同理,不再直接操作 `detailMapper`
|
||||
|
||||
**向后兼容:** 在 `EnergyDetailImportListener`(氢费监听器)中构造 `DeductionRequest.fromHydrogenDetail(detail)`,调用新接口后手动更新氢费明细扣款状态。
|
||||
|
||||
### 1.3 `energy_account_transaction` 表加 `fee_type` 列
|
||||
|
||||
**DDL 迁移脚本:** `db/energy/V2__add_fee_type_to_transaction.sql`
|
||||
|
||||
```sql
|
||||
ALTER TABLE energy_account_transaction
|
||||
ADD COLUMN fee_type TINYINT NOT NULL DEFAULT 1 COMMENT '费用类型:1-氢费 2-ETC 3-电费';
|
||||
ALTER TABLE energy_account_transaction
|
||||
ADD INDEX idx_eat_fee_type (fee_type);
|
||||
```
|
||||
|
||||
`EnergyAccountTransaction.java` 新增 `feeType` 字段。
|
||||
|
||||
### 1.4 `customer_payment_item` 款项解构扩展
|
||||
|
||||
`fee_type` 枚举扩展(已有字段):
|
||||
- 现有:1-租赁费 2-氢费充值 3-押金 4-违约金 5-其他
|
||||
- 新增:6-ETC充值 7-电费充值
|
||||
|
||||
`PaymentFlowService` 的流转逻辑需根据新 fee_type 创建对应充值单。
|
||||
|
||||
### 1.5 通用合同匹配抽取
|
||||
|
||||
现有合同匹配逻辑在 `EnergyDetailImportListener` 中(车牌+时间→合同)。
|
||||
抽取为共享服务:
|
||||
|
||||
**新建:** `modules/energy/service/IContractMatchService.java`
|
||||
```java
|
||||
public interface IContractMatchService {
|
||||
ContractMatchResult matchContract(String plateNumber, Date eventTime);
|
||||
}
|
||||
```
|
||||
|
||||
返回 `ContractMatchResult`(customerId, customerName, contractId, contractCode, costType, paymentMode)。
|
||||
ETC/电费/氢费三个监听器共用此服务。
|
||||
|
||||
### 1.6 审核配置扩展
|
||||
|
||||
`energy_review_config` 表加 `fee_type` 列:
|
||||
```sql
|
||||
ALTER TABLE energy_review_config
|
||||
ADD COLUMN fee_type TINYINT NOT NULL DEFAULT 1 COMMENT '费用类型:1-氢费 2-ETC 3-电费';
|
||||
DROP INDEX uk_erc_level_customer ON energy_review_config;
|
||||
CREATE UNIQUE INDEX uk_erc_level_customer_fee ON energy_review_config (config_level, customer_id, fee_type);
|
||||
```
|
||||
|
||||
每种费用类型可独立配置"是否需要审核后才扣款"。
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: ETC 模块(`modules/etc/`)
|
||||
|
||||
### 2.0 ETC 数据源逆向分析(etczj.com - 浙江货车ETC)
|
||||
|
||||
**平台信息:** https://www.etczj.com (Nuxt.js + Element UI 前端,nginx/1.27.2 后端)
|
||||
|
||||
**认证流程:**
|
||||
```
|
||||
1. GET /createCaptcha?verifyCodeSize=null&tmp={random}&secret={guid} → 图形验证码图片
|
||||
2. POST /member/toLogin?secret={guid}
|
||||
Body: { acctId: "手机号", password: "Base64编码密码", checkCode: "验证码", msgCode: "", secret: guid }
|
||||
Response: { state: "1", payload: { randomVal: "xxx", signState: "1" } }
|
||||
3. 后续请求 Header: Randomval: {randomVal}(存 localStorage)
|
||||
```
|
||||
|
||||
**核心业务 API:**
|
||||
|
||||
| API | 方法 | 说明 | 关键参数 |
|
||||
|-----|------|------|----------|
|
||||
| `/vehicleManage/queryVehicleConsumptionDetailPage` | POST | **通行记录分页** | transTimeBegin/End, postingTimeBegin/End, vehicleCode, licenseColor, vcEnStation, vcExStation, currPage, pageSize |
|
||||
| `/vehicleManage/sumVehicleToll` | POST | **通行费汇总** | 同上(返回 totalPassAmount, totalServiceAmount) |
|
||||
| `/vehicleManage/viewVehiclePage` | POST | **车辆列表** | vehicleCode, currPage, pageSize |
|
||||
| `/largeFileExport/billDetails` | POST | **通行记录导出** | postingTimeBegin/End, transTimeBegin/End, vehicleCode |
|
||||
| `/member/flowList` | POST | **账单流水** | - |
|
||||
| `/member/viewMember` | POST | **用户信息** | - |
|
||||
|
||||
**通行记录响应字段映射 → `etc_toll_record` 表:**
|
||||
|
||||
| etczj 字段 | 含义 | → 表字段 |
|
||||
|------------|------|----------|
|
||||
| vehicleCode | 车牌号码 | plate_number |
|
||||
| licenseColor | 车牌颜色(0-6) | license_color |
|
||||
| cardType | 卡类型 | card_type |
|
||||
| cardCode | 通行卡号 | etc_card_number |
|
||||
| postingTimeStr | 记账日期 | posting_time |
|
||||
| transDate + transTime | 通行日期+时间 | trans_time |
|
||||
| enStation | 入口站 | entry_station_name |
|
||||
| exStation | 出口站 | exit_station_name |
|
||||
| dToll | 通行费用(元) | toll_amount |
|
||||
| serviceFee | 服务费(元) | service_fee |
|
||||
| appealStatus | 申诉状态 | appeal_status |
|
||||
|
||||
**同步策略设计要点:**
|
||||
- 登录需图形验证码 → 需 OCR 或人工介入。建议:首次登录获取 session 后缓存 `randomVal`,session 过期时告警人工重新登录
|
||||
- 增量同步:按 `postingTimeBegin/End`(记账日期)拉取,每次从 `lastSyncTime` 开始
|
||||
- 去重键:`vehicleCode + transDate + transTime + enStation + exStation`(平台无唯一订单号)
|
||||
- 分页:`currPage` + `pageSize`,默认 pageSize=5,建议调大到 100
|
||||
- 日期限制:时间范围最多 31 天
|
||||
|
||||
### 2.1 数据库表
|
||||
|
||||
**`etc_toll_record`(ETC通行记录表):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | BIGINT | 主键 |
|
||||
| record_code | VARCHAR(32) | 记录编码(系统生成,唯一) |
|
||||
| etc_card_number | VARCHAR(64) | 通行卡号(cardCode) |
|
||||
| card_type | VARCHAR(32) | 卡类型(cardType) |
|
||||
| plate_number | VARCHAR(16) | 车牌号 |
|
||||
| license_color | TINYINT | 车牌颜色(0-6) |
|
||||
| vehicle_id | BIGINT | 车辆ID(匹配后) |
|
||||
| contract_id | BIGINT | 合同ID(匹配后) |
|
||||
| customer_id | BIGINT | 客户ID(匹配后) |
|
||||
| customer_name | VARCHAR(128) | 客户名称 |
|
||||
| entry_station_name | VARCHAR(128) | 入口站 |
|
||||
| exit_station_name | VARCHAR(128) | 出口站 |
|
||||
| trans_time | DATETIME | 通行时间(transDate+transTime) |
|
||||
| posting_time | DATETIME | 记账日期(postingTimeStr) |
|
||||
| toll_amount | DECIMAL(10,2) | 通行费(元)(dToll) |
|
||||
| service_fee | DECIMAL(10,2) | 服务费(元)(serviceFee) |
|
||||
| total_amount | DECIMAL(10,2) | 合计金额(toll+service) |
|
||||
| appeal_status | TINYINT | 申诉状态 |
|
||||
| cost_type | TINYINT | 费用承担方 |
|
||||
| payment_mode | TINYINT | 付款模式 |
|
||||
| contract_matched | TINYINT | 合同匹配状态 |
|
||||
| review_status | TINYINT | 审核状态 |
|
||||
| deduction_status | TINYINT | 扣款状态 |
|
||||
| bill_id | BIGINT | 关联账单ID |
|
||||
| source_type | TINYINT | 来源:1-API同步 |
|
||||
| source_dedup_key | VARCHAR(128) | 去重键(车牌+通行时间+入口+出口) |
|
||||
| is_oneos_vehicle | TINYINT | 是否OneOS车辆 |
|
||||
| + BaseEntity 审计字段 | | |
|
||||
|
||||
**`etc_sync_config`** 和 **`etc_sync_log`**:复用 station 的 sync_config/sync_log 结构,`provider_type` 固定为 `ETCZJ`。额外字段:`acct_id`(手机号)、`password`(加密存储)。
|
||||
|
||||
**`energy_etc_bill`(ETC账单表):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | BIGINT | 主键 |
|
||||
| bill_code | VARCHAR(32) | 账单编码 |
|
||||
| customer_id | BIGINT | 客户ID |
|
||||
| bill_period_start/end | DATE | 账单周期 |
|
||||
| total_toll_count | INT | 通行笔数 |
|
||||
| total_toll_amount | DECIMAL(12,2) | 通行总金额 |
|
||||
| total_service_fee | DECIMAL(12,2) | 服务费总额 |
|
||||
| receivable_amount | DECIMAL(12,2) | 应收金额 |
|
||||
| actual_amount | DECIMAL(12,2) | 实收金额 |
|
||||
| adjustment_amount | DECIMAL(12,2) | 调整额 |
|
||||
| payment_status | TINYINT | 支付状态 |
|
||||
| review_status | TINYINT | 审核状态 |
|
||||
| + 审核/财务/审计字段 | | |
|
||||
|
||||
### 2.2 代码结构
|
||||
|
||||
```
|
||||
modules/etc/
|
||||
entity/
|
||||
record/po/EtcTollRecord.java
|
||||
record/vo/EtcTollRecordVO.java
|
||||
record/query/EtcTollRecordQuery.java
|
||||
record/req/UpdateEtcRecordReq.java
|
||||
sync/po/EtcSyncConfig.java
|
||||
sync/po/EtcSyncLog.java
|
||||
bill/po/EnergyEtcBill.java
|
||||
bill/vo/EtcBillVO.java
|
||||
bill/req/EtcBillGenerateReq.java
|
||||
mapper/
|
||||
EtcTollRecordMapper.java
|
||||
EtcSyncConfigMapper.java
|
||||
EtcSyncLogMapper.java
|
||||
EnergyEtcBillMapper.java
|
||||
service/
|
||||
IEtcTollRecordService.java
|
||||
IEtcSyncConfigService.java
|
||||
IEtcBillService.java
|
||||
impl/EtcTollRecordServiceImpl.java
|
||||
impl/EtcSyncConfigServiceImpl.java
|
||||
impl/EtcBillServiceImpl.java
|
||||
sync/EtcSyncStrategy.java ← 策略接口
|
||||
sync/EtczjSyncStrategy.java ← etczj.com 具体实现(登录+分页拉取+字段映射)
|
||||
sync/EtczjApiClient.java ← HTTP 客户端(登录/session管理/通行记录查询)
|
||||
sync/EtczjApiResponse.java ← 响应 DTO
|
||||
ingest/EtcIngestTemplate.java ← 模板方法(extract→validate→dedup→match→persist→event)
|
||||
controller/
|
||||
EtcTollRecordController.java
|
||||
EtcSyncConfigController.java
|
||||
EtcBillController.java
|
||||
event/
|
||||
EtcRecordImportedEvent.java
|
||||
listener/
|
||||
EtcDetailImportListener.java ← 监听 EtcRecordImportedEvent,执行合同匹配+扣款
|
||||
validation/
|
||||
EtcValidationChain.java
|
||||
```
|
||||
|
||||
### 2.3 数据流
|
||||
|
||||
```
|
||||
EtczjSyncStrategy 定时/手动同步:
|
||||
1. EtczjApiClient.login(acctId, password) → 获取 randomVal session
|
||||
2. 按日期范围分页调用 /vehicleManage/queryVehicleConsumptionDetailPage
|
||||
- 从 lastSyncTime 开始,每次最多31天
|
||||
- pageSize=100,循环翻页直到无更多数据
|
||||
3. 字段映射:etczj 响应 → EtcTollRecordDTO
|
||||
4. → EtcIngestTemplate.ingest()
|
||||
→ extract → validate → dedup(source_dedup_key) → matchVehicle → persist(etc_toll_record)
|
||||
→ publishEvent(EtcRecordImportedEvent)
|
||||
5. → EtcDetailImportListener
|
||||
→ 筛选 is_oneos_vehicle=1
|
||||
→ contractMatchService.matchContract(plateNumber, transTime)
|
||||
→ 审核配置检查(FeeType.ETC)
|
||||
→ deductionService.deduct(DeductionRequest)
|
||||
→ 更新 etc_toll_record.deduction_status
|
||||
|
||||
Session 管理:
|
||||
- randomVal 缓存在 Redis,key: `etc:session:{configId}`,过期后自动重新登录
|
||||
- 图形验证码处理:OCR 自动识别(Tesseract/百度OCR)
|
||||
- GET /createCaptcha?secret={guid} → 图片
|
||||
- OCR 识别 → 4位数字
|
||||
- 识别失败重试(刷新验证码重试,最多3次)
|
||||
- 3次均失败 → 告警通知人工介入
|
||||
- 去重策略:vehicleCode + transTime + enStation + exStation 组合键
|
||||
```
|
||||
|
||||
### 2.4 API 接口(约 20 个)
|
||||
|
||||
| 模块 | 接口数 |
|
||||
|------|--------|
|
||||
| ETC 通行记录 CRUD + 审核 + 导出 | 8 |
|
||||
| ETC 同步配置 + 手动触发 + 日志 | 7 |
|
||||
| ETC 账单生成 + 审核 + 调整项 | 8 |
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: 电费模块(`modules/electricity/`)
|
||||
|
||||
### 3.1 数据库表
|
||||
|
||||
**`electricity_charge_record`(充电记录表):**
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| id | BIGINT | 主键 |
|
||||
| record_code | VARCHAR(32) | 记录编码 |
|
||||
| charging_station_id | BIGINT | 充电站ID(关联已有 charging_station 表) |
|
||||
| charging_station_name | VARCHAR(128) | 充电站名称 |
|
||||
| plate_number | VARCHAR(16) | 车牌号 |
|
||||
| vehicle_id / contract_id / customer_id | BIGINT | 匹配后填充 |
|
||||
| charging_start_time | DATETIME | 充电开始 |
|
||||
| charging_end_time | DATETIME | 充电结束 |
|
||||
| charging_duration | INT | 时长(分钟) |
|
||||
| kwh | DECIMAL(10,4) | 充电量(度) |
|
||||
| unit_price | DECIMAL(10,4) | 单价(元/度) |
|
||||
| charge_amount | DECIMAL(10,2) | 电费(元) |
|
||||
| service_fee | DECIMAL(10,2) | 服务费(元) |
|
||||
| total_amount | DECIMAL(10,2) | 合计金额 |
|
||||
| cost_type / payment_mode / contract_matched / review_status / deduction_status / bill_id | | 与氢费/ETC 一致 |
|
||||
| source_type | TINYINT | 来源:1-Excel导入 2-RPA导入 |
|
||||
| source_row_key | VARCHAR(64) | 源Excel行唯一键(去重) |
|
||||
| is_oneos_vehicle | TINYINT | 是否OneOS车辆 |
|
||||
| + BaseEntity 审计字段 | | |
|
||||
|
||||
**`energy_electricity_bill`(电费账单表):** 结构类似 ETC 账单,特有字段:`total_kwh`, `total_charge_amount`, `total_service_fee`。
|
||||
|
||||
### 3.2 代码结构
|
||||
|
||||
与 ETC 模块镜像,核心差异:
|
||||
- **无 API 同步**(暂时),数据入口为 Excel 导入(手动上传 / RPA 落盘后系统扫描)
|
||||
- `ElectricityExcelIngestStrategy` 实现 `ElectricityIngestTemplate`
|
||||
- 关联已有 `chargingstation/` 模块的充电站主数据
|
||||
- 校验链包含:电量/金额一致性校验、充电站匹配校验
|
||||
|
||||
### 3.3 数据流
|
||||
|
||||
```
|
||||
Excel 手动导入 / RPA 定期落盘
|
||||
→ ElectricityIngestTemplate.ingest()
|
||||
→ extract(解析Excel) → validate → dedup(source_row_key) → matchVehicle → persist
|
||||
→ publishEvent(ElectricityRecordImportedEvent)
|
||||
→ ElectricityDetailImportListener
|
||||
→ 合同匹配 → 审核配置检查(FeeType.ELECTRICITY)→ 扣款 → 更新状态
|
||||
```
|
||||
|
||||
### 3.4 API 接口(约 15 个)
|
||||
|
||||
| 模块 | 接口数 |
|
||||
|------|--------|
|
||||
| 充电记录 CRUD + 审核 + 导入/导出 | 8 |
|
||||
| 电费账单生成 + 审核 + 调整项 | 7 |
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: 账户层统一视图
|
||||
|
||||
### 4.1 流水查询增加 fee_type 过滤
|
||||
|
||||
**改动文件:**
|
||||
- `TransactionQuery.java` — 新增 `feeType` 可选参数
|
||||
- `EnergyAccountController.java` — 流水分页接口支持按费用类型筛选
|
||||
|
||||
### 4.2 账户汇总接口
|
||||
|
||||
**新增接口:** `GET /energy/account/{id}/fee-summary`
|
||||
|
||||
返回各费用类型的扣款/充值汇总:
|
||||
```json
|
||||
{
|
||||
"hydrogen": { "totalDeducted": 50000, "count": 320 },
|
||||
"etc": { "totalDeducted": 12000, "count": 580 },
|
||||
"electricity": { "totalDeducted": 8000, "count": 150 }
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: 前端菜单与页面
|
||||
|
||||
### 5.1 菜单结构
|
||||
|
||||
```
|
||||
能源管理
|
||||
├── 加氢明细(已有)
|
||||
├── ETC 通行记录(新增)
|
||||
├── 充电记录(新增)
|
||||
├── 能源账户(已有,增加 fee_type 筛选)
|
||||
├── 充值单管理(已有)
|
||||
├── 氢费账单(已有)
|
||||
├── ETC 账单(新增)
|
||||
├── 电费账单(新增)
|
||||
└── 审核配置(已有,增加 fee_type 维度)
|
||||
|
||||
加氢站管理(已有,不动)
|
||||
|
||||
ETC 管理(新增)
|
||||
├── ETC 同步配置
|
||||
└── ETC 同步日志
|
||||
|
||||
款项管理(已有)
|
||||
└── fee_type 解构类型新增 ETC充值/电费充值
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 实施顺序
|
||||
|
||||
| 步骤 | 内容 | 依赖 |
|
||||
|------|------|------|
|
||||
| **1** | Phase 1 基础设施(FeeType枚举、DeductionRequest、IDeductionService重构、DB迁移、ContractMatchService抽取、ReviewConfig扩展) | 无 |
|
||||
| **2** | Phase 1 向后兼容(更新氢费监听器/服务适配新接口) | Step 1 |
|
||||
| **3** | Phase 2 ETC 模块(实体→Mapper→Service→Controller→事件→监听器→测试) | Step 2 |
|
||||
| **4** | Phase 3 电费模块(同上) | Step 2,可与 Step 3 并行 |
|
||||
| **5** | Phase 4 账户层统一视图 | Step 3 & 4 |
|
||||
| **6** | Phase 5 前端页面 | Step 3 & 4 |
|
||||
| **7** | Payment 域扩展(fee_type 解构+流转) | Step 1 |
|
||||
|
||||
---
|
||||
|
||||
## 关键改动文件清单
|
||||
|
||||
### 修改已有文件
|
||||
| 文件 | 改动 |
|
||||
|------|------|
|
||||
| `energy/service/IDeductionService.java` | 接口签名改为 `DeductionRequest` |
|
||||
| `energy/service/impl/DeductionServiceImpl.java` | 实现适配,移除 detailMapper 直接操作 |
|
||||
| `energy/entity/account/po/EnergyAccountTransaction.java` | 新增 `feeType` 字段 |
|
||||
| `energy/listener/EnergyDetailImportListener.java` | 适配新 DeductionRequest,抽取合同匹配逻辑 |
|
||||
| `energy/entity/review/po/EnergyReviewConfig.java` | 新增 `feeType` 字段 |
|
||||
| `energy/service/impl/ReviewConfigServiceImpl.java` | 查询时加 feeType 条件 |
|
||||
| `energy/controller/EnergyAccountController.java` | 流水查询加 feeType 过滤 |
|
||||
| `energy/entity/account/query/TransactionQuery.java` | 新增 feeType 参数 |
|
||||
| `payment/entity/po/CustomerPaymentItem.java` | fee_type 枚举扩展文档 |
|
||||
| `payment/service/impl/PaymentFlowServiceImpl.java` | 支持 ETC/电费充值流转 |
|
||||
|
||||
### 新建文件(按 Phase)
|
||||
- Phase 1: ~5 文件(FeeType, DeductionRequest, ContractMatchService 接口+实现, V2迁移脚本)
|
||||
- Phase 2: ~20 文件(ETC 全套 entity/mapper/service/controller/event/listener)+ 3 张表 DDL
|
||||
- Phase 3: ~18 文件(电费全套)+ 2 张表 DDL
|
||||
- Phase 4: ~2 文件改动
|
||||
|
||||
---
|
||||
|
||||
## 验证方案
|
||||
|
||||
### 单元测试
|
||||
- `DeductionServiceTest` —— 验证新接口支持三种 FeeType
|
||||
- `EtcIngestTemplateTest` —— ETC 数据导入全流程
|
||||
- `ElectricityIngestTemplateTest` —— 电费 Excel 导入全流程
|
||||
- `ContractMatchServiceTest` —— 合同匹配逻辑
|
||||
|
||||
### 集成测试
|
||||
- `EtcModuleIntegrationTest` —— ETC 同步 → 导入 → 合同匹配 → 审核 → 扣款 → 账单生成
|
||||
- `ElectricityModuleIntegrationTest` —— Excel 导入 → 合同匹配 → 审核 → 扣款 → 账单生成
|
||||
- `MultiFeeBillingIntegrationTest` —— 三种费用共享账户扣款 → 流水 fee_type 正确 → 各自独立出账单
|
||||
|
||||
### 回归验证
|
||||
- 现有氢费全链路测试通过(`EnergyModuleIntegrationTest`)
|
||||
- 现有款项管理测试通过(`PaymentModuleIntegrationTest`)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 剩余实施计划(本次任务)
|
||||
|
||||
### 已完成
|
||||
- [x] Phase 1 基础设施层(FeeType/DeductionRequest/IDeductionService/ContractMatchService/ReviewConfig)
|
||||
- [x] Phase 2 DDL(`db/etc/V1__create_etc_tables.sql` - 4 张表)
|
||||
- [x] Phase 2 Entity PO(EtcTollRecord/EtcSyncConfig/EtcSyncLog/EnergyEtcBill)
|
||||
- [x] Phase 2 DTO(EtcTollRecordVO/EtcTollRecordQuery/EtcBillVO/EtcBillGenerateReq)
|
||||
- [x] Phase 2 Mapper(4 个)
|
||||
- [x] Phase 2 Service 接口 + 实现(IEtcTollRecordService/IEtcSyncConfigService/IEtcBillService)
|
||||
- [x] Phase 2 Event(EtcRecordImportedEvent)
|
||||
- [x] Phase 2 Listener(EtcDetailImportListener)
|
||||
- [x] 编译通过
|
||||
|
||||
### Step 1: ETC 后端 Controller 层
|
||||
|
||||
**3 个 Controller,沿用 `EnergyBillController` 的精确模式:**
|
||||
|
||||
#### 1.1 `EtcTollRecordController.java`
|
||||
**路径:** `modules/etc/controller/EtcTollRecordController.java`
|
||||
**前缀:** `@RequestMapping("/etc/record")`
|
||||
**Tag:** `@Tag(name = "ETC通行记录管理")`
|
||||
|
||||
| 方法 | 路径 | 说明 | 调用 |
|
||||
|------|------|------|------|
|
||||
| GET | /page | 分页查询 | service.pageList(query) |
|
||||
| GET | /detail/{id} | 详情 | service.getDetail(id) |
|
||||
| PUT | /review | 单条审核 | service.review(id, approved, remark) |
|
||||
| PUT | /batch-review | 批量审核 | service.batchReview(ids, approved) |
|
||||
| PUT | /manual-match | 手动匹配合同 | service.manualMatch(detailId, contractId) |
|
||||
| POST | /export | 导出 | TODO |
|
||||
| GET | /statistics | 统计数据 | service.getStatistics() |
|
||||
|
||||
#### 1.2 `EtcSyncConfigController.java`
|
||||
**路径:** `modules/etc/controller/EtcSyncConfigController.java`
|
||||
**前缀:** `@RequestMapping("/etc/sync-config")`
|
||||
**Tag:** `@Tag(name = "ETC同步配置")`
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | /page | 分页查询 |
|
||||
| GET | /detail/{id} | 详情 |
|
||||
| POST | / | 新增 |
|
||||
| PUT | / | 编辑 |
|
||||
| DELETE | /{id} | 删除 |
|
||||
| PUT | /toggle/{id} | 启用/停用 |
|
||||
| POST | /trigger/{id} | 手动触发同步 |
|
||||
| GET | /log/{configId} | 同步日志 |
|
||||
|
||||
#### 1.3 `EtcBillController.java`
|
||||
**路径:** `modules/etc/controller/EtcBillController.java`
|
||||
**前缀:** `@RequestMapping("/etc/bill")`
|
||||
**Tag:** `@Tag(name = "ETC账单管理")`
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | /page | 分页查询 |
|
||||
| GET | /detail/{id} | 详情 |
|
||||
| POST | /generate/preview | 生成预览 |
|
||||
| POST | /generate | 确认生成 |
|
||||
| PUT | /review | 审核 |
|
||||
| PUT | /submit-finance/{id} | 提交财务 |
|
||||
| DELETE | /{id} | 删除 |
|
||||
| POST | /export | 导出 |
|
||||
| GET | /statistics | 统计 |
|
||||
| GET | /record/page/{billId} | 关联通行记录分页 |
|
||||
|
||||
**参考文件:**
|
||||
- `ln-asset-management/src/main/java/com/ln/asset/modules/energy/controller/EnergyBillController.java`
|
||||
- `ln-asset-management/src/main/java/com/ln/asset/modules/station/controller/SyncConfigController.java`
|
||||
|
||||
### Step 2: EtczjApiClient(etczj.com HTTP 客户端)
|
||||
|
||||
**新建文件:**
|
||||
- `modules/etc/service/sync/EtczjApiClient.java` — HTTP 客户端
|
||||
- `modules/etc/service/sync/EtczjApiResponse.java` — 响应 DTO
|
||||
- `modules/etc/service/sync/EtczjTollRecordDTO.java` — 通行记录 DTO
|
||||
- `modules/etc/service/sync/EtczjSyncStrategy.java` — 同步策略实现
|
||||
|
||||
**EtczjApiClient 核心方法:**
|
||||
```java
|
||||
@Slf4j
|
||||
@Component
|
||||
public class EtczjApiClient {
|
||||
private final RestTemplate restTemplate;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
// 登录(获取 randomVal session)
|
||||
public String login(String baseUrl, String acctId, String password) {
|
||||
// 1. GET /createCaptcha?secret={guid} → 验证码图片
|
||||
// 2. OCR 识别验证码(Tesseract)
|
||||
// 3. POST /member/toLogin?secret={guid}
|
||||
// Body: { acctId, password: Base64(password), checkCode, secret }
|
||||
// 4. 返回 randomVal
|
||||
}
|
||||
|
||||
// 查询通行记录
|
||||
public EtczjApiResponse<List<EtczjTollRecordDTO>> queryTollRecords(
|
||||
String baseUrl, String randomVal,
|
||||
String transTimeBegin, String transTimeEnd,
|
||||
int currPage, int pageSize) {
|
||||
// POST /vehicleManage/queryVehicleConsumptionDetailPage
|
||||
// Header: Randomval: {randomVal}
|
||||
}
|
||||
|
||||
// 查询通行费汇总
|
||||
public EtczjApiResponse<Map<String, Object>> sumToll(
|
||||
String baseUrl, String randomVal,
|
||||
String transTimeBegin, String transTimeEnd) {
|
||||
// POST /vehicleManage/sumVehicleToll
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**参考文件:**
|
||||
- `ln-asset-management/src/main/java/com/ln/asset/modules/station/feign/HecriApiClient.java`(RestTemplate + ObjectMapper 模式)
|
||||
|
||||
### Step 3: 前端 ETC 页面
|
||||
|
||||
**技术栈:** Vben Admin (Vue 3 + TypeScript + Ant Design Vue + VxeTable)
|
||||
|
||||
**模式参考:**
|
||||
- 路由:`playground/src/router/routes/modules/system.ts`
|
||||
- API:`playground/src/api/system/role.ts`
|
||||
- 列表页:`playground/src/views/system/role/list.vue`
|
||||
- 列定义:`playground/src/views/system/role/data.ts`
|
||||
|
||||
#### 3.1 API 层
|
||||
**新建文件:** `playground/src/api/etc/index.ts`
|
||||
|
||||
```typescript
|
||||
export namespace EtcApi {
|
||||
export interface EtcSyncConfig { id: string; providerType: string; acctId: string; syncEnabled: 0|1; syncCron: string; lastSyncTime: string; lastSyncStatus: 0|1; }
|
||||
export interface EtcTollRecord { id: string; recordCode: string; plateNumber: string; entryStationName: string; exitStationName: string; transTime: string; tollAmount: number; serviceFee: number; totalAmount: number; reviewStatus: number; deductionStatus: number; }
|
||||
export interface EtcBill { id: string; billCode: string; customerName: string; billPeriodStart: string; billPeriodEnd: string; totalTollCount: number; totalTollAmount: number; receivableAmount: number; actualAmount: number; reviewStatus: number; paymentStatus: number; }
|
||||
}
|
||||
|
||||
// ETC 同步配置 API
|
||||
async function getEtcSyncConfigPage(params) { return requestClient.get('/etc/sync-config/page', { params }); }
|
||||
async function addEtcSyncConfig(data) { return requestClient.post('/etc/sync-config', data); }
|
||||
async function updateEtcSyncConfig(data) { return requestClient.put('/etc/sync-config', data); }
|
||||
async function deleteEtcSyncConfig(id) { return requestClient.delete(`/etc/sync-config/${id}`); }
|
||||
async function toggleEtcSync(id) { return requestClient.put(`/etc/sync-config/toggle/${id}`); }
|
||||
async function triggerEtcSync(id) { return requestClient.post(`/etc/sync-config/trigger/${id}`); }
|
||||
async function getEtcSyncLog(configId, params) { return requestClient.get(`/etc/sync-config/log/${configId}`, { params }); }
|
||||
|
||||
// ETC 账单 API
|
||||
async function getEtcBillPage(params) { return requestClient.get('/etc/bill/page', { params }); }
|
||||
async function getEtcBillDetail(id) { return requestClient.get(`/etc/bill/detail/${id}`); }
|
||||
async function reviewEtcBill(data) { return requestClient.put('/etc/bill/review', data); }
|
||||
// ... 其他
|
||||
```
|
||||
|
||||
#### 3.2 ETC 同步配置页面
|
||||
**新建文件:**
|
||||
- `playground/src/views/etc/sync-config/list.vue` — 配置列表(useVbenVxeGrid)
|
||||
- `playground/src/views/etc/sync-config/data.ts` — 列定义 + 表单 schema
|
||||
- `playground/src/views/etc/sync-config/modules/form.vue` — 新增/编辑表单(useVbenDrawer)
|
||||
|
||||
**页面功能:**
|
||||
- 表格列:供应商类型、登录账号、API地址、同步频率、上次同步时间/状态、启用状态
|
||||
- 操作:编辑、删除、启用/停用切换、手动触发同步
|
||||
- 新增/编辑抽屉:账号、密码、API地址、同步 cron 表达式
|
||||
|
||||
#### 3.3 ETC 账单查询页面
|
||||
**新建文件:**
|
||||
- `playground/src/views/etc/bill/list.vue` — 账单列表
|
||||
- `playground/src/views/etc/bill/data.ts` — 列定义 + 搜索表单
|
||||
- `playground/src/views/etc/bill/modules/detail.vue` — 账单详情抽屉
|
||||
|
||||
**页面功能:**
|
||||
- 搜索条件:客户名称、账单周期、审核状态、支付状态
|
||||
- 表格列:账单编码、客户名称、账期、通行笔数、通行费总额、服务费总额、应收、实收、审核状态、支付状态
|
||||
- 操作:查看详情、审核、提交财务、删除
|
||||
- 详情抽屉:账单信息 + 关联通行记录分页列表
|
||||
|
||||
#### 3.4 路由配置
|
||||
**新建文件:** `playground/src/router/routes/modules/etc.ts`
|
||||
|
||||
```typescript
|
||||
const routes: RouteRecordRaw[] = [{
|
||||
meta: { icon: 'mdi:highway', order: 30, title: 'ETC管理' },
|
||||
name: 'Etc',
|
||||
path: '/etc',
|
||||
children: [
|
||||
{ path: '/etc/sync-config', name: 'EtcSyncConfig', meta: { icon: 'mdi:sync', title: 'ETC同步配置' }, component: () => import('#/views/etc/sync-config/list.vue') },
|
||||
{ path: '/etc/bill', name: 'EtcBill', meta: { icon: 'mdi:file-document-outline', title: 'ETC账单' }, component: () => import('#/views/etc/bill/list.vue') },
|
||||
],
|
||||
}];
|
||||
```
|
||||
|
||||
### 实施顺序
|
||||
|
||||
| 步骤 | 内容 | 文件数 |
|
||||
|------|------|--------|
|
||||
| 1 | 3 个 ETC Controller | 3 |
|
||||
| 2 | EtczjApiClient + DTO + SyncStrategy | 4 |
|
||||
| 3 | 前端 API 层 | 1 |
|
||||
| 4 | 前端 ETC 同步配置页面 | 3 |
|
||||
| 5 | 前端 ETC 账单查询页面 | 3 |
|
||||
| 6 | 前端路由配置 | 1 |
|
||||
| **合计** | | **15 个文件** |
|
||||
|
||||
### 验证方案
|
||||
- 后端:`mvn compile` 编译通过
|
||||
- 前端:`cd ln-one-os-web/playground && pnpm dev` 启动无报错
|
||||
- ETC 同步配置页面:可访问 /etc/sync-config,表格加载正常
|
||||
- ETC 账单页面:可访问 /etc/bill,表格加载正常
|
||||
- Swagger:`/swagger-ui.html` 可看到 ETC 相关 3 组 API
|
||||
Reference in New Issue
Block a user