# 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> queryTollRecords( String baseUrl, String randomVal, String transTimeBegin, String transTimeEnd, int currPage, int pageSize) { // POST /vehicleManage/queryVehicleConsumptionDetailPage // Header: Randomval: {randomVal} } // 查询通行费汇总 public EtczjApiResponse> 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