From b50e57bf7e17d696ddb4e9785c909052da034ef4 Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期二, 14 七月 2026 09:10:47 +0800
Subject: [PATCH] Merge branch '2.1' into 2.3

---
 ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/AppUserInfoDto.java                                         |   12 
 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/TAppUserServiceImpl.java                               |   17 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java                             | 1108 +++++++++++++
 ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/impl/IcbcPayServiceImpl.java                           |  668 ++++++++
 docs/superpowers/plans/2026-07-11-icbc-admin-fund-operations.md                                                                 |   66 
 ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderListVO.java                                         |    8 
 ruoyi-service/ruoyi-payment/lib/InfosecCrypto_Java1_02_JDK14+.jar                                                               |    0 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/export/TChargingBillExport.java                                         |    4 
 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/TAppUserService.java                                        |    2 
 ruoyi-service/ruoyi-payment/lib/readme.md                                                                                       |   14 
 ruoyi-service/ruoyi-other/src/main/resources/bootstrap.yml                                                                      |    6 
 ruoyi-service/ruoyi-payment/lib/hsm-software-share-1.0.5.jar                                                                    |    0 
 ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderTimeVO.java                                         |    4 
 ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TChargingOrderShareRecord.java                                |  124 +
 ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/PartnerServiceImpl.java                      |   25 
 ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml                                              |  164 +
 ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Partner.java                                    |   18 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/IcbcAdminFundOperationReq.java                                      |   12 
 ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/ChargingSimulationSupportTest.java                         |   49 
 ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/domain/IcbcPaymentFlowRecord.java                                   |   31 
 ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillVO.java                                              |    4 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingBillServiceImpl.java                              |  225 ++
 ruoyi-service/ruoyi-payment/lib/update-info.md                                                                                  |    2 
 ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcPaymentResp.java                                         |   34 
 ruoyi-service/ruoyi-order/src/main/resources/bootstrap.yml                                                                      |    2 
 ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderRefundReq.java                                   |   27 
 ruoyi-service/ruoyi-account/src/main/resources/bootstrap.yml                                                                    |    6 
 ruoyi-service/ruoyi-payment/src/test/java/com/ruoyi/payment/icbc/service/impl/IcbcNotifyParserTest.java                         |   27 
 ruoyi-api/ruoyi-api-payment/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports |    3 
 ruoyi-service/ruoyi-payment/lib/icbc-ca.jar                                                                                     |    0 
 ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/mapper/IcbcPaymentFlowRecordMapper.java                             |    7 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/ChargingOrderSettlementExecutor.java                            |   22 
 ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillWapper.java                                          |    4 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/ChargingSimulationVO.java                                            |   25 
 ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/impl/IcbcNotifyParser.java                             |   54 
 ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TApplyChargingPileController.java              |   15 
 ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml                                                                     |    5 
 ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderSplitResp.java                                   |   20 
 ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderQueryResp.java                                   |   22 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/IcbcAdminSubOrderVO.java                                             |   45 
 ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/IcbcPaymentFlowRecorder.java                           |   92 +
 nacos-config/ruoyi-gateway-prod-whitelist-addition.md                                                                           |   17 
 ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/PartnerMapper.xml                                       |    7 
 ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingBillMapper.xml                                               |    6 
 ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderVO.java                                             |    4 
 ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/IcbcPayService.java                                    |   35 
 .gitignore                                                                                                                      |    3 
 ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/config/IcbcPayProperties.java                                  |   39 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java                                        |    9 
 ruoyi-service/ruoyi-chargingPile/src/main/resources/bootstrap.yml                                                               |    6 
 ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/dto/PartnerListDTO.java                               |    8 
 sql/20260711_icbc_payment_flow_record.sql                                                                                       |   22 
 ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderRefundResp.java                                  |   20 
 ruoyi-service/ruoyi-payment/lib/icbc-api-sdk-cop-io.jar                                                                         |    0 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/IcbcAdminFundOperationSupport.java                         |  148 +
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java                                      |   25 
 ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/factory/IcbcPaymentFallbackFactory.java                         |   67 
 ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderSplitReq.java                                    |   24 
 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java                                  |    7 
 ruoyi-service/ruoyi-jianguan/src/main/resources/bootstrap.yml                                                                   |    7 
 sql/charging_pile_service.sql                                                                                                   |    6 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/ChargingSimulationReq.java                                          |   12 
 ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/IcbcAdminFundOperationSupportTest.java                     |  159 ++
 ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderQueryReq.java                                    |   19 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/export/TChargingOrderExport.java                                        |    4 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java                                |   71 
 ruoyi-service/ruoyi-payment/lib/commons-logging-1.2.jar                                                                         |    0 
 ruoyi-service/ruoyi-payment/pom.xml                                                                                             |   45 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/ChargingBillController.java                                  |    6 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderShareRecordMapper.java                             |    7 
 ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcRefundReq.java                                           |   19 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/ChargingSimulationSupport.java                             |  127 +
 ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUserApplet.java                                        |   20 
 ruoyi-auth/src/main/resources/bootstrap.yml                                                                                     |    2 
 docs/superpowers/specs/2026-07-11-icbc-admin-fund-operations-design.md                                                          |   57 
 ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/controller/IcbcPayController.java                                   |  327 ++++
 ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/feignClient/IcbcPaymentClient.java                              |   68 
 ruoyi-service/ruoyi-integration/src/main/resources/bootstrap.yml                                                                |    7 
 ruoyi-service/ruoyi-payment/lib/icbc-api-sdk-cop.jar                                                                            |    0 
 ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AliLoginController.java                                  |   68 
 ruoyi-service/ruoyi-payment/src/main/resources/bootstrap.yml                                                                    |    6 
 ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/ChargingListQuery.java                                        |    2 
 ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcPaymentReq.java                                          |   66 
 ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcRefundResp.java                                          |   18 
 sql/20260529_icbc_partner_profit_sharing.sql                                                                                    |    7 
 sql/20260530_charging_order_share_record.sql                                                                                    |   44 
 ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/AccountListVO.java                                               |    4 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/FinancialSettlementController.java                           |    2 
 ruoyi-visual/ruoyi-monitor/src/main/resources/bootstrap.yml                                                                     |    6 
 ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillListVO.java                                          |    4 
 90 files changed, 4,479 insertions(+), 131 deletions(-)

diff --git a/.gitignore b/.gitignore
index 09e64c6..f467dca 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
 
 # Package Files #
 *.jar
+!ruoyi-service/ruoyi-payment/lib/*.jar
 *.war
 *.ear
 
@@ -17,4 +18,4 @@
 
 *.yaml
 *.yml
-*.log
\ No newline at end of file
+*.log
diff --git a/docs/superpowers/plans/2026-07-11-icbc-admin-fund-operations.md b/docs/superpowers/plans/2026-07-11-icbc-admin-fund-operations.md
new file mode 100644
index 0000000..38ccfd4
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-11-icbc-admin-fund-operations.md
@@ -0,0 +1,66 @@
+# ICBC Admin Fund Operations Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Add management-console operations for querying and manually operating ICBC funds for real charging orders.
+
+**Architecture:** The order service validates local charging orders and share records, then calls the payment service through Feign. The management frontend exposes one fund-operation modal from the charging-order list.
+
+**Tech Stack:** Java 8, Spring Boot, MyBatis-Plus, OpenFeign, JUnit 5, React 18, Ant Design Pro.
+
+## Global Constraints
+
+- Only ICBC paid charging orders are eligible.
+- Refund operations affect real bank funds but do not update local business status.
+- A specific share record must be selected for sub-order refunds.
+- Existing unrelated workspace changes must remain untouched.
+
+---
+
+### Task 1: Request Resolution and Validation
+
+**Files:**
+- Create: `ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/IcbcAdminFundOperationSupport.java`
+- Test: `ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/IcbcAdminFundOperationSupportTest.java`
+
+- [ ] Write failing tests for ICBC order eligibility, positive amounts and DTO assembly.
+- [ ] Run the focused test and confirm failure because the support class is missing.
+- [ ] Implement the minimal pure support class.
+- [ ] Run the focused test and confirm success.
+
+### Task 2: Backend Management APIs
+
+**Files:**
+- Modify: `ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/feignClient/IcbcPaymentClient.java`
+- Modify: `ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/factory/IcbcPaymentFallbackFactory.java`
+- Create: `ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/IcbcAdminFundOperationReq.java`
+- Create: `ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/IcbcAdminSubOrderVO.java`
+- Modify: `ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java`
+- Modify: `ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java`
+- Modify: `ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java`
+
+- [ ] Add the missing Feign sub-order-refund operation and fallback.
+- [ ] Add request/response models for order code, amount and selected share record.
+- [ ] Add service methods that query payment, refund the main order, query all share records and refund one selected sub-order.
+- [ ] Add permission-protected controller endpoints.
+- [ ] Compile the order and payment dependency graph.
+
+### Task 3: Management Fund Operation Modal
+
+**Files:**
+- Modify: `chargingPile_management/src/pages/order/pileOrder/service.js`
+- Modify: `chargingPile_management/src/pages/order/pileOrder/index.jsx`
+- Modify: `chargingPile_management/src/pages/order/pileOrder/style.less`
+
+- [ ] Add four API wrappers.
+- [ ] Add the ICBC-only operation button.
+- [ ] Add payment query, main refund and sub-order table sections.
+- [ ] Add real-fund warnings, amount validation, loading states and confirmation dialogs.
+- [ ] Run frontend syntax/type/build checks.
+
+### Task 4: Final Verification
+
+- [ ] Run focused JUnit tests.
+- [ ] Run Maven compile for order and payment modules.
+- [ ] Run frontend static checks.
+- [ ] Run scoped `git diff --check` without touching unrelated changes.
diff --git a/docs/superpowers/specs/2026-07-11-icbc-admin-fund-operations-design.md b/docs/superpowers/specs/2026-07-11-icbc-admin-fund-operations-design.md
new file mode 100644
index 0000000..6e13df2
--- /dev/null
+++ b/docs/superpowers/specs/2026-07-11-icbc-admin-fund-operations-design.md
@@ -0,0 +1,57 @@
+# 工行管理后台资金操作设计
+
+## 目标
+
+在“订单管理/充电桩订单列表”为正常扫码支付生成的 ICBC 充电订单提供支付查询、主订单退款、分账查询和分账退款操作。
+
+## 约束
+
+- 仅 `source_name=ICBC` 且已支付的充电订单允许操作。
+- 管理端只提交业务订单号;退款类操作额外提交金额。
+- 分账查询返回订单下全部工行分账记录,分账退款必须选择具体记录。
+- 操作只调用工行接口并返回结果,不修改充电订单、退款记录或分账记录的业务状态。
+- 工行支付审计表继续记录查询、退款、分账查询和分账退款请求及响应。
+- 管理端必须显示“仅操作银行资金,不同步本地订单状态”的风险提示。
+
+## 后端架构
+
+订单服务负责管理端编排:校验订单、读取 `t_charging_order` 和 `t_charging_order_share_record`,自动组装工行参数,再通过 `IcbcPaymentClient` 调用支付服务。
+
+接口:
+
+- `POST /order/t-charging-order/icbc/fund/payment-query`
+- `POST /order/t-charging-order/icbc/fund/refund`
+- `POST /order/t-charging-order/icbc/fund/sub-orders`
+- `POST /order/t-charging-order/icbc/fund/sub-order-refund`
+
+主订单退款使用订单 `code`、`recharge_serial_number` 和管理端金额。若本地缺少工行订单号,先调用支付查询补充本次请求上下文,但不回写订单。
+
+分账查询按订单读取全部 `pay_channel=ICBC` 且未删除的分账记录,并逐条调用工行子订单查询。单条查询失败不影响其他记录返回。
+
+分账退款以选中的分账记录为准,使用其原订单号、子订单号、商户号、协议号和原交易日期组装请求。
+
+## 管理界面
+
+充电桩订单列表中,符合条件的订单显示“资金操作”。弹窗顶部展示订单号、工行订单号和风险提示,并提供:
+
+- 支付查询按钮及原始结果。
+- 主订单退款金额输入框和二次确认。
+- 分账子订单表格、逐条查询结果和逐条退款入口。
+
+提交期间按钮禁用,退款必须再次确认真实资金影响。
+
+## 错误处理
+
+- 非 ICBC、未支付、订单不存在直接拒绝。
+- 金额必须大于零。
+- 分账记录必须属于当前订单且为 ICBC 渠道。
+- 工行调用失败时返回明确错误及原始响应,不修改本地业务状态。
+- 后端使用订单级本地锁避免同一实例上的并发重复操作;跨实例重复操作风险由工行结果和审计记录追踪。
+
+## 测试
+
+- 参数解析与订单校验单元测试。
+- 工行订单号缺失时查询补全测试。
+- 多分账记录全部返回、单条失败隔离测试。
+- 非法分账记录、非 ICBC、未支付和非法金额测试。
+- 后端模块编译及管理端构建/类型检查。
diff --git a/nacos-config/ruoyi-gateway-prod-whitelist-addition.md b/nacos-config/ruoyi-gateway-prod-whitelist-addition.md
new file mode 100644
index 0000000..405c423
--- /dev/null
+++ b/nacos-config/ruoyi-gateway-prod-whitelist-addition.md
@@ -0,0 +1,17 @@
+# ruoyi-gateway-prod whitelist addition
+
+Add this entry to `DEFAULT_GROUP/ruoyi-gateway-prod.yml` in Nacos:
+
+```yaml
+security:
+  ignore:
+    whites:
+      - /payment/icbc/callBack
+```
+
+Context:
+
+- Controller: `ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/controller/IcbcPayController.java`
+- Internal route: `@RequestMapping("/icbc")` + `@PostMapping("/callBack")`
+- Public gateway route: `/payment/icbc/callBack`
+
diff --git a/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/AppUserInfoDto.java b/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/AppUserInfoDto.java
index 7ed53c5..b70ce2a 100644
--- a/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/AppUserInfoDto.java
+++ b/ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/dto/AppUserInfoDto.java
@@ -24,6 +24,18 @@
     @ApiModelProperty("当前积分")
     private Integer points;
 
+    @ApiModelProperty("微信openid")
+    private String wxOpenid;
+
+    @ApiModelProperty("支付宝openid")
+    private String aliOpenid;
+
+    @ApiModelProperty("支付宝userId")
+    private String aliUserId;
+
+    @ApiModelProperty("支付宝unionId")
+    private String aliUnionId;
+
     @ApiModelProperty("当前余额")
     private BigDecimal balance;
     @ApiModelProperty("当前优惠卷数量")
diff --git a/ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/dto/PartnerListDTO.java b/ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/dto/PartnerListDTO.java
index 397bc76..9ea918b 100644
--- a/ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/dto/PartnerListDTO.java
+++ b/ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/dto/PartnerListDTO.java
@@ -31,6 +31,14 @@
 	private String account;
 	@ApiModelProperty(value = "备注")
 	private String remark;
+	@ApiModelProperty(value = "工行合作商ID")
+	private String icbcPartnerId;
+	@ApiModelProperty(value = "工行二级商户编号")
+	private String icbcSubMerId;
+	@ApiModelProperty(value = "工行二级商户协议编号")
+	private String icbcSubMerPrtclNo;
+	@ApiModelProperty(value = "工行直接分账商户清算账号")
+	private String icbcMerAcct;
 	@ApiModelProperty(value = "电费分成比例")
 	private BigDecimal electricityShareRatio;
 	@ApiModelProperty(value = "服务费分成比例")
diff --git a/ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Partner.java b/ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Partner.java
index c6c2ea0..2411274 100644
--- a/ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Partner.java
+++ b/ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Partner.java
@@ -75,6 +75,24 @@
 	@ApiModelProperty(value = "工行合作商ID")
 	private String icbcPartnerId;
 	/**
+	 * 工行二级商户编号
+	 */
+	@TableField("icbc_sub_mer_id")
+	@ApiModelProperty(value = "工行二级商户编号")
+	private String icbcSubMerId;
+	/**
+	 * 工行二级商户协议编号
+	 */
+	@TableField("icbc_sub_mer_prtcl_no")
+	@ApiModelProperty(value = "工行二级商户协议编号")
+	private String icbcSubMerPrtclNo;
+	/**
+	 * 工行直接分账商户清算账号
+	 */
+	@TableField("icbc_mer_acct")
+	@ApiModelProperty(value = "工行直接分账商户清算账号")
+	private String icbcMerAcct;
+	/**
 	 * 电费分成比例
 	 */
 	@TableField("electricity_share_ratio")
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/ChargingListQuery.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/ChargingListQuery.java
index e62a390..24ae53d 100644
--- a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/ChargingListQuery.java
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/ChargingListQuery.java
@@ -24,6 +24,8 @@
     private Integer state;
     @ApiModelProperty("状态(1日结 2月结)")
     private Integer orderState;
+    @ApiModelProperty("账户类型(1微信商户 2支付宝商户 3工商银行商户)")
+    private Integer payType;
     @ApiModelProperty("uid 前端忽略")
     private String uid;
 
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TChargingOrderShareRecord.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TChargingOrderShareRecord.java
new file mode 100644
index 0000000..24f7d91
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/model/TChargingOrderShareRecord.java
@@ -0,0 +1,124 @@
+package com.ruoyi.order.api.model;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.ruoyi.common.core.web.domain.BasePojo;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 充电订单分账记录,覆盖余额、微信、支付宝、工行等实际支付渠道。
+ */
+@Data
+@TableName("t_charging_order_share_record")
+public class TChargingOrderShareRecord extends BasePojo {
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @TableField("charging_order_id")
+    private Long chargingOrderId;
+
+    @TableField("charging_order_code")
+    private String chargingOrderCode;
+
+    @TableField("site_id")
+    private Integer siteId;
+
+    @TableField("partner_id")
+    private Integer partnerId;
+
+    @TableField("pay_channel")
+    private String payChannel;
+
+    @TableField("channel_pay_amount")
+    private BigDecimal channelPayAmount;
+
+    @TableField("channel_gift_amount")
+    private BigDecimal channelGiftAmount;
+
+    @TableField("channel_pay_ratio")
+    private BigDecimal channelPayRatio;
+
+    @TableField("channel_electricity_amount")
+    private BigDecimal channelElectricityAmount;
+
+    @TableField("channel_service_amount")
+    private BigDecimal channelServiceAmount;
+
+    @TableField("electricity_share_ratio")
+    private BigDecimal electricityShareRatio;
+
+    @TableField("service_share_ratio")
+    private BigDecimal serviceShareRatio;
+
+    @TableField("electricity_share_amount")
+    private BigDecimal electricityShareAmount;
+
+    @TableField("service_share_amount")
+    private BigDecimal serviceShareAmount;
+
+    @TableField("share_amount")
+    private BigDecimal shareAmount;
+
+    @TableField("need_online_share")
+    private Boolean needOnlineShare;
+
+    @TableField("online_share_status")
+    private String onlineShareStatus;
+
+    @TableField("seq_no")
+    private String seqNo;
+
+    @TableField("busi_type")
+    private String busiType;
+
+    @TableField("ori_mer_id")
+    private String oriMerId;
+
+    @TableField("ori_trx_date")
+    private LocalDate oriTrxDate;
+
+    @TableField("sub_order_id")
+    private String subOrderId;
+
+    @TableField("sub_mer_id")
+    private String subMerId;
+
+    @TableField("sub_mer_prtcl_no")
+    private String subMerPrtclNo;
+
+    @TableField("mer_acct")
+    private String merAcct;
+
+    @TableField("icbc_order_id")
+    private String icbcOrderId;
+
+    @TableField("sub_order_seq")
+    private String subOrderSeq;
+
+    @TableField("sub_order_trxid")
+    private String subOrderTrxid;
+
+    @TableField("rec_result")
+    private String recResult;
+
+    @TableField("request_json")
+    private String requestJson;
+
+    @TableField("response_json")
+    private String responseJson;
+
+    @TableField("fail_reason")
+    private String failReason;
+
+    @TableField("retry_count")
+    private Integer retryCount;
+
+    @TableField("last_retry_time")
+    private LocalDateTime lastRetryTime;
+}
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/AccountListVO.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/AccountListVO.java
index b4913d0..9d65389 100644
--- a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/AccountListVO.java
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/AccountListVO.java
@@ -26,6 +26,10 @@
     private BigDecimal commissionAmount;
     @ApiModelProperty(value = "分佣")
     private BigDecimal sharingAmount;
+    @ApiModelProperty(value = "平台收入")
+    private BigDecimal platformIncome;
+    @ApiModelProperty(value = "合作商收入")
+    private BigDecimal partnerIncome;
     @ApiModelProperty(value = "列表数据")
     private PageInfo<ChargingBillListVO> list;
     @ApiModelProperty(value = "导出数据")
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillListVO.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillListVO.java
index 5814a48..5b6338e 100644
--- a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillListVO.java
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillListVO.java
@@ -37,6 +37,10 @@
     private BigDecimal serviceCharge;
     @ApiModelProperty(value = "平台手续费")
     private BigDecimal commissionAmount;
+    @ApiModelProperty(value = "平台收入")
+    private BigDecimal platformIncome;
+    @ApiModelProperty(value = "合作商收入")
+    private BigDecimal partnerIncome;
     @ApiModelProperty(value = "充电电流 度")
     private BigDecimal chargingCapacity;
     @ApiModelProperty(value = "入账金额")
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillVO.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillVO.java
index 2f68bfb..6ef06fc 100644
--- a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillVO.java
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillVO.java
@@ -30,6 +30,10 @@
     private BigDecimal commissionAmount;
     @ApiModelProperty(value = "三方平台分佣")
     private BigDecimal sharingAmount;
+    @ApiModelProperty(value = "平台收入")
+    private BigDecimal platformIncome;
+    @ApiModelProperty(value = "合作商收入")
+    private BigDecimal partnerIncome;
     @ApiModelProperty(value = "优惠抵扣")
     private BigDecimal discount;
     @ApiModelProperty(value = "账单分类 1全站 2各个站点 前端忽略")
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillWapper.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillWapper.java
index da4f1f3..3b84807 100644
--- a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillWapper.java
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingBillWapper.java
@@ -31,6 +31,10 @@
     private BigDecimal commissionAmount;
     @ApiModelProperty(value = "三方平台分佣")
     private BigDecimal sharingAmount;
+    @ApiModelProperty(value = "平台收入")
+    private BigDecimal platformIncome;
+    @ApiModelProperty(value = "合作商收入")
+    private BigDecimal partnerIncome;
     @ApiModelProperty(value = "优惠抵扣")
     private BigDecimal discount;
     @ApiModelProperty(value = "账单分类 1全站 2各个站点 前端忽略")
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderListVO.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderListVO.java
index 25092cd..e46ece6 100644
--- a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderListVO.java
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderListVO.java
@@ -38,6 +38,10 @@
     private Integer chargingGunId;
     @ApiModelProperty(value = "订单来源(1=自己平台,2=三方平台)")
     private Integer orderSource;
+    @ApiModelProperty(value = "支付渠道来源")
+    private String sourceName;
+    @ApiModelProperty(value = "第三方支付流水号或工行订单号")
+    private String rechargeSerialNumber;
     @ApiModelProperty(value = "是否白名单用户")
     private Integer isWhitelist;
     @ApiModelProperty(value = "白名单用户退款金额")
@@ -80,6 +84,10 @@
     private BigDecimal balanceIncome;
     @ApiModelProperty(value = "余额赠送收入")
     private BigDecimal balanceGiftIncome;
+    @ApiModelProperty(value = "平台收入")
+    private BigDecimal platformIncome;
+    @ApiModelProperty(value = "合作商收入")
+    private BigDecimal partnerIncome;
     @ApiModelProperty(value = "充电后剩余金额")
     private BigDecimal residualAmount;
     @ApiModelProperty(value = "累计电费")
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderTimeVO.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderTimeVO.java
index 02a5021..10c8eba 100644
--- a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderTimeVO.java
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderTimeVO.java
@@ -23,6 +23,10 @@
     private BigDecimal electrovalence;
     @ApiModelProperty(value = "累计服务费")
     private BigDecimal serviceCharge;
+    @ApiModelProperty(value = "平台收入")
+    private BigDecimal platformIncome;
+    @ApiModelProperty(value = "合作商收入")
+    private BigDecimal partnerIncome;
     @ApiModelProperty(value = "尖峰平谷充电量")
     private String capacityProportion;
     @ApiModelProperty(value = "尖峰平谷服务费")
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderVO.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderVO.java
index c998fb4..687897a 100644
--- a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderVO.java
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/ChargingOrderVO.java
@@ -27,6 +27,10 @@
     private BigDecimal balanceIncome;
     @ApiModelProperty(value = "赠送费用")
     private BigDecimal balanceGiftIncome;
+    @ApiModelProperty(value = "平台收入")
+    private BigDecimal platformIncome;
+    @ApiModelProperty(value = "合作商收入")
+    private BigDecimal partnerIncome;
     @ApiModelProperty(value = "白名单用户退款金额")
     private BigDecimal whitelistRefundAmount;
 
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/factory/IcbcPaymentFallbackFactory.java b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/factory/IcbcPaymentFallbackFactory.java
new file mode 100644
index 0000000..e8d70f6
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/factory/IcbcPaymentFallbackFactory.java
@@ -0,0 +1,67 @@
+package com.ruoyi.payment.api.factory;
+
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.payment.api.feignClient.IcbcPaymentClient;
+import com.ruoyi.payment.api.vo.IcbcPaymentReq;
+import com.ruoyi.payment.api.vo.IcbcPaymentResp;
+import com.ruoyi.payment.api.vo.IcbcRefundReq;
+import com.ruoyi.payment.api.vo.IcbcRefundResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderSplitReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderSplitResp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.cloud.openfeign.FallbackFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * 工行支付调用降级处理。
+ */
+@Component
+public class IcbcPaymentFallbackFactory implements FallbackFactory<IcbcPaymentClient> {
+    private static final Logger log = LoggerFactory.getLogger(IcbcPaymentFallbackFactory.class);
+
+    @Override
+    public IcbcPaymentClient create(Throwable throwable) {
+        log.error("工行支付调用失败:{}", throwable.getMessage());
+        return new IcbcPaymentClient() {
+            @Override
+            public R<IcbcPaymentResp> payment(IcbcPaymentReq req) {
+                return R.fail("调起工行支付失败:" + throwable.getMessage());
+            }
+
+            @Override
+            public R<IcbcPaymentResp> query(String outTradeNo) {
+                return R.fail("查询工行支付订单失败:" + throwable.getMessage());
+            }
+
+            @Override
+            public R<IcbcPaymentResp> close(String outTradeNo) {
+                return R.fail("关闭工行支付订单失败:" + throwable.getMessage());
+            }
+
+            @Override
+            public R<IcbcRefundResp> refund(IcbcRefundReq req) {
+                return R.fail("工行退款失败:" + throwable.getMessage());
+            }
+
+            @Override
+            public R<IcbcSubOrderSplitResp> splitSubOrder(IcbcSubOrderSplitReq req) {
+                return R.fail("工行小订单拆分失败:" + throwable.getMessage());
+            }
+
+            @Override
+            public R<IcbcSubOrderRefundResp> refundSubOrder(IcbcSubOrderRefundReq req) {
+                return R.fail("工行小订单拆分退款失败:" + throwable.getMessage());
+            }
+
+            @Override
+            public R<IcbcSubOrderQueryResp> querySubOrder(IcbcSubOrderQueryReq req) {
+                return R.fail("查询工行子订单失败:" + throwable.getMessage());
+            }
+        };
+    }
+}
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/feignClient/IcbcPaymentClient.java b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/feignClient/IcbcPaymentClient.java
new file mode 100644
index 0000000..4cd1d87
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/feignClient/IcbcPaymentClient.java
@@ -0,0 +1,68 @@
+package com.ruoyi.payment.api.feignClient;
+
+import com.ruoyi.common.core.constant.ServiceNameConstants;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.payment.api.factory.IcbcPaymentFallbackFactory;
+import com.ruoyi.payment.api.vo.IcbcPaymentReq;
+import com.ruoyi.payment.api.vo.IcbcPaymentResp;
+import com.ruoyi.payment.api.vo.IcbcRefundReq;
+import com.ruoyi.payment.api.vo.IcbcRefundResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderSplitReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderSplitResp;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * 工行统一支付服务。
+ */
+@FeignClient(contextId = "IcbcPaymentClient", value = ServiceNameConstants.PAYMENT_SERVICE, fallbackFactory = IcbcPaymentFallbackFactory.class)
+public interface IcbcPaymentClient {
+
+    /**
+     * 调起工行聚合支付。
+     */
+    @PostMapping("/icbc/payment")
+    R<IcbcPaymentResp> payment(@RequestBody IcbcPaymentReq req);
+
+    /**
+     * 查询工行支付订单。
+     */
+    @PostMapping("/icbc/query")
+    R<IcbcPaymentResp> query(@RequestParam("outTradeNo") String outTradeNo);
+
+    /**
+     * 关闭工行待支付订单。
+     */
+    @PostMapping("/icbc/close")
+    R<IcbcPaymentResp> close(@RequestParam("outTradeNo") String outTradeNo);
+
+    /**
+     * 工行聚合支付退款。
+     */
+    @PostMapping("/icbc/refund")
+    R<IcbcRefundResp> refund(@RequestBody IcbcRefundReq req);
+
+    /**
+     * 工行小订单拆分。
+     */
+    @PostMapping("/icbc/sub-order/split")
+    R<IcbcSubOrderSplitResp> splitSubOrder(@RequestBody IcbcSubOrderSplitReq req);
+
+    /**
+     * 工行小订单拆分退款。
+     */
+    @PostMapping("/icbc/sub-order/refund")
+    R<IcbcSubOrderRefundResp> refundSubOrder(@RequestBody IcbcSubOrderRefundReq req);
+
+    /**
+     * 查询工行子订单。
+     */
+    @PostMapping("/icbc/sub-order/query")
+    R<IcbcSubOrderQueryResp> querySubOrder(@RequestBody IcbcSubOrderQueryReq req);
+}
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcPaymentReq.java b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcPaymentReq.java
new file mode 100644
index 0000000..4b5eb0f
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcPaymentReq.java
@@ -0,0 +1,66 @@
+package com.ruoyi.payment.api.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 工行统一支付请求。
+ */
+@Data
+public class IcbcPaymentReq {
+    /**
+     * 业务订单号。
+     */
+    private String outTradeNo;
+
+    /**
+     * 支付金额,单位元。
+     */
+    private BigDecimal amount;
+
+    /**
+     * 订单标题。
+     */
+    private String subject;
+
+    /**
+     * 订单描述。
+     */
+    private String body;
+
+    /**
+     * 工行 open_id。微信小程序传微信 openId;支付宝小程序传支付宝 16 位 userId。
+     */
+    private String openId;
+
+    /**
+     * 工行 union_id,预留给支付宝生活号等 access_type=8 场景。
+     */
+    private String unionId;
+
+    /**
+     * 小程序设备标识,用于工行 deviceInfo/decive_info。
+     */
+    private String deviceInfo;
+
+    /**
+     * 兼容原支付方式:1=微信,2=支付宝,3=支付宝H5。
+     */
+    private Integer paymentType;
+
+    /**
+     * 支付结果通知地址。
+     */
+    private String notifyUrl;
+
+    /**
+     * H5 支付回跳地址。
+     */
+    private String returnUrl;
+
+    /**
+     * 分账预留字段,后续接入站点、运营商、平台分账规则后映射到工行请求。
+     */
+    private String profitSharing;
+}
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcPaymentResp.java b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcPaymentResp.java
new file mode 100644
index 0000000..380cd4d
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcPaymentResp.java
@@ -0,0 +1,34 @@
+package com.ruoyi.payment.api.vo;
+
+import lombok.Data;
+
+/**
+ * 工行统一支付响应。
+ */
+@Data
+public class IcbcPaymentResp {
+    /**
+     * 业务订单号。
+     */
+    private String orderId;
+
+    /**
+     * 工行侧交易号或支付流水。
+     */
+    private String tradeNo;
+
+    /**
+     * 工行支付状态或本地骨架状态。
+     */
+    private String status;
+
+    /**
+     * 给前端调起支付使用的数据。
+     */
+    private String payData;
+
+    /**
+     * 工行 SDK 原始响应摘要。
+     */
+    private String rawResponse;
+}
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcRefundReq.java b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcRefundReq.java
new file mode 100644
index 0000000..7fa761f
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcRefundReq.java
@@ -0,0 +1,19 @@
+package com.ruoyi.payment.api.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 工行聚合支付退款请求。
+ */
+@Data
+public class IcbcRefundReq {
+    private String outTradeNo;
+    private String orderId;
+    private String outRefundNo;
+    private BigDecimal refundAmount;
+    private BigDecimal totalAmount;
+    private String reason;
+    private String merAcct;
+}
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcRefundResp.java b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcRefundResp.java
new file mode 100644
index 0000000..777a594
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcRefundResp.java
@@ -0,0 +1,18 @@
+package com.ruoyi.payment.api.vo;
+
+import lombok.Data;
+
+/**
+ * 工行聚合支付退款响应。
+ */
+@Data
+public class IcbcRefundResp {
+    private String outTradeNo;
+    private String orderId;
+    private String outRefundNo;
+    private String refundId;
+    private String refundTime;
+    private String refundAmount;
+    private String status;
+    private String rawResponse;
+}
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderQueryReq.java b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderQueryReq.java
new file mode 100644
index 0000000..1f24419
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderQueryReq.java
@@ -0,0 +1,19 @@
+package com.ruoyi.payment.api.vo;
+
+import lombok.Data;
+
+/**
+ * 工行聚合支付子订单查询请求。
+ */
+@Data
+public class IcbcSubOrderQueryReq {
+    private String serNo;
+    private String orderNoType;
+    private String orderId;
+    private String merOrderId;
+    private String prtiId;
+    private String queryType;
+    private String subOrderNo;
+    private String merTrnscSerno;
+    private String subPrtiId;
+}
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderQueryResp.java b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderQueryResp.java
new file mode 100644
index 0000000..a2c8ebc
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderQueryResp.java
@@ -0,0 +1,22 @@
+package com.ruoyi.payment.api.vo;
+
+import lombok.Data;
+
+/**
+ * 工行聚合支付子订单查询响应。
+ */
+@Data
+public class IcbcSubOrderQueryResp {
+    private String orderStatus;
+    private String subOutMerNo;
+    private String subOutAgreeNo;
+    private String outTradeNo;
+    private String orderId;
+    private String subOrderNo;
+    private String subMerOrderId;
+    private String orderAmnt;
+    private String orderRefundAmnt;
+    private String payDate;
+    private String status;
+    private String rawResponse;
+}
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderRefundReq.java b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderRefundReq.java
new file mode 100644
index 0000000..ac948a1
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderRefundReq.java
@@ -0,0 +1,27 @@
+package com.ruoyi.payment.api.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 工行聚合支付小订单拆分退款请求。
+ */
+@Data
+public class IcbcSubOrderRefundReq {
+    private String chargingOrderCode;
+    private String seqNo;
+    private String busiType;
+    private String oriMerId;
+    private String oriTrxDate;
+    private String subOrderId;
+    private String retSubOrderId;
+    private String outtrxSerialNo;
+    private String subMerId;
+    private String subMerPrtclNo;
+    private BigDecimal refundAmount;
+    private String refundSource;
+    private String merAcct;
+    private String merSpInfor;
+    private String termId;
+}
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderRefundResp.java b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderRefundResp.java
new file mode 100644
index 0000000..882871d
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderRefundResp.java
@@ -0,0 +1,20 @@
+package com.ruoyi.payment.api.vo;
+
+import lombok.Data;
+
+/**
+ * 工行聚合支付小订单拆分退款响应。
+ */
+@Data
+public class IcbcSubOrderRefundResp {
+    private String chargingOrderCode;
+    private String subOrderId;
+    private String retSubOrderId;
+    private String outtrxSerialNo;
+    private String orderId;
+    private String subOrderSeq;
+    private String subOrderTrxid;
+    private String refundAmount;
+    private String status;
+    private String rawResponse;
+}
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderSplitReq.java b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderSplitReq.java
new file mode 100644
index 0000000..86465d5
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderSplitReq.java
@@ -0,0 +1,24 @@
+package com.ruoyi.payment.api.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 工行聚合支付小订单拆分请求。
+ */
+@Data
+public class IcbcSubOrderSplitReq {
+    private String chargingOrderCode;
+    private String seqNo;
+    private String busiType;
+    private String oriMerId;
+    private String oriTrxDate;
+    private String subOrderId;
+    private String subMerId;
+    private String subMerPrtclNo;
+    private BigDecimal classifyAmt;
+    private String merAcct;
+    private String merSpInfor;
+    private String termId;
+}
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderSplitResp.java b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderSplitResp.java
new file mode 100644
index 0000000..0d0322a
--- /dev/null
+++ b/ruoyi-api/ruoyi-api-payment/src/main/java/com/ruoyi/payment/api/vo/IcbcSubOrderSplitResp.java
@@ -0,0 +1,20 @@
+package com.ruoyi.payment.api.vo;
+
+import lombok.Data;
+
+/**
+ * 工行聚合支付小订单拆分响应。
+ */
+@Data
+public class IcbcSubOrderSplitResp {
+    private String chargingOrderCode;
+    private String subOrderId;
+    private String orderId;
+    private String subOrderSeq;
+    private String subOrderTrxid;
+    private String recResult;
+    private String classifyAmt;
+    private String subMerId;
+    private String status;
+    private String rawResponse;
+}
diff --git a/ruoyi-api/ruoyi-api-payment/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-api/ruoyi-api-payment/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 1e04370..c1a0848 100644
--- a/ruoyi-api/ruoyi-api-payment/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/ruoyi-api/ruoyi-api-payment/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1,4 +1,5 @@
 com.ruoyi.payment.api.factory.WxPaymentFallbackFactory
 com.ruoyi.payment.api.factory.AliPaymentFallbackFactory
 com.ruoyi.payment.api.factory.AppAliPaymentFallbackFactory
-com.ruoyi.payment.api.factory.H5AliPaymentFallbackFactory
\ No newline at end of file
+com.ruoyi.payment.api.factory.H5AliPaymentFallbackFactory
+com.ruoyi.payment.api.factory.IcbcPaymentFallbackFactory
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUserApplet.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUserApplet.java
index be73042..23b185b 100644
--- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUserApplet.java
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/LoginUserApplet.java
@@ -54,4 +54,24 @@
      * 地址
      */
     private String address;
+
+    /**
+     * 微信小程序 openId
+     */
+    private String wxOpenid;
+
+    /**
+     * 支付宝小程序 openId
+     */
+    private String aliOpenid;
+
+    /**
+     * 支付宝 userId。
+     */
+    private String aliUserId;
+
+    /**
+     * 支付宝 unionId,工行支付宝小程序支付 union_id 使用。
+     */
+    private String aliUnionId;
 }
diff --git a/ruoyi-auth/src/main/resources/bootstrap.yml b/ruoyi-auth/src/main/resources/bootstrap.yml
index 5b28ed3..de780b5 100644
--- a/ruoyi-auth/src/main/resources/bootstrap.yml
+++ b/ruoyi-auth/src/main/resources/bootstrap.yml
@@ -6,8 +6,8 @@
 spring:
   profiles:
     # 环境配置
+#    active: dev
     active: dev
-#    active: prod
   application:
     # 应用名称
     name: ruoyi-auth
diff --git a/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml b/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml
index 4de4316..dea794e 100644
--- a/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml
+++ b/ruoyi-modules/ruoyi-system/src/main/resources/bootstrap.yml
@@ -3,7 +3,7 @@
   tomcat:
     basedir: /data/tomcat
 # Spring
-spring:
+spring: 
   application:
     # 应用名称
     name: ruoyi-system
@@ -11,9 +11,8 @@
     allow-bean-definition-overriding: true
   profiles:
     # 环境配置
-#    active: local
+#    active: dev
     active: dev
-#    active: prod
 ---
 spring:
   config:
diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AliLoginController.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AliLoginController.java
index 6db885e..1c775d9 100644
--- a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AliLoginController.java
+++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/AliLoginController.java
@@ -9,6 +9,7 @@
 import com.alipay.api.response.AlipaySystemOauthTokenResponse;
 import com.alipay.api.response.AlipayUserInfoShareResponse;
 import com.alipay.api.response.AlipayUserUserinfoShareResponse;
+import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.ruoyi.account.ali.Constant.AliConstant;
@@ -28,6 +29,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
+import java.lang.reflect.Method;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -56,8 +58,16 @@
         // 登录
         AlipaySystemOauthTokenResponse response = new AliAppletTools(aliProperties).login(appletUserLogin.getCode());
         if (response.isSuccess()) {
-            // 获取用户信息
-//            AlipayUserInfoShareResponse userInfo = new AliAppletTools(aliProperties).getUserInfo(response.getAccessToken());
+            String aliUnionId = firstNotBlank(readStringProperty(response, "getUnionId"), readStringProperty(response, "getUnion_id"),
+                    getResponseBodyValue(response, "union_id"), getResponseBodyValue(response, "unionId"));
+            String aliUserId = firstNotBlank(response.getUserId(), getResponseBodyValue(response, "user_id"), getResponseBodyValue(response, "userId"));
+            log.info("支付宝授权换取用户标识成功,openId:{},userId:{},unionId:{}", response.getOpenId(), aliUserId, aliUnionId);
+            AlipayUserInfoShareResponse userInfo = new AliAppletTools(aliProperties).getUserInfo(response.getAccessToken());
+            log.info("支付宝用户信息接口返回:{}", JSON.toJSONString(userInfo));
+            aliUserId = firstNotBlank(aliUserId, userInfo == null ? null : userInfo.getUserId(),
+                    getResponseBodyValue(userInfo, "user_id"), getResponseBodyValue(userInfo, "userId"));
+            aliUnionId = firstNotBlank(aliUnionId, readStringProperty(userInfo, "getUnionId"), readStringProperty(userInfo, "getUnion_id"),
+                    getResponseBodyValue(userInfo, "union_id"), getResponseBodyValue(userInfo, "unionId"));
             if(StringUtils.isEmpty(appletUserLogin.getToken())){
                 return AjaxResult.success("已拒绝授权");
             }
@@ -65,7 +75,8 @@
             String phoneNumber = new AliAppletTools(aliProperties).getPhoneNumber(appletUserLogin.getToken());
             log.info("获取支付宝用户信息:{}", phoneNumber);
             // 用户信息封装
-            return AjaxResult.success(appUserService.aliLogin(response,phoneNumber,appletUserLogin.getInviteUserId()));
+            return AjaxResult.success(appUserService.aliLogin(response,phoneNumber,appletUserLogin.getInviteUserId(),
+                    aliUserId, aliUnionId));
         } else {
              String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
              log.warn("诊断结果:{}",diagnosisUrl);
@@ -73,4 +84,55 @@
         }
     }
 
+    private String getResponseBodyValue(Object response, String key) {
+        String body = readStringProperty(response, "getBody");
+        if (StringUtils.isEmpty(body)) {
+            return null;
+        }
+        try {
+            com.alibaba.fastjson.JSONObject bodyJson = JSON.parseObject(body);
+            String value = bodyJson.getString(key);
+            if (StringUtils.isNotEmpty(value)) {
+                return value;
+            }
+            for (String nestedKey : bodyJson.keySet()) {
+                com.alibaba.fastjson.JSONObject nested = bodyJson.getJSONObject(nestedKey);
+                if (nested != null) {
+                    value = nested.getString(key);
+                    if (StringUtils.isNotEmpty(value)) {
+                        return value;
+                    }
+                }
+            }
+            return null;
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private String readStringProperty(Object target, String methodName) {
+        if (target == null) {
+            return null;
+        }
+        try {
+            Method method = target.getClass().getMethod(methodName);
+            Object value = method.invoke(target);
+            return value == null ? null : String.valueOf(value);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private String firstNotBlank(String... values) {
+        if (values == null) {
+            return null;
+        }
+        for (String value : values) {
+            if (StringUtils.isNotEmpty(value)) {
+                return value;
+            }
+        }
+        return null;
+    }
+
 }
diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java
index 6274d4b..814bab6 100644
--- a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java
+++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java
@@ -889,7 +889,8 @@
     @ApiOperation(value = "个人中心信息", tags = {"小程序-个人中心"})
     @GetMapping(value = "/user/info")
     public R<AppUserInfoDto> info() {
-        Long userId = tokenService.getLoginUserApplet().getUserId();
+        LoginUserApplet loginUserApplet = tokenService.getLoginUserApplet();
+        Long userId = loginUserApplet.getUserId();
         TAppUser byId = appUserService.getById(userId);
         AppUserInfoDto appUserInfoDto = new AppUserInfoDto();
         TVip data = vipClient.getVipInfoByType(2).getData();
@@ -924,6 +925,10 @@
         appUserInfoDto.setUserCars(list);
         appUserInfoDto.setFirstAdd(byId.getFirstAdd()!=null?byId.getFirstAdd():0);
         appUserInfoDto.setPoints(byId.getPoints()==null?0:byId.getPoints());
+        appUserInfoDto.setWxOpenid(byId.getWxOpenid());
+        appUserInfoDto.setAliOpenid(byId.getAliOpenid());
+        appUserInfoDto.setAliUserId(loginUserApplet.getAliUserId());
+        appUserInfoDto.setAliUnionId(loginUserApplet.getAliUnionId());
         appUserInfoDto.setBalance(byId.getBalance()==null?BigDecimal.ZERO:byId.getBalance());
         Partner boundPartner = null;
         try {
diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/TAppUserService.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/TAppUserService.java
index 2e29c49..60e5059 100644
--- a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/TAppUserService.java
+++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/TAppUserService.java
@@ -32,7 +32,7 @@
      * @return
      */
 //    Map<String, Object> aliLogin(AlipaySystemOauthTokenResponse response, AlipayUserInfoShareResponse userInfo);
-    Map<String, Object> aliLogin(AlipaySystemOauthTokenResponse response,String phone,Long inviteUserId);
+    Map<String, Object> aliLogin(AlipaySystemOauthTokenResponse response,String phone,Long inviteUserId,String aliUserId,String aliUnionId);
 
     /**
      * 封装用户信息和token
diff --git a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/TAppUserServiceImpl.java b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/TAppUserServiceImpl.java
index 879ca16..36a7866 100644
--- a/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/TAppUserServiceImpl.java
+++ b/ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/service/impl/TAppUserServiceImpl.java
@@ -88,7 +88,7 @@
     }
 
     @Override
-    public Map<String, Object> aliLogin(AlipaySystemOauthTokenResponse response,String phone,Long inviteUserId) {
+    public Map<String, Object> aliLogin(AlipaySystemOauthTokenResponse response,String phone,Long inviteUserId,String aliUserId,String aliUnionId) {
         // 通过手机号查询用户,是否已存在手动导入用户,包含微信用户
         TAppUser appUser = this.getOne(Wrappers.lambdaQuery(TAppUser.class)
                 .eq(TAppUser::getPhone, phone)
@@ -129,7 +129,10 @@
         if(Objects.nonNull(inviteUserId)){
             inviteUserService.saveInviteUser(appUser.getId(), inviteUserId);
         }
-        return this.getUserInfo(appUser);
+        LoginUserApplet loginUserApplet = buildLoginUserApplet(appUser);
+        loginUserApplet.setAliUserId(aliUserId);
+        loginUserApplet.setAliUnionId(aliUnionId);
+        return buildTokenInfo(loginUserApplet);
     }
 //    @Override
 //    public Map<String, Object> aliLogin(AlipaySystemOauthTokenResponse response, AlipayUserInfoShareResponse userInfo) {
@@ -163,6 +166,10 @@
 
     @Override
     public Map<String, Object> getUserInfo(TAppUser appUser) {
+        return buildTokenInfo(buildLoginUserApplet(appUser));
+    }
+
+    private LoginUserApplet buildLoginUserApplet(TAppUser appUser) {
         LoginUserApplet loginUserApplet = new LoginUserApplet();
         if(ObjectUtils.isNotNull(appUser)){
             loginUserApplet.setUserId(appUser.getId());
@@ -171,7 +178,13 @@
             loginUserApplet.setPhone(appUser.getPhone());
             loginUserApplet.setAvatar(appUser.getAvatar());
             loginUserApplet.setAddress(appUser.getProvince()+appUser.getCity());
+            loginUserApplet.setWxOpenid(appUser.getWxOpenid());
+            loginUserApplet.setAliOpenid(appUser.getAliOpenid());
         }
+        return loginUserApplet;
+    }
+
+    private Map<String, Object> buildTokenInfo(LoginUserApplet loginUserApplet) {
         Map<String, Object> tokenInfos = new HashMap<>();
         tokenInfos.put("token",tokenService.createTokenApplet(loginUserApplet));
         tokenInfos.put("info",loginUserApplet);
diff --git a/ruoyi-service/ruoyi-account/src/main/resources/bootstrap.yml b/ruoyi-service/ruoyi-account/src/main/resources/bootstrap.yml
index c820626..a7ff26b 100644
--- a/ruoyi-service/ruoyi-account/src/main/resources/bootstrap.yml
+++ b/ruoyi-service/ruoyi-account/src/main/resources/bootstrap.yml
@@ -13,7 +13,7 @@
   profiles:
     # 环境配置
 #    active: dev
-    active: prod
+    active: dev
 ---
 spring:
   config:
@@ -23,7 +23,7 @@
     nacos:
       discovery:
         # 服务注册地址
-        server-addr: 192.168.110.169:8848
+        server-addr: 192.168.110.80:8848
         service: ${spring.application.name}
         group: DEFAULT_GROUP
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
@@ -31,7 +31,7 @@
         password: nacos
       config:
         # 配置中心地址
-        server-addr: 192.168.110.169:8848
+        server-addr: 192.168.110.80:8848
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
         group: DEFAULT_GROUP
         name: ${spring.application.name}
diff --git a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TApplyChargingPileController.java b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TApplyChargingPileController.java
index 5120ee2..faed8a8 100644
--- a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TApplyChargingPileController.java
+++ b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TApplyChargingPileController.java
@@ -337,8 +337,11 @@
         BigDecimal bigDecimal = new BigDecimal("0");
 
         for (TChargingOrder chargingOrder : collect) {
-            bigDecimal = bigDecimal.add(chargingOrder.getRefundAmount());
-            if (chargingOrder.getRechargePaymentType()!=null){
+            bigDecimal = bigDecimal.add(chargingOrder.getRefundAmount() == null ? BigDecimal.ZERO : chargingOrder.getRefundAmount());
+            if ("ICBC".equals(chargingOrder.getSourceName())) {
+                chargingOrder.setPayPlatform("工商银行");
+                chargingOrder.setRechargePaymentType1("工商银行支付");
+            } else if (chargingOrder.getRechargePaymentType()!=null){
                 switch (chargingOrder.getRechargePaymentType()){
                     case 1:
                         chargingOrder.setPayPlatform("微信");
@@ -348,12 +351,18 @@
                         chargingOrder.setPayPlatform("支付宝");
                         chargingOrder.setRechargePaymentType1("支付宝小程序支付");
                         break;
+                    case 3:
+                        chargingOrder.setPayPlatform("支付宝");
+                        chargingOrder.setRechargePaymentType1("支付宝H5支付");
+                        break;
                 }
             }
             chargingOrder.setXuhao(i);
             i++;
         }
-        collect.get(0).setTotal(bigDecimal);
+        if (!collect.isEmpty()) {
+            collect.get(0).setTotal(bigDecimal);
+        }
         try {
             response.setCharacterEncoding(Constants.UTF8);
             response.setContentType("application/vnd.ms-excel");
diff --git a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/PartnerServiceImpl.java b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/PartnerServiceImpl.java
index 1fafeec..36844e2 100644
--- a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/PartnerServiceImpl.java
+++ b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/PartnerServiceImpl.java
@@ -25,6 +25,7 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
+import java.math.BigDecimal;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -151,8 +152,32 @@
 		if(null == partner.getId() && StringUtils.isEmpty(partner.getPassword())){
 			return AjaxResult.error("登录密码不能为空");
 		}
+		if (isInvalidShareRatio(partner.getElectricityShareRatio())) {
+			return AjaxResult.error("电费分成比例必须为0-100且最多两位小数");
+		}
+		if (isInvalidShareRatio(partner.getServiceShareRatio())) {
+			return AjaxResult.error("服务费分成比例必须为0-100且最多两位小数");
+		}
+		if (partner.getElectricityShareRatio() != null || partner.getServiceShareRatio() != null) {
+			if (StringUtils.isEmpty(partner.getIcbcSubMerId())) {
+				return AjaxResult.error("填写分成比例时工行二级商户编号不能为空");
+			}
+			if (StringUtils.isEmpty(partner.getIcbcSubMerPrtclNo())) {
+				return AjaxResult.error("填写分成比例时工行二级商户协议编号不能为空");
+			}
+		}
 		return AjaxResult.success();
 	}
+
+	private boolean isInvalidShareRatio(BigDecimal ratio) {
+		if (ratio == null) {
+			return false;
+		}
+		if (ratio.compareTo(BigDecimal.ZERO) < 0 || ratio.compareTo(new BigDecimal("100")) > 0) {
+			return true;
+		}
+		return Math.max(ratio.stripTrailingZeros().scale(), 0) > 2;
+	}
 	
 	
 	/**
diff --git a/ruoyi-service/ruoyi-chargingPile/src/main/resources/bootstrap.yml b/ruoyi-service/ruoyi-chargingPile/src/main/resources/bootstrap.yml
index eefa2d5..48a10b0 100644
--- a/ruoyi-service/ruoyi-chargingPile/src/main/resources/bootstrap.yml
+++ b/ruoyi-service/ruoyi-chargingPile/src/main/resources/bootstrap.yml
@@ -13,7 +13,7 @@
   profiles:
     # 环境配置
 #    active: dev
-    active: prod
+    active: dev
 ---
 spring:
   config:
@@ -23,7 +23,7 @@
     nacos:
       discovery:
         # 服务注册地址
-        server-addr: 192.168.110.169:8848
+        server-addr: 192.168.110.80:8848
         service: ${spring.application.name}
         group: DEFAULT_GROUP
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
@@ -31,7 +31,7 @@
         password: nacos
       config:
         # 配置中心地址
-        server-addr: 192.168.110.169:8848
+        server-addr: 192.168.110.80:8848
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
         group: DEFAULT_GROUP
         name: ${spring.application.name}
diff --git a/ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/PartnerMapper.xml b/ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/PartnerMapper.xml
index 9af10a6..92706a4 100644
--- a/ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/PartnerMapper.xml
+++ b/ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/PartnerMapper.xml
@@ -12,6 +12,9 @@
 		<result column="logo_url" property="logoUrl" />
 		<result column="rate" property="rate" />
 		<result column="icbc_partner_id" property="icbcPartnerId" />
+		<result column="icbc_sub_mer_id" property="icbcSubMerId" />
+		<result column="icbc_sub_mer_prtcl_no" property="icbcSubMerPrtclNo" />
+		<result column="icbc_mer_acct" property="icbcMerAcct" />
 		<result column="electricity_share_ratio" property="electricityShareRatio" />
 		<result column="service_share_ratio" property="serviceShareRatio" />
 		<result column="venue_share_amount" property="venueShareAmount" />
@@ -38,6 +41,10 @@
 		p.address,
 		p.account,
 		p.remark,
+		p.icbc_partner_id as icbcPartnerId,
+		p.icbc_sub_mer_id as icbcSubMerId,
+		p.icbc_sub_mer_prtcl_no as icbcSubMerPrtclNo,
+		p.icbc_mer_acct as icbcMerAcct,
 		p.electricity_share_ratio as electricityShareRatio,
 		p.service_share_ratio as serviceShareRatio,
 		p.venue_share_amount as venueShareAmount
diff --git a/ruoyi-service/ruoyi-integration/src/main/resources/bootstrap.yml b/ruoyi-service/ruoyi-integration/src/main/resources/bootstrap.yml
index ea6e110..31199bf 100644
--- a/ruoyi-service/ruoyi-integration/src/main/resources/bootstrap.yml
+++ b/ruoyi-service/ruoyi-integration/src/main/resources/bootstrap.yml
@@ -13,8 +13,7 @@
   profiles:
     # 环境配置
 #    active: dev
-    active: prod
-    
+    active: dev
 ---
 spring:
   config:
@@ -24,7 +23,7 @@
     nacos:
       discovery:
         # 服务注册地址
-        server-addr: 192.168.110.169:8848
+        server-addr: 192.168.110.80:8848
         service: ${spring.application.name}
         group: DEFAULT_GROUP
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
@@ -32,7 +31,7 @@
         password: nacos
       config:
         # 配置中心地址
-        server-addr: 192.168.110.169:8848
+        server-addr: 192.168.110.80:8848
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
         group: DEFAULT_GROUP
         name: ${spring.application.name}
diff --git a/ruoyi-service/ruoyi-jianguan/src/main/resources/bootstrap.yml b/ruoyi-service/ruoyi-jianguan/src/main/resources/bootstrap.yml
index ff76b29..2fc0648 100644
--- a/ruoyi-service/ruoyi-jianguan/src/main/resources/bootstrap.yml
+++ b/ruoyi-service/ruoyi-jianguan/src/main/resources/bootstrap.yml
@@ -13,8 +13,7 @@
   profiles:
     # 环境配置
 #    active: dev
-    active: prod
-    
+    active: dev
 ---
 spring:
   config:
@@ -24,7 +23,7 @@
     nacos:
       discovery:
         # 服务注册地址
-        server-addr: 192.168.110.169:8848
+        server-addr: 192.168.110.80:8848
         service: ${spring.application.name}
         group: DEFAULT_GROUP
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
@@ -32,7 +31,7 @@
         password: nacos
       config:
         # 配置中心地址
-        server-addr: 192.168.110.169:8848
+        server-addr: 192.168.110.80:8848
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
         group: DEFAULT_GROUP
         name: ${spring.application.name}
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/ChargingBillController.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/ChargingBillController.java
index 4b26ca7..a6fda40 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/ChargingBillController.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/ChargingBillController.java
@@ -154,6 +154,8 @@
         accountListVO.setRefundAmount(res.getRefundAmount().setScale(2, BigDecimal.ROUND_DOWN));
         accountListVO.setCommissionAmount(res.getCommissionAmount().setScale(2, BigDecimal.ROUND_DOWN));
         accountListVO.setSharingAmount(res.getSharingAmount().setScale(2, BigDecimal.ROUND_DOWN));
+        accountListVO.setPlatformIncome(res.getPlatformIncome().setScale(2, BigDecimal.ROUND_DOWN));
+        accountListVO.setPartnerIncome(res.getPartnerIncome().setScale(2, BigDecimal.ROUND_DOWN));
         accountListVO.setList(res.getList());
         accountListVO.setOrderList(res.getExportList());
         return R.ok(accountListVO);
@@ -203,6 +205,8 @@
             tChargingBillExport.setBillWeek(orderInvoiceVO.getBillWeek());
             tChargingBillExport.setSiteName(orderInvoiceVO.getSiteName());
             tChargingBillExport.setPaymentAmount(orderInvoiceVO.getPaymentAmount());
+            tChargingBillExport.setPlatformIncome(orderInvoiceVO.getPlatformIncome());
+            tChargingBillExport.setPartnerIncome(orderInvoiceVO.getPartnerIncome());
             tChargingBillExport.setElectrovalence(orderInvoiceVO.getElectrovalence());
             tChargingBillExport.setServiceCharge(orderInvoiceVO.getServiceCharge());
             tChargingBillExport.setChargingCapacity(orderInvoiceVO.getChargingCapacity());
@@ -276,6 +280,8 @@
             tChargingBillExport.setBillWeek(orderInvoiceVO.getBillWeek());
             tChargingBillExport.setSiteName(orderInvoiceVO.getSiteName());
             tChargingBillExport.setPaymentAmount(orderInvoiceVO.getPaymentAmount());
+            tChargingBillExport.setPlatformIncome(orderInvoiceVO.getPlatformIncome());
+            tChargingBillExport.setPartnerIncome(orderInvoiceVO.getPartnerIncome());
             tChargingBillExport.setElectrovalence(orderInvoiceVO.getElectrovalence());
             tChargingBillExport.setServiceCharge(orderInvoiceVO.getServiceCharge());
             tChargingBillExport.setChargingCapacity(orderInvoiceVO.getChargingCapacity());
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/FinancialSettlementController.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/FinancialSettlementController.java
index 3e32e86..aa91cec 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/FinancialSettlementController.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/FinancialSettlementController.java
@@ -250,6 +250,8 @@
             }
             tChargingOrderExport.setBalanceIncome(formatMoney(chargingOrderListVO.getBalanceIncome()));
             tChargingOrderExport.setBalanceGiftIncome(formatMoney(chargingOrderListVO.getBalanceGiftIncome()));
+            tChargingOrderExport.setPlatformIncome(formatMoney(chargingOrderListVO.getPlatformIncome()));
+            tChargingOrderExport.setPartnerIncome(formatMoney(chargingOrderListVO.getPartnerIncome()));
             tChargingOrderExport.setTripartiteCommission(formatMoney(chargingOrderListVO.getSharingAmount()));
             tChargingOrderExport.setOrderCode(chargingOrderListVO.getStartChargeSeq());
             tChargingOrderExport.setIsSingle("是");
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java
index 3a23f90..c6323f7 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java
@@ -58,6 +58,8 @@
 import com.ruoyi.order.util.PreviousSixMonths;
 import com.ruoyi.order.util.mongodb.service.UploadRealTimeMonitoringDataService;
 import com.ruoyi.order.vo.EndOfChargePageInfo;
+import com.ruoyi.order.vo.ChargingSimulationVO;
+import com.ruoyi.order.vo.IcbcAdminSubOrderVO;
 import com.ruoyi.order.vo.StatisticsOfBatteryVO;
 import com.ruoyi.order.vo.UpdateOrderPaymentAmountDto;
 import com.ruoyi.order.vo.WatchChargingOrderVo;
@@ -65,6 +67,9 @@
 import com.ruoyi.other.api.feignClient.UserSiteClient;
 import com.ruoyi.payment.api.feignClient.AliPaymentClient;
 import com.ruoyi.payment.api.feignClient.WxPaymentClient;
+import com.ruoyi.payment.api.vo.IcbcPaymentResp;
+import com.ruoyi.payment.api.vo.IcbcRefundResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundResp;
 import com.ruoyi.system.api.domain.SysUser;
 import com.ruoyi.system.api.feignClient.SysUserClient;
 import com.ruoyi.system.api.feignClient.SysUserRoleClient;
@@ -948,6 +953,48 @@
     public AjaxResult paymentChargingOrder(@RequestBody AddChargingOrder addChargingOrder) {
         return chargingOrderService.paymentChargingOrder(addChargingOrder);
     }
+
+    @RequiresPermissions(value = {"/chargingPileOrder", "/paymentOrder/refund"}, logical = Logical.AND)
+    @PostMapping("/icbc/fund/payment-query")
+    @ApiOperation(value = "管理后台查询工行支付订单", tags = {"后台-充电桩订单"})
+    public AjaxResult<IcbcPaymentResp> queryIcbcAdminPayment(@RequestBody IcbcAdminFundOperationReq request) {
+        return AjaxResult.success(chargingOrderService.queryIcbcAdminPayment(request == null ? null : request.getOrderCode()));
+    }
+
+    @RequiresPermissions(value = {"/chargingPileOrder", "/paymentOrder/refund"}, logical = Logical.AND)
+    @PostMapping("/icbc/fund/refund")
+    @ApiOperation(value = "管理后台工行主订单退款", tags = {"后台-充电桩订单"})
+    public AjaxResult<IcbcRefundResp> refundIcbcAdminPayment(@RequestBody IcbcAdminFundOperationReq request) {
+        return AjaxResult.success(chargingOrderService.refundIcbcAdminPayment(request));
+    }
+
+    @RequiresPermissions(value = {"/chargingPileOrder", "/paymentOrder/refund"}, logical = Logical.AND)
+    @PostMapping("/icbc/fund/sub-orders")
+    @ApiOperation(value = "管理后台查询工行分账子订单", tags = {"后台-充电桩订单"})
+    public AjaxResult<List<IcbcAdminSubOrderVO>> queryIcbcAdminSubOrders(@RequestBody IcbcAdminFundOperationReq request) {
+        return AjaxResult.success(chargingOrderService.queryIcbcAdminSubOrders(request == null ? null : request.getOrderCode()));
+    }
+
+    @RequiresPermissions(value = {"/chargingPileOrder", "/paymentOrder/refund"}, logical = Logical.AND)
+    @PostMapping("/icbc/fund/sub-order-refund")
+    @ApiOperation(value = "管理后台工行分账子订单退款", tags = {"后台-充电桩订单"})
+    public AjaxResult<IcbcSubOrderRefundResp> refundIcbcAdminSubOrder(@RequestBody IcbcAdminFundOperationReq request) {
+        return AjaxResult.success(chargingOrderService.refundIcbcAdminSubOrder(request));
+    }
+
+    @ResponseBody
+    @GetMapping("/simulation/current")
+    @ApiOperation(value = "查询当前枪可模拟结算的工行订单", tags = {"小程序-模拟充电"})
+    public AjaxResult<ChargingSimulationVO> getCurrentSimulationOrder(@RequestParam("gunId") Integer gunId) {
+        return AjaxResult.success(chargingOrderService.getCurrentSimulationOrder(gunId));
+    }
+
+    @ResponseBody
+    @PostMapping("/simulation/complete")
+    @ApiOperation(value = "模拟完成充电并执行真实结算", tags = {"小程序-模拟充电"})
+    public AjaxResult<ChargingSimulationVO> completeSimulation(@RequestBody ChargingSimulationReq request) {
+        return AjaxResult.success(chargingOrderService.completeSimulation(request));
+    }
     
     /**
      * 充电充值支付回调
@@ -1123,11 +1170,12 @@
     public R<List<SixChargingDto>> charge(Integer siteId) {
         Long userId = tokenService.getLoginUser().getUserid();
         SysUser sysUser = sysUserClient.getSysUser(userId).getData();
+        boolean partnerView = isPartnerView(sysUser);
         List<Integer> siteIds = resolvePlatformRevenueSiteIds(userId, sysUser, siteId);
         LocalDate sixBefore = PreviousSixMonths.get();
         //通过siteIds进行sql查询统计
         List<SixChargingDto> sixChargingDtos = generateLastSixMonths();
-        List<SixChargingDto> chargingDtos = chargingOrderService.charge(sixBefore, siteIds);
+        List<SixChargingDto> chargingDtos = chargingOrderService.charge(sixBefore, siteIds, partnerView);
         for (SixChargingDto sixChargingDto : sixChargingDtos) {
             for (SixChargingDto chargingDto : chargingDtos) {
                 if (sixChargingDto.getMonth().equals(chargingDto.getMonth())){
@@ -1165,9 +1213,10 @@
     public R<List<SixCircleDto>> circle() {
         Long userId = tokenService.getLoginUser().getUserid();
         SysUser sysUser = sysUserClient.getSysUser(userId).getData();
+        boolean partnerView = isPartnerView(sysUser);
         List<Integer> siteIds = resolvePlatformRevenueSiteIds(userId, sysUser, null);
         LocalDate sixBefore = PreviousSixMonths.get();
-        List<SixCircleDto> sixCircleDtos = chargingOrderService.circle(siteIds,sixBefore);
+        List<SixCircleDto> sixCircleDtos = chargingOrderService.circle(siteIds,sixBefore, partnerView);
         for (SixCircleDto sixCircleDto : sixCircleDtos) {
             Site site = siteClient.getSiteByIds(Arrays.asList(sixCircleDto.getSiteId())).getData().get(0);
             sixCircleDto.setSiteName(site.getName());
@@ -1252,14 +1301,15 @@
         //count近6个月的数据
         Long userId = tokenService.getLoginUser().getUserid();
         SysUser sysUser = sysUserClient.getSysUser(userId).getData();
+        boolean partnerView = isPartnerView(sysUser);
         List<Integer> siteIds = resolvePlatformRevenueSiteIds(userId, sysUser, null);
-        Map<String,Object>  map = chargingOrderService.countAll(siteIds);
-        boolean platformVisible = 1 == sysUser.getObjectId();
+        Map<String,Object>  map = chargingOrderService.countAll(siteIds, partnerView);
+        if (map == null) {
+            map = new HashMap<String,Object>();
+        }
+        boolean platformVisible = !partnerView;
         if(platformVisible){
             BigDecimal data = parkingLotClient.getRecordAmount().getData();
-            if (map ==null){
-                map = new HashMap<String,Object>();
-            }
             map.put("parkingAmount",data);
             BigDecimal data1 =   shoppingOrderService.getSumAmount();
             map.put("shopAmount",data1);
@@ -1281,9 +1331,14 @@
     public R<List<SixIncomeCircleDto>> payTypeCircle() {
         Long userId = tokenService.getLoginUser().getUserid();
         SysUser sysUser = sysUserClient.getSysUser(userId).getData();
+        boolean partnerView = isPartnerView(sysUser);
         List<Integer> siteIds = resolvePlatformRevenueSiteIds(userId, sysUser, null);
         LocalDate sixBefore = PreviousSixMonths.get();
-        return R.ok(chargingOrderService.payTypeCircle(sixBefore, siteIds));
+        return R.ok(chargingOrderService.payTypeCircle(sixBefore, siteIds, partnerView));
+    }
+
+    private boolean isPartnerView(SysUser sysUser) {
+        return sysUser != null && Integer.valueOf(2).equals(sysUser.getRoleType());
     }
 
     /**
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/ChargingSimulationReq.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/ChargingSimulationReq.java
new file mode 100644
index 0000000..8a978d5
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/ChargingSimulationReq.java
@@ -0,0 +1,12 @@
+package com.ruoyi.order.dto;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class ChargingSimulationReq {
+    private Integer gunId;
+    private BigDecimal electricityFee;
+    private BigDecimal serviceFee;
+}
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/IcbcAdminFundOperationReq.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/IcbcAdminFundOperationReq.java
new file mode 100644
index 0000000..83f571d
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/dto/IcbcAdminFundOperationReq.java
@@ -0,0 +1,12 @@
+package com.ruoyi.order.dto;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class IcbcAdminFundOperationReq {
+    private String orderCode;
+    private Long shareRecordId;
+    private BigDecimal amount;
+}
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/export/TChargingBillExport.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/export/TChargingBillExport.java
index 39e61c9..caa518d 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/export/TChargingBillExport.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/export/TChargingBillExport.java
@@ -24,6 +24,10 @@
     private String siteName;
     @Excel(name  = "总金额",width = 30)
     private BigDecimal paymentAmount;
+    @Excel(name  = "平台收入",width = 30)
+    private BigDecimal platformIncome;
+    @Excel(name  = "合作商收入",width = 30)
+    private BigDecimal partnerIncome;
     @Excel(name  = "总电费",width = 30)
     private BigDecimal electrovalence;
     @Excel(name  = "总服务费",width = 30)
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/export/TChargingOrderExport.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/export/TChargingOrderExport.java
index 5e19b08..7214c5b 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/export/TChargingOrderExport.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/export/TChargingOrderExport.java
@@ -96,6 +96,10 @@
     private String balanceIncome;
     @Excel(name = "余额赠送收入",width = 30)
     private String balanceGiftIncome;
+    @Excel(name = "平台收入",width = 30)
+    private String platformIncome;
+    @Excel(name = "合作商收入",width = 30)
+    private String partnerIncome;
     @Excel(name = "引流平台分佣",width = 30)
     private String tripartiteCommission;
     @Excel(name = "车架号来源",width = 30)
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java
index 3ca1971..5f19cc7 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java
@@ -65,6 +65,8 @@
     Map<String, Object> exchangeCodeBalanceSummary(@Param("orderIds") List<Long> orderIds);
     List<Map<String, Object>> exchangeCodeBalanceIncomeByOrderIds(@Param("orderIds") List<Long> orderIds);
     List<ChargingOrderBalanceIncomeDetailVO> exchangeCodeBalanceIncomeDetail(@Param("orderIds") List<Long> orderIds);
+    List<Map<String, Object>> chargingOrderShareIncomeByOrderIds(@Param("orderIds") List<Long> orderIds);
+    Map<String, Object> chargingOrderShareIncomeSummaryByOrderIds(@Param("orderIds") List<Long> orderIds);
 
     List<PayOrderDto> payOrderQuery(@Param("pageInfo")PageInfo<PayOrderDto> pageInfo,@Param("data") PayOrderQueryDto payOrderQueryDto);
     List<EnterpriseChargingAnalysisVO> enterpriseChargingAnalysis(@Param("pageInfo") PageInfo<EnterpriseChargingAnalysisVO> pageInfo,
@@ -83,11 +85,12 @@
 
     List<ChargingOrderGroup> chargingOrderGroup(@Param("chargingPercentProvinceDto")ChargingPercentProvinceDto chargingPercentProvinceDto);
 
-    List<SixChargingDto> charge(@Param("sixBefore")LocalDate sixBefore,@Param("siteIds") List<Integer> siteIds);
+    List<SixChargingDto> charge(@Param("sixBefore")LocalDate sixBefore,@Param("siteIds") List<Integer> siteIds, @Param("partnerView") Boolean partnerView);
 
-	List<SixCircleDto> circle(@Param("siteIds")List<Integer> siteIds,@Param("sixBefore")LocalDate sixBefore);
+	List<SixCircleDto> circle(@Param("siteIds")List<Integer> siteIds,@Param("sixBefore")LocalDate sixBefore, @Param("partnerView") Boolean partnerView);
 
-	Map<String, Object> countAll(@Param("siteIds")List<Integer> siteIds);
+	Map<String, Object> countAll(@Param("siteIds")List<Integer> siteIds, @Param("partnerView") Boolean partnerView);
+	List<Map<String, Object>> chargingOrderShareIncomeByPayChannel(@Param("sixBefore")LocalDate sixBefore,@Param("siteIds") List<Integer> siteIds);
 
     List<Map<String, Object>> getSumByType(@Param("start")LocalDateTime start,@Param("end")LocalDateTime end,@Param("siteIds")List<Integer> siteIds);
 
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderShareRecordMapper.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderShareRecordMapper.java
new file mode 100644
index 0000000..29085e8
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderShareRecordMapper.java
@@ -0,0 +1,7 @@
+package com.ruoyi.order.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.order.api.model.TChargingOrderShareRecord;
+
+public interface TChargingOrderShareRecordMapper extends BaseMapper<TChargingOrderShareRecord> {
+}
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/ChargingOrderSettlementExecutor.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/ChargingOrderSettlementExecutor.java
new file mode 100644
index 0000000..e062ac1
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/ChargingOrderSettlementExecutor.java
@@ -0,0 +1,22 @@
+package com.ruoyi.order.service;
+
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.order.api.vo.TransactionRecordMessageVO;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+
+/**
+ * 通过独立Spring Bean调用结算服务,确保@Transactional代理生效。
+ */
+@Service
+public class ChargingOrderSettlementExecutor {
+    @Lazy
+    @Resource
+    private TChargingOrderService chargingOrderService;
+
+    public R settle(TransactionRecordMessageVO record) {
+        return chargingOrderService.endChargeBillingCharge(record);
+    }
+}
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java
index 2eeade7..90878cb 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java
@@ -14,6 +14,8 @@
 import com.ruoyi.order.api.vo.*;
 import com.ruoyi.order.dto.*;
 import com.ruoyi.order.vo.ChargingOrderListInfoVO;
+import com.ruoyi.order.vo.ChargingSimulationVO;
+import com.ruoyi.order.vo.IcbcAdminSubOrderVO;
 import com.ruoyi.order.vo.EndOfChargePageInfo;
 import com.ruoyi.order.vo.StatisticsOfBatteryVO;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -24,6 +26,9 @@
 import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Map;
+import com.ruoyi.payment.api.vo.IcbcPaymentResp;
+import com.ruoyi.payment.api.vo.IcbcRefundResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundResp;
 
 /**
  * <p>
@@ -66,6 +71,18 @@
 	 * @return
 	 */
 	AjaxResult paymentChargingOrder(AddChargingOrder addChargingOrder);
+
+	ChargingSimulationVO getCurrentSimulationOrder(Integer gunId);
+
+	ChargingSimulationVO completeSimulation(ChargingSimulationReq request);
+
+	IcbcPaymentResp queryIcbcAdminPayment(String orderCode);
+
+	IcbcRefundResp refundIcbcAdminPayment(IcbcAdminFundOperationReq request);
+
+	List<IcbcAdminSubOrderVO> queryIcbcAdminSubOrders(String orderCode);
+
+	IcbcSubOrderRefundResp refundIcbcAdminSubOrder(IcbcAdminFundOperationReq request);
 	
 	
 	/**
@@ -157,13 +174,13 @@
 
     List<ChargingOrderGroup> chargingOrderGroup(ChargingPercentProvinceDto chargingPercentProvinceDto);
 
-	List<SixChargingDto> charge(LocalDate sixBefore, List<Integer> siteIds);
+	List<SixChargingDto> charge(LocalDate sixBefore, List<Integer> siteIds, Boolean partnerView);
 
-	List<SixCircleDto> circle(List<Integer> siteIds,LocalDate sixBefore);
+	List<SixCircleDto> circle(List<Integer> siteIds,LocalDate sixBefore, Boolean partnerView);
 
-	Map<String, Object> countAll(List<Integer> siteIds);
+	Map<String, Object> countAll(List<Integer> siteIds, Boolean partnerView);
 
-	List<SixIncomeCircleDto> payTypeCircle(LocalDate sixBefore, List<Integer> siteIds);
+	List<SixIncomeCircleDto> payTypeCircle(LocalDate sixBefore, List<Integer> siteIds, Boolean partnerView);
 
     List<Map<String, Object>> getSumByType(LocalDate start,LocalDate end,List<Integer> siteIds);
 
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/ChargingSimulationSupport.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/ChargingSimulationSupport.java
new file mode 100644
index 0000000..3b54db4
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/ChargingSimulationSupport.java
@@ -0,0 +1,127 @@
+package com.ruoyi.order.service.impl;
+
+import com.ruoyi.order.api.model.AccountingStrategyDetailOrder;
+import com.ruoyi.order.api.vo.TransactionRecordMessageVO;
+
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+final class ChargingSimulationSupport {
+    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SS");
+
+    private ChargingSimulationSupport() {
+    }
+
+    static PreparedSimulation prepare(String orderCode,
+                                      List<AccountingStrategyDetailOrder> tariffs,
+                                      BigDecimal electricityFee,
+                                      BigDecimal serviceFee,
+                                      LocalDateTime endTime) {
+        validate(orderCode, tariffs, electricityFee, serviceFee);
+        if (tariffs.size() > 12) {
+            throw new IllegalArgumentException("模拟计费最多支持12个计费时段");
+        }
+        int index = findTariffIndex(tariffs, endTime.toLocalTime());
+        AccountingStrategyDetailOrder tariff = tariffs.get(index);
+        tariff.setElectrovalence(electricityFee);
+        tariff.setServiceCharge(serviceFee);
+        tariff.setCostServiceCharge(serviceFee);
+
+        TransactionRecordMessageVO record = new TransactionRecordMessageVO();
+        record.setTransaction_serial_number(orderCode);
+        record.setStart_time(endTime.minusSeconds(1).format(DATE_TIME_FORMATTER));
+        record.setEnd_time(endTime.format(DATE_TIME_FORMATTER));
+        record.setTrade_date(endTime.format(DATE_TIME_FORMATTER));
+        record.setTotal_electricity(BigDecimal.ONE);
+        record.setLoss_total_electricity(BigDecimal.ONE);
+        record.setPayment_amount(electricityFee.add(serviceFee));
+        record.setMethod(1);
+        record.setStop_reason(1);
+        setPeriodCharge(record, tariff.getType(), index + 1);
+        return new PreparedSimulation(tariff, record);
+    }
+
+    private static void validate(String orderCode,
+                                 List<AccountingStrategyDetailOrder> tariffs,
+                                 BigDecimal electricityFee,
+                                 BigDecimal serviceFee) {
+        if (orderCode == null || orderCode.trim().isEmpty()) {
+            throw new IllegalArgumentException("模拟订单号不能为空");
+        }
+        if (tariffs == null || tariffs.isEmpty()) {
+            throw new IllegalArgumentException("订单计费策略为空");
+        }
+        if (electricityFee == null || electricityFee.compareTo(BigDecimal.ZERO) < 0) {
+            throw new IllegalArgumentException("电费不能小于0");
+        }
+        if (serviceFee == null || serviceFee.compareTo(BigDecimal.ZERO) < 0) {
+            throw new IllegalArgumentException("服务费不能小于0");
+        }
+        if (electricityFee.add(serviceFee).compareTo(BigDecimal.ZERO) <= 0) {
+            throw new IllegalArgumentException("模拟消费金额必须大于0");
+        }
+    }
+
+    private static int findTariffIndex(List<AccountingStrategyDetailOrder> tariffs, LocalTime time) {
+        for (int i = 0; i < tariffs.size(); i++) {
+            AccountingStrategyDetailOrder tariff = tariffs.get(i);
+            LocalTime start = parseTime(tariff.getStartTime(), false);
+            LocalTime end = parseTime(tariff.getEndTime(), true);
+            if (!time.isBefore(start) && !time.isAfter(end)) {
+                return i;
+            }
+        }
+        throw new IllegalArgumentException("当前时间未匹配到订单计费时段");
+    }
+
+    private static LocalTime parseTime(String value, boolean end) {
+        if ("24:00".equals(value)) {
+            return LocalTime.MAX;
+        }
+        LocalTime time = LocalTime.parse(value, DateTimeFormatter.ofPattern("HH:mm"));
+        return end ? time.withSecond(59).withNano(999999999) : time;
+    }
+
+    private static void setPeriodCharge(TransactionRecordMessageVO record, Integer type, int index) {
+        String prefix;
+        if (Integer.valueOf(1).equals(type)) {
+            prefix = "Spike";
+        } else if (Integer.valueOf(2).equals(type)) {
+            prefix = "Peak";
+        } else if (Integer.valueOf(3).equals(type)) {
+            prefix = "Flat";
+        } else if (Integer.valueOf(4).equals(type)) {
+            prefix = "Valley";
+        } else {
+            throw new IllegalArgumentException("不支持的计费时段类型:" + type);
+        }
+        try {
+            Method setter = TransactionRecordMessageVO.class.getMethod("set" + prefix + "_charge" + index, BigDecimal.class);
+            setter.invoke(record, BigDecimal.ONE);
+        } catch (Exception e) {
+            throw new IllegalStateException("构造模拟计费时段失败", e);
+        }
+    }
+
+    static final class PreparedSimulation {
+        private final AccountingStrategyDetailOrder tariff;
+        private final TransactionRecordMessageVO record;
+
+        PreparedSimulation(AccountingStrategyDetailOrder tariff, TransactionRecordMessageVO record) {
+            this.tariff = tariff;
+            this.record = record;
+        }
+
+        AccountingStrategyDetailOrder getTariff() {
+            return tariff;
+        }
+
+        TransactionRecordMessageVO getRecord() {
+            return record;
+        }
+    }
+}
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/IcbcAdminFundOperationSupport.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/IcbcAdminFundOperationSupport.java
new file mode 100644
index 0000000..c6d8f5b
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/IcbcAdminFundOperationSupport.java
@@ -0,0 +1,148 @@
+package com.ruoyi.order.service.impl;
+
+import com.ruoyi.order.api.model.TChargingOrder;
+import com.ruoyi.order.api.model.TChargingOrderShareRecord;
+import com.ruoyi.payment.api.vo.IcbcRefundReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundReq;
+
+import java.math.BigDecimal;
+
+final class IcbcAdminFundOperationSupport {
+    private IcbcAdminFundOperationSupport() {
+    }
+
+    static void validateOrder(TChargingOrder order) {
+        if (order == null) {
+            throw new IllegalArgumentException("充电订单不存在");
+        }
+        if (!"ICBC".equals(order.getSourceName())) {
+            throw new IllegalArgumentException("仅支持工商银行支付订单");
+        }
+        if (!Integer.valueOf(2).equals(order.getRechargePaymentStatus())) {
+            throw new IllegalArgumentException("订单尚未支付成功");
+        }
+    }
+
+    static BigDecimal requireAmount(BigDecimal amount) {
+        if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
+            throw new IllegalArgumentException("操作金额必须大于0");
+        }
+        if (amount.stripTrailingZeros().scale() > 2) {
+            throw new IllegalArgumentException("操作金额最多保留两位小数");
+        }
+        BigDecimal normalized = amount.setScale(2, BigDecimal.ROUND_HALF_UP);
+        if (normalized.compareTo(BigDecimal.ZERO) <= 0) {
+            throw new IllegalArgumentException("操作金额必须大于0");
+        }
+        return normalized;
+    }
+
+    static String buildPendingRefundValue(String operationToken, BigDecimal amount) {
+        if (isBlank(operationToken)) {
+            throw new IllegalArgumentException("退款流水号不能为空");
+        }
+        return operationToken + "|" + requireAmount(amount).toPlainString();
+    }
+
+    static String resolveRefundNo(String pendingValue, BigDecimal amount, String newOperationToken) {
+        BigDecimal normalized = requireAmount(amount);
+        if (isBlank(pendingValue)) {
+            return newOperationToken;
+        }
+        int separator = pendingValue.lastIndexOf('|');
+        if (separator <= 0 || separator == pendingValue.length() - 1) {
+            throw new IllegalArgumentException("退款幂等记录格式错误");
+        }
+        BigDecimal pendingAmount;
+        try {
+            pendingAmount = new BigDecimal(pendingValue.substring(separator + 1));
+        } catch (NumberFormatException e) {
+            throw new IllegalArgumentException("退款幂等记录格式错误");
+        }
+        if (pendingAmount.compareTo(normalized) != 0) {
+            throw new IllegalArgumentException("上一笔退款结果未知,请按原金额重试");
+        }
+        return pendingValue.substring(0, separator);
+    }
+
+    static boolean shouldClearPendingRefund(boolean reusedPending, String status, boolean definitiveResponse) {
+        return "SUCCESS".equals(status) || (!reusedPending && definitiveResponse);
+    }
+
+    static IcbcRefundReq buildRefundRequest(TChargingOrder order, BigDecimal amount, String outRefundNo) {
+        validateOrder(order);
+        IcbcRefundReq request = new IcbcRefundReq();
+        request.setOutTradeNo(order.getCode());
+        request.setOrderId(order.getRechargeSerialNumber());
+        request.setOutRefundNo(outRefundNo);
+        request.setRefundAmount(requireAmount(amount));
+        request.setTotalAmount(order.getThirdPayAmount());
+        request.setReason("管理后台工行退款");
+        return request;
+    }
+
+    static IcbcSubOrderQueryReq buildSubOrderQueryRequest(TChargingOrder order, TChargingOrderShareRecord shareRecord) {
+        validateOrder(order);
+        validateShareRecord(order, shareRecord);
+        IcbcSubOrderQueryReq request = new IcbcSubOrderQueryReq();
+        request.setOrderNoType("2");
+        request.setMerOrderId(order.getCode());
+        request.setPrtiId(shareRecord.getOriMerId());
+        request.setQueryType("1");
+        request.setMerTrnscSerno(shareRecord.getSubOrderId());
+        request.setSubPrtiId(shareRecord.getSubMerId());
+        return request;
+    }
+
+    static IcbcSubOrderRefundReq buildSubOrderRefundRequest(TChargingOrder order,
+                                                             TChargingOrderShareRecord shareRecord,
+                                                             BigDecimal amount,
+                                                             String retSubOrderId,
+                                                             String outtrxSerialNo) {
+        validateOrder(order);
+        validateShareRecord(order, shareRecord);
+        if (Boolean.TRUE.equals(shareRecord.getDelFlag())) {
+            throw new IllegalArgumentException("分账记录已删除");
+        }
+        if (!"SUCCESS".equals(shareRecord.getOnlineShareStatus())) {
+            throw new IllegalArgumentException("仅支持已成功的工行分账记录退款");
+        }
+        if (isBlank(shareRecord.getSeqNo()) || isBlank(shareRecord.getBusiType())
+                || isBlank(shareRecord.getOriMerId()) || shareRecord.getOriTrxDate() == null
+                || isBlank(shareRecord.getSubMerId()) || isBlank(shareRecord.getSubMerPrtclNo())) {
+            throw new IllegalArgumentException("分账记录缺少工行退款参数");
+        }
+        IcbcSubOrderRefundReq request = new IcbcSubOrderRefundReq();
+        request.setChargingOrderCode(order.getCode());
+        request.setSeqNo(shareRecord.getSeqNo());
+        request.setBusiType(shareRecord.getBusiType());
+        request.setOriMerId(shareRecord.getOriMerId());
+        request.setOriTrxDate(shareRecord.getOriTrxDate() == null ? null : shareRecord.getOriTrxDate().toString());
+        request.setSubOrderId(shareRecord.getSubOrderId());
+        request.setRetSubOrderId(retSubOrderId);
+        request.setOuttrxSerialNo(outtrxSerialNo);
+        request.setSubMerId(shareRecord.getSubMerId());
+        request.setSubMerPrtclNo(shareRecord.getSubMerPrtclNo());
+        request.setRefundAmount(requireAmount(amount));
+        request.setMerAcct(shareRecord.getMerAcct());
+        request.setMerSpInfor("管理后台工行分账退款");
+        return request;
+    }
+
+    private static void validateShareRecord(TChargingOrder order, TChargingOrderShareRecord shareRecord) {
+        if (shareRecord == null || !order.getId().equals(shareRecord.getChargingOrderId())) {
+            throw new IllegalArgumentException("分账记录不属于当前订单");
+        }
+        if (!"ICBC".equals(shareRecord.getPayChannel())) {
+            throw new IllegalArgumentException("仅支持工商银行分账记录");
+        }
+        if (shareRecord.getSubOrderId() == null || shareRecord.getSubOrderId().trim().isEmpty()) {
+            throw new IllegalArgumentException("分账记录缺少工行子订单号");
+        }
+    }
+
+    private static boolean isBlank(String value) {
+        return value == null || value.trim().isEmpty();
+    }
+}
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingBillServiceImpl.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingBillServiceImpl.java
index e5b8809..29e25b1 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingBillServiceImpl.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingBillServiceImpl.java
@@ -10,9 +10,11 @@
 import com.ruoyi.chargingPile.api.feignClient.ChargingPileClient;
 import com.ruoyi.chargingPile.api.feignClient.PartnerClient;
 import com.ruoyi.chargingPile.api.feignClient.SiteClient;
+import com.ruoyi.chargingPile.api.model.Partner;
 import com.ruoyi.chargingPile.api.model.Site;
 import com.ruoyi.chargingPile.api.model.TChargingGun;
 import com.ruoyi.chargingPile.api.model.TChargingPile;
+import com.ruoyi.common.core.domain.R;
 import com.ruoyi.common.core.utils.DateUtils;
 import com.ruoyi.common.core.web.page.PageInfo;
 import com.ruoyi.common.security.service.TokenService;
@@ -31,10 +33,13 @@
 
 import javax.annotation.Resource;
 import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.TemporalAdjusters;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -151,9 +156,12 @@
         BigDecimal serviceChargeTotal = new BigDecimal("0");
         BigDecimal commissionAmountTotal = new BigDecimal("0");
         BigDecimal sharingAmountTotal = new BigDecimal("0");
+        BigDecimal platformIncomeTotal = new BigDecimal("0");
+        BigDecimal partnerIncomeTotal = new BigDecimal("0");
         BigDecimal chargingCapacityTotal = new BigDecimal("0");
         BigDecimal discountTotal = new BigDecimal("0");
         List<TChargingOrderAccountingStrategy> list3 = chargingOrderAccountingStrategyService.list();
+        Map<Integer, BigDecimal> siteServiceShareRatioMap = loadSiteServiceShareRatioMap();
 
 
         for (ChargingBillListVO chargingBillListVO : list) {
@@ -209,6 +217,8 @@
                 tChargingOrders = total.stream().filter(e ->  e.getPayTime().isBefore(lastDayOfMonth.withHour(23).withMinute(59).withSecond(59))
                         && e.getPayTime().isAfter(firstDayOfMonth.withHour(0).withMinute(0).withSecond(0))).collect(Collectors.toList());
             }
+            tChargingOrders = filterAccountSettlementOrders(tChargingOrders, chargingBillListVO.getPayType());
+            chargingBillListVO.setPayTypeName(resolveAccountPayTypeName(chargingBillListVO.getPayType(), tChargingOrders));
 
 
 
@@ -235,16 +245,6 @@
                 if (data1!=null && data2!=null){
                     tChargingOrder.setTerminalName(data1.getName()+data2.getName());
                     tChargingOrder.setTerminalCode(data2.getCode());
-                }
-                if (tChargingOrder.getRechargePaymentType()!=null && tChargingOrder.getRechargePaymentType()==1){
-                    chargingBillListVO.setPayTypeName("微信商户");
-                }
-                if (tChargingOrder.getRechargePaymentType()!=null && tChargingOrder.getRechargePaymentType()==2){
-                    if (StringUtils.hasLength(chargingBillListVO.getPayTypeName())){
-                        chargingBillListVO.setPayTypeName("微信商户+支付宝商户");
-                    }else{
-                        chargingBillListVO.setPayTypeName("支付宝商户");
-                    }
                 }
                 tChargingOrder.setOrderClassification1("线上订单");
                 tChargingOrder.setRechargePaymentType1("预付");
@@ -393,6 +393,10 @@
             chargingBillListVO.setChargingCapacity(chargingCapacity == null?BigDecimal.ZERO:chargingCapacity);
             chargingBillListVO.setBillMoney(paymentAmount
                             .subtract(commissionAmount.setScale(2,BigDecimal.ROUND_DOWN)).subtract(sharingAmount).setScale(2, BigDecimal.ROUND_DOWN));
+            Map<String, BigDecimal> incomeMap = calculateChargingBillIncome(chargingBillListVO.getType(), chargingBillListVO.getSiteId(),
+                    chargingBillListVO.getBillWeek(), tChargingOrders, list3, siteServiceShareRatioMap);
+            chargingBillListVO.setPlatformIncome(incomeMap.get("platformIncome"));
+            chargingBillListVO.setPartnerIncome(incomeMap.get("partnerIncome"));
             switch (chargingBillListVO.getType()){
                 case 1:
                     chargingBillListVO.setSiteName("全站");
@@ -447,6 +451,7 @@
                 tChargingOrders = total.stream().filter(e ->  e.getPayTime().isBefore(lastDayOfMonth.withHour(23).withMinute(59).withSecond(59))
                         && e.getPayTime().isAfter(firstDayOfMonth.withHour(0).withMinute(0).withSecond(0))).collect(Collectors.toList());
             }
+            tChargingOrders = filterAccountSettlementOrders(tChargingOrders, chargingBillListVO.getPayType());
             for (TChargingOrder tChargingOrder : tChargingOrders) {
                 List<TChargingOrderAccountingStrategy> list4 = list3.stream().filter(e -> e.getChargingOrderId().equals(tChargingOrder.getId())).collect(Collectors.toList());
                 BigDecimal electronic_reduce = list4.stream().map(TChargingOrderAccountingStrategy::getPeriodElectricPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
@@ -498,6 +503,10 @@
                     discountTotal = discountTotal.add(tChargingOrder.getVipDiscountAmount());
                 }
             }
+            Map<String, BigDecimal> incomeMap = calculateChargingBillIncome(chargingBillListVO.getType(), chargingBillListVO.getSiteId(),
+                    chargingBillListVO.getBillWeek(), tChargingOrders, list3, siteServiceShareRatioMap);
+            platformIncomeTotal = platformIncomeTotal.add(incomeMap.get("platformIncome"));
+            partnerIncomeTotal = partnerIncomeTotal.add(incomeMap.get("partnerIncome"));
             tChargingOrders1.addAll(tChargingOrders);
             chargingBillVO.setExportList(tChargingOrders);
         }
@@ -509,6 +518,8 @@
         chargingBillVO.setServiceCharge(serviceChargeTotal);
         chargingBillVO.setCommissionAmount(commissionAmountTotal);
         chargingBillVO.setSharingAmount(sharingAmountTotal);
+        chargingBillVO.setPlatformIncome(platformIncomeTotal.setScale(2, RoundingMode.HALF_UP));
+        chargingBillVO.setPartnerIncome(partnerIncomeTotal.setScale(2, RoundingMode.HALF_UP));
         chargingBillVO.setDiscount(discountTotal);
         chargingBillVO.setRefundAmount(refundAmountTotal);
         List<TChargingOrder> collect = tChargingOrders1.stream().distinct().collect(Collectors.toList());
@@ -547,6 +558,7 @@
         List<TAppUser> userList = appUserClient.getAllUser().getData();
         List<TAppUserCar> carList = appUserCarClient.getAllCar().getData();
         List<TChargingOrderAccountingStrategy> list3 = chargingOrderAccountingStrategyService.list();
+        Map<Integer, BigDecimal> siteServiceShareRatioMap = loadSiteServiceShareRatioMap();
 
         Long userId = tokenService.getLoginUser().getUserid();
         // 查询当前登陆人按钮权限
@@ -598,6 +610,8 @@
         BigDecimal serviceChargeTotal = new BigDecimal("0");
         BigDecimal commissionAmountTotal = new BigDecimal("0");
         BigDecimal sharingAmountTotal = new BigDecimal("0");
+        BigDecimal platformIncomeTotal = new BigDecimal("0");
+        BigDecimal partnerIncomeTotal = new BigDecimal("0");
         BigDecimal chargingCapacityTotal = new BigDecimal("0");
         BigDecimal discountTotal = new BigDecimal("0");
         BigDecimal refundAmountTotal = new BigDecimal("0");
@@ -858,6 +872,10 @@
             chargingBillListVO.setOrderCount(tChargingOrders.size());
             // 充电时间秒
             chargingBillListVO.setChargingSecond(chargingSecond);
+            Map<String, BigDecimal> incomeMap = calculateChargingBillIncome(chargingBillListVO.getType(), chargingBillListVO.getSiteId(),
+                    chargingBillListVO.getBillWeek(), tChargingOrders, list3, siteServiceShareRatioMap);
+            chargingBillListVO.setPlatformIncome(incomeMap.get("platformIncome"));
+            chargingBillListVO.setPartnerIncome(incomeMap.get("partnerIncome"));
         }
         for (ChargingBillListVO chargingBillListVO : list1) {
             if (dto.getType() == 1){
@@ -965,6 +983,10 @@
                     discountTotal = discountTotal.add(tChargingOrder.getVipDiscountAmount());
                 }
             }
+            Map<String, BigDecimal> incomeMap = calculateChargingBillIncome(chargingBillListVO.getType(), chargingBillListVO.getSiteId(),
+                    chargingBillListVO.getBillWeek(), tChargingOrders, list3, siteServiceShareRatioMap);
+            platformIncomeTotal = platformIncomeTotal.add(incomeMap.get("platformIncome"));
+            partnerIncomeTotal = partnerIncomeTotal.add(incomeMap.get("partnerIncome"));
         }
         chargingBillVO.setChargingCapacity(chargingCapacityTotal);
         chargingBillVO.setPaymentAmount(paymentAmountTotal.subtract(commissionAmountTotal).subtract(sharingAmountTotal).setScale(2, BigDecimal.ROUND_DOWN));
@@ -974,6 +996,8 @@
         chargingBillVO.setOrderCount(orderCount);
         chargingBillVO.setCommissionAmount(commissionAmountTotal.setScale(2, BigDecimal.ROUND_DOWN));
         chargingBillVO.setSharingAmount(sharingAmountTotal.setScale(2, BigDecimal.ROUND_DOWN));
+        chargingBillVO.setPlatformIncome(platformIncomeTotal.setScale(2, RoundingMode.HALF_UP));
+        chargingBillVO.setPartnerIncome(partnerIncomeTotal.setScale(2, RoundingMode.HALF_UP));
         chargingBillVO.setDiscount(discountTotal.setScale(2, BigDecimal.ROUND_DOWN));
         pageInfo.setRecords(chargingBillListVOS);
         chargingBillVO.setList(pageInfo);
@@ -1033,6 +1057,10 @@
         List<Map<String, Object>> list = chargingSettlementBillService.queryList(pageInfo, dto.getType(), dto.getState(), dto.getDateRange(), statisticalDateType, dto.getSiteIds());
         ChargingBillWapper chargingBillVO = new ChargingBillWapper();
         List<Map<String, Object>> pageList = new ArrayList<>();
+        List<TChargingOrder> total = chargingOrderList.selectList(new LambdaQueryWrapper<>(TChargingOrder.class)
+                .isNotNull(TChargingOrder::getPayTime));
+        List<TChargingOrderAccountingStrategy> strategyList = chargingOrderAccountingStrategyService.list();
+        Map<Integer, BigDecimal> siteServiceShareRatioMap = loadSiteServiceShareRatioMap();
         if(null != list){
             for (Map<String, Object> map : list) {
                 Integer site_id = Integer.valueOf(map.get("site_id").toString());
@@ -1049,6 +1077,7 @@
                 String chargingCapacity = map.get("total_electricity").toString();
                 String electrovalence = map.get("total_electricity_charge").toString();
                 String serviceCharge = map.get("total_service_fee").toString();
+                Map<String, BigDecimal> incomeMap = calculateChargingBillIncome(dto.getType(), site_id, billingCycle, total, strategyList, siteServiceShareRatioMap);
                 Map<String, Object> data1 = new HashMap<>();
                 data1.put("code", code);
                 data1.put("orderState", 2);
@@ -1062,6 +1091,8 @@
                 data1.put("commissionAmount", commissionAmount);
                 data1.put("sharingAmount", sharingAmount);
                 data1.put("paymentAmount", paymentAmount);
+                data1.put("platformIncome", incomeMap.get("platformIncome"));
+                data1.put("partnerIncome", incomeMap.get("partnerIncome"));
                 data1.put("chargingCapacity", chargingCapacity);
                 data1.put("electrovalence", electrovalence);
                 data1.put("serviceCharge", serviceCharge);
@@ -1085,6 +1116,19 @@
         BigDecimal commissionAmount = (null == map || null == map.get("platformFee")) ? BigDecimal.ZERO : new BigDecimal(map.get("platformFee").toString());
         BigDecimal sharingAmount = (null == map || null == map.get("tripartiteCommission")) ? BigDecimal.ZERO : new BigDecimal(map.get("tripartiteCommission").toString());
         BigDecimal discount = (null == map || null == map.get("couponDeduction")) ? BigDecimal.ZERO : new BigDecimal(map.get("couponDeduction").toString());
+        BigDecimal platformIncome = BigDecimal.ZERO;
+        BigDecimal partnerIncome = BigDecimal.ZERO;
+        PageInfo<Map<String, Object>> sumPageInfo = new PageInfo<>(1, 100000);
+        List<Map<String, Object>> sumList = chargingSettlementBillService.queryList(sumPageInfo, dto.getType(), dto.getState(), dto.getDateRange(), statisticalDateType, dto.getSiteIds());
+        if (sumList != null) {
+            for (Map<String, Object> row : sumList) {
+                Integer siteId = Integer.valueOf(row.get("site_id").toString());
+                String billingCycle = row.get("billingCycle").toString();
+                Map<String, BigDecimal> incomeMap = calculateChargingBillIncome(dto.getType(), siteId, billingCycle, total, strategyList, siteServiceShareRatioMap);
+                platformIncome = platformIncome.add(incomeMap.get("platformIncome"));
+                partnerIncome = partnerIncome.add(incomeMap.get("partnerIncome"));
+            }
+        }
         chargingBillVO.setChargingCapacity(chargingCapacity);
         chargingBillVO.setPaymentAmount(paymentAmount.setScale(2, BigDecimal.ROUND_DOWN));
         chargingBillVO.setOrderAmount(orderAmount.setScale(2, BigDecimal.ROUND_DOWN));
@@ -1093,6 +1137,8 @@
         chargingBillVO.setOrderCount(orderCount);
         chargingBillVO.setCommissionAmount(commissionAmount.setScale(2, BigDecimal.ROUND_DOWN));
         chargingBillVO.setSharingAmount(sharingAmount.setScale(2, BigDecimal.ROUND_DOWN));
+        chargingBillVO.setPlatformIncome(platformIncome.setScale(2, RoundingMode.HALF_UP));
+        chargingBillVO.setPartnerIncome(partnerIncome.setScale(2, RoundingMode.HALF_UP));
         chargingBillVO.setDiscount(discount.setScale(2, BigDecimal.ROUND_DOWN));
         pageInfo.setRecords(pageList);
         chargingBillVO.setList(pageInfo);
@@ -1125,6 +1171,7 @@
         List<TAppUser> userList = appUserClient.getAllUser().getData();
         List<TAppUserCar> carList = appUserCarClient.getAllCar().getData();
         List<TChargingOrderAccountingStrategy> list3 = chargingOrderAccountingStrategyService.list();
+        Map<Integer, BigDecimal> siteServiceShareRatioMap = loadSiteServiceShareRatioMap();
 
         Long userId = tokenService.getLoginUser().getUserid();
         // 查询当前登陆人按钮权限
@@ -1176,6 +1223,8 @@
         BigDecimal serviceChargeTotal = new BigDecimal("0");
         BigDecimal commissionAmountTotal = new BigDecimal("0");
         BigDecimal sharingAmountTotal = new BigDecimal("0");
+        BigDecimal platformIncomeTotal = new BigDecimal("0");
+        BigDecimal partnerIncomeTotal = new BigDecimal("0");
         BigDecimal chargingCapacityTotal = new BigDecimal("0");
         BigDecimal discountTotal = new BigDecimal("0");
         BigDecimal refundAmountTotal = new BigDecimal("0");
@@ -1436,6 +1485,10 @@
             chargingBillListVO.setOrderCount(tChargingOrders.size());
             // 充电时间秒
             chargingBillListVO.setChargingSecond(chargingSecond);
+            Map<String, BigDecimal> incomeMap = calculateChargingBillIncome(chargingBillListVO.getType(), chargingBillListVO.getSiteId(),
+                    chargingBillListVO.getBillWeek(), tChargingOrders, list3, siteServiceShareRatioMap);
+            chargingBillListVO.setPlatformIncome(incomeMap.get("platformIncome"));
+            chargingBillListVO.setPartnerIncome(incomeMap.get("partnerIncome"));
         }
         for (ChargingBillListVO chargingBillListVO : list1) {
             if (dto.getType() == 1){
@@ -1543,6 +1596,10 @@
                     discountTotal = discountTotal.add(tChargingOrder.getVipDiscountAmount());
                 }
             }
+            Map<String, BigDecimal> incomeMap = calculateChargingBillIncome(chargingBillListVO.getType(), chargingBillListVO.getSiteId(),
+                    chargingBillListVO.getBillWeek(), tChargingOrders, list3, siteServiceShareRatioMap);
+            platformIncomeTotal = platformIncomeTotal.add(incomeMap.get("platformIncome"));
+            partnerIncomeTotal = partnerIncomeTotal.add(incomeMap.get("partnerIncome"));
         }
         chargingBillVO.setChargingCapacity(chargingCapacityTotal);
         chargingBillVO.setPaymentAmount(paymentAmountTotal.subtract(commissionAmountTotal).subtract(sharingAmountTotal).setScale(2, BigDecimal.ROUND_DOWN));
@@ -1552,12 +1609,160 @@
         chargingBillVO.setOrderCount(orderCount);
         chargingBillVO.setCommissionAmount(commissionAmountTotal.setScale(2, BigDecimal.ROUND_DOWN));
         chargingBillVO.setSharingAmount(sharingAmountTotal.setScale(2, BigDecimal.ROUND_DOWN));
+        chargingBillVO.setPlatformIncome(platformIncomeTotal.setScale(2, RoundingMode.HALF_UP));
+        chargingBillVO.setPartnerIncome(partnerIncomeTotal.setScale(2, RoundingMode.HALF_UP));
         chargingBillVO.setDiscount(discountTotal.setScale(2, BigDecimal.ROUND_DOWN));
         pageInfo.setRecords(chargingBillListVOS);
         chargingBillVO.setList(pageInfo);
         return chargingBillVO;
     }
 
+    private Map<String, BigDecimal> calculateChargingBillIncome(Integer billType, Integer billSiteId, String billingCycle,
+                                                                List<TChargingOrder> allOrders,
+                                                                List<TChargingOrderAccountingStrategy> allStrategies,
+                                                                Map<Integer, BigDecimal> siteServiceShareRatioMap) {
+        Map<String, BigDecimal> incomeMap = new HashMap<>();
+        BigDecimal platformIncome = BigDecimal.ZERO;
+        BigDecimal partnerIncome = BigDecimal.ZERO;
+        if (!StringUtils.hasLength(billingCycle) || allOrders == null || allOrders.isEmpty()) {
+            incomeMap.put("platformIncome", platformIncome);
+            incomeMap.put("partnerIncome", partnerIncome);
+            return incomeMap;
+        }
 
+        LocalDate billMonth = LocalDate.parse(billingCycle + "-01");
+        LocalDateTime firstDayOfMonth = billMonth.withDayOfMonth(1).atStartOfDay();
+        LocalDateTime lastDayOfMonth = billMonth.with(TemporalAdjusters.lastDayOfMonth()).atTime(23, 59, 59);
+        List<TChargingOrderAccountingStrategy> strategies = allStrategies == null ? Collections.emptyList() : allStrategies;
+        for (TChargingOrder order : allOrders) {
+            if (order == null || order.getPayTime() == null) {
+                continue;
+            }
+            if (billType != null && billType == 2 && !billSiteId.equals(order.getSiteId())) {
+                continue;
+            }
+            if (order.getPayTime().isBefore(firstDayOfMonth) || order.getPayTime().isAfter(lastDayOfMonth)) {
+                continue;
+            }
+            List<TChargingOrderAccountingStrategy> orderStrategies = strategies.stream()
+                    .filter(strategy -> strategy.getChargingOrderId().equals(order.getId()))
+                    .collect(Collectors.toList());
+            BigDecimal electronicReduce = orderStrategies.stream()
+                    .map(TChargingOrderAccountingStrategy::getPeriodElectricPrice)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            BigDecimal serviceReduce = orderStrategies.stream()
+                    .map(TChargingOrderAccountingStrategy::getPeriodServicePrice)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+            BigDecimal paymentAmount;
+            if (Integer.valueOf(2).equals(order.getOrderSource())) {
+                paymentAmount = electronicReduce.add(serviceReduce.multiply(new BigDecimal("0.8")).setScale(2, RoundingMode.DOWN));
+            } else {
+                paymentAmount = electronicReduce.add(serviceReduce);
+                if (Integer.valueOf(1).equals(order.getOrderSource())) {
+                    BigDecimal serviceShareRatio = siteServiceShareRatioMap.get(order.getSiteId());
+                    if (serviceShareRatio != null && serviceShareRatio.compareTo(BigDecimal.ZERO) > 0) {
+                        partnerIncome = partnerIncome.add(serviceReduce.multiply(serviceShareRatio).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP));
+                    }
+                }
+            }
+            platformIncome = platformIncome.add(paymentAmount);
+        }
+        platformIncome = platformIncome.subtract(partnerIncome).max(BigDecimal.ZERO);
+        incomeMap.put("platformIncome", platformIncome.setScale(2, RoundingMode.HALF_UP));
+        incomeMap.put("partnerIncome", partnerIncome.setScale(2, RoundingMode.HALF_UP));
+        return incomeMap;
+    }
 
+    private List<TChargingOrder> filterAccountSettlementOrders(List<TChargingOrder> orders, Integer payType) {
+        if (orders == null || orders.isEmpty() || payType == null || payType == 0) {
+            return orders == null ? Collections.emptyList() : orders;
+        }
+        return orders.stream()
+                .filter(order -> accountSettlementOrderMatches(order, payType))
+                .collect(Collectors.toList());
+    }
+
+    private boolean accountSettlementOrderMatches(TChargingOrder order, Integer payType) {
+        if (order == null || payType == null) {
+            return true;
+        }
+        if (payType == 1) {
+            return !isIcbcOrder(order) && Integer.valueOf(1).equals(order.getRechargePaymentType());
+        }
+        if (payType == 2) {
+            return !isIcbcOrder(order) && (Integer.valueOf(2).equals(order.getRechargePaymentType()) || Integer.valueOf(3).equals(order.getRechargePaymentType()));
+        }
+        if (payType == 3) {
+            return isIcbcOrder(order) && Integer.valueOf(1).equals(order.getOrderSource());
+        }
+        return true;
+    }
+
+    private String resolveAccountPayTypeName(Integer payType, List<TChargingOrder> orders) {
+        if (payType != null && payType != 0) {
+            switch (payType) {
+                case 1:
+                    return "微信商户";
+                case 2:
+                    return "支付宝商户";
+                case 3:
+                    return "工商银行商户";
+                default:
+                    break;
+            }
+        }
+        boolean hasWx = false;
+        boolean hasAli = false;
+        boolean hasIcbc = false;
+        if (orders != null) {
+            for (TChargingOrder order : orders) {
+                if (isIcbcOrder(order)) {
+                    hasIcbc = true;
+                } else if (Integer.valueOf(1).equals(order.getRechargePaymentType())) {
+                    hasWx = true;
+                } else if (Integer.valueOf(2).equals(order.getRechargePaymentType()) || Integer.valueOf(3).equals(order.getRechargePaymentType())) {
+                    hasAli = true;
+                }
+            }
+        }
+        List<String> names = new ArrayList<>();
+        if (hasWx) {
+            names.add("微信商户");
+        }
+        if (hasAli) {
+            names.add("支付宝商户");
+        }
+        if (hasIcbc) {
+            names.add("工商银行商户");
+        }
+        return names.isEmpty() ? "" : String.join("+", names);
+    }
+
+    private boolean isIcbcOrder(TChargingOrder order) {
+        return order != null && "ICBC".equals(order.getSourceName());
+    }
+
+    private Map<Integer, BigDecimal> loadSiteServiceShareRatioMap() {
+        Map<Integer, BigDecimal> ratioMap = new HashMap<>();
+        R<List<Site>> siteResult = siteClient.getSiteAll();
+        R<List<Partner>> partnerResult = siteClient.getAllPartner();
+        List<Site> sites = siteResult == null ? Collections.emptyList() : siteResult.getData();
+        List<Partner> partners = partnerResult == null ? Collections.emptyList() : partnerResult.getData();
+        if (sites == null || sites.isEmpty() || partners == null || partners.isEmpty()) {
+            return ratioMap;
+        }
+        Map<Integer, Partner> partnerMap = partners.stream()
+                .filter(partner -> partner != null && partner.getId() != null)
+                .collect(Collectors.toMap(Partner::getId, partner -> partner, (left, right) -> left));
+        for (Site site : sites) {
+            if (site == null || site.getId() == null || site.getPartnerId() == null) {
+                continue;
+            }
+            Partner partner = partnerMap.get(site.getPartnerId());
+            if (partner != null && partner.getServiceShareRatio() != null) {
+                ratioMap.put(site.getId(), partner.getServiceShareRatio());
+            }
+        }
+        return ratioMap;
+    }
 }
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
index 593aff1..4d6455b 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -5,6 +5,7 @@
 import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.account.api.dto.SendMessageDTO;
@@ -51,6 +52,7 @@
 import com.ruoyi.order.api.vo.PlatformStopChargingReplyVO;
 import com.ruoyi.order.dto.*;
 import com.ruoyi.order.mapper.TChargingOrderMapper;
+import com.ruoyi.order.mapper.TChargingOrderShareRecordMapper;
 import com.ruoyi.order.mapper.TSettlementConfirmMapper;
 import com.ruoyi.order.service.TChargingOrderAccountingStrategyService;
 import com.ruoyi.order.service.TChargingOrderRefundService;
@@ -61,6 +63,8 @@
 import com.ruoyi.order.util.mongodb.service.TransactionRecordService;
 import com.ruoyi.order.util.mongodb.service.UploadRealTimeMonitoringDataService;
 import com.ruoyi.order.vo.EndOfChargePageInfo;
+import com.ruoyi.order.vo.ChargingSimulationVO;
+import com.ruoyi.order.vo.IcbcAdminSubOrderVO;
 import com.ruoyi.order.vo.OrderCountByDate;
 import com.ruoyi.order.vo.StatisticsOfBatteryVO;
 import com.ruoyi.other.api.domain.*;
@@ -71,11 +75,13 @@
 import com.ruoyi.other.api.vo.ExchangeCodeConsumeResultVO;
 import com.ruoyi.payment.api.feignClient.AliPaymentClient;
 import com.ruoyi.payment.api.feignClient.H5AliPaymentClient;
+import com.ruoyi.payment.api.feignClient.IcbcPaymentClient;
 import com.ruoyi.payment.api.feignClient.WxPaymentClient;
 import com.ruoyi.payment.api.model.*;
 import com.ruoyi.payment.api.vo.*;
 import com.ruoyi.system.api.domain.SysUser;
 import com.ruoyi.system.api.feignClient.SysUserClient;
+import org.redisson.api.RBucket;
 import org.redisson.api.RLock;
 import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
@@ -150,6 +156,12 @@
 	private AliPaymentClient aliPaymentClient;
 	@Resource
 	private H5AliPaymentClient h5AliPaymentClient;
+	@Resource
+	private IcbcPaymentClient icbcPaymentClient;
+	@Resource
+	private TChargingOrderShareRecordMapper chargingOrderShareRecordMapper;
+	@Resource
+	private ChargingOrderSettlementExecutor chargingOrderSettlementExecutor;
 	
 	@Resource
 	private AppCouponClient appCouponClient;
@@ -593,7 +605,38 @@
             changeUserBalance(chargingOrder.getAppUserId(), balanceRefundAmount);
             recordBalanceRefund(chargingOrder, balanceRefundAmount, refund.getRefundCode());
         }
+        triggerChargingOrderShare(chargingOrder);
     }
+
+	private boolean refundByIcbcAndFinish(TChargingOrder chargingOrder, TChargingOrderRefund refund, BigDecimal thirdRefundAmount,
+	                                      String reason, boolean finishManual, BigDecimal refundAmount, BigDecimal balanceRefundAmount) {
+		IcbcRefundReq req = new IcbcRefundReq();
+		req.setOutTradeNo(chargingOrder.getCode());
+		req.setOrderId(chargingOrder.getRechargeSerialNumber());
+		req.setOutRefundNo(refund.getRefundCode());
+		req.setRefundAmount(thirdRefundAmount);
+		req.setTotalAmount(getRefundableThirdPayAmount(chargingOrder));
+		req.setReason(reason);
+		R<IcbcRefundResp> result = icbcPaymentClient.refund(req);
+		IcbcRefundResp resp = result == null ? null : result.getData();
+		if (result == null || result.getCode() != 200 || resp == null || !"SUCCESS".equals(resp.getStatus())) {
+			log.error("工行退款失败,订单号:{},退款单号:{},返回:{}", chargingOrder.getCode(), refund.getRefundCode(), result == null ? null : result.getMsg());
+			return false;
+		}
+		if (finishManual) {
+			refund.setRefundSerialNumber(firstNotBlank(resp.getRefundId(), resp.getOutRefundNo()));
+			refund.setRefundTime(LocalDateTime.now());
+			finishManualChargingRefund(chargingOrder, refund, refundAmount, balanceRefundAmount, thirdRefundAmount);
+		} else {
+			chargingOrderRefundService.save(refund);
+			chargingOrderStartupFailureWxRefund(refund.getRefundCode(), firstNotBlank(resp.getRefundId(), resp.getOutRefundNo()), "SUCCESS", resp.getRefundTime());
+		}
+		return true;
+	}
+
+	private boolean isIcbcOrder(TChargingOrder chargingOrder) {
+		return chargingOrder != null && "ICBC".equals(chargingOrder.getSourceName());
+	}
 
     /**
      * 判断订单是否为白名单订单
@@ -696,7 +739,13 @@
 				.eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0));
 		for (TChargingOrder tChargingOrder : list) {
 			Integer rechargePaymentType = tChargingOrder.getRechargePaymentType();
-			if (1 == rechargePaymentType) {
+			if ("ICBC".equals(tChargingOrder.getSourceName())) {
+				IcbcPaymentResp data = icbcPaymentClient.query(tChargingOrder.getCode()).getData();
+				if (null != data && isIcbcRemovableStatus(data.getStatus(), tChargingOrder)) {
+					this.removeById(tChargingOrder.getId());
+				}
+			}
+			if (!"ICBC".equals(tChargingOrder.getSourceName()) && 1 == rechargePaymentType) {
 				NotifyV3PayDecodeRespBody data = wxPaymentClient.queryOrderInfo(tChargingOrder.getCode()).getData();
 				if (null != data) {
 					String trade_state = data.getTrade_state();
@@ -706,7 +755,7 @@
 					}
 				}
 			}
-			if (2 == rechargePaymentType) {
+			if (!"ICBC".equals(tChargingOrder.getSourceName()) && 2 == rechargePaymentType) {
 				AliQueryOrder data = aliPaymentClient.query(tChargingOrder.getCode()).getData();
 				if (null != data) {
 					//支付失败,删除无效的订单
@@ -716,7 +765,7 @@
 					}
 				}
 			}
-			if (3 == rechargePaymentType) {
+			if (!"ICBC".equals(tChargingOrder.getSourceName()) && 3 == rechargePaymentType) {
 				AliQueryOrder data = h5AliPaymentClient.query(tChargingOrder.getCode()).getData();
 				if (null != data) {
 					//支付失败,删除无效的订单
@@ -739,7 +788,15 @@
 				.eq(TChargingOrder::getAppUserCarId, userId).eq(TChargingOrder::getRechargePaymentStatus, 1).eq(TChargingOrder::getDelFlag, 0));
 		if (null != one) {
 			//查询三方支付数据,支付中直接结束三方订单
-			if (1 == one.getRechargePaymentType()) {
+			if ("ICBC".equals(one.getSourceName())) {
+				IcbcPaymentResp data = icbcPaymentClient.query(one.getCode()).getData();
+				if (null != data && isIcbcRemovableStatus(data.getStatus(), one)) {
+					this.removeById(one.getId());
+				} else {
+					return AjaxResult.error("存在支付中订单,请稍后再试");
+				}
+			}
+			if (!"ICBC".equals(one.getSourceName()) && 1 == one.getRechargePaymentType()) {
 				NotifyV3PayDecodeRespBody data = wxPaymentClient.queryOrderInfo(one.getCode()).getData();
 				if (null != data) {
 					String trade_state = data.getTrade_state();
@@ -753,7 +810,7 @@
 					}
 				}
 			}
-			if (2 == one.getRechargePaymentType()) {
+			if (!"ICBC".equals(one.getSourceName()) && 2 == one.getRechargePaymentType()) {
 				AliQueryOrder data = aliPaymentClient.query(one.getCode()).getData();
 				if (null != data) {
 					String trade_state = data.getTradeStatus();
@@ -768,7 +825,7 @@
 					}
 				}
 			}
-			if (3 == one.getRechargePaymentType()) {
+			if (!"ICBC".equals(one.getSourceName()) && 3 == one.getRechargePaymentType()) {
 				AliQueryOrder data = h5AliPaymentClient.query(one.getCode()).getData();
 				if (null != data) {
 					String trade_state = data.getTradeStatus();
@@ -820,6 +877,9 @@
 		chargingOrder.setWhitelistServiceRefundAmount(BigDecimal.ZERO);
 		chargingOrder.setVipDiscountAmount(BigDecimal.ZERO);
 		chargingOrder.setOrderSource(1);
+		if (thirdPayAmount.compareTo(BigDecimal.ZERO) > 0 && Integer.valueOf(1).equals(addChargingOrder.getPaymentType())) {
+			chargingOrder.setSourceName("ICBC");
+		}
 		chargingOrder.setTitle("【充电桩充电】" + chargingPile.getNumber() + "号桩/" + tChargingGun.getCode() + "号枪");
 		
 		Site site = siteClient.getSiteByIds(Arrays.asList(tChargingGun.getSiteId())).getData().get(0);
@@ -884,17 +944,28 @@
 		}
 		//会员优惠折扣将其计入增加充电时长(增加总充电金额)
 		//如果充电总金额未使用完,则需要退回费用=(原金额/总金额)*(总金额-实际充电金额)
-		if (1 == addChargingOrder.getPaymentType()) {
-			PaymentOrder paymentOrder = new PaymentOrder();
-			paymentOrder.setCode(chargingOrder.getCode());
-			paymentOrder.setAmount(thirdPayAmount);
-			paymentOrder.setOpenId(appUser.getWxOpenid());
-			paymentOrder.setDescription("充电充值");
-			paymentOrder.setNotifyUrl("/payment/wx/pay/notify");
-			Map<String, Object> data = wxPaymentClient.orderPay(paymentOrder).getData();
-			return AjaxResult.success(data);
+		if (Integer.valueOf(1).equals(addChargingOrder.getPaymentType())) {
+			IcbcPaymentReq req = new IcbcPaymentReq();
+			req.setOutTradeNo(chargingOrder.getCode());
+			req.setAmount(thirdPayAmount);
+			req.setSubject("充电充值");
+			req.setBody("充电充值");
+			req.setPaymentType(1);
+			req.setNotifyUrl("/payment/icbc/callBack");
+			req.setReturnUrl(addChargingOrder.getReturnUrl());
+			req.setOpenId(appUser.getWxOpenid());
+			req.setProfitSharing(buildIcbcProfitSharing(site));
+			IcbcPaymentResp data = icbcPaymentClient.payment(req).getData();
+			if (isIcbcPaymentCreated(data)) {
+				if (StringUtils.hasLength(data.getTradeNo())) {
+					chargingOrder.setRechargeSerialNumber(data.getTradeNo());
+					this.updateById(chargingOrder);
+				}
+				return AjaxResult.success(data);
+			}
+			throw new RuntimeException("调起工行支付失败");
 		}
-		if (2 == addChargingOrder.getPaymentType()) {
+		if (Integer.valueOf(2).equals(addChargingOrder.getPaymentType())) {
 			AliPaymentReq req = new AliPaymentReq();
 			req.setOutTradeNo(chargingOrder.getCode());
 			req.setTotalAmount(thirdPayAmount.toString());
@@ -903,11 +974,12 @@
 			req.setBody("充电充值");
 			req.setNotifyUrl("/payment/ali/callBack");
 			AliPaymentResp data = aliPaymentClient.payment(req).getData();
-			if (null != data) {
+			if (data != null) {
 				return AjaxResult.success(data);
 			}
+			throw new RuntimeException("调起支付宝支付失败");
 		}
-		if (3 == addChargingOrder.getPaymentType()) {
+		if (Integer.valueOf(3).equals(addChargingOrder.getPaymentType())) {
 			AliPaymentReq req = new AliPaymentReq();
 			req.setOutTradeNo(chargingOrder.getCode());
 			req.setTotalAmount(thirdPayAmount.toString());
@@ -922,6 +994,321 @@
 			}
 		}
 		throw new RuntimeException("无效的支付方式");
+	}
+
+	@Override
+	public ChargingSimulationVO getCurrentSimulationOrder(Integer gunId) {
+		if (gunId == null) {
+			throw new ServiceException("充电枪不能为空");
+		}
+		Long userId = tokenService.getLoginUserApplet().getUserId();
+		return buildSimulationVO(findSimulationOrder(userId, gunId));
+	}
+
+	@Override
+	public ChargingSimulationVO completeSimulation(ChargingSimulationReq request) {
+		if (request == null || request.getGunId() == null) {
+			throw new ServiceException("充电枪不能为空");
+		}
+		Long userId = tokenService.getLoginUserApplet().getUserId();
+		TChargingOrder order = findSimulationOrder(userId, request.getGunId());
+		List<UploadRealTimeMonitoringData> monitoringData = uploadRealTimeMonitoringDataService.getDataByOrderCode(order.getCode());
+		if (monitoringData != null && !monitoringData.isEmpty()) {
+			throw new ServiceException("订单已存在真实充电监控数据,禁止模拟结算");
+		}
+		BigDecimal electricityFee = money(request.getElectricityFee());
+		BigDecimal serviceFee = money(request.getServiceFee());
+		BigDecimal simulationTotal = electricityFee.add(serviceFee).setScale(2, RoundingMode.HALF_UP);
+		BigDecimal availableAmount = money(order.getChargeAmount()).compareTo(BigDecimal.ZERO) > 0
+				? money(order.getChargeAmount()) : money(order.getRechargeAmount());
+		if (simulationTotal.compareTo(availableAmount) > 0) {
+			throw new ServiceException("模拟消费金额不能超过订单可消费金额" + availableAmount.toPlainString() + "元");
+		}
+
+		List<AccountingStrategyDetailOrder> tariffs = accountingStrategyDetailOrderService.list(
+				new LambdaQueryWrapper<AccountingStrategyDetailOrder>()
+						.eq(AccountingStrategyDetailOrder::getChargingOrderId, order.getId())
+						.orderByAsc(AccountingStrategyDetailOrder::getStartTime));
+		ChargingSimulationSupport.PreparedSimulation prepared;
+		try {
+			prepared = ChargingSimulationSupport.prepare(order.getCode(), tariffs, electricityFee, serviceFee, LocalDateTime.now());
+		} catch (IllegalArgumentException e) {
+			throw new ServiceException(e.getMessage());
+		}
+		accountingStrategyDetailOrderService.updateById(prepared.getTariff());
+
+		TChargingGun gun = chargingGunClient.getChargingGunById(order.getChargingGunId()).getData();
+		TChargingPile pile = chargingPileClient.getChargingPileById(order.getChargingPileId()).getData();
+		TransactionRecordMessageVO record = prepared.getRecord();
+		record.setCharging_gun_code(gun == null ? null : gun.getCode());
+		record.setCharging_pile_code(pile == null ? null : pile.getCode());
+		if (Integer.valueOf(2).equals(order.getStatus())) {
+			TChargingOrder charging = new TChargingOrder();
+			charging.setId(order.getId());
+			charging.setStatus(3);
+			charging.setStartTime(LocalDateTime.now().minusSeconds(1));
+			this.updateById(charging);
+		}
+		R settlement = chargingOrderSettlementExecutor.settle(record);
+		if (R.isError(settlement)) {
+			throw new ServiceException(StringUtils.hasLength(settlement.getMsg()) ? settlement.getMsg() : "模拟结算失败");
+		}
+		return buildSimulationVO(this.getById(order.getId()));
+	}
+
+	private TChargingOrder findSimulationOrder(Long userId, Integer gunId) {
+		TChargingOrder order = this.getOne(new LambdaQueryWrapper<TChargingOrder>()
+				.eq(TChargingOrder::getAppUserId, userId)
+				.eq(TChargingOrder::getChargingGunId, gunId)
+				.eq(TChargingOrder::getSourceName, "ICBC")
+				.eq(TChargingOrder::getRechargePaymentStatus, 2)
+				.in(TChargingOrder::getStatus, Arrays.asList(2, 3, 4))
+				.eq(TChargingOrder::getDelFlag, 0)
+				.isNull(TChargingOrder::getRefundStatus)
+				.orderByDesc(TChargingOrder::getId)
+				.last("limit 1"));
+		if (order == null) {
+			throw new ServiceException("当前枪没有属于你的可模拟ICBC已支付订单");
+		}
+		return order;
+	}
+
+	private ChargingSimulationVO buildSimulationVO(TChargingOrder order) {
+		ChargingSimulationVO result = new ChargingSimulationVO();
+		result.setOrderId(order.getId());
+		result.setOrderCode(order.getCode());
+		result.setOrderStatus(order.getStatus());
+		result.setRechargeAmount(order.getRechargeAmount());
+		result.setChargeAmount(order.getChargeAmount());
+		result.setElectricityFee(order.getElectrovalence());
+		result.setServiceFee(order.getServiceCharge());
+		result.setPaymentAmount(order.getPaymentAmount());
+		result.setRefundAmount(order.getRefundAmount());
+		result.setRefundStatus(order.getRefundStatus());
+		result.setIcbcOrderId(order.getRechargeSerialNumber());
+		TChargingOrderShareRecord shareRecord = chargingOrderShareRecordMapper.selectOne(
+				new LambdaQueryWrapper<TChargingOrderShareRecord>()
+						.eq(TChargingOrderShareRecord::getChargingOrderId, order.getId())
+						.eq(TChargingOrderShareRecord::getPayChannel, "ICBC")
+						.eq(TChargingOrderShareRecord::getDelFlag, false)
+						.orderByDesc(TChargingOrderShareRecord::getId)
+						.last("limit 1"));
+		if (shareRecord != null) {
+			result.setShareAmount(shareRecord.getShareAmount());
+			result.setShareStatus(shareRecord.getOnlineShareStatus());
+			result.setSubOrderId(shareRecord.getSubOrderId());
+			result.setSubOrderSeq(shareRecord.getSubOrderSeq());
+			result.setSubOrderTrxid(shareRecord.getSubOrderTrxid());
+		}
+		return result;
+	}
+
+	@Override
+	public IcbcPaymentResp queryIcbcAdminPayment(String orderCode) {
+		TChargingOrder order = loadIcbcAdminOrder(orderCode);
+		return requireFeignData(icbcPaymentClient.query(order.getCode()), "工行支付查询失败");
+	}
+
+	@Override
+	public IcbcRefundResp refundIcbcAdminPayment(IcbcAdminFundOperationReq request) {
+		if (request == null) {
+			throw new ServiceException("请求参数不能为空");
+		}
+		TChargingOrder order = loadIcbcAdminOrder(request.getOrderCode());
+		BigDecimal amount;
+		try {
+			amount = IcbcAdminFundOperationSupport.requireAmount(request.getAmount());
+		} catch (IllegalArgumentException e) {
+			throw new ServiceException(e.getMessage());
+		}
+		BigDecimal paidAmount = money(order.getThirdPayAmount()).compareTo(BigDecimal.ZERO) > 0
+				? money(order.getThirdPayAmount()) : money(order.getRechargeAmount());
+		if (amount.compareTo(paidAmount) > 0) {
+			throw new ServiceException("退款金额不能超过订单第三方支付金额" + paidAmount.toPlainString() + "元");
+		}
+		RLock lock = redissonClient.getLock("icbcAdminFund:order:" + order.getCode());
+		if (!lock.tryLock()) {
+			throw new ServiceException("该订单正在执行工行资金操作,请勿重复提交");
+		}
+		try {
+			IcbcPaymentResp payment = requireFeignData(icbcPaymentClient.query(order.getCode()), "工行支付查询失败");
+			String orderId = extractIcbcOrderId(payment);
+			order.setRechargeSerialNumber(orderId);
+			RBucket<String> pendingRefund = redissonClient.getBucket("icbcAdminFund:pending:main:" + order.getCode());
+			String generatedRefundNo = "ICBCADMINREFUND" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
+			String pendingValue = pendingRefund.get();
+			boolean reusingPending = StringUtils.hasLength(pendingValue);
+			String refundNo;
+			try {
+				refundNo = IcbcAdminFundOperationSupport.resolveRefundNo(pendingValue, amount, generatedRefundNo);
+			} catch (IllegalArgumentException e) {
+				throw new ServiceException(e.getMessage());
+			}
+			IcbcRefundReq refundRequest = IcbcAdminFundOperationSupport.buildRefundRequest(order, amount, refundNo);
+			if (!reusingPending) {
+				pendingRefund.set(IcbcAdminFundOperationSupport.buildPendingRefundValue(refundNo, amount));
+			}
+			IcbcRefundResp response = requireFeignData(icbcPaymentClient.refund(refundRequest), "工行退款调用失败");
+			if (IcbcAdminFundOperationSupport.shouldClearPendingRefund(
+					reusingPending, response.getStatus(), isDefinitiveIcbcResponse(response.getRawResponse()))) {
+				pendingRefund.delete();
+			}
+			return response;
+		} finally {
+			if (lock.isHeldByCurrentThread()) {
+				lock.unlock();
+			}
+		}
+	}
+
+	@Override
+	public List<IcbcAdminSubOrderVO> queryIcbcAdminSubOrders(String orderCode) {
+		TChargingOrder order = loadIcbcAdminOrder(orderCode);
+		List<TChargingOrderShareRecord> records = chargingOrderShareRecordMapper.selectList(
+				new LambdaQueryWrapper<TChargingOrderShareRecord>()
+						.eq(TChargingOrderShareRecord::getChargingOrderId, order.getId())
+						.eq(TChargingOrderShareRecord::getPayChannel, "ICBC")
+						.eq(TChargingOrderShareRecord::getDelFlag, false)
+						.orderByAsc(TChargingOrderShareRecord::getId));
+		List<IcbcAdminSubOrderVO> result = new ArrayList<>();
+		for (TChargingOrderShareRecord record : records) {
+			IcbcAdminSubOrderVO item = buildIcbcAdminSubOrderVO(record);
+			try {
+				IcbcSubOrderQueryReq query = IcbcAdminFundOperationSupport.buildSubOrderQueryRequest(order, record);
+				item.applyQuery(requireFeignData(icbcPaymentClient.querySubOrder(query), "工行子订单查询失败"));
+			} catch (Exception e) {
+				item.setQueryStatus("FAIL");
+				item.setErrorMessage(e.getMessage());
+			}
+			result.add(item);
+		}
+		return result;
+	}
+
+	@Override
+	public IcbcSubOrderRefundResp refundIcbcAdminSubOrder(IcbcAdminFundOperationReq request) {
+		if (request == null || request.getShareRecordId() == null) {
+			throw new ServiceException("请选择需要退款的分账子订单");
+		}
+		TChargingOrder order = loadIcbcAdminOrder(request.getOrderCode());
+		TChargingOrderShareRecord shareRecord = chargingOrderShareRecordMapper.selectById(request.getShareRecordId());
+		if (shareRecord == null || !order.getId().equals(shareRecord.getChargingOrderId())) {
+			throw new ServiceException("分账记录不属于当前订单");
+		}
+		BigDecimal amount;
+		try {
+			amount = IcbcAdminFundOperationSupport.requireAmount(request.getAmount());
+		} catch (IllegalArgumentException e) {
+			throw new ServiceException(e.getMessage());
+		}
+		if (shareRecord.getShareAmount() != null && amount.compareTo(shareRecord.getShareAmount()) > 0) {
+			throw new ServiceException("分账退款金额不能超过分账金额" + shareRecord.getShareAmount().toPlainString() + "元");
+		}
+		RLock lock = redissonClient.getLock("icbcAdminFund:order:" + order.getCode());
+		if (!lock.tryLock()) {
+			throw new ServiceException("该分账子订单正在退款,请勿重复提交");
+		}
+		try {
+			String suffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
+			RBucket<String> pendingRefund = redissonClient.getBucket("icbcAdminFund:pending:sub:" + shareRecord.getId());
+			String generatedToken = "ICBCSUBREFUND" + suffix + ":ICBCSUBREFUNDNO" + suffix;
+			String pendingValue = pendingRefund.get();
+			boolean reusingPending = StringUtils.hasLength(pendingValue);
+			String operationToken;
+			try {
+				operationToken = IcbcAdminFundOperationSupport.resolveRefundNo(pendingValue, amount, generatedToken);
+			} catch (IllegalArgumentException e) {
+				throw new ServiceException(e.getMessage());
+			}
+			String[] refundNumbers = operationToken.split(":", 2);
+			if (refundNumbers.length != 2) {
+				throw new ServiceException("工行分账退款幂等记录格式错误");
+			}
+			IcbcSubOrderRefundReq refundRequest;
+			try {
+				refundRequest = IcbcAdminFundOperationSupport.buildSubOrderRefundRequest(
+						order, shareRecord, amount, refundNumbers[0], refundNumbers[1]);
+			} catch (IllegalArgumentException e) {
+				throw new ServiceException(e.getMessage());
+			}
+			if (!reusingPending) {
+				pendingRefund.set(IcbcAdminFundOperationSupport.buildPendingRefundValue(operationToken, amount));
+			}
+			IcbcSubOrderRefundResp response = requireFeignData(
+					icbcPaymentClient.refundSubOrder(refundRequest), "工行分账退款调用失败");
+			if (IcbcAdminFundOperationSupport.shouldClearPendingRefund(
+					reusingPending, response.getStatus(), isDefinitiveIcbcResponse(response.getRawResponse()))) {
+				pendingRefund.delete();
+			}
+			return response;
+		} finally {
+			if (lock.isHeldByCurrentThread()) {
+				lock.unlock();
+			}
+		}
+	}
+
+	private TChargingOrder loadIcbcAdminOrder(String orderCode) {
+		if (!StringUtils.hasLength(orderCode)) {
+			throw new ServiceException("订单号不能为空");
+		}
+		TChargingOrder order = this.getOne(new LambdaQueryWrapper<TChargingOrder>()
+				.eq(TChargingOrder::getCode, orderCode)
+				.eq(TChargingOrder::getDelFlag, 0)
+				.last("limit 1"));
+		try {
+			IcbcAdminFundOperationSupport.validateOrder(order);
+		} catch (IllegalArgumentException e) {
+			throw new ServiceException(e.getMessage());
+		}
+		return order;
+	}
+
+	private String extractIcbcOrderId(IcbcPaymentResp payment) {
+		try {
+			JSONObject response = JSON.parseObject(payment.getRawResponse());
+			String orderId = response == null ? null : response.getString("order_id");
+			if (StringUtils.hasLength(orderId)) {
+				return orderId;
+			}
+		} catch (Exception ignored) {
+		}
+		throw new ServiceException("工行支付查询未返回真实order_id,禁止退款");
+	}
+
+	private boolean isDefinitiveIcbcResponse(String rawResponse) {
+		if (!StringUtils.hasLength(rawResponse)) {
+			return false;
+		}
+		try {
+			JSONObject response = JSON.parseObject(rawResponse);
+			return response != null && (response.containsKey("returnCode") || response.containsKey("return_code"));
+		} catch (Exception ignored) {
+			return false;
+		}
+	}
+
+	private IcbcAdminSubOrderVO buildIcbcAdminSubOrderVO(TChargingOrderShareRecord record) {
+		IcbcAdminSubOrderVO item = new IcbcAdminSubOrderVO();
+		item.setShareRecordId(record.getId());
+		item.setOrderCode(record.getChargingOrderCode());
+		item.setShareAmount(record.getShareAmount());
+		item.setOnlineShareStatus(record.getOnlineShareStatus());
+		item.setSubOrderId(record.getSubOrderId());
+		item.setSubMerId(record.getSubMerId());
+		item.setSubMerPrtclNo(record.getSubMerPrtclNo());
+		item.setOriTrxDate(record.getOriTrxDate());
+		item.setBankOrderId(record.getIcbcOrderId());
+		item.setSubOrderSeq(record.getSubOrderSeq());
+		item.setSubOrderTrxid(record.getSubOrderTrxid());
+		return item;
+	}
+
+	private <T> T requireFeignData(R<T> result, String message) {
+		if (result == null || R.isError(result) || result.getData() == null) {
+			throw new ServiceException(result != null && StringUtils.hasLength(result.getMsg()) ? result.getMsg() : message);
+		}
+		return result.getData();
 	}
 	
 	
@@ -938,7 +1325,26 @@
 	@Override
 	public AjaxResult chargingOrderCallback(Integer paymentType, String out_trade_no, String transaction_id, String attach) {
 		TChargingOrder chargingOrder = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getCode, out_trade_no));
+		if (chargingOrder == null) {
+			return AjaxResult.error("支付回调订单不存在");
+		}
 		if (chargingOrder.getRechargePaymentStatus() == 2) {
+			if (isIcbcOrder(chargingOrder) && StringUtils.hasLength(transaction_id)
+					&& !transaction_id.equals(chargingOrder.getRechargeSerialNumber())) {
+				chargingOrder.setRechargeSerialNumber(transaction_id);
+				this.updateById(chargingOrder);
+			}
+			return AjaxResult.success();
+		}
+		LocalDateTime callbackPayTime = LocalDateTime.now();
+		int claimed = this.baseMapper.update(null, new LambdaUpdateWrapper<TChargingOrder>()
+				.eq(TChargingOrder::getId, chargingOrder.getId())
+				.eq(TChargingOrder::getRechargePaymentStatus, 1)
+				.set(TChargingOrder::getRechargePaymentStatus, 2)
+				.set(TChargingOrder::getRechargeSerialNumber, transaction_id)
+				.set(TChargingOrder::getStatus, 2)
+				.set(TChargingOrder::getPayTime, callbackPayTime));
+		if (claimed == 0) {
 			return AjaxResult.success();
 		}
 		if (isBalancePayMode(chargingOrder.getPayMethod())
@@ -954,7 +1360,7 @@
 		chargingOrder.setRechargePaymentStatus(2);
 		chargingOrder.setRechargeSerialNumber(transaction_id);
 		chargingOrder.setStatus(2);
-		chargingOrder.setPayTime(LocalDateTime.now());
+		chargingOrder.setPayTime(callbackPayTime);
 		
 		//添加安全检测数据到缓存中,每步安全检测完成后需要更新缓存数据
 		PreChargeCheck preChargeCheck = new PreChargeCheck();
@@ -1509,6 +1915,11 @@
                         return;
                     }
                     this.updateById(chargingOrder);
+                    if (isIcbcOrder(chargingOrder)) {
+                        refundByIcbcAndFinish(chargingOrder, chargingOrderRefund, thirdRefundAmount, "充电失败,取消充电订单", false,
+                                recordedRefundAmount, balanceRefundAmount);
+                        return;
+                    }
                     if (thirdRefundAmount.compareTo(BigDecimal.ZERO) > 0 && 1 == rechargePaymentType) {
                         WxPaymentRefundModel model = new WxPaymentRefundModel();
                         model.setOut_trade_no(chargingOrder.getCode());
@@ -1660,6 +2071,11 @@
 				finishManualChargingRefund(chargingOrder, chargingOrderRefund, money, balanceRefundAmount, thirdRefundAmount);
 				return;
 			}
+			if (isIcbcOrder(chargingOrder)) {
+				refundByIcbcAndFinish(chargingOrder, chargingOrderRefund, thirdRefundAmount, "充电完成退款", true,
+						money, balanceRefundAmount);
+				return;
+			}
 			if (1 == rechargePaymentType) {
 				WxPaymentRefundModel model = new WxPaymentRefundModel();
 				model.setOut_trade_no(chargingOrder.getCode());
@@ -1764,6 +2180,7 @@
                 chargingOrder.setRefundSerialNumber(refund_id);
                 chargingOrder.setRefundTime(LocalDateTime.now());
                 this.updateById(chargingOrder);
+                triggerChargingOrderShare(this.getById(order.getId()));
             }
         } catch (InterruptedException e) {
             throw new RuntimeException(e);
@@ -2089,6 +2506,7 @@
 			List<Integer> siteIds = dto.getSiteIds();
 			List<ChargingOrderVO> list = this.baseMapper.chargingOrder(pageInfo, dto, startTime1, startTime2, endTime1, endTime2);
 			fillChargingOrderBalanceIncome(list);
+			fillChargingOrderShareIncome(list);
 			List<TChargingOrderAccountingStrategy> list3 = chargingOrderAccountingStrategyService.list();
 		for (ChargingOrderVO chargingOrderVO : list) {
 			if (roleType == 2) {
@@ -2164,13 +2582,14 @@
 		}
 		Map<String, Object> fundMap = this.baseMapper.chargingOrderFundSummary(dto, startTime1, startTime2, endTime1, endTime2);
 		if (null != fundMap) {
-			tCharingOrderVO.setPlatformIncomeTotal(getSummaryDecimal(fundMap, "platformIncomeTotal"));
-			tCharingOrderVO.setPartnerIncomeTotal(getSummaryDecimal(fundMap, "partnerIncomeTotal"));
 			tCharingOrderVO.setWechatIncome(getSummaryDecimal(fundMap, "wechatIncome"));
 			tCharingOrderVO.setAlipayIncome(getSummaryDecimal(fundMap, "alipayIncome"));
 			tCharingOrderVO.setWhitelistChargingIncome(getSummaryDecimal(fundMap, "whitelistChargingIncome"));
 			tCharingOrderVO.setWhitelistRefund(getSummaryDecimal(fundMap, "whitelistRefund"));
 		}
+		Map<String, Object> shareIncomeMap = getChargingOrderShareIncomeSummary(dto, startTime1, startTime2, endTime1, endTime2);
+		tCharingOrderVO.setPlatformIncomeTotal(getSummaryDecimal(shareIncomeMap, "platformIncomeTotal"));
+		tCharingOrderVO.setPartnerIncomeTotal(getSummaryDecimal(shareIncomeMap, "partnerIncomeTotal"));
 		Map<String, Object> balanceMap = getExchangeCodeBalanceSummary(dto, startTime1, startTime2, endTime1, endTime2);
 		tCharingOrderVO.setBalanceIncome(getSummaryDecimal(balanceMap, "balanceIncome"));
 		tCharingOrderVO.setBalanceGiftIncome(getSummaryDecimal(balanceMap, "balanceGiftIncome"));
@@ -2351,6 +2770,30 @@
 		return totalMap;
 	}
 
+	private Map<String, Object> getChargingOrderShareIncomeSummary(ChargingOrderQuery dto, String startTime1, String startTime2, String endTime1, String endTime2) {
+		Map<String, Object> totalMap = new HashMap<>();
+		totalMap.put("platformIncomeTotal", BigDecimal.ZERO);
+		totalMap.put("partnerIncomeTotal", BigDecimal.ZERO);
+		List<Long> orderIds = this.baseMapper.chargingOrderSummaryOrderIds(dto, startTime1, startTime2, endTime1, endTime2);
+		if (orderIds == null || orderIds.isEmpty()) {
+			return totalMap;
+		}
+		BigDecimal platformIncomeTotal = BigDecimal.ZERO;
+		BigDecimal partnerIncomeTotal = BigDecimal.ZERO;
+		int batchSize = 500;
+		for (int i = 0; i < orderIds.size(); i += batchSize) {
+			List<Long> batchIds = orderIds.subList(i, Math.min(i + batchSize, orderIds.size()));
+			Map<String, Object> batchMap = this.baseMapper.chargingOrderShareIncomeSummaryByOrderIds(batchIds);
+			if (batchMap != null) {
+				platformIncomeTotal = platformIncomeTotal.add(getSummaryDecimal(batchMap, "platformIncomeTotal"));
+				partnerIncomeTotal = partnerIncomeTotal.add(getSummaryDecimal(batchMap, "partnerIncomeTotal"));
+			}
+		}
+		totalMap.put("platformIncomeTotal", platformIncomeTotal);
+		totalMap.put("partnerIncomeTotal", partnerIncomeTotal);
+		return totalMap;
+	}
+
 		private BigDecimal getSummaryDecimal(Map<String, Object> map, String key) {
 			if (map == null) {
 				return BigDecimal.ZERO;
@@ -2471,27 +2914,30 @@
 	}
 	
 	@Override
-	public List<SixChargingDto> charge(LocalDate sixBefore, List<Integer> siteIds) {
-		return this.baseMapper.charge(sixBefore, siteIds);
+	public List<SixChargingDto> charge(LocalDate sixBefore, List<Integer> siteIds, Boolean partnerView) {
+		return this.baseMapper.charge(sixBefore, siteIds, partnerView);
 	}
 	
 	@Override
-	public List<SixCircleDto> circle(List<Integer> siteIds, LocalDate sixBefore) {
-		return this.baseMapper.circle(siteIds, sixBefore);
+	public List<SixCircleDto> circle(List<Integer> siteIds, LocalDate sixBefore, Boolean partnerView) {
+		return this.baseMapper.circle(siteIds, sixBefore, partnerView);
 	}
 	
 	@Override
-	public Map<String, Object> countAll(List<Integer> siteIds) {
-		return this.baseMapper.countAll(siteIds);
+	public Map<String, Object> countAll(List<Integer> siteIds, Boolean partnerView) {
+		return this.baseMapper.countAll(siteIds, partnerView);
 	}
 
 	/**
 	 * 统计近六月订单收入支付方式占比;服务层聚合避免复杂CASE SQL在当前数据库驱动中转换异常。
 	 */
 	@Override
-	public List<SixIncomeCircleDto> payTypeCircle(LocalDate sixBefore, List<Integer> siteIds) {
+	public List<SixIncomeCircleDto> payTypeCircle(LocalDate sixBefore, List<Integer> siteIds, Boolean partnerView) {
 		if (siteIds == null || siteIds.isEmpty()) {
 			return buildPayTypeCircleData(BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO);
+		}
+		if (Boolean.TRUE.equals(partnerView)) {
+			return buildPartnerShareCircleData(this.baseMapper.chargingOrderShareIncomeByPayChannel(sixBefore, siteIds));
 		}
 		List<TChargingOrder> orders = this.lambdaQuery()
 				.select(TChargingOrder::getRechargePaymentType, TChargingOrder::getPayMethod,
@@ -2521,6 +2967,28 @@
 			Integer payMethod = order.getPayMethod();
 			if (Integer.valueOf(1).equals(payMethod) || Integer.valueOf(2).equals(payMethod)) {
 				balanceAmount = balanceAmount.add(balancePayAmount);
+			}
+		}
+		return buildPayTypeCircleData(wechatAmount, alipayAmount, balanceAmount);
+	}
+
+	private List<SixIncomeCircleDto> buildPartnerShareCircleData(List<Map<String, Object>> rows) {
+		BigDecimal wechatAmount = BigDecimal.ZERO;
+		BigDecimal alipayAmount = BigDecimal.ZERO;
+		BigDecimal balanceAmount = BigDecimal.ZERO;
+		if (rows != null) {
+			for (Map<String, Object> row : rows) {
+				String channel = row.get("payChannel") == null ? "" : row.get("payChannel").toString();
+				BigDecimal shareAmount = getSummaryDecimal(row, "shareAmount");
+				if ("WECHAT".equalsIgnoreCase(channel) || "WX".equalsIgnoreCase(channel)) {
+					wechatAmount = wechatAmount.add(shareAmount);
+				} else if ("ALIPAY".equalsIgnoreCase(channel) || "ALI".equalsIgnoreCase(channel)) {
+					alipayAmount = alipayAmount.add(shareAmount);
+				} else if ("BALANCE".equalsIgnoreCase(channel) || "WHITELIST".equalsIgnoreCase(channel)) {
+					balanceAmount = balanceAmount.add(shareAmount);
+				} else {
+					wechatAmount = wechatAmount.add(shareAmount);
+				}
 			}
 		}
 		return buildPayTypeCircleData(wechatAmount, alipayAmount, balanceAmount);
@@ -2717,6 +3185,7 @@
 		PageInfo<ChargingOrderListVO> pageInfo = new PageInfo<>(dto.getPageCurr(), dto.getPageSize());
 
 		List<ChargingOrderListVO> list = new ArrayList<>();
+		Map<Integer, BigDecimal> siteServiceShareRatioMap = loadSiteServiceShareRatioMap();
 		List<Map<String, Object>> maps = this.baseMapper.chargingList1(pageInfo, dto, startTime1, startTime2, endTime1, endTime2);
 		for (Map<String, Object> map : maps) {
 			if(null == map){
@@ -2730,34 +3199,36 @@
 			}
 			chargingOrderListVO.setUid(map.get("id").toString());
 			chargingOrderListVO.setCode(map.get("code").toString());
+			chargingOrderListVO.setId(Long.valueOf(map.get("id").toString()));
+			chargingOrderListVO.setSiteId(Integer.valueOf(map.get("siteId").toString()));
+			chargingOrderListVO.setOrderSource(Integer.valueOf(map.get("orderSource").toString()));
 			chargingOrderListVO.setSiteName(map.get("siteName").toString());
 			chargingOrderListVO.setOrderType(map.get("orderType").toString());
 			chargingOrderListVO.setTerminalName(map.get("terminalName").toString());
 			chargingOrderListVO.setElectricity(null == map.get("electricity") ? BigDecimal.ZERO : new BigDecimal(map.get("electricity").toString()));
 			chargingOrderListVO.setChargingCapacity(chargingOrderListVO.getElectricity());
-			Object periodElectricPrice = map.get("periodElectricPrice");
-			Object periodServicePrice = map.get("periodServicePrice");
-			if(null != periodServicePrice){
-				Integer orderSource = Integer.valueOf(map.get("orderSource").toString());
+			BigDecimal periodElectricPrice = getSummaryDecimal(map, "periodElectricPrice");
+			BigDecimal periodServicePrice = getSummaryDecimal(map, "periodServicePrice");
+			if(periodServicePrice.compareTo(BigDecimal.ZERO) != 0){
+				Integer orderSource = chargingOrderListVO.getOrderSource();
 				if(2 == orderSource){
-					BigDecimal bigDecimal = new BigDecimal(periodServicePrice.toString()).multiply(new BigDecimal(0.8)).setScale(2, RoundingMode.HALF_EVEN);
-					chargingOrderListVO.setPaymentAmount(new BigDecimal(periodElectricPrice.toString()).add(bigDecimal));
-					BigDecimal sharingAmount = new BigDecimal(periodServicePrice.toString()).multiply(new BigDecimal(0.2)).setScale(2, RoundingMode.HALF_EVEN);
+					BigDecimal bigDecimal = periodServicePrice.multiply(new BigDecimal("0.8")).setScale(2, RoundingMode.HALF_EVEN);
+					chargingOrderListVO.setServiceCharge(bigDecimal);
+					chargingOrderListVO.setPaymentAmount(periodElectricPrice.add(bigDecimal).setScale(2, RoundingMode.HALF_EVEN));
+					BigDecimal sharingAmount = periodServicePrice.multiply(new BigDecimal("0.2")).setScale(2, RoundingMode.HALF_EVEN);
 					chargingOrderListVO.setSharingAmount(sharingAmount);
 				}else{
-					chargingOrderListVO.setPaymentAmount(new BigDecimal(periodElectricPrice.toString()).add(new BigDecimal(periodServicePrice.toString())).setScale(2, RoundingMode.HALF_EVEN));
+					chargingOrderListVO.setServiceCharge(periodServicePrice);
+					chargingOrderListVO.setPaymentAmount(periodElectricPrice.add(periodServicePrice).setScale(2, RoundingMode.HALF_EVEN));
 					chargingOrderListVO.setSharingAmount(BigDecimal.ZERO);
 				}
 			}else{
+				chargingOrderListVO.setServiceCharge(BigDecimal.ZERO);
 				chargingOrderListVO.setPaymentAmount(BigDecimal.ZERO);
 				chargingOrderListVO.setSharingAmount(BigDecimal.ZERO);
 			}
 			chargingOrderListVO.setElectrovalence(null == map.get("electrovalence") ? BigDecimal.ZERO : new BigDecimal(map.get("electrovalence").toString()));
-			chargingOrderListVO.setServiceCharge(null == map.get("serviceCharge") ? BigDecimal.ZERO : new BigDecimal(map.get("serviceCharge").toString()));
-			BigDecimal bigDecimal = new BigDecimal("0.006");
-			if (!"0".equals(map.get("rechargePaymentType").toString())) {
-				chargingOrderListVO.setCommissionAmount(new BigDecimal(map.get("rechargeAmount").toString()).multiply(bigDecimal));
-			}
+			fillChargingPeriodIncome(chargingOrderListVO, siteServiceShareRatioMap);
 			if(null != map.get("tripartitePlatformName")){
 				chargingOrderListVO.setTripartitePlatformName(map.get("tripartitePlatformName").toString());
 			}
@@ -2854,21 +3325,41 @@
         Map<String, Object> map = this.baseMapper.chargingListSum1(dto, startTime1, startTime2, endTime1, endTime2);
         System.err.println("充电时段统计-查询参数:" + JSON.toJSONString(dto));
         System.err.println("充电时段统计-汇总数据:" + JSON.toJSONString(map));
+		if (map == null) {
+			map = new HashMap<>();
+		}
         //订单数量
-        int orderCount = Integer.parseInt(map.get("orderCount").toString());
+        int orderCount = Integer.parseInt(String.valueOf(map.getOrDefault("orderCount", "0")));
         // 计算充电总度数
-        BigDecimal electronic = new BigDecimal(map.get("electronic").toString());
+        BigDecimal electronic = new BigDecimal(String.valueOf(map.getOrDefault("electronic", "0")));
         // 支付金额
-        BigDecimal paymentAmount = new BigDecimal(map.get("paymentAmount").toString());
+        BigDecimal paymentAmount = new BigDecimal(String.valueOf(map.getOrDefault("paymentAmount", "0")));
         // 电费
-        BigDecimal electrovalence = new BigDecimal(map.get("electrovalence").toString());
+        BigDecimal electrovalence = new BigDecimal(String.valueOf(map.getOrDefault("electrovalence", "0")));
         // 服务费
-        BigDecimal serviceCharge = new BigDecimal(map.get("serviceCharge").toString());
-        chargingOrderTimeVO.setOrderCount(orderCount);
+		BigDecimal serviceCharge = new BigDecimal(String.valueOf(map.getOrDefault("serviceCharge", "0")));
+		chargingOrderTimeVO.setOrderCount(orderCount);
 		chargingOrderTimeVO.setChargingCapacity(electronic.setScale(2, BigDecimal.ROUND_HALF_EVEN));
 		chargingOrderTimeVO.setPaymentAmount(paymentAmount.setScale(2, BigDecimal.ROUND_HALF_EVEN));
 		chargingOrderTimeVO.setElectrovalence(electrovalence.setScale(2, BigDecimal.ROUND_HALF_EVEN));
 		chargingOrderTimeVO.setServiceCharge(serviceCharge.setScale(2, BigDecimal.ROUND_HALF_EVEN));
+		BigDecimal platformIncome = BigDecimal.ZERO;
+		BigDecimal partnerIncome = BigDecimal.ZERO;
+		for (Map<String, Object> sumMap : list1) {
+			if (sumMap == null) {
+				continue;
+			}
+			ChargingOrderListVO item = new ChargingOrderListVO();
+			item.setSiteId(getSummaryLong(sumMap, "siteId").intValue());
+			item.setOrderSource(getSummaryLong(sumMap, "orderSource").intValue());
+			item.setPaymentAmount(getSummaryDecimal(sumMap, "paymentAmount"));
+			item.setServiceCharge(getSummaryDecimal(sumMap, "serviceCharge"));
+			fillChargingPeriodIncome(item, siteServiceShareRatioMap);
+			platformIncome = platformIncome.add(nullToZero(item.getPlatformIncome()));
+			partnerIncome = partnerIncome.add(nullToZero(item.getPartnerIncome()));
+		}
+		chargingOrderTimeVO.setPlatformIncome(platformIncome.setScale(2, RoundingMode.HALF_UP));
+		chargingOrderTimeVO.setPartnerIncome(partnerIncome.setScale(2, RoundingMode.HALF_UP));
 		// 计算尖峰平谷充电到账占比
 		BigDecimal add = jianElectronic.add(fengElectronic).add(pingElectronic).add(guElectronic);
 		// 计算尖峰平谷服务费占比
@@ -2998,7 +3489,60 @@
 		dto.setSiteIds(siteIds);
 		List<ChargingOrderListVO> list1 = this.baseMapper.chargingList(dto, startTime1, startTime2, endTime1, endTime2);
 		fillChargingPeriodExportBalanceIncome(list1);
+		fillChargingPeriodIncome(list1);
 		return list1;
+	}
+
+	private void fillChargingPeriodIncome(List<ChargingOrderListVO> list) {
+		if (list == null || list.isEmpty()) {
+			return;
+		}
+		Map<Integer, BigDecimal> siteServiceShareRatioMap = loadSiteServiceShareRatioMap();
+		for (ChargingOrderListVO item : list) {
+			fillChargingPeriodIncome(item, siteServiceShareRatioMap);
+		}
+	}
+
+	private void fillChargingPeriodIncome(ChargingOrderListVO item, Map<Integer, BigDecimal> siteServiceShareRatioMap) {
+		if (item == null) {
+			return;
+		}
+		BigDecimal paymentAmount = nullToZero(item.getPaymentAmount());
+		BigDecimal partnerIncome = BigDecimal.ZERO;
+		if (!Integer.valueOf(2).equals(item.getOrderSource())) {
+			BigDecimal serviceShareRatio = siteServiceShareRatioMap.get(item.getSiteId());
+			if (serviceShareRatio != null && serviceShareRatio.compareTo(BigDecimal.ZERO) > 0) {
+				partnerIncome = nullToZero(item.getServiceCharge())
+						.multiply(serviceShareRatio)
+						.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
+			}
+		}
+		item.setPartnerIncome(partnerIncome);
+		item.setPlatformIncome(paymentAmount.subtract(partnerIncome).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP));
+	}
+
+	private Map<Integer, BigDecimal> loadSiteServiceShareRatioMap() {
+		Map<Integer, BigDecimal> ratioMap = new HashMap<>();
+		R<List<Site>> siteResult = siteClient.getSiteAll();
+		R<List<Partner>> partnerResult = siteClient.getAllPartner();
+		List<Site> sites = siteResult == null ? Collections.emptyList() : siteResult.getData();
+		List<Partner> partners = partnerResult == null ? Collections.emptyList() : partnerResult.getData();
+		if (sites == null || sites.isEmpty() || partners == null || partners.isEmpty()) {
+			return ratioMap;
+		}
+		Map<Integer, Partner> partnerMap = partners.stream()
+				.filter(partner -> partner != null && partner.getId() != null)
+				.collect(Collectors.toMap(Partner::getId, partner -> partner, (left, right) -> left));
+		for (Site site : sites) {
+			if (site == null || site.getId() == null || site.getPartnerId() == null) {
+				continue;
+			}
+			Partner partner = partnerMap.get(site.getPartnerId());
+			if (partner != null && partner.getServiceShareRatio() != null) {
+				ratioMap.put(site.getId(), partner.getServiceShareRatio());
+			}
+		}
+		return ratioMap;
 	}
 
 	/**
@@ -3026,6 +3570,31 @@
 			Map<String, Object> row = incomeMap.get(item.getId());
 			item.setBalanceIncome(getSummaryDecimal(row, "balanceIncome"));
 			item.setBalanceGiftIncome(getSummaryDecimal(row, "balanceGiftIncome"));
+		}
+	}
+
+	private void fillChargingOrderShareIncome(List<ChargingOrderVO> list) {
+		if (list == null || list.isEmpty()) {
+			return;
+		}
+		List<Long> orderIds = list.stream().map(ChargingOrderVO::getId).filter(Objects::nonNull).collect(Collectors.toList());
+		if (orderIds.isEmpty()) {
+			return;
+		}
+		Map<Long, Map<String, Object>> incomeMap = new HashMap<>();
+		List<Map<String, Object>> rows = this.baseMapper.chargingOrderShareIncomeByOrderIds(orderIds);
+		if (rows != null) {
+			for (Map<String, Object> row : rows) {
+				Long orderId = getSummaryLong(row, "orderId");
+				if (orderId != null) {
+					incomeMap.put(orderId, row);
+				}
+			}
+		}
+		for (ChargingOrderVO item : list) {
+			Map<String, Object> row = incomeMap.get(item.getId());
+			item.setPlatformIncome(getSummaryDecimal(row, "platformIncome"));
+			item.setPartnerIncome(getSummaryDecimal(row, "partnerIncome"));
 		}
 	}
 
@@ -3220,15 +3789,19 @@
     @Transactional
 	public R endChargeBillingCharge(TransactionRecordMessageVO vo) {
 		RLock lock = redissonClient.getLock("endOrder:" + vo.getTransaction_serial_number());
+		boolean locked = false;
 		try {
-            boolean tryLock = lock.tryLock(60, 60, TimeUnit.SECONDS);
+            locked = lock.tryLock(60, 60, TimeUnit.SECONDS);
+            if (!locked) {
+                return R.fail("订单正在结算,请勿重复操作");
+            }
             //添加redis缓存数据,过期时间,用于防止处理缓慢且硬件重复发送报文导致的重复退款
             Boolean hasKey = redisService.hasKey("END_ORDER:" + vo.getTransaction_serial_number());
             if(hasKey){
                 return R.fail("不能重复退款");
             }
             redisService.setCacheObject("END_ORDER:" + vo.getTransaction_serial_number(), System.currentTimeMillis() + "", 10L, TimeUnit.MINUTES);
-            if(tryLock){
+            if(locked){
                 TChargingOrder chargingOrder = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getCode, vo.getTransaction_serial_number()));
                 if (null == chargingOrder) {
                     log.error("订单不存在,订单号:{}", vo.getTransaction_serial_number());
@@ -3724,6 +4297,17 @@
                             chargingOrder.setRefundStatus(2);
                             chargingOrder.setRefundTime(LocalDateTime.now());
                             this.updateById(chargingOrder);
+                            triggerChargingOrderShare(chargingOrder);
+                        } else if (isIcbcOrder(chargingOrder)) {
+                            boolean refundSuccess = refundByIcbcAndFinish(chargingOrder, chargingOrderRefund, thirdRefundAmount,
+                                    "充电完成退款", false, refundAmount, balanceRefundAmount);
+                            if (!refundSuccess) {
+                                chargingOrderRefund.setRefundStatus(1);
+                                chargingOrderRefundService.save(chargingOrderRefund);
+                                chargingOrder.setRefundStatus(1);
+                                this.updateById(chargingOrder);
+                                throw new ServiceException("工行退款失败,结算未完成,请检查支付流水后重试退款");
+                            }
                         } else if (1 == rechargePaymentType) {
                             WxPaymentRefundModel model = new WxPaymentRefundModel();
                             model.setOut_trade_no(chargingOrder.getCode());
@@ -3793,6 +4377,7 @@
                         chargingOrder.setRefundStatus(2);
                         chargingOrder.setRefundTime(LocalDateTime.now());
                         this.updateById(chargingOrder);
+                        triggerChargingOrderShare(chargingOrder);
                     } else {
                         //构建退款明细
                         TChargingOrderRefund chargingOrderRefund = new TChargingOrderRefund();
@@ -3821,6 +4406,7 @@
                         chargingOrder.setRefundStatus(2);
                         chargingOrder.setRefundTime(LocalDateTime.now());
                         this.updateById(chargingOrder);
+                        triggerChargingOrderShare(chargingOrder);
                     }
                 }
 
@@ -3853,6 +4439,9 @@
                 summaryData.setElectricPrice(electronic_reduce.setScale(2, BigDecimal.ROUND_HALF_EVEN));
                 summaryData.setServicePrice(serviceMoney.setScale(2, BigDecimal.ROUND_HALF_EVEN));
                 chargingOrderSummaryDataService.save(summaryData);
+                if (channelRefundAmount.compareTo(BigDecimal.ZERO) == 0 && Integer.valueOf(2).equals(chargingOrder.getRefundStatus())) {
+                    triggerChargingOrderShare(chargingOrder, site);
+                }
 
                 //推送监管平台订单状态和订单详情
                 ChargingOrderVo chargingOrderVo = new ChargingOrderVo();
@@ -3861,9 +4450,12 @@
                 chargingMessageClient.pushOrderStatus(chargingOrderVo);
             }
 		} catch (Exception e) {
-			e.printStackTrace();
+			log.error("充电订单结算失败,订单号:{}", vo == null ? null : vo.getTransaction_serial_number(), e);
+			return R.fail("充电订单结算失败:" + firstNotBlank(e.getMessage(), "未知异常"));
 		} finally {
-			lock.unlock();
+			if (locked && lock.isHeldByCurrentThread()) {
+				lock.unlock();
+			}
 		}
 		return R.ok();
 	}
@@ -4004,6 +4596,10 @@
 			if (thirdRefundAmount.compareTo(BigDecimal.ZERO) == 0) {
 				chargingOrderRefund.setRefundSerialNumber("BALANCE");
 				finishManualChargingRefund(tChargingOrder, chargingOrderRefund, manualRefundAmount, balanceRefundAmount, thirdRefundAmount);
+			}
+			else if (isIcbcOrder(tChargingOrder)) {
+				refundByIcbcAndFinish(tChargingOrder, chargingOrderRefund, thirdRefundAmount, "取消订单", true,
+						manualRefundAmount, balanceRefundAmount);
 			}
 			else if (1 == tChargingOrder.getRechargePaymentType()) {
 				WxPaymentRefundModel model = new WxPaymentRefundModel();
@@ -6143,4 +6739,414 @@
             e.printStackTrace();
         }
     }
+
+	private boolean isIcbcRemovableStatus(String status, TChargingOrder order) {
+		if (!StringUtils.hasLength(status)) {
+			return false;
+		}
+		if (Arrays.asList("FAIL", "FAILED", "CLOSED", "TRADE_CLOSED", "REVOKED", "PAYERROR").contains(status)) {
+			return true;
+		}
+		// 工行B2C不支持主动关单;NOTPAY超时仅释放本地待支付订单,不代表远端订单已关闭。
+		return "NOTPAY".equals(status) && isLocalPendingOrderExpired(order);
+	}
+
+	private boolean isLocalPendingOrderExpired(TChargingOrder order) {
+		return order != null && order.getCreateTime() != null && order.getCreateTime().plusMinutes(30).isBefore(LocalDateTime.now());
+	}
+
+	private boolean isIcbcPaymentCreated(IcbcPaymentResp resp) {
+		if (resp == null || !StringUtils.hasLength(resp.getStatus())) {
+			return false;
+		}
+		if (!"SUCCESS".equals(resp.getStatus()) || !StringUtils.hasLength(resp.getPayData())) {
+			return false;
+		}
+		try {
+			JSONObject payData = JSON.parseObject(resp.getPayData());
+			return StringUtils.hasLength(payData.getString("wxDataPackage")) || StringUtils.hasLength(payData.getString("package"));
+		} catch (Exception e) {
+			return false;
+		}
+	}
+
+	private void triggerChargingOrderShare(TChargingOrder chargingOrder, Site site) {
+		try {
+			if (!isNeedChargingOrderShare(chargingOrder)) {
+				return;
+			}
+			Partner partner = loadPartner(site);
+			for (TChargingOrderShareRecord shareRecord : buildChargingOrderShareRecords(chargingOrder, site, partner)) {
+				TChargingOrderShareRecord exist = chargingOrderShareRecordMapper.selectOne(new LambdaQueryWrapper<TChargingOrderShareRecord>()
+						.eq(TChargingOrderShareRecord::getChargingOrderCode, shareRecord.getChargingOrderCode())
+						.eq(TChargingOrderShareRecord::getPayChannel, shareRecord.getPayChannel())
+						.eq(TChargingOrderShareRecord::getDelFlag, false)
+						.last("limit 1"));
+				if (exist != null && ("SUCCESS".equals(exist.getOnlineShareStatus()) || "PROCESSING".equals(exist.getOnlineShareStatus()))) {
+					continue;
+				}
+				shareRecord = mergeChargingOrderShareRecord(exist, shareRecord);
+				saveOrUpdateChargingOrderShareRecord(shareRecord);
+				if (Boolean.TRUE.equals(shareRecord.getNeedOnlineShare())) {
+					processIcbcOnlineShare(shareRecord, partner);
+				}
+			}
+		} catch (Exception e) {
+			log.error("充电订单分账记录触发失败,订单号:{}", chargingOrder == null ? null : chargingOrder.getCode(), e);
+			recordChargingOrderShareFailure(chargingOrder, site, e);
+		}
+	}
+
+	private void triggerChargingOrderShare(TChargingOrder chargingOrder) {
+		try {
+			if (chargingOrder == null || chargingOrder.getSiteId() == null) {
+				return;
+			}
+			R<List<Site>> siteResult = siteClient.getSiteByIds(Arrays.asList(chargingOrder.getSiteId()));
+			List<Site> sites = siteResult == null ? null : siteResult.getData();
+			Site site = sites == null || sites.isEmpty() ? null : sites.get(0);
+			triggerChargingOrderShare(chargingOrder, site);
+		} catch (Exception e) {
+			log.error("充电订单分账站点加载失败,订单号:{}", chargingOrder == null ? null : chargingOrder.getCode(), e);
+		}
+	}
+
+	private boolean isNeedChargingOrderShare(TChargingOrder chargingOrder) {
+		return chargingOrder != null
+				&& Integer.valueOf(5).equals(chargingOrder.getStatus())
+				&& Integer.valueOf(2).equals(chargingOrder.getRechargePaymentStatus())
+				&& Integer.valueOf(2).equals(chargingOrder.getRefundStatus())
+				&& getChargingOrderShareBaseAmount(chargingOrder).compareTo(BigDecimal.ZERO) > 0;
+	}
+
+	private Partner loadPartner(Site site) {
+		if (site == null || site.getPartnerId() == null) {
+			return null;
+		}
+		R<Partner> partnerResult = partnerClient.getPartnerById(site.getPartnerId());
+		return partnerResult == null ? null : partnerResult.getData();
+	}
+
+	private List<TChargingOrderShareRecord> buildChargingOrderShareRecords(TChargingOrder chargingOrder, Site site, Partner partner) {
+		BigDecimal actualThirdPay = getActualThirdPayAmount(chargingOrder);
+		BalanceShareAmount balanceShareAmount = getBalanceShareAmount(chargingOrder);
+		BigDecimal actualBalancePay = balanceShareAmount.getPrincipalAmount();
+		BigDecimal actualTotalPay = actualThirdPay.add(actualBalancePay).setScale(2, RoundingMode.HALF_UP);
+		BigDecimal shareElectricityAmount = getChargingOrderShareElectricityAmount(chargingOrder);
+		BigDecimal shareServiceAmount = getChargingOrderShareServiceAmount(chargingOrder);
+		BigDecimal shareBaseAmount = shareElectricityAmount.add(shareServiceAmount).setScale(2, RoundingMode.HALF_UP);
+		if (actualTotalPay.compareTo(BigDecimal.ZERO) <= 0 && isWhitelistOrder(chargingOrder)) {
+			actualTotalPay = shareBaseAmount;
+		}
+		if (actualTotalPay.compareTo(BigDecimal.ZERO) <= 0) {
+			return Collections.emptyList();
+		}
+		if (!isWhitelistOrder(chargingOrder) && shareBaseAmount.compareTo(BigDecimal.ZERO) > 0 && actualTotalPay.compareTo(shareBaseAmount) < 0) {
+			BigDecimal effectiveRatio = actualTotalPay.divide(shareBaseAmount, 6, RoundingMode.HALF_UP);
+			shareElectricityAmount = shareElectricityAmount.multiply(effectiveRatio).setScale(2, RoundingMode.HALF_UP);
+			shareServiceAmount = shareServiceAmount.multiply(effectiveRatio).setScale(2, RoundingMode.HALF_UP);
+		}
+		List<TChargingOrderShareRecord> records = new ArrayList<>();
+		if (actualBalancePay.compareTo(BigDecimal.ZERO) > 0) {
+			records.add(buildChargingOrderShareRecord(chargingOrder, site, partner, "BALANCE", actualBalancePay,
+					balanceShareAmount.getGiftAmount(), actualTotalPay, shareElectricityAmount, shareServiceAmount));
+		}
+		if (actualThirdPay.compareTo(BigDecimal.ZERO) > 0) {
+			records.add(buildChargingOrderShareRecord(chargingOrder, site, partner, resolveThirdPayChannel(chargingOrder), actualThirdPay,
+					BigDecimal.ZERO, actualTotalPay, shareElectricityAmount, shareServiceAmount));
+		}
+		if (records.isEmpty() && isWhitelistOrder(chargingOrder)) {
+			records.add(buildChargingOrderShareRecord(chargingOrder, site, partner, "WHITELIST", actualTotalPay,
+					BigDecimal.ZERO, actualTotalPay, shareElectricityAmount, shareServiceAmount));
+		}
+		return records;
+	}
+
+	private TChargingOrderShareRecord buildChargingOrderShareRecord(TChargingOrder chargingOrder, Site site, Partner partner,
+	                                                                 String payChannel, BigDecimal channelPayAmount, BigDecimal channelGiftAmount,
+	                                                                 BigDecimal actualTotalPay, BigDecimal shareElectricityAmount,
+	                                                                 BigDecimal shareServiceAmount) {
+		TChargingOrderShareRecord record = new TChargingOrderShareRecord();
+		record.setChargingOrderId(chargingOrder.getId());
+		record.setChargingOrderCode(chargingOrder.getCode());
+		record.setSiteId(chargingOrder.getSiteId());
+		record.setPartnerId(site == null ? null : site.getPartnerId());
+		record.setPayChannel(payChannel);
+		record.setChannelPayAmount(money(channelPayAmount));
+		record.setChannelGiftAmount(money(channelGiftAmount));
+		BigDecimal channelRatio = channelPayAmount.divide(actualTotalPay, 6, RoundingMode.HALF_UP);
+		record.setChannelPayRatio(channelRatio);
+		BigDecimal channelElectricityAmount = shareElectricityAmount.multiply(channelRatio).setScale(2, RoundingMode.HALF_UP);
+		BigDecimal channelServiceAmount = shareServiceAmount.multiply(channelRatio).setScale(2, RoundingMode.HALF_UP);
+		record.setChannelElectricityAmount(channelElectricityAmount);
+		record.setChannelServiceAmount(channelServiceAmount);
+		record.setElectricityShareRatio(partner == null ? null : partner.getElectricityShareRatio());
+		record.setServiceShareRatio(partner == null ? null : partner.getServiceShareRatio());
+		BigDecimal electricityShareAmount = calculateShareAmount(channelElectricityAmount, record.getElectricityShareRatio());
+		BigDecimal serviceShareAmount = calculateShareAmount(channelServiceAmount, record.getServiceShareRatio());
+		record.setElectricityShareAmount(electricityShareAmount);
+		record.setServiceShareAmount(serviceShareAmount);
+		record.setShareAmount(electricityShareAmount.add(serviceShareAmount).setScale(2, RoundingMode.HALF_UP));
+		record.setNeedOnlineShare("ICBC".equals(payChannel) && record.getShareAmount().compareTo(BigDecimal.ZERO) > 0);
+		record.setOnlineShareStatus(Boolean.TRUE.equals(record.getNeedOnlineShare()) ? "INIT" : "RECORDED");
+		if ("ICBC".equals(payChannel)) {
+			record.setSeqNo(chargingOrder.getCode());
+			record.setBusiType("1");
+			record.setOriTrxDate(chargingOrder.getPayTime() == null ? LocalDate.now() : chargingOrder.getPayTime().toLocalDate());
+			record.setSubOrderId(chargingOrder.getCode());
+		}
+		if ("ICBC".equals(payChannel) && partner != null) {
+			record.setSubMerId(partner.getIcbcSubMerId());
+			record.setSubMerPrtclNo(partner.getIcbcSubMerPrtclNo());
+			record.setMerAcct(partner.getIcbcMerAcct());
+		}
+		record.setRetryCount(0);
+		record.setDelFlag(false);
+		return record;
+	}
+
+	private TChargingOrderShareRecord mergeChargingOrderShareRecord(TChargingOrderShareRecord exist, TChargingOrderShareRecord fresh) {
+		if (exist == null) {
+			return fresh;
+		}
+		fresh.setId(exist.getId());
+		fresh.setRetryCount(exist.getRetryCount() == null ? 0 : exist.getRetryCount());
+		fresh.setRequestJson(exist.getRequestJson());
+		fresh.setResponseJson(exist.getResponseJson());
+		fresh.setIcbcOrderId(exist.getIcbcOrderId());
+		fresh.setSubOrderSeq(exist.getSubOrderSeq());
+		fresh.setSubOrderTrxid(exist.getSubOrderTrxid());
+		fresh.setRecResult(exist.getRecResult());
+		fresh.setLastRetryTime(exist.getLastRetryTime());
+		return fresh;
+	}
+
+	private BigDecimal getActualThirdPayAmount(TChargingOrder chargingOrder) {
+		return getRefundableThirdPayAmount(chargingOrder).subtract(money(chargingOrder.getThirdRefundAmount())).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP);
+	}
+
+	private BigDecimal getActualBalancePayAmount(TChargingOrder chargingOrder) {
+		return money(chargingOrder.getBalancePayAmount()).subtract(money(chargingOrder.getBalanceRefundAmount())).max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP);
+	}
+
+	private BalanceShareAmount getBalanceShareAmount(TChargingOrder chargingOrder) {
+		if (chargingOrder == null || chargingOrder.getId() == null) {
+			return new BalanceShareAmount(BigDecimal.ZERO, BigDecimal.ZERO);
+		}
+		BigDecimal fallbackBalancePay = getActualBalancePayAmount(chargingOrder);
+		if (fallbackBalancePay.compareTo(BigDecimal.ZERO) <= 0) {
+			return new BalanceShareAmount(BigDecimal.ZERO, BigDecimal.ZERO);
+		}
+		List<Map<String, Object>> rows = this.baseMapper.exchangeCodeBalanceIncomeByOrderIds(Collections.singletonList(chargingOrder.getId()));
+		if (rows == null || rows.isEmpty()) {
+			return new BalanceShareAmount(fallbackBalancePay, BigDecimal.ZERO);
+		}
+		Map<String, Object> row = rows.get(0);
+		BigDecimal principalAmount = getSummaryDecimal(row, "balanceIncome").max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP);
+		BigDecimal giftAmount = getSummaryDecimal(row, "balanceGiftIncome").max(BigDecimal.ZERO).setScale(2, RoundingMode.HALF_UP);
+		return new BalanceShareAmount(principalAmount, giftAmount);
+	}
+
+	private BigDecimal getChargingOrderShareElectricityAmount(TChargingOrder chargingOrder) {
+		if (chargingOrder == null) {
+			return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+		}
+		return money(chargingOrder.getElectrovalence()).add(money(chargingOrder.getWhitelistElectricRefundAmount())).setScale(2, RoundingMode.HALF_UP);
+	}
+
+	private BigDecimal getChargingOrderShareServiceAmount(TChargingOrder chargingOrder) {
+		if (chargingOrder == null) {
+			return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+		}
+		return money(chargingOrder.getServiceCharge()).add(money(chargingOrder.getWhitelistServiceRefundAmount())).setScale(2, RoundingMode.HALF_UP);
+	}
+
+	private BigDecimal getChargingOrderShareBaseAmount(TChargingOrder chargingOrder) {
+		return getChargingOrderShareElectricityAmount(chargingOrder).add(getChargingOrderShareServiceAmount(chargingOrder)).setScale(2, RoundingMode.HALF_UP);
+	}
+
+	private String resolveThirdPayChannel(TChargingOrder chargingOrder) {
+		if ("ICBC".equals(chargingOrder.getSourceName())) {
+			return "ICBC";
+		}
+		if (Integer.valueOf(1).equals(chargingOrder.getRechargePaymentType())) {
+			return "WECHAT";
+		}
+		if (Integer.valueOf(2).equals(chargingOrder.getRechargePaymentType()) || Integer.valueOf(3).equals(chargingOrder.getRechargePaymentType())) {
+			return "ALIPAY";
+		}
+		return "OTHER";
+	}
+
+	private void processIcbcOnlineShare(TChargingOrderShareRecord record, Partner partner) {
+		try {
+			String invalidReason = validateIcbcShareRecord(record, partner);
+			if (StringUtils.hasLength(invalidReason)) {
+				markChargingOrderShare(record, "FAIL", invalidReason);
+				return;
+			}
+			IcbcSubOrderSplitReq req = buildIcbcSubOrderSplitReq(record);
+			record.setRequestJson(JSON.toJSONString(req));
+			record.setOnlineShareStatus("PROCESSING");
+			record.setRetryCount(record.getRetryCount() == null ? 1 : record.getRetryCount() + 1);
+			record.setLastRetryTime(LocalDateTime.now());
+			saveOrUpdateChargingOrderShareRecord(record);
+
+			R<IcbcSubOrderSplitResp> result = icbcPaymentClient.splitSubOrder(req);
+			IcbcSubOrderSplitResp resp = result == null ? null : result.getData();
+			record.setResponseJson(resp == null ? (result == null ? null : JSON.toJSONString(result)) : resp.getRawResponse());
+			if (result == null || result.getCode() != 200 || resp == null) {
+				markChargingOrderShare(record, "FAIL", result == null ? "工行拆分接口无响应" : result.getMsg());
+				return;
+			}
+			record.setIcbcOrderId(resp.getOrderId());
+			record.setSubOrderSeq(resp.getSubOrderSeq());
+			record.setSubOrderTrxid(resp.getSubOrderTrxid());
+			record.setRecResult(resp.getRecResult());
+			if ("SUCCESS".equals(resp.getStatus()) && ("0".equals(resp.getRecResult()) || !StringUtils.hasLength(resp.getRecResult()))) {
+				markChargingOrderShare(record, "SUCCESS", null);
+			} else {
+				markChargingOrderShare(record, "FAIL", "工行拆分未成功:" + firstNotBlank(resp.getRecResult(), resp.getStatus()));
+			}
+		} catch (Exception e) {
+			log.error("工行线上分账处理失败,订单号:{},渠道:{}", record == null ? null : record.getChargingOrderCode(), record == null ? null : record.getPayChannel(), e);
+			if (record != null) {
+				markChargingOrderShare(record, "FAIL", firstNotBlank(e.getMessage(), "工行线上分账处理异常"));
+			}
+		}
+	}
+
+	private void recordChargingOrderShareFailure(TChargingOrder chargingOrder, Site site, Exception e) {
+		try {
+			if (chargingOrder == null || !"ICBC".equals(chargingOrder.getSourceName()) || getActualThirdPayAmount(chargingOrder).compareTo(BigDecimal.ZERO) <= 0) {
+				return;
+			}
+			TChargingOrderShareRecord record = chargingOrderShareRecordMapper.selectOne(new LambdaQueryWrapper<TChargingOrderShareRecord>()
+					.eq(TChargingOrderShareRecord::getChargingOrderCode, chargingOrder.getCode())
+					.eq(TChargingOrderShareRecord::getPayChannel, "ICBC")
+					.eq(TChargingOrderShareRecord::getDelFlag, false)
+					.last("limit 1"));
+			if (record == null) {
+				BigDecimal actualThirdPay = getActualThirdPayAmount(chargingOrder);
+				BigDecimal actualTotalPay = actualThirdPay.add(getBalanceShareAmount(chargingOrder).getPrincipalAmount()).setScale(2, RoundingMode.HALF_UP);
+				BigDecimal shareElectricityAmount = getChargingOrderShareElectricityAmount(chargingOrder);
+				BigDecimal shareServiceAmount = getChargingOrderShareServiceAmount(chargingOrder);
+				record = buildChargingOrderShareRecord(chargingOrder, site, null, "ICBC", actualThirdPay, BigDecimal.ZERO,
+						actualTotalPay.compareTo(BigDecimal.ZERO) > 0 ? actualTotalPay : actualThirdPay, shareElectricityAmount, shareServiceAmount);
+			}
+			record.setNeedOnlineShare(true);
+			markChargingOrderShare(record, "FAIL", firstNotBlank(e.getMessage(), "充电订单分账记录触发异常"));
+		} catch (Exception ex) {
+			log.error("充电订单分账失败补偿记录落库失败,订单号:{}", chargingOrder == null ? null : chargingOrder.getCode(), ex);
+		}
+	}
+
+	private String validateIcbcShareRecord(TChargingOrderShareRecord record, Partner partner) {
+		if (partner == null) {
+			return "站点未配置合作商,无法工行拆分";
+		}
+		if (partner.getElectricityShareRatio() == null && partner.getServiceShareRatio() == null) {
+			return "合作商未配置分成比例,跳过工行拆分";
+		}
+		if (!StringUtils.hasLength(partner.getIcbcSubMerId())) {
+			return "合作商未配置工行二级商户编号";
+		}
+		if (!StringUtils.hasLength(partner.getIcbcSubMerPrtclNo())) {
+			return "合作商未配置工行二级商户协议编号";
+		}
+		if (money(record.getShareAmount()).compareTo(BigDecimal.ZERO) <= 0) {
+			return "工行拆分金额为0";
+		}
+		return null;
+	}
+
+	private BigDecimal calculateShareAmount(BigDecimal amount, BigDecimal ratio) {
+		if (amount == null || ratio == null) {
+			return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+		}
+		return amount.multiply(ratio).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
+	}
+
+	private IcbcSubOrderSplitReq buildIcbcSubOrderSplitReq(TChargingOrderShareRecord record) {
+		IcbcSubOrderSplitReq req = new IcbcSubOrderSplitReq();
+		req.setChargingOrderCode(record.getChargingOrderCode());
+		req.setSeqNo(record.getSeqNo());
+		req.setBusiType(record.getBusiType());
+		req.setOriMerId(record.getOriMerId());
+		req.setOriTrxDate(record.getOriTrxDate() == null ? null : record.getOriTrxDate().toString());
+		req.setSubOrderId(record.getSubOrderId());
+		req.setSubMerId(record.getSubMerId());
+		req.setSubMerPrtclNo(record.getSubMerPrtclNo());
+		req.setClassifyAmt(record.getShareAmount());
+		req.setMerAcct(record.getMerAcct());
+		req.setMerSpInfor("充电订单分账:" + record.getChargingOrderCode());
+		return req;
+	}
+
+	private void markChargingOrderShare(TChargingOrderShareRecord record, String status, String reason) {
+		record.setOnlineShareStatus(status);
+		record.setFailReason(reason);
+		saveOrUpdateChargingOrderShareRecord(record);
+	}
+
+	private void saveOrUpdateChargingOrderShareRecord(TChargingOrderShareRecord record) {
+		if (record.getId() == null) {
+			chargingOrderShareRecordMapper.insert(record);
+		} else {
+			chargingOrderShareRecordMapper.updateById(record);
+		}
+	}
+
+	private String buildIcbcProfitSharing(Site site) {
+		if (site == null || site.getPartnerId() == null) {
+			return null;
+		}
+		R<Partner> partnerResult = partnerClient.getPartnerById(site.getPartnerId());
+		Partner partner = partnerResult == null ? null : partnerResult.getData();
+		if (partner == null || !StringUtils.hasLength(partner.getIcbcSubMerId()) || !StringUtils.hasLength(partner.getIcbcSubMerPrtclNo())) {
+			return null;
+		}
+		if (partner.getElectricityShareRatio() == null && partner.getServiceShareRatio() == null) {
+			return null;
+		}
+		JSONObject data = new JSONObject();
+		data.put("icbcPartnerId", partner.getIcbcPartnerId());
+		data.put("icbcSubMerId", partner.getIcbcSubMerId());
+		data.put("icbcSubMerPrtclNo", partner.getIcbcSubMerPrtclNo());
+		data.put("icbcMerAcct", partner.getIcbcMerAcct());
+		if (partner.getElectricityShareRatio() != null) {
+			data.put("electricityShareRatio", partner.getElectricityShareRatio());
+		}
+		if (partner.getServiceShareRatio() != null) {
+			data.put("serviceShareRatio", partner.getServiceShareRatio());
+		}
+		return data.toJSONString();
+	}
+
+	private static class BalanceShareAmount {
+		private final BigDecimal principalAmount;
+		private final BigDecimal giftAmount;
+
+		private BalanceShareAmount(BigDecimal principalAmount, BigDecimal giftAmount) {
+			this.principalAmount = principalAmount == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : principalAmount.setScale(2, RoundingMode.HALF_UP);
+			this.giftAmount = giftAmount == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : giftAmount.setScale(2, RoundingMode.HALF_UP);
+		}
+
+		private BigDecimal getPrincipalAmount() {
+			return principalAmount;
+		}
+
+		private BigDecimal getGiftAmount() {
+			return giftAmount;
+		}
+	}
+
+	private String firstNotBlank(String... values) {
+		for (String value : values) {
+			if (StringUtils.hasLength(value)) {
+				return value;
+			}
+		}
+		return "";
+	}
 }
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/ChargingSimulationVO.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/ChargingSimulationVO.java
new file mode 100644
index 0000000..922ca1b
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/ChargingSimulationVO.java
@@ -0,0 +1,25 @@
+package com.ruoyi.order.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class ChargingSimulationVO {
+    private Long orderId;
+    private String orderCode;
+    private Integer orderStatus;
+    private BigDecimal rechargeAmount;
+    private BigDecimal chargeAmount;
+    private BigDecimal electricityFee;
+    private BigDecimal serviceFee;
+    private BigDecimal paymentAmount;
+    private BigDecimal refundAmount;
+    private Integer refundStatus;
+    private BigDecimal shareAmount;
+    private String shareStatus;
+    private String icbcOrderId;
+    private String subOrderId;
+    private String subOrderSeq;
+    private String subOrderTrxid;
+}
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/IcbcAdminSubOrderVO.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/IcbcAdminSubOrderVO.java
new file mode 100644
index 0000000..83c1dda
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/vo/IcbcAdminSubOrderVO.java
@@ -0,0 +1,45 @@
+package com.ruoyi.order.vo;
+
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryResp;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@Data
+public class IcbcAdminSubOrderVO {
+    private Long shareRecordId;
+    private String orderCode;
+    private BigDecimal shareAmount;
+    private String onlineShareStatus;
+    private String subOrderId;
+    private String subMerId;
+    private String subMerPrtclNo;
+    private LocalDate oriTrxDate;
+    private String queryStatus;
+    private String bankOrderStatus;
+    private String bankOrderId;
+    private String subOrderSeq;
+    private String subOrderTrxid;
+    private String bankOrderAmount;
+    private String bankRefundAmount;
+    private String rawResponse;
+    private String errorMessage;
+
+    public void applyQuery(IcbcSubOrderQueryResp response) {
+        if (response == null) {
+            queryStatus = "FAIL";
+            errorMessage = "工行子订单查询无响应";
+            return;
+        }
+        queryStatus = response.getStatus();
+        bankOrderStatus = response.getOrderStatus();
+        bankOrderId = response.getOrderId();
+        bankOrderAmount = response.getOrderAmnt();
+        bankRefundAmount = response.getOrderRefundAmnt();
+        rawResponse = response.getRawResponse();
+        if (!"SUCCESS".equals(response.getStatus())) {
+            errorMessage = response.getRawResponse();
+        }
+    }
+}
diff --git a/ruoyi-service/ruoyi-order/src/main/resources/bootstrap.yml b/ruoyi-service/ruoyi-order/src/main/resources/bootstrap.yml
index 60d6be1..f157478 100644
--- a/ruoyi-service/ruoyi-order/src/main/resources/bootstrap.yml
+++ b/ruoyi-service/ruoyi-order/src/main/resources/bootstrap.yml
@@ -12,8 +12,8 @@
     allow-circular-references: true
   profiles:
     # 环境配置
+#    active: dev
     active: dev
-#    active: prod
 ---
 spring:
   config:
diff --git a/ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingBillMapper.xml b/ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingBillMapper.xml
index 344a768..da29af7 100644
--- a/ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingBillMapper.xml
+++ b/ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingBillMapper.xml
@@ -43,6 +43,9 @@
         <if test="req.uid != null ">
             and t1.id = #{req.uid}
         </if>
+        <if test="req.payType != null and req.payType != 0">
+            and t1.payType = #{req.payType}
+        </if>
         <if test="null != req.siteIds and req.siteIds.size()>0" >
             and t1.site_id in
             <foreach collection="req.siteIds" close=")" open="(" item="item" separator=",">
@@ -93,6 +96,9 @@
         <if test="req.uid != null ">
             and t1.id = #{req.uid}
         </if>
+        <if test="req.payType != null and req.payType != 0">
+            and t1.payType = #{req.payType}
+        </if>
         <if test="null != req.siteIds and req.siteIds.size()>0" >
             and t1.site_id in
             <foreach collection="req.siteIds" close=")" open="(" item="item" separator=",">
diff --git a/ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml b/ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml
index cd8128d..578d252 100644
--- a/ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml
+++ b/ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml
@@ -387,6 +387,36 @@
 
     </select>
     <select id="charge" resultType="com.ruoyi.order.dto.SixChargingDto">
+        <choose>
+        <when test="partnerView != null and partnerView">
+        SELECT
+        DATE_FORMAT(a.create_time, '%m') AS MONTH,
+        ROUND(SUM(IFNULL(s.electricity_share_amount, 0)), 2) AS electrovalence,
+        ROUND(SUM(IFNULL(s.service_share_amount, 0)), 2) AS service_charge
+        FROM t_charging_order a
+        LEFT JOIN (
+            SELECT charging_order_id,
+            SUM(IFNULL(electricity_share_amount, 0)) AS electricity_share_amount,
+            SUM(IFNULL(service_share_amount, 0)) AS service_share_amount
+            FROM t_charging_order_share_record
+            WHERE del_flag = 0 AND charging_order_id IS NOT NULL
+            GROUP BY charging_order_id
+        ) s ON a.id = s.charging_order_id
+        <where>
+            a.del_flag = 0 and a.recharge_payment_status = 2 and a.status = 5
+            <if test="sixBefore != null">
+                AND a.create_time &gt;= #{sixBefore}
+            </if>
+            <if test="siteIds != null and siteIds.size() > 0">
+                AND a.site_id IN
+                <foreach collection="siteIds" item="siteId" open="(" separator="," close=")">
+                    #{siteId}
+                </foreach>
+            </if>
+        </where>
+        GROUP BY DATE_FORMAT(a.create_time, '%m')
+        </when>
+        <otherwise>
         SELECT
         DATE_FORMAT(a.create_time, '%m') AS MONTH,
         SUM(IFNULL(b.period_electric_price, 0)) AS electrovalence,
@@ -414,10 +444,39 @@
         </where>
         GROUP BY
         DATE_FORMAT(a.create_time, '%m')
+        </otherwise>
+        </choose>
 
 
     </select>
     <select id="circle" resultType="com.ruoyi.order.dto.SixCircleDto">
+        <choose>
+        <when test="partnerView != null and partnerView">
+        SELECT
+        a.site_id as siteId,
+        ROUND(SUM(IFNULL(s.share_amount, 0)), 2) AS paymentAmount
+        FROM t_charging_order a
+        LEFT JOIN (
+            SELECT charging_order_id, SUM(IFNULL(share_amount, 0)) AS share_amount
+            FROM t_charging_order_share_record
+            WHERE del_flag = 0 AND charging_order_id IS NOT NULL
+            GROUP BY charging_order_id
+        ) s ON a.id = s.charging_order_id
+        <where>
+            a.del_flag = 0 and a.recharge_payment_status = 2 and a.status = 5
+            <if test="siteIds != null and siteIds.size() > 0">
+                AND a.site_id IN
+                <foreach collection="siteIds" item="siteId" open="(" separator="," close=")">
+                    #{siteId}
+                </foreach>
+            </if>
+            <if test="sixBefore != null">
+                AND a.create_time &gt;= #{sixBefore}
+            </if>
+        </where>
+        GROUP BY a.site_id
+        </when>
+        <otherwise>
         SELECT
         a.site_id as siteId,
         ROUND(SUM(CASE
@@ -441,26 +500,44 @@
             </if>
         </where>
         GROUP BY a.site_id
+        </otherwise>
+        </choose>
 
 
     </select>
     <select id="countAll" resultType="java.util.Map">
         SELECT
+        <choose>
+        <when test="partnerView != null and partnerView">
+        ROUND(SUM(IFNULL(s.electricity_share_amount, 0)), 2) AS electrovalence,
+        ROUND(SUM(IFNULL(s.service_share_amount, 0)), 2) AS service_charge,
+        ROUND(SUM(IFNULL(s.share_amount, 0)), 2) AS commission_amount,
+        0 AS sharing_amount
+        </when>
+        <otherwise>
         ROUND(SUM(IFNULL(b.period_electric_price, 0)), 2) AS electrovalence,
-        ROUND(SUM(CASE
-        WHEN a.order_source = 2 THEN IFNULL(b.period_service_price, 0) * 0.8
-        ELSE IFNULL(b.period_service_price, 0)
-        END), 2) AS service_charge,
-        ROUND(SUM(CASE
-        WHEN a.order_source = 2 THEN 0
-        ELSE IFNULL(b.period_electric_price, 0) + IFNULL(b.period_service_price, 0)
-        END) * 0.006, 2) AS commission_amount,
-        ROUND(SUM(CASE
-        WHEN a.order_source = 2 THEN IFNULL(b.period_service_price, 0) * 0.2
-        ELSE 0
-        END), 2) AS sharing_amount
+        ROUND(SUM(CASE WHEN a.order_source = 2 THEN IFNULL(b.period_service_price, 0) * 0.8 ELSE IFNULL(b.period_service_price, 0) END), 2) AS service_charge,
+        ROUND(SUM(IFNULL(s.share_amount, 0)), 2) AS commission_amount,
+        ROUND(SUM(CASE WHEN a.order_source = 2 THEN IFNULL(b.period_service_price, 0) * 0.2 ELSE 0 END), 2) AS sharing_amount
+        </otherwise>
+        </choose>
         FROM t_charging_order a
-        LEFT JOIN t_charging_order_accounting_strategy b ON a.id = b.charging_order_id
+        LEFT JOIN (
+            SELECT charging_order_id,
+            SUM(IFNULL(period_electric_price, 0)) AS period_electric_price,
+            SUM(IFNULL(period_service_price, 0)) AS period_service_price
+            FROM t_charging_order_accounting_strategy
+            GROUP BY charging_order_id
+        ) b ON a.id = b.charging_order_id
+        LEFT JOIN (
+            SELECT charging_order_id,
+            SUM(IFNULL(electricity_share_amount, 0)) AS electricity_share_amount,
+            SUM(IFNULL(service_share_amount, 0)) AS service_share_amount,
+            SUM(IFNULL(share_amount, 0)) AS share_amount
+            FROM t_charging_order_share_record
+            WHERE del_flag = 0 AND charging_order_id IS NOT NULL
+            GROUP BY charging_order_id
+        ) s ON a.id = s.charging_order_id
         WHERE a.del_flag = 0 AND a.recharge_payment_status = 2 AND a.status = 5
         <if test="siteIds != null and siteIds.size() > 0">
             AND a.site_id IN
@@ -1029,6 +1106,8 @@
         a.start_charge_seq as startChargeSeq,
         a.charging_gun_id as chargingGunId,
         a.order_source as orderSource,
+        a.source_name as sourceName,
+        a.recharge_serial_number as rechargeSerialNumber,
         a.`code`,
         a.order_type as orderType,
         d.`name` as terminalName,
@@ -1257,6 +1336,8 @@
     <select id="chargingListSum" resultType="map">
         select
         a.id,
+        a.siteId,
+        a.order_source as orderSource,
         sum(a.charging_capacity) as electronic,
         sum(a.period_electric_price) as electrovalence,
         sum(CASE WHEN a.order_source = 2 THEN a.period_service_price * 0.8 ELSE a.period_service_price END) as serviceCharge,
@@ -1274,7 +1355,7 @@
         sum(CASE WHEN a.type = 3 THEN a.charging_capacity ELSE 0 END) as ping,
         sum(CASE WHEN a.type = 4 THEN a.charging_capacity ELSE 0 END) as gu
         from (
-            select co.id,coas.type, coas.charging_capacity, coas.period_electric_price, coas.period_service_price, co.order_source
+            select co.id, co.site_id as siteId, coas.type, coas.charging_capacity, coas.period_electric_price, coas.period_service_price, co.order_source
             from t_charging_order_accounting_strategy coas
             left join t_charging_order co on (coas.charging_order_id = co.id)
             where co.del_flag = 0 and co.recharge_payment_status = 2
@@ -1321,7 +1402,7 @@
                 and co.end_time between #{endTime1} and #{endTime2}
             </if>
             and co.start_time IS NOT NULL and co.end_time is not null
-        ) as a group by a.id
+        ) as a group by a.id, a.siteId, a.order_source
     </select>
 
 
@@ -2231,6 +2312,59 @@
         ) as aa
     </select>
 
+    <select id="chargingOrderShareIncomeByOrderIds" resultType="map">
+        select
+        charging_order_id as orderId,
+        sum(IFNULL(channel_pay_amount, 0) - IFNULL(share_amount, 0)) as platformIncome,
+        sum(IFNULL(share_amount, 0)) as partnerIncome
+        from `charging_pile_order`.t_charging_order_share_record
+        where del_flag = 0 and charging_order_id is not null
+        <if test="orderIds != null and orderIds.size()>0">
+            and charging_order_id in
+            <foreach collection="orderIds" item="item" index="index" separator="," open="(" close=")">
+                #{item}
+            </foreach>
+        </if>
+        group by charging_order_id
+    </select>
+
+    <select id="chargingOrderShareIncomeByPayChannel" resultType="map">
+        select
+        s.pay_channel as payChannel,
+        sum(IFNULL(s.share_amount, 0)) as shareAmount
+        from t_charging_order_share_record s
+        inner join t_charging_order o on s.charging_order_id = o.id
+        where s.del_flag = 0
+        and s.charging_order_id is not null
+        and o.del_flag = 0
+        and o.recharge_payment_status = 2
+        and o.status = 5
+        <if test="sixBefore != null">
+            and o.create_time &gt;= #{sixBefore}
+        </if>
+        <if test="siteIds != null and siteIds.size() > 0">
+            and o.site_id in
+            <foreach collection="siteIds" item="siteId" open="(" separator="," close=")">
+                #{siteId}
+            </foreach>
+        </if>
+        group by s.pay_channel
+    </select>
+
+    <select id="chargingOrderShareIncomeSummaryByOrderIds" resultType="map">
+        select
+        sum(IFNULL(channel_pay_amount, 0) - IFNULL(share_amount, 0)) as platformIncomeTotal,
+        sum(IFNULL(share_amount, 0)) as partnerIncomeTotal
+        from `charging_pile_order`.t_charging_order_share_record
+        where del_flag = 0 and charging_order_id is not null
+        <if test="orderIds != null and orderIds.size()>0">
+            and charging_order_id in
+            <foreach collection="orderIds" item="item" index="index" separator="," open="(" close=")">
+                #{item}
+            </foreach>
+        </if>
+    </select>
+
     <select id="chargingOrderSummaryOrderIds" resultType="java.lang.Long">
         select b.id
         from `charging_pile_order`.t_charging_order b
diff --git a/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/ChargingSimulationSupportTest.java b/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/ChargingSimulationSupportTest.java
new file mode 100644
index 0000000..13d2754
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/ChargingSimulationSupportTest.java
@@ -0,0 +1,49 @@
+package com.ruoyi.order.service.impl;
+
+import com.ruoyi.order.api.model.AccountingStrategyDetailOrder;
+import com.ruoyi.order.api.vo.TransactionRecordMessageVO;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ChargingSimulationSupportTest {
+
+    @Test
+    void buildsOneKwhRecordForSelectedTariffPeriod() {
+        AccountingStrategyDetailOrder first = detail(1, 1, "00:00", "12:00");
+        AccountingStrategyDetailOrder second = detail(2, 3, "12:00", "23:59");
+        LocalDateTime endTime = LocalDateTime.of(2026, 7, 11, 15, 30, 0);
+
+        ChargingSimulationSupport.PreparedSimulation prepared = ChargingSimulationSupport.prepare(
+                "CD202607110001",
+                Arrays.asList(first, second),
+                new BigDecimal("0.03"),
+                new BigDecimal("0.02"),
+                endTime);
+
+        assertEquals(second, prepared.getTariff());
+        assertEquals(new BigDecimal("0.03"), second.getElectrovalence());
+        assertEquals(new BigDecimal("0.02"), second.getServiceCharge());
+        assertEquals(new BigDecimal("0.02"), second.getCostServiceCharge());
+
+        TransactionRecordMessageVO record = prepared.getRecord();
+        assertEquals("CD202607110001", record.getTransaction_serial_number());
+        assertEquals(BigDecimal.ONE, record.getTotal_electricity());
+        assertEquals(BigDecimal.ONE, record.getLoss_total_electricity());
+        assertEquals(BigDecimal.ONE, record.getFlat_charge2());
+        assertEquals(new BigDecimal("0.05"), record.getPayment_amount());
+    }
+
+    private AccountingStrategyDetailOrder detail(int id, int type, String start, String end) {
+        AccountingStrategyDetailOrder detail = new AccountingStrategyDetailOrder();
+        detail.setId(id);
+        detail.setType(type);
+        detail.setStartTime(start);
+        detail.setEndTime(end);
+        return detail;
+    }
+}
diff --git a/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/IcbcAdminFundOperationSupportTest.java b/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/IcbcAdminFundOperationSupportTest.java
new file mode 100644
index 0000000..41d9cb3
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/IcbcAdminFundOperationSupportTest.java
@@ -0,0 +1,159 @@
+package com.ruoyi.order.service.impl;
+
+import com.ruoyi.order.api.model.TChargingOrder;
+import com.ruoyi.order.api.model.TChargingOrderShareRecord;
+import com.ruoyi.payment.api.vo.IcbcRefundReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundReq;
+import org.junit.jupiter.api.Test;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class IcbcAdminFundOperationSupportTest {
+
+    @Test
+    void rejectsNonIcbcOrder() {
+        TChargingOrder order = paidOrder();
+        order.setSourceName("ALIPAY");
+
+        IllegalArgumentException error = assertThrows(IllegalArgumentException.class,
+                () -> IcbcAdminFundOperationSupport.validateOrder(order));
+
+        assertEquals("仅支持工商银行支付订单", error.getMessage());
+    }
+
+    @Test
+    void buildsMainOrderRefundFromBusinessOrder() {
+        IcbcRefundReq request = IcbcAdminFundOperationSupport.buildRefundRequest(
+                paidOrder(), new BigDecimal("0.12"), "ICBCADMINREFUND001");
+
+        assertEquals("CD202607110001", request.getOutTradeNo());
+        assertEquals("231011990056000012345", request.getOrderId());
+        assertEquals("ICBCADMINREFUND001", request.getOutRefundNo());
+        assertEquals(new BigDecimal("0.12"), request.getRefundAmount());
+        assertEquals("管理后台工行退款", request.getReason());
+    }
+
+    @Test
+    void buildsConsumptionSubOrderQueryFromShareRecord() {
+        IcbcSubOrderQueryReq request = IcbcAdminFundOperationSupport.buildSubOrderQueryRequest(
+                paidOrder(), shareRecord());
+
+        assertEquals("2", request.getOrderNoType());
+        assertEquals("CD202607110001", request.getMerOrderId());
+        assertEquals("231011990056", request.getPrtiId());
+        assertEquals("1", request.getQueryType());
+        assertEquals("ICBCSUB202607110001", request.getMerTrnscSerno());
+        assertEquals("231011990055", request.getSubPrtiId());
+    }
+
+    @Test
+    void buildsSubOrderRefundFromSelectedShareRecord() {
+        IcbcSubOrderRefundReq request = IcbcAdminFundOperationSupport.buildSubOrderRefundRequest(
+                paidOrder(), shareRecord(), new BigDecimal("0.03"),
+                "ICBCSUBREFUND001", "ICBCSUBREFUNDSERIAL001");
+
+        assertEquals("CD202607110001", request.getChargingOrderCode());
+        assertEquals("CD202607110001", request.getSeqNo());
+        assertEquals("1", request.getBusiType());
+        assertEquals("231011990056", request.getOriMerId());
+        assertEquals("2026-07-11", request.getOriTrxDate());
+        assertEquals("ICBCSUB202607110001", request.getSubOrderId());
+        assertEquals("231011990055", request.getSubMerId());
+        assertEquals("2310119900550201", request.getSubMerPrtclNo());
+        assertEquals(new BigDecimal("0.03"), request.getRefundAmount());
+    }
+
+    @Test
+    void rejectsAmountWithMoreThanTwoDecimalPlaces() {
+        IllegalArgumentException error = assertThrows(IllegalArgumentException.class,
+                () -> IcbcAdminFundOperationSupport.requireAmount(new BigDecimal("0.004")));
+
+        assertEquals("操作金额最多保留两位小数", error.getMessage());
+    }
+
+    @Test
+    void rejectsDeletedOrUnsuccessfulShareRecord() {
+        TChargingOrderShareRecord deleted = shareRecord();
+        deleted.setDelFlag(true);
+        IllegalArgumentException deletedError = assertThrows(IllegalArgumentException.class,
+                () -> IcbcAdminFundOperationSupport.buildSubOrderRefundRequest(
+                        paidOrder(), deleted, new BigDecimal("0.01"), "REFUND1", "SERIAL1"));
+        assertEquals("分账记录已删除", deletedError.getMessage());
+
+        TChargingOrderShareRecord failed = shareRecord();
+        failed.setOnlineShareStatus("FAIL");
+        IllegalArgumentException failedError = assertThrows(IllegalArgumentException.class,
+                () -> IcbcAdminFundOperationSupport.buildSubOrderRefundRequest(
+                        paidOrder(), failed, new BigDecimal("0.01"), "REFUND2", "SERIAL2"));
+        assertEquals("仅支持已成功的工行分账记录退款", failedError.getMessage());
+    }
+
+    @Test
+    void rejectsIncompleteSuccessfulShareRecord() {
+        TChargingOrderShareRecord record = shareRecord();
+        record.setSubMerPrtclNo(null);
+
+        IllegalArgumentException error = assertThrows(IllegalArgumentException.class,
+                () -> IcbcAdminFundOperationSupport.buildSubOrderRefundRequest(
+                        paidOrder(), record, new BigDecimal("0.01"), "REFUND3", "SERIAL3"));
+
+        assertEquals("分账记录缺少工行退款参数", error.getMessage());
+    }
+
+    @Test
+    void reusesUnknownRefundSerialOnlyForTheSameAmount() {
+        String pending = IcbcAdminFundOperationSupport.buildPendingRefundValue(
+                "ICBCADMINREFUND001", new BigDecimal("0.04"));
+
+        assertEquals("ICBCADMINREFUND001", IcbcAdminFundOperationSupport.resolveRefundNo(
+                pending, new BigDecimal("0.04"), "ICBCADMINREFUND002"));
+
+        IllegalArgumentException error = assertThrows(IllegalArgumentException.class,
+                () -> IcbcAdminFundOperationSupport.resolveRefundNo(
+                        pending, new BigDecimal("0.03"), "ICBCADMINREFUND003"));
+        assertEquals("上一笔退款结果未知,请按原金额重试", error.getMessage());
+    }
+
+    @Test
+    void keepsUnknownRetrySerialUntilAConfirmedSuccess() {
+        assertFalse(IcbcAdminFundOperationSupport.shouldClearPendingRefund(true, "FAIL", true));
+        assertTrue(IcbcAdminFundOperationSupport.shouldClearPendingRefund(false, "FAIL", true));
+        assertTrue(IcbcAdminFundOperationSupport.shouldClearPendingRefund(true, "SUCCESS", true));
+        assertFalse(IcbcAdminFundOperationSupport.shouldClearPendingRefund(false, "FAIL", false));
+    }
+
+    private TChargingOrder paidOrder() {
+        TChargingOrder order = new TChargingOrder();
+        order.setId(100L);
+        order.setCode("CD202607110001");
+        order.setSourceName("ICBC");
+        order.setRechargePaymentStatus(2);
+        order.setRechargeSerialNumber("231011990056000012345");
+        return order;
+    }
+
+    private TChargingOrderShareRecord shareRecord() {
+        TChargingOrderShareRecord record = new TChargingOrderShareRecord();
+        record.setId(200L);
+        record.setChargingOrderId(100L);
+        record.setChargingOrderCode("CD202607110001");
+        record.setPayChannel("ICBC");
+        record.setDelFlag(false);
+        record.setOnlineShareStatus("SUCCESS");
+        record.setSeqNo("CD202607110001");
+        record.setBusiType("1");
+        record.setOriMerId("231011990056");
+        record.setOriTrxDate(LocalDate.of(2026, 7, 11));
+        record.setSubOrderId("ICBCSUB202607110001");
+        record.setSubMerId("231011990055");
+        record.setSubMerPrtclNo("2310119900550201");
+        return record;
+    }
+}
diff --git a/ruoyi-service/ruoyi-other/src/main/resources/bootstrap.yml b/ruoyi-service/ruoyi-other/src/main/resources/bootstrap.yml
index 8f7006c..3535253 100644
--- a/ruoyi-service/ruoyi-other/src/main/resources/bootstrap.yml
+++ b/ruoyi-service/ruoyi-other/src/main/resources/bootstrap.yml
@@ -13,7 +13,7 @@
   profiles:
     # 环境配置
 #    active: dev
-    active: prod
+    active: dev
 ---
 spring:
   config:
@@ -23,7 +23,7 @@
     nacos:
       discovery:
         # 服务注册地址
-        server-addr: 192.168.110.169:8848
+        server-addr: 192.168.110.80:8848
         service: ${spring.application.name}
         group: DEFAULT_GROUP
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
@@ -31,7 +31,7 @@
         password: nacos
       config:
         # 配置中心地址
-        server-addr: 192.168.110.169:8848
+        server-addr: 192.168.110.80:8848
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
         group: DEFAULT_GROUP
         name: ${spring.application.name}
diff --git a/ruoyi-service/ruoyi-payment/lib/InfosecCrypto_Java1_02_JDK14+.jar b/ruoyi-service/ruoyi-payment/lib/InfosecCrypto_Java1_02_JDK14+.jar
new file mode 100644
index 0000000..353497a
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/lib/InfosecCrypto_Java1_02_JDK14+.jar
Binary files differ
diff --git a/ruoyi-service/ruoyi-payment/lib/commons-logging-1.2.jar b/ruoyi-service/ruoyi-payment/lib/commons-logging-1.2.jar
new file mode 100644
index 0000000..2cd945a
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/lib/commons-logging-1.2.jar
Binary files differ
diff --git a/ruoyi-service/ruoyi-payment/lib/hsm-software-share-1.0.5.jar b/ruoyi-service/ruoyi-payment/lib/hsm-software-share-1.0.5.jar
new file mode 100644
index 0000000..486426b
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/lib/hsm-software-share-1.0.5.jar
Binary files differ
diff --git a/ruoyi-service/ruoyi-payment/lib/icbc-api-sdk-cop-io.jar b/ruoyi-service/ruoyi-payment/lib/icbc-api-sdk-cop-io.jar
new file mode 100644
index 0000000..ca9738e
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/lib/icbc-api-sdk-cop-io.jar
Binary files differ
diff --git a/ruoyi-service/ruoyi-payment/lib/icbc-api-sdk-cop.jar b/ruoyi-service/ruoyi-payment/lib/icbc-api-sdk-cop.jar
new file mode 100644
index 0000000..3c8a72e
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/lib/icbc-api-sdk-cop.jar
Binary files differ
diff --git a/ruoyi-service/ruoyi-payment/lib/icbc-ca.jar b/ruoyi-service/ruoyi-payment/lib/icbc-ca.jar
new file mode 100644
index 0000000..870b73b
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/lib/icbc-ca.jar
Binary files differ
diff --git a/ruoyi-service/ruoyi-payment/lib/readme.md b/ruoyi-service/ruoyi-payment/lib/readme.md
new file mode 100644
index 0000000..a86d9eb
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/lib/readme.md
@@ -0,0 +1,14 @@
+### jar包说明
+commons-logging-1.2.jar----------------与日志记录相关
+icbc-api-sdk-cop.jar-------------------sdk基础jar包
+icbc-api-sdk-cop-io.jar----------------sdk各接口输入输出
+icbc-ca.jar----------------------------SHA1&SHA256签名工具包
+InfosecCrypto_Java1_02_JDK14+.jar------SHA1&SHA256签名工具包依赖
+
+### jar包使用说明
+1.支持java版本1.5及以上
+2.icbc-api-sdk-cop.jar、icbc-api-sdk-cop-io.jar为必须jar包
+3.icbc-ca.jar、InfosecCrypto_Java1_02_JDK14+.jar工具包升级后兼容SHA256和SHA1算法。
+
+### 版本升级
+版本升级说明请参考update-info.md
\ No newline at end of file
diff --git a/ruoyi-service/ruoyi-payment/lib/update-info.md b/ruoyi-service/ruoyi-payment/lib/update-info.md
new file mode 100644
index 0000000..5674d9e
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/lib/update-info.md
@@ -0,0 +1,2 @@
+## 20201113
+1. 升级SHA256签名工具包icbc-ca.jar、InfosecCrypto_Java1_02_JDK14+.jar
\ No newline at end of file
diff --git a/ruoyi-service/ruoyi-payment/pom.xml b/ruoyi-service/ruoyi-payment/pom.xml
index eda3a8e..2b57139 100644
--- a/ruoyi-service/ruoyi-payment/pom.xml
+++ b/ruoyi-service/ruoyi-payment/pom.xml
@@ -133,6 +133,48 @@
             <groupId>com.ruoyi</groupId>
             <artifactId>ruoyi-api-order</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.icbc.sdk</groupId>
+            <artifactId>commons-logging</artifactId>
+            <version>1.2</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/lib/commons-logging-1.2.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.icbc.sdk</groupId>
+            <artifactId>hsm-software-share</artifactId>
+            <version>1.0.5</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/lib/hsm-software-share-1.0.5.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.icbc.sdk</groupId>
+            <artifactId>icbc-api-sdk-cop-io</artifactId>
+            <version>1.0.0</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/lib/icbc-api-sdk-cop-io.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.icbc.sdk</groupId>
+            <artifactId>icbc-api-sdk-cop</artifactId>
+            <version>1.0.0</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/lib/icbc-api-sdk-cop.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.icbc.sdk</groupId>
+            <artifactId>icbc-ca</artifactId>
+            <version>1.0.0</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/lib/icbc-ca.jar</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>com.icbc.sdk</groupId>
+            <artifactId>infosec-crypto</artifactId>
+            <version>1.0.2</version>
+            <scope>system</scope>
+            <systemPath>${project.basedir}/lib/InfosecCrypto_Java1_02_JDK14+.jar</systemPath>
+        </dependency>
     </dependencies>
 
     <build>
@@ -141,6 +183,9 @@
             <plugin>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <includeSystemScope>true</includeSystemScope>
+                </configuration>
                 <executions>
                     <execution>
                         <goals>
diff --git a/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/controller/IcbcPayController.java b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/controller/IcbcPayController.java
new file mode 100644
index 0000000..52bdbb1
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/controller/IcbcPayController.java
@@ -0,0 +1,327 @@
+package com.ruoyi.payment.controller;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.order.api.feignClient.ChargingOrderClient;
+import com.ruoyi.payment.api.vo.IcbcPaymentReq;
+import com.ruoyi.payment.api.vo.IcbcPaymentResp;
+import com.ruoyi.payment.api.vo.IcbcRefundReq;
+import com.ruoyi.payment.api.vo.IcbcRefundResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderSplitReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderSplitResp;
+import com.ruoyi.payment.icbc.config.IcbcPayProperties;
+import com.ruoyi.payment.icbc.service.IcbcPayService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.BufferedReader;
+import java.math.BigDecimal;
+import java.io.PrintWriter;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Enumeration;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 工行统一支付入口。
+ */
+@Slf4j
+@RestController
+@RequestMapping("/icbc")
+public class IcbcPayController {
+    private static final String TEST_ORDER_PREFIX = "ICBCTEST";
+    private static final String TEST_SUB_MER_ID = "231011990055";
+    private static final String TEST_SUB_MER_PRTCL_NO = "2310119900550201";
+
+    @Resource
+    private IcbcPayService icbcPayService;
+
+    @Resource
+    private IcbcPayProperties icbcPayProperties;
+
+    @Resource
+    private ChargingOrderClient chargingOrderClient;
+
+    @PostMapping("/payment")
+    public R<IcbcPaymentResp> payment(@RequestBody IcbcPaymentReq req) {
+        log.info("工行支付请求入参:{}", JSON.toJSONString(req));
+        IcbcPaymentResp resp = icbcPayService.payment(req);
+        log.info("工行支付返回前端:{}", JSON.toJSONString(resp));
+        return R.ok(resp);
+    }
+
+    @PostMapping("/test/payment")
+    public R<IcbcPaymentResp> testPayment(@RequestBody(required = false) IcbcPaymentReq req) {
+        log.info("工行测试支付请求入参:{}", JSON.toJSONString(req));
+        IcbcPaymentReq paymentReq = req == null ? new IcbcPaymentReq() : req;
+        paymentReq.setOutTradeNo(TEST_ORDER_PREFIX + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")));
+        if (paymentReq.getAmount() == null || paymentReq.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
+            paymentReq.setAmount(new BigDecimal("0.01"));
+        }
+        if (paymentReq.getSubject() == null || paymentReq.getSubject().trim().isEmpty()) {
+            paymentReq.setSubject("工行测试支付");
+        }
+        if (paymentReq.getBody() == null || paymentReq.getBody().trim().isEmpty()) {
+            paymentReq.setBody("工行测试支付");
+        }
+        if (paymentReq.getPaymentType() == null) {
+            paymentReq.setPaymentType(1);
+        }
+        log.info("工行测试支付实际下单参数:{}", JSON.toJSONString(paymentReq));
+        IcbcPaymentResp resp = icbcPayService.payment(paymentReq);
+        log.info("工行测试支付返回前端:{}", JSON.toJSONString(resp));
+        return R.ok(resp);
+    }
+
+    @PostMapping("/test/query")
+    public R<IcbcPaymentResp> testQuery(@RequestBody Map<String, Object> req) {
+        String outTradeNo = getString(req, "outTradeNo");
+        log.info("工行测试查询请求入参:{}", JSON.toJSONString(req));
+        IcbcPaymentResp resp = icbcPayService.query(outTradeNo);
+        log.info("工行测试查询返回前端:{}", JSON.toJSONString(resp));
+        return R.ok(resp);
+    }
+
+    @PostMapping("/test/refund")
+    public R<IcbcRefundResp> testRefund(@RequestBody(required = false) IcbcRefundReq req) {
+        req = req == null ? new IcbcRefundReq() : req;
+        log.info("工行测试退款请求入参:{}", JSON.toJSONString(req));
+        if (req.getRefundAmount() == null || req.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) {
+            req.setRefundAmount(new BigDecimal("0.01"));
+        }
+        if (req.getTotalAmount() == null || req.getTotalAmount().compareTo(BigDecimal.ZERO) <= 0) {
+            req.setTotalAmount(req.getRefundAmount());
+        }
+        if (req.getOutRefundNo() == null || req.getOutRefundNo().trim().isEmpty()) {
+            req.setOutRefundNo("ICBCREFUND" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")));
+        }
+        if (req.getReason() == null || req.getReason().trim().isEmpty()) {
+            req.setReason("工行测试退款");
+        }
+        log.info("工行测试退款实际参数:{}", JSON.toJSONString(req));
+        IcbcRefundResp resp = icbcPayService.refund(req);
+        log.info("工行测试退款返回前端:{}", JSON.toJSONString(resp));
+        return R.ok(resp);
+    }
+
+    @PostMapping("/test/sub-order/split")
+    public R<IcbcSubOrderSplitResp> testSplitSubOrder(@RequestBody(required = false) IcbcSubOrderSplitReq req) {
+        req = req == null ? new IcbcSubOrderSplitReq() : req;
+        log.info("工行测试分账请求入参:{}", JSON.toJSONString(req));
+        if (req.getSubOrderId() == null || req.getSubOrderId().trim().isEmpty()) {
+            req.setSubOrderId("ICBCSUB" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")));
+        }
+        if (req.getSeqNo() == null || req.getSeqNo().trim().isEmpty()) {
+            req.setSeqNo(req.getChargingOrderCode());
+        }
+        if (req.getBusiType() == null || req.getBusiType().trim().isEmpty()) {
+            req.setBusiType("1");
+        }
+        if (req.getOriTrxDate() == null || req.getOriTrxDate().trim().isEmpty()) {
+            req.setOriTrxDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+        }
+        if (req.getClassifyAmt() == null || req.getClassifyAmt().compareTo(BigDecimal.ZERO) <= 0) {
+            req.setClassifyAmt(new BigDecimal("0.01"));
+        }
+        if (req.getSubMerId() == null || req.getSubMerId().trim().isEmpty()) {
+            req.setSubMerId(TEST_SUB_MER_ID);
+        }
+        if (req.getSubMerPrtclNo() == null || req.getSubMerPrtclNo().trim().isEmpty()) {
+            req.setSubMerPrtclNo(TEST_SUB_MER_PRTCL_NO);
+        }
+        if (req.getOriMerId() == null || req.getOriMerId().trim().isEmpty()) {
+            req.setOriMerId(icbcPayProperties.getMerId());
+        }
+        log.info("工行测试分账实际参数:{}", JSON.toJSONString(req));
+        IcbcSubOrderSplitResp resp = icbcPayService.splitSubOrder(req);
+        log.info("工行测试分账返回前端:{}", JSON.toJSONString(resp));
+        return R.ok(resp);
+    }
+
+    @PostMapping("/test/sub-order/refund")
+    public R<IcbcSubOrderRefundResp> testRefundSubOrder(@RequestBody(required = false) IcbcSubOrderRefundReq req) {
+        req = req == null ? new IcbcSubOrderRefundReq() : req;
+        log.info("工行测试分账退款请求入参:{}", JSON.toJSONString(req));
+        if (req.getSeqNo() == null || req.getSeqNo().trim().isEmpty()) {
+            req.setSeqNo(req.getChargingOrderCode());
+        }
+        if (req.getBusiType() == null || req.getBusiType().trim().isEmpty()) {
+            req.setBusiType("1");
+        }
+        if (req.getOriTrxDate() == null || req.getOriTrxDate().trim().isEmpty()) {
+            req.setOriTrxDate(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+        }
+        if (req.getRefundAmount() == null || req.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) {
+            req.setRefundAmount(new BigDecimal("0.01"));
+        }
+        if (req.getRetSubOrderId() == null || req.getRetSubOrderId().trim().isEmpty()) {
+            req.setRetSubOrderId("ICBCSUBREFUND" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")));
+        }
+        if (req.getOuttrxSerialNo() == null || req.getOuttrxSerialNo().trim().isEmpty()) {
+            req.setOuttrxSerialNo("ICBCSUBREFUNDNO" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")));
+        }
+        if (req.getSubMerId() == null || req.getSubMerId().trim().isEmpty()) {
+            req.setSubMerId(TEST_SUB_MER_ID);
+        }
+        if (req.getSubMerPrtclNo() == null || req.getSubMerPrtclNo().trim().isEmpty()) {
+            req.setSubMerPrtclNo(TEST_SUB_MER_PRTCL_NO);
+        }
+        if (req.getOriMerId() == null || req.getOriMerId().trim().isEmpty()) {
+            req.setOriMerId(icbcPayProperties.getMerId());
+        }
+        log.info("工行测试分账退款实际参数:{}", JSON.toJSONString(req));
+        IcbcSubOrderRefundResp resp = icbcPayService.refundSubOrder(req);
+        log.info("工行测试分账退款返回前端:{}", JSON.toJSONString(resp));
+        return R.ok(resp);
+    }
+
+    @PostMapping("/query")
+    public R<IcbcPaymentResp> query(@RequestParam("outTradeNo") String outTradeNo) {
+        return R.ok(icbcPayService.query(outTradeNo));
+    }
+
+    @PostMapping("/close")
+    public R<IcbcPaymentResp> close(@RequestParam("outTradeNo") String outTradeNo) {
+        return R.ok(icbcPayService.close(outTradeNo));
+    }
+
+    @PostMapping("/refund")
+    public R<IcbcRefundResp> refund(@RequestBody IcbcRefundReq req) {
+        return R.ok(icbcPayService.refund(req));
+    }
+
+    @PostMapping("/sub-order/split")
+    public R<IcbcSubOrderSplitResp> splitSubOrder(@RequestBody IcbcSubOrderSplitReq req) {
+        return R.ok(icbcPayService.splitSubOrder(req));
+    }
+
+    @PostMapping("/sub-order/refund")
+    public R<IcbcSubOrderRefundResp> refundSubOrder(@RequestBody IcbcSubOrderRefundReq req) {
+        return R.ok(icbcPayService.refundSubOrder(req));
+    }
+
+    @PostMapping("/sub-order/query")
+    public R<IcbcSubOrderQueryResp> querySubOrder(@RequestBody IcbcSubOrderQueryReq req) {
+        return R.ok(icbcPayService.querySubOrder(req));
+    }
+
+    @RequestMapping(value = "/callBack", method = {RequestMethod.GET, RequestMethod.POST})
+    public void callBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
+        Map<String, String[]> requestParams = new LinkedHashMap<>(request.getParameterMap());
+        String rawBody = requestParams.isEmpty() ? readRequestBody(request) : "";
+        mergeJsonBody(requestParams, rawBody);
+        log.info("工行支付回调原始请求,method:{},uri:{},query:{},contentType:{},params:{},body:{},headers:{}",
+                request.getMethod(),
+                request.getRequestURI(),
+                request.getQueryString(),
+                request.getContentType(),
+                JSON.toJSONString(requestParams),
+                rawBody,
+                summarizeHeaders(request));
+        try {
+            Map<String, String> callback = icbcPayService.parseNotify(requestParams);
+            log.info("工行支付回调解析结果:{}", JSON.toJSONString(callback));
+            dispatchChargingOrderCallback(callback);
+        } catch (Exception e) {
+            log.error("工行支付回调处理失败,已记录原始请求并返回success避免重复通知", e);
+        } finally {
+            writeCallbackResponse(response);
+        }
+    }
+
+    private void dispatchChargingOrderCallback(Map<String, String> callback) {
+        String outTradeNo = callback.get("out_trade_no");
+        String transactionId = callback.get("transaction_id");
+        String attach = callback.get("attach");
+        if (outTradeNo != null && outTradeNo.startsWith(TEST_ORDER_PREFIX)) {
+            log.info("工行测试支付回调成功,订单号:{},流水号:{},支付类型:{}", outTradeNo, transactionId, attach);
+            return;
+        }
+        Integer paymentType = Integer.valueOf(attach);
+        if (Integer.valueOf(1).equals(paymentType)) {
+            chargingOrderClient.chargingOrderWXCallback(outTradeNo, transactionId, attach);
+            return;
+        }
+        if (Integer.valueOf(2).equals(paymentType)) {
+            chargingOrderClient.chargingOrderALICallback(outTradeNo, transactionId, attach);
+            return;
+        }
+        if (Integer.valueOf(3).equals(paymentType)) {
+            chargingOrderClient.chargingOrderALIH5Callback(outTradeNo, transactionId, attach);
+            return;
+        }
+        throw new IllegalArgumentException("工行回调支付类型不支持:" + attach);
+    }
+
+    private String getString(Map<String, Object> req, String key) {
+        if (req == null || req.get(key) == null) {
+            throw new IllegalArgumentException("缺少参数:" + key);
+        }
+        return String.valueOf(req.get(key));
+    }
+
+    private String readRequestBody(HttpServletRequest request) {
+        StringBuilder body = new StringBuilder();
+        try (BufferedReader reader = request.getReader()) {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                body.append(line);
+            }
+        } catch (Exception e) {
+            log.warn("读取工行支付回调body失败", e);
+        }
+        return body.toString();
+    }
+
+    private void mergeJsonBody(Map<String, String[]> requestParams, String rawBody) {
+        if (rawBody == null || rawBody.trim().isEmpty()) {
+            return;
+        }
+        try {
+            JSONObject jsonObject = JSON.parseObject(rawBody);
+            if (jsonObject == null) {
+                return;
+            }
+            for (Map.Entry<String, Object> entry : jsonObject.entrySet()) {
+                if (entry.getValue() != null && !requestParams.containsKey(entry.getKey())) {
+                    requestParams.put(entry.getKey(), new String[]{String.valueOf(entry.getValue())});
+                }
+            }
+        } catch (Exception e) {
+            log.warn("工行支付回调body不是JSON格式,body:{}", rawBody);
+        }
+    }
+
+    private String summarizeHeaders(HttpServletRequest request) {
+        Map<String, String> headers = new LinkedHashMap<>();
+        Enumeration<String> names = request.getHeaderNames();
+        while (names != null && names.hasMoreElements()) {
+            String name = names.nextElement();
+            if ("user-agent".equalsIgnoreCase(name)
+                    || "host".equalsIgnoreCase(name)
+                    || "x-forwarded-for".equalsIgnoreCase(name)
+                    || "x-real-ip".equalsIgnoreCase(name)) {
+                headers.put(name, request.getHeader(name));
+            }
+        }
+        return JSON.toJSONString(headers);
+    }
+
+    private void writeCallbackResponse(HttpServletResponse response) throws Exception {
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType("text/plain;charset=UTF-8");
+        PrintWriter writer = response.getWriter();
+        writer.println("success");
+        writer.flush();
+        writer.close();
+    }
+}
diff --git a/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/domain/IcbcPaymentFlowRecord.java b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/domain/IcbcPaymentFlowRecord.java
new file mode 100644
index 0000000..11e8d2c
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/domain/IcbcPaymentFlowRecord.java
@@ -0,0 +1,31 @@
+package com.ruoyi.payment.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("t_icbc_payment_flow_record")
+public class IcbcPaymentFlowRecord {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private String outTradeNo;
+    private String businessType;
+    private String eventType;
+    private Integer paymentType;
+    private BigDecimal amount;
+    private String icbcOrderId;
+    private String icbcMsgId;
+    private String thirdTradeNo;
+    private String status;
+    private String requestJson;
+    private String responseJson;
+    private String errorMessage;
+    private LocalDateTime createTime;
+    private LocalDateTime updateTime;
+    private Integer delFlag;
+}
diff --git a/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/config/IcbcPayProperties.java b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/config/IcbcPayProperties.java
new file mode 100644
index 0000000..5ea37d0
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/config/IcbcPayProperties.java
@@ -0,0 +1,39 @@
+package com.ruoyi.payment.icbc.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+
+/**
+ * 工行支付配置,实际值从 Nacos 的 icbc.pay 前缀读取。
+ */
+@Data
+@Component
+@Configuration
+@ConfigurationProperties(prefix = "icbc.pay")
+public class IcbcPayProperties {
+    private String appId;
+    private String privateKey;
+    private String icbcPublicKey;
+    private String signType = "RSA2";
+    private String charset = "UTF-8";
+    private String format = "json";
+    private String gatewayUrl;
+    private String orderQueryUrl;
+    private String refundUrl;
+    private String subOrderSplitUrl;
+    private String subOrderRefundUrl;
+    private String subOrderQueryUrl;
+    private String notifyUrl;
+    private String merId;
+    private String merPrtclNo;
+    private String serviceMerId;
+    private String serviceMerPrtclNo;
+    private String tpAppId;
+    private String wxTpAppId;
+    private String aliTpAppId;
+    private String spbillCreateIp;
+    private String profitSharing;
+    private Boolean verifyNotifySign = true;
+}
diff --git a/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/IcbcPayService.java b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/IcbcPayService.java
new file mode 100644
index 0000000..a7d3c43
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/IcbcPayService.java
@@ -0,0 +1,35 @@
+package com.ruoyi.payment.icbc.service;
+
+import com.ruoyi.payment.api.vo.IcbcPaymentReq;
+import com.ruoyi.payment.api.vo.IcbcPaymentResp;
+import com.ruoyi.payment.api.vo.IcbcRefundReq;
+import com.ruoyi.payment.api.vo.IcbcRefundResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderSplitReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderSplitResp;
+
+import java.util.Map;
+
+/**
+ * 工行统一支付服务。
+ */
+public interface IcbcPayService {
+    IcbcPaymentResp payment(IcbcPaymentReq req);
+
+    IcbcPaymentResp query(String outTradeNo);
+
+    IcbcPaymentResp close(String outTradeNo);
+
+    IcbcRefundResp refund(IcbcRefundReq req);
+
+    IcbcSubOrderSplitResp splitSubOrder(IcbcSubOrderSplitReq req);
+
+    IcbcSubOrderRefundResp refundSubOrder(IcbcSubOrderRefundReq req);
+
+    IcbcSubOrderQueryResp querySubOrder(IcbcSubOrderQueryReq req);
+
+    Map<String, String> parseNotify(Map<String, String[]> requestParams);
+}
diff --git a/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/IcbcPaymentFlowRecorder.java b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/IcbcPaymentFlowRecorder.java
new file mode 100644
index 0000000..fd9729f
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/IcbcPaymentFlowRecorder.java
@@ -0,0 +1,92 @@
+package com.ruoyi.payment.icbc.service;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.ruoyi.payment.domain.IcbcPaymentFlowRecord;
+import com.ruoyi.payment.mapper.IcbcPaymentFlowRecordMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Slf4j
+@Service
+public class IcbcPaymentFlowRecorder {
+    @Resource
+    private IcbcPaymentFlowRecordMapper mapper;
+
+    public void record(String eventType,
+                       String outTradeNo,
+                       Integer paymentType,
+                       BigDecimal amount,
+                       Object request,
+                       Object response,
+                       String status,
+                       String errorMessage) {
+        try {
+            String requestJson = toJson(request);
+            String responseJson = toJson(response);
+            JSONObject requestObject = parseObject(requestJson);
+            JSONObject responseObject = parseObject(responseJson);
+            IcbcPaymentFlowRecord record = new IcbcPaymentFlowRecord();
+            record.setOutTradeNo(outTradeNo);
+            record.setBusinessType(outTradeNo != null && outTradeNo.startsWith("ICBCTEST") ? "TEST" : "CHARGING_ORDER");
+            record.setEventType(eventType);
+            record.setPaymentType(paymentType);
+            record.setAmount(amount);
+            record.setIcbcOrderId(firstNotBlank(value(responseObject, "order_id"), value(responseObject, "orderId"),
+                    value(responseObject, "transaction_id"), value(requestObject, "order_id"), value(requestObject, "orderId")));
+            record.setIcbcMsgId(firstNotBlank(value(responseObject, "msg_id"), value(responseObject, "msgId"),
+                    value(requestObject, "msg_id"), value(requestObject, "msgId")));
+            record.setThirdTradeNo(firstNotBlank(value(responseObject, "third_trade_no"), value(responseObject, "thirdTradeNo"),
+                    value(requestObject, "third_trade_no"), value(requestObject, "thirdTradeNo")));
+            record.setStatus(status);
+            record.setRequestJson(requestJson);
+            record.setResponseJson(responseJson);
+            record.setErrorMessage(truncate(errorMessage, 1000));
+            record.setCreateTime(LocalDateTime.now());
+            record.setUpdateTime(LocalDateTime.now());
+            record.setDelFlag(0);
+            mapper.insert(record);
+        } catch (Exception e) {
+            log.warn("工行支付流水审计记录失败,事件:{},订单号:{}", eventType, outTradeNo, e);
+        }
+    }
+
+    private String toJson(Object value) {
+        if (value == null) {
+            return null;
+        }
+        return value instanceof String ? (String) value : JSON.toJSONString(value);
+    }
+
+    private JSONObject parseObject(String value) {
+        if (value == null || value.trim().isEmpty()) {
+            return null;
+        }
+        try {
+            return JSON.parseObject(value);
+        } catch (Exception ignored) {
+            return null;
+        }
+    }
+
+    private String value(JSONObject object, String key) {
+        return object == null ? null : object.getString(key);
+    }
+
+    private String firstNotBlank(String... values) {
+        for (String value : values) {
+            if (value != null && !value.trim().isEmpty()) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    private String truncate(String value, int maxLength) {
+        return value == null || value.length() <= maxLength ? value : value.substring(0, maxLength);
+    }
+}
diff --git a/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/impl/IcbcNotifyParser.java b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/impl/IcbcNotifyParser.java
new file mode 100644
index 0000000..cc028f9
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/impl/IcbcNotifyParser.java
@@ -0,0 +1,54 @@
+package com.ruoyi.payment.icbc.service.impl;
+
+import com.ruoyi.common.core.utils.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+final class IcbcNotifyParser {
+    private IcbcNotifyParser() {
+    }
+
+    static Map<String, String> parseVerified(Map<String, String> params) {
+        String status = firstNotBlank(params.get("return_code"), params.get("orderStatus"), params.get("pay_status"), params.get("status"));
+        if (StringUtils.isNotEmpty(status) && !isPaidStatus(status)) {
+            throw new IllegalArgumentException("工行回调支付状态非成功:" + status);
+        }
+        String outTradeNo = firstNotBlank(params.get("outTradeNo"), params.get("out_trade_no"));
+        if (StringUtils.isEmpty(outTradeNo)) {
+            throw new IllegalArgumentException("工行回调缺少订单号");
+        }
+        String tradeNo = firstNotBlank(params.get("order_id"), params.get("tradeNo"), params.get("trade_no"), params.get("msg_id"));
+        if (StringUtils.isEmpty(tradeNo)) {
+            throw new IllegalArgumentException("工行回调缺少工行订单号");
+        }
+        String attach = firstNotBlank(params.get("attach"), params.get("paymentType"), params.get("payType"));
+        if (StringUtils.isEmpty(attach)) {
+            throw new IllegalArgumentException("工行回调缺少支付类型");
+        }
+        Map<String, String> result = new HashMap<>();
+        result.put("out_trade_no", outTradeNo);
+        result.put("transaction_id", tradeNo);
+        result.put("attach", attach);
+        return result;
+    }
+
+    private static boolean isPaidStatus(String status) {
+        return "0".equalsIgnoreCase(status)
+                || "SUCCESS".equalsIgnoreCase(status)
+                || "PAID".equalsIgnoreCase(status)
+                || "PAY_SUCCESS".equalsIgnoreCase(status);
+    }
+
+    private static String firstNotBlank(String... values) {
+        if (values == null) {
+            return null;
+        }
+        for (String value : values) {
+            if (StringUtils.isNotEmpty(value)) {
+                return value;
+            }
+        }
+        return null;
+    }
+}
diff --git a/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/impl/IcbcPayServiceImpl.java b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/impl/IcbcPayServiceImpl.java
new file mode 100644
index 0000000..20196ab
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/icbc/service/impl/IcbcPayServiceImpl.java
@@ -0,0 +1,668 @@
+package com.ruoyi.payment.icbc.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.icbc.api.DefaultIcbcClient;
+import com.icbc.api.IcbcRequest;
+import com.icbc.api.IcbcResponse;
+import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineOrderqryRequestV2;
+import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2;
+import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineMerrefundRequestV1;
+import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineSersuborderRequestV1;
+import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1;
+import com.icbc.api.request.CardbusinessOrderSuborderqueryRequestV1;
+import com.icbc.api.internal.util.internal.util.fastjson.JSONArray;
+import com.icbc.api.internal.util.internal.util.fastjson.JSONObject;
+import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineOrderqryResponseV2;
+import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV2;
+import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineMerrefundResponseV1;
+import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineSersuborderResponseV1;
+import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineSersuborderrefundResponseV1;
+import com.icbc.api.response.CardbusinessOrderSuborderqueryResponseV1;
+import com.icbc.api.utils.IcbcSignature;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.payment.api.vo.IcbcPaymentReq;
+import com.ruoyi.payment.api.vo.IcbcPaymentResp;
+import com.ruoyi.payment.api.vo.IcbcRefundReq;
+import com.ruoyi.payment.api.vo.IcbcRefundResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderQueryResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderRefundResp;
+import com.ruoyi.payment.api.vo.IcbcSubOrderSplitReq;
+import com.ruoyi.payment.api.vo.IcbcSubOrderSplitResp;
+import com.ruoyi.payment.icbc.config.IcbcPayProperties;
+import com.ruoyi.payment.icbc.service.IcbcPaymentFlowRecorder;
+import com.ruoyi.payment.icbc.service.IcbcPayService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * 工行聚合支付骨架实现。
+ */
+@Slf4j
+@Service
+public class IcbcPayServiceImpl implements IcbcPayService {
+    @Resource
+    private IcbcPayProperties properties;
+    @Resource
+    private IcbcPaymentFlowRecorder flowRecorder;
+
+    @Override
+    public IcbcPaymentResp payment(IcbcPaymentReq req) {
+        IcbcPaymentResp resp = new IcbcPaymentResp();
+        if (req != null) {
+            resp.setOrderId(req.getOutTradeNo());
+        }
+        try {
+            validateCommonConfig();
+            validateConfig("icbc.pay.gatewayUrl", properties.getGatewayUrl());
+            validatePaymentReq(req);
+            CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2 request = new CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2();
+            request.setServiceUrl(properties.getGatewayUrl());
+            request.setBizContent(buildConsumePurchaseBiz(req));
+            flowRecorder.record("PAY_REQUEST", req.getOutTradeNo(), req.getPaymentType(), req.getAmount(),
+                    request.getBizContent(), null, "SENT", null);
+
+            CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV2 response = executeIcbc("聚合支付下单", request);
+            resp.setOrderId(firstNotBlank(response.getOut_trade_no(), req.getOutTradeNo()));
+            resp.setTradeNo(response.getOrder_id());
+            resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
+            resp.setPayData(buildConsumePurchasePayData(response, req.getPaymentType()));
+            resp.setRawResponse(JSON.toJSONString(response));
+            flowRecorder.record("PAY_RESPONSE", resp.getOrderId(), req.getPaymentType(), req.getAmount(),
+                    request.getBizContent(), response, resp.getStatus(), null);
+        } catch (Exception e) {
+            log.error("工行聚合支付调用失败,订单号:{}", req == null ? null : req.getOutTradeNo(), e);
+            resp.setStatus("FAIL");
+            resp.setRawResponse(e.getMessage());
+            flowRecorder.record("PAY_RESPONSE", req == null ? null : req.getOutTradeNo(), req == null ? null : req.getPaymentType(),
+                    req == null ? null : req.getAmount(), req, null, "FAIL", e.getMessage());
+        }
+        return resp;
+    }
+
+    @Override
+    public IcbcPaymentResp query(String outTradeNo) {
+        IcbcPaymentResp resp = new IcbcPaymentResp();
+        resp.setOrderId(outTradeNo);
+        try {
+            validateCommonConfig();
+            validateConfig("icbc.pay.orderQueryUrl", properties.getOrderQueryUrl());
+            CardbusinessAggregatepayB2cOnlineOrderqryRequestV2 request = new CardbusinessAggregatepayB2cOnlineOrderqryRequestV2();
+            request.setServiceUrl(properties.getOrderQueryUrl());
+            request.setBizContent(buildOrderQueryBiz(outTradeNo));
+            CardbusinessAggregatepayB2cOnlineOrderqryResponseV2 response = executeIcbc("聚合支付订单查询", request);
+            resp.setOrderId(firstNotBlank(response.getOut_trade_no(), outTradeNo));
+            resp.setTradeNo(firstNotBlank(response.getOrder_id(), response.getThird_trade_no(), response.getMsgId()));
+            resp.setStatus(firstNotBlank(response.getOrderStatus(), response.getPay_status()));
+            resp.setPayData(JSON.toJSONString(response));
+            resp.setRawResponse(JSON.toJSONString(response));
+            flowRecorder.record("QUERY", outTradeNo, null, null, buildOrderQueryBiz(outTradeNo), response,
+                    response.isSuccess() ? "SUCCESS" : "FAIL", response.isSuccess() ? null : response.getReturnMsg());
+        } catch (Exception e) {
+            log.error("工行支付订单查询失败,订单号:{}", outTradeNo, e);
+            resp.setStatus("QUERY_FAIL");
+            resp.setRawResponse(e.getMessage());
+            flowRecorder.record("QUERY", outTradeNo, null, null, null, null, "FAIL", e.getMessage());
+        }
+        return resp;
+    }
+
+    @Override
+    public IcbcPaymentResp close(String outTradeNo) {
+        IcbcPaymentResp resp = new IcbcPaymentResp();
+        resp.setOrderId(outTradeNo);
+        resp.setStatus("CLOSE_TODO");
+        resp.setRawResponse("ICBC close skeleton: SDK close request is not mapped yet");
+        log.info("工行关闭订单骨架,订单号:{}", outTradeNo);
+        return resp;
+    }
+
+    @Override
+    public IcbcRefundResp refund(IcbcRefundReq req) {
+        IcbcRefundResp resp = new IcbcRefundResp();
+        if (req != null) {
+            resp.setOutTradeNo(req.getOutTradeNo());
+            resp.setOrderId(req.getOrderId());
+            resp.setOutRefundNo(req.getOutRefundNo());
+        }
+        try {
+            validateCommonConfig();
+            validateConfig("icbc.pay.refundUrl", properties.getRefundUrl());
+            if (req == null) {
+                throw new IllegalArgumentException("工行退款请求不能为空");
+            }
+            CardbusinessAggregatepayB2cOnlineMerrefundRequestV1 request = new CardbusinessAggregatepayB2cOnlineMerrefundRequestV1();
+            request.setServiceUrl(properties.getRefundUrl());
+            request.setBizContent(buildRefundBiz(req));
+            CardbusinessAggregatepayB2cOnlineMerrefundResponseV1 response = executeIcbc("线上POS退款", request);
+            resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
+            resp.setRawResponse(JSON.toJSONString(response));
+            resp.setOutTradeNo(firstNotBlank(response.getOutTradeNo(), req.getOutTradeNo()));
+            resp.setOrderId(firstNotBlank(response.getOrderId(), req.getOrderId()));
+            resp.setOutRefundNo(firstNotBlank(response.getOuttrxSerialNo(), req.getOutRefundNo()));
+            resp.setRefundId(firstNotBlank(response.getIntrx_serial_no(), response.getOuttrxSerialNo()));
+            resp.setRefundTime(response.getRefund_time());
+            resp.setRefundAmount(firstNotBlank(response.getRealRejectAmt(), response.getRejectAmt(), toFen(req.getRefundAmount())));
+            flowRecorder.record("REFUND", req.getOutTradeNo(), null, req.getRefundAmount(), request.getBizContent(), response,
+                    resp.getStatus(), null);
+        } catch (Exception e) {
+            log.error("工行退款失败,订单号:{},退款单号:{}",
+                    req == null ? null : req.getOutTradeNo(), req == null ? null : req.getOutRefundNo(), e);
+            resp.setStatus("FAIL");
+            resp.setRawResponse(e.getMessage());
+            flowRecorder.record("REFUND", req == null ? null : req.getOutTradeNo(), null, req == null ? null : req.getRefundAmount(),
+                    req, null, "FAIL", e.getMessage());
+        }
+        return resp;
+    }
+
+    @Override
+    public IcbcSubOrderSplitResp splitSubOrder(IcbcSubOrderSplitReq req) {
+        IcbcSubOrderSplitResp resp = new IcbcSubOrderSplitResp();
+        if (req != null) {
+            resp.setChargingOrderCode(req.getChargingOrderCode());
+            resp.setSubOrderId(req.getSubOrderId());
+        }
+        try {
+            validateCommonConfig();
+            validateConfig("icbc.pay.merPrtclNo", properties.getMerPrtclNo());
+            validateConfig("icbc.pay.subOrderSplitUrl", properties.getSubOrderSplitUrl());
+            if (req == null) {
+                throw new IllegalArgumentException("工行分账请求不能为空");
+            }
+            CardbusinessAggregatepayB2cOnlineSersuborderRequestV1 request = new CardbusinessAggregatepayB2cOnlineSersuborderRequestV1();
+            request.setServiceUrl(properties.getSubOrderSplitUrl());
+            request.setBizContent(buildSubOrderSplitBiz(req));
+            CardbusinessAggregatepayB2cOnlineSersuborderResponseV1 response = executeIcbc("B2C小订单拆分", request);
+            resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
+            resp.setRawResponse(JSON.toJSONString(response));
+            fillSplitOutput(resp, response.getOrderSubmitActOutput());
+            flowRecorder.record("SPLIT", req.getChargingOrderCode(), null, req.getClassifyAmt(), request.getBizContent(), response,
+                    resp.getStatus(), null);
+        } catch (Exception e) {
+            log.error("工行小订单拆分失败,订单号:{}", req == null ? null : req.getChargingOrderCode(), e);
+            resp.setStatus("FAIL");
+            resp.setRawResponse(e.getMessage());
+            flowRecorder.record("SPLIT", req == null ? null : req.getChargingOrderCode(), null, req == null ? null : req.getClassifyAmt(),
+                    req, null, "FAIL", e.getMessage());
+        }
+        return resp;
+    }
+
+    @Override
+    public IcbcSubOrderRefundResp refundSubOrder(IcbcSubOrderRefundReq req) {
+        IcbcSubOrderRefundResp resp = new IcbcSubOrderRefundResp();
+        if (req != null) {
+            resp.setChargingOrderCode(req.getChargingOrderCode());
+            resp.setSubOrderId(req.getSubOrderId());
+            resp.setRetSubOrderId(req.getRetSubOrderId());
+            resp.setOuttrxSerialNo(req.getOuttrxSerialNo());
+        }
+        try {
+            validateCommonConfig();
+            validateConfig("icbc.pay.subOrderRefundUrl", properties.getSubOrderRefundUrl());
+            if (req == null) {
+                throw new IllegalArgumentException("工行分账退款请求不能为空");
+            }
+            CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1 request = new CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1();
+            request.setServiceUrl(properties.getSubOrderRefundUrl());
+            request.setBizContent(buildSubOrderRefundBiz(req));
+            CardbusinessAggregatepayB2cOnlineSersuborderrefundResponseV1 response = executeIcbc("B2C小订单拆分退款", request);
+            resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
+            resp.setRawResponse(JSON.toJSONString(response));
+            resp.setOrderId(response.getOrder_id());
+            resp.setSubOrderSeq(response.getSub_order_seq());
+            resp.setSubOrderTrxid(response.getSub_order_trxid());
+            resp.setRefundAmount(toFen(req.getRefundAmount()));
+            flowRecorder.record("SPLIT_REFUND", req.getChargingOrderCode(), null, req.getRefundAmount(), request.getBizContent(), response,
+                    resp.getStatus(), null);
+        } catch (Exception e) {
+            log.error("工行小订单拆分退款失败,订单号:{},子订单号:{},退款流水:{}",
+                    req == null ? null : req.getChargingOrderCode(), req == null ? null : req.getSubOrderId(),
+                    req == null ? null : req.getOuttrxSerialNo(), e);
+            resp.setStatus("FAIL");
+            resp.setRawResponse(e.getMessage());
+            flowRecorder.record("SPLIT_REFUND", req == null ? null : req.getChargingOrderCode(), null,
+                    req == null ? null : req.getRefundAmount(), req, null, "FAIL", e.getMessage());
+        }
+        return resp;
+    }
+
+    @Override
+    public IcbcSubOrderQueryResp querySubOrder(IcbcSubOrderQueryReq req) {
+        IcbcSubOrderQueryResp resp = new IcbcSubOrderQueryResp();
+        try {
+            validateCommonConfig();
+            validateConfig("icbc.pay.subOrderQueryUrl", properties.getSubOrderQueryUrl());
+            if (req == null) {
+                throw new IllegalArgumentException("工行子订单查询请求不能为空");
+            }
+            CardbusinessOrderSuborderqueryRequestV1 request = new CardbusinessOrderSuborderqueryRequestV1();
+            request.setServiceUrl(properties.getSubOrderQueryUrl());
+            request.setBizContent(buildSubOrderQueryBiz(req));
+            CardbusinessOrderSuborderqueryResponseV1 response = executeIcbc("子订单查询", request);
+            resp.setOrderStatus(response.getOrderStatus());
+            resp.setSubOutMerNo(response.getSubOutMerNo());
+            resp.setSubOutAgreeNo(response.getSubOutAgreeNo());
+            resp.setOutTradeNo(response.getOutTradeNo());
+            resp.setOrderId(response.getOrderId());
+            resp.setSubOrderNo(response.getSubOrderNo());
+            resp.setSubMerOrderId(response.getSubMerOrderId());
+            resp.setOrderAmnt(response.getOrderAmnt());
+            resp.setOrderRefundAmnt(response.getOrderRefundAmnt());
+            resp.setPayDate(response.getPayDate());
+            resp.setStatus(response.isSuccess() ? "SUCCESS" : "FAIL");
+            resp.setRawResponse(JSON.toJSONString(response));
+            flowRecorder.record("SPLIT_QUERY", firstNotBlank(req.getMerOrderId(), req.getOrderId(), req.getSubOrderNo()), null, null, request.getBizContent(), response,
+                    resp.getStatus(), null);
+        } catch (Exception e) {
+            log.error("工行子订单查询失败,子订单号:{}", req == null ? null : req.getSubOrderNo(), e);
+            resp.setStatus("FAIL");
+            resp.setRawResponse(e.getMessage());
+            flowRecorder.record("SPLIT_QUERY", req == null ? null : firstNotBlank(req.getMerOrderId(), req.getOrderId(), req.getSubOrderNo()), null, null,
+                    req, null, "FAIL", e.getMessage());
+        }
+        return resp;
+    }
+
+    @Override
+    public Map<String, String> parseNotify(Map<String, String[]> requestParams) {
+        Map<String, String> params = new HashMap<>();
+        for (Map.Entry<String, String[]> entry : requestParams.entrySet()) {
+            String[] values = entry.getValue();
+            params.put(entry.getKey(), values == null || values.length == 0 ? "" : String.join(",", values));
+        }
+        try {
+            if (!verifyNotify(params)) {
+                throw new IllegalArgumentException("工行回调验签失败");
+            }
+            mergeBizContent(params);
+            Map<String, String> result = IcbcNotifyParser.parseVerified(params);
+            flowRecorder.record("PAY_NOTIFY", result.get("out_trade_no"), parseInteger(result.get("attach")), null,
+                    params, result, "SUCCESS", null);
+            return result;
+        } catch (RuntimeException e) {
+            flowRecorder.record("PAY_NOTIFY", firstNotBlank(params.get("out_trade_no"), params.get("outTradeNo")),
+                    parseInteger(firstNotBlank(params.get("attach"), params.get("paymentType"))), null,
+                    params, null, "FAIL", e.getMessage());
+            throw e;
+        }
+    }
+
+    private Integer parseInteger(String value) {
+        try {
+            return StringUtils.isEmpty(value) ? null : Integer.valueOf(value);
+        } catch (NumberFormatException ignored) {
+            return null;
+        }
+    }
+
+    private DefaultIcbcClient buildClient() {
+        return new DefaultIcbcClient(
+                properties.getAppId(),
+                properties.getSignType(),
+                properties.getPrivateKey(),
+                properties.getIcbcPublicKey());
+    }
+
+    private <T extends IcbcResponse> T executeIcbc(String scene, IcbcRequest<T> request) throws Exception {
+        log.info("工行{}请求,url:{},bizContent:{}", scene, request.getServiceUrl(), JSON.toJSONString(request.getBizContent()));
+        T response = buildClient().execute(request, System.currentTimeMillis() + "");
+        log.info("工行{}响应:{}", scene, JSON.toJSONString(response));
+        return response;
+    }
+
+    private void validateCommonConfig() {
+        validateConfig("icbc.pay.appId", properties.getAppId());
+        validateConfig("icbc.pay.privateKey", properties.getPrivateKey());
+        validateConfig("icbc.pay.icbcPublicKey", properties.getIcbcPublicKey());
+        validateConfig("icbc.pay.merId", properties.getMerId());
+    }
+
+    private void validateConfig(String name, String value) {
+        if (StringUtils.isEmpty(value)) {
+            throw new IllegalStateException("工行配置缺失:" + name);
+        }
+    }
+
+    private void validatePaymentReq(IcbcPaymentReq req) {
+        if (req == null) {
+            throw new IllegalArgumentException("工行支付请求不能为空");
+        }
+        validateConfig("outTradeNo", req.getOutTradeNo());
+        if (req.getAmount() == null || req.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
+            throw new IllegalArgumentException("工行支付金额必须大于0");
+        }
+        if (Integer.valueOf(1).equals(req.getPaymentType()) || Integer.valueOf(2).equals(req.getPaymentType())) {
+            validateConfig("icbc.pay." + (Integer.valueOf(1).equals(req.getPaymentType()) ? "wxTpAppId" : "aliTpAppId"), getTpAppId(req.getPaymentType()));
+        }
+        if (Integer.valueOf(1).equals(req.getPaymentType()) || Integer.valueOf(2).equals(req.getPaymentType())) {
+            validateConfig("openId", req.getOpenId());
+        }
+    }
+
+    private CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2Biz buildConsumePurchaseBiz(IcbcPaymentReq req) {
+        CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2Biz biz =
+                new CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2Biz();
+        biz.setMer_id(properties.getMerId());
+        setMerPrtclNoIfPresent(biz::setMer_prtcl_no);
+        biz.setOut_trade_no(req.getOutTradeNo());
+        biz.setIcbc_appid(properties.getAppId());
+        biz.setShop_appid(getTpAppId(req.getPaymentType()));
+        if (Integer.valueOf(1).equals(req.getPaymentType()) || Integer.valueOf(2).equals(req.getPaymentType())) {
+            biz.setOpen_id(req.getOpenId());
+        }
+        setAlipayUnionIdIfNeeded(biz, req);
+        biz.setPay_mode(toIcbcPayMode(req.getPaymentType()));
+        biz.setAccess_type(toIcbcAccessType(req.getPaymentType()));
+        biz.setOrig_date_time(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(new Date()));
+        biz.setDecive_info(buildDeviceInfo(req));
+        biz.setBody(firstNotBlank(req.getBody(), req.getSubject(), "充电充值"));
+        biz.setFee_type("001");
+        biz.setTotal_fee(toFen(req.getAmount()));
+        biz.setSpbill_create_ip(properties.getSpbillCreateIp());
+        biz.setMer_url(StringUtils.isNotEmpty(properties.getNotifyUrl()) ? properties.getNotifyUrl() : req.getNotifyUrl());
+        biz.setReturn_url(req.getReturnUrl());
+        biz.setAttach(String.valueOf(req.getPaymentType()));
+        biz.setNotify_type("HS");
+        biz.setResult_type("0");
+        biz.setOrder_apd_inf(req.getSubject());
+        return biz;
+    }
+
+    private void setAlipayUnionIdIfNeeded(CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV2Biz biz,
+                                          IcbcPaymentReq req) {
+        if (!Integer.valueOf(2).equals(req.getPaymentType()) && !Integer.valueOf(3).equals(req.getPaymentType())) {
+            return;
+        }
+        if (StringUtils.isNotEmpty(req.getUnionId())) {
+            biz.setUnion_id(truncate(req.getUnionId(), 16));
+        }
+    }
+
+    private String buildConsumePurchasePayData(CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV2 response, Integer paymentType) {
+        Map<String, Object> payData = new HashMap<>();
+        payData.put("orderId", response.getOrder_id());
+        payData.put("outTradeNo", response.getOut_trade_no());
+        payData.put("totalAmt", response.getTotal_amt());
+        payData.put("payMode", response.getPay_mode());
+        payData.put("accessType", response.getAccess_type());
+        payData.put("tradeType", response.getTrade_type());
+        payData.put("wxDataPackage", response.getWx_data_package());
+        payData.put("zfbDataPackage", response.getZfb_data_package());
+        payData.put("unionDataPackage", response.getUnion_data_package());
+        if (Integer.valueOf(1).equals(paymentType) && StringUtils.isNotEmpty(response.getWx_data_package())) {
+            payData.put("package", response.getWx_data_package());
+        }
+        if ((Integer.valueOf(2).equals(paymentType) || Integer.valueOf(3).equals(paymentType)) && StringUtils.isNotEmpty(response.getZfb_data_package())) {
+            payData.put("package", response.getZfb_data_package());
+        }
+        return JSON.toJSONString(payData);
+    }
+
+    private CardbusinessAggregatepayB2cOnlineOrderqryRequestV2.CardbusinessAggregatepayB2cOnlineOrderqryRequestV2Biz buildOrderQueryBiz(String outTradeNo) {
+        CardbusinessAggregatepayB2cOnlineOrderqryRequestV2.CardbusinessAggregatepayB2cOnlineOrderqryRequestV2Biz biz =
+                new CardbusinessAggregatepayB2cOnlineOrderqryRequestV2.CardbusinessAggregatepayB2cOnlineOrderqryRequestV2Biz();
+        biz.setOut_trade_no(outTradeNo);
+        biz.setDeal_flag("0");
+        biz.setIcbc_appid(properties.getAppId());
+        biz.setMer_id(properties.getMerId());
+        setMerPrtclNoIfPresent(biz::setMer_prtcl_no);
+        return biz;
+    }
+
+    private CardbusinessAggregatepayB2cOnlineMerrefundRequestV1.CardbusinessAggregatepayB2cOnlineMerrefundRequestV1Biz buildRefundBiz(IcbcRefundReq req) {
+        CardbusinessAggregatepayB2cOnlineMerrefundRequestV1.CardbusinessAggregatepayB2cOnlineMerrefundRequestV1Biz biz =
+                new CardbusinessAggregatepayB2cOnlineMerrefundRequestV1.CardbusinessAggregatepayB2cOnlineMerrefundRequestV1Biz();
+        biz.setOrder_id(req.getOrderId());
+        biz.setOut_trade_no(req.getOutTradeNo());
+        biz.setOuttrx_serial_no(req.getOutRefundNo());
+        biz.setRet_total_amt(toFen(req.getRefundAmount()));
+        biz.setTrnsc_ccy("001");
+        biz.setMer_id(properties.getMerId());
+        setMerPrtclNoIfPresent(biz::setMer_prtcl_no);
+        biz.setIcbc_appid(properties.getAppId());
+        biz.setMer_acct(req.getMerAcct());
+        biz.setOrder_apd_inf(req.getReason());
+        return biz;
+    }
+
+    private CardbusinessAggregatepayB2cOnlineSersuborderRequestV1.CardbusinessAggregatepayB2cOnlineSersuborderRequestV1Biz buildSubOrderSplitBiz(IcbcSubOrderSplitReq req) {
+        CardbusinessAggregatepayB2cOnlineSersuborderRequestV1.CardbusinessAggregatepayB2cOnlineSersuborderRequestV1Biz biz =
+                new CardbusinessAggregatepayB2cOnlineSersuborderRequestV1.CardbusinessAggregatepayB2cOnlineSersuborderRequestV1Biz();
+        JSONArray orderSubmitActInput = new JSONArray();
+        JSONObject item = new JSONObject();
+        item.put("rec_num", "1");
+        item.put("oper_flag", "0");
+        item.put("sub_order_id", req.getSubOrderId());
+        item.put("busi_type", firstNotBlank(req.getBusiType(), "1"));
+        item.put("seq_no", req.getSeqNo());
+        item.put("ori_mer_id", firstNotBlank(req.getOriMerId(), properties.getMerId()));
+        item.put("ori_trx_date", req.getOriTrxDate());
+        item.put("sub_mer_id", req.getSubMerId());
+        item.put("sub_mer_prtcl_no", req.getSubMerPrtclNo());
+        item.put("classify_amt", toFen(req.getClassifyAmt()));
+        item.put("mer_acct", req.getMerAcct());
+        item.put("mer_sp_infor", req.getMerSpInfor());
+        item.put("term_id", req.getTermId());
+        orderSubmitActInput.add(item);
+        biz.setMerId(firstNotBlank(properties.getServiceMerId(), properties.getMerId()));
+        biz.setMerPrtclNo(firstNotBlank(properties.getServiceMerPrtclNo(), properties.getMerPrtclNo()));
+        biz.setIcbcAppid(properties.getAppId());
+        biz.setOrderNum("1");
+        biz.setOrderSubmitActInput(orderSubmitActInput);
+        return biz;
+    }
+
+    private CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1.CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1Biz buildSubOrderRefundBiz(IcbcSubOrderRefundReq req) {
+        CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1.CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1Biz biz =
+                new CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1.CardbusinessAggregatepayB2cOnlineSersuborderrefundRequestV1Biz();
+        biz.setMerId(firstNotBlank(properties.getServiceMerId(), properties.getMerId()));
+        biz.setMerPrtclNo(firstNotBlank(properties.getServiceMerPrtclNo(), properties.getMerPrtclNo()));
+        biz.setIcbcAppid(properties.getAppId());
+        biz.setOperFlag("0");
+        biz.setSubOrderId(req.getSubOrderId());
+        biz.setSeqNo(req.getSeqNo());
+        biz.setBusiType(firstNotBlank(req.getBusiType(), "1"));
+        biz.setOriTrxDate(req.getOriTrxDate());
+        biz.setOriMerId(firstNotBlank(req.getOriMerId(), properties.getMerId()));
+        biz.setSubMerId(req.getSubMerId());
+        biz.setSubMerPrtclNo(req.getSubMerPrtclNo());
+        biz.setClassifyAmt(toFen(req.getRefundAmount()));
+        biz.setRetSubOrderId(req.getRetSubOrderId());
+        biz.setOuttrxSerialNo(req.getOuttrxSerialNo());
+        biz.setRefundSource(req.getRefundSource());
+        biz.setMerAcct(req.getMerAcct());
+        biz.setMerSpInfor(req.getMerSpInfor());
+        biz.setTermId(req.getTermId());
+        return biz;
+    }
+
+    private CardbusinessOrderSuborderqueryRequestV1.CardbusinessOrderSuborderqueryRequestV1Biz buildSubOrderQueryBiz(IcbcSubOrderQueryReq req) {
+        CardbusinessOrderSuborderqueryRequestV1.CardbusinessOrderSuborderqueryRequestV1Biz biz =
+                new CardbusinessOrderSuborderqueryRequestV1.CardbusinessOrderSuborderqueryRequestV1Biz();
+        biz.setIcbcAppid(properties.getAppId());
+        biz.setSerNo(firstNotBlank(req.getSerNo(), properties.getServiceMerId(), properties.getMerId()));
+        biz.setOrderNoType(req.getOrderNoType());
+        biz.setOrderId(req.getOrderId());
+        biz.setMerOrderId(req.getMerOrderId());
+        biz.setPrtiId(firstNotBlank(req.getPrtiId(), properties.getMerId()));
+        biz.setQueryType(req.getQueryType());
+        biz.setSubOrderNo(req.getSubOrderNo());
+        biz.setMerTrnscSerno(req.getMerTrnscSerno());
+        biz.setSubPrtiId(req.getSubPrtiId());
+        return biz;
+    }
+
+    private void fillSplitOutput(IcbcSubOrderSplitResp resp, JSONArray output) {
+        if (output == null || output.isEmpty()) {
+            return;
+        }
+        JSONObject item = output.getJSONObject(0);
+        resp.setRecResult(item.getString("rec_result"));
+        resp.setClassifyAmt(item.getString("classify_amt"));
+        resp.setSubMerId(item.getString("sub_mer_id"));
+        resp.setOrderId(item.getString("order_id"));
+        resp.setSubOrderSeq(item.getString("sub_order_seq"));
+        resp.setSubOrderTrxid(item.getString("sub_order_trxid"));
+        resp.setSubOrderId(firstNotBlank(item.getString("sub_order_id"), resp.getSubOrderId()));
+        if (!"0".equals(resp.getRecResult())) {
+            resp.setStatus("FAIL");
+        }
+    }
+
+    private String toFen(BigDecimal amount) {
+        return amount == null ? "0" : amount.multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP).toPlainString();
+    }
+
+    private String getTpAppId(Integer paymentType) {
+        if (Integer.valueOf(1).equals(paymentType)) {
+            return firstNotBlank(properties.getWxTpAppId(), properties.getTpAppId());
+        }
+        if (isAlipay(paymentType)) {
+            return firstNotBlank(properties.getAliTpAppId(), properties.getTpAppId());
+        }
+        return properties.getTpAppId();
+    }
+
+    private boolean isAlipay(Integer paymentType) {
+        return Integer.valueOf(2).equals(paymentType) || Integer.valueOf(3).equals(paymentType);
+    }
+
+    private void setMerPrtclNoIfPresent(java.util.function.Consumer<String> setter) {
+        if (StringUtils.isNotEmpty(properties.getMerPrtclNo())) {
+            setter.accept(properties.getMerPrtclNo());
+        }
+    }
+
+    private String buildDeviceInfo(IcbcPaymentReq req) {
+        String value = firstNotBlank(req.getOpenId(), req.getUnionId(), "USER") + "_" + firstNotBlank(req.getDeviceInfo(), properties.getSpbillCreateIp(), "MINIPROGRAM");
+        return value.length() > 32 ? value.substring(0, 32) : value;
+    }
+
+    private String toIcbcPayMode(Integer paymentType) {
+        if (Integer.valueOf(1).equals(paymentType)) {
+            return "9";
+        }
+        if (Integer.valueOf(2).equals(paymentType) || Integer.valueOf(3).equals(paymentType)) {
+            return "10";
+        }
+        return "9";
+    }
+
+    private String toIcbcAccessType(Integer paymentType) {
+        if (Integer.valueOf(3).equals(paymentType)) {
+            return "7";
+        }
+        return "9";
+    }
+
+    private boolean verifyNotify(Map<String, String> params) {
+        if (Boolean.FALSE.equals(properties.getVerifyNotifySign())) {
+            return true;
+        }
+        String sign = params.get("sign");
+        if (StringUtils.isEmpty(sign)) {
+            return false;
+        }
+        try {
+            String content = buildSignContent(params);
+            String signType = firstNotBlank(params.get("sign_type"), properties.getSignType());
+            String publicKey = normalizePublicKey(properties.getIcbcPublicKey());
+            log.info("工行回调验签参数,signType:{},publicKeyLength:{},content:{}", signType, publicKey == null ? 0 : publicKey.length(), content);
+            return IcbcSignature.verify(content, signType, sign, properties.getCharset(), publicKey);
+        } catch (Exception e) {
+            log.error("工行回调验签异常", e);
+            return false;
+        }
+    }
+
+    private void mergeBizContent(Map<String, String> params) {
+        String bizContent = params.get("biz_content");
+        if (StringUtils.isEmpty(bizContent)) {
+            return;
+        }
+        try {
+            JSONObject biz = JSONObject.parseObject(bizContent);
+            if (biz == null) {
+                return;
+            }
+            for (Map.Entry<String, Object> entry : biz.entrySet()) {
+                if (entry.getValue() != null && !params.containsKey(entry.getKey())) {
+                    params.put(entry.getKey(), String.valueOf(entry.getValue()));
+                }
+            }
+        } catch (Exception e) {
+            log.warn("工行回调biz_content解析失败,bizContent:{}", bizContent, e);
+        }
+    }
+
+    private String normalizePublicKey(String publicKey) {
+        if (publicKey == null) {
+            return null;
+        }
+        String normalized = publicKey;
+        String lower = normalized.toLowerCase();
+        int start = lower.indexOf("start public key");
+        int end = lower.indexOf("end public key");
+        if (start >= 0 && end > start) {
+            normalized = normalized.substring(start + "start public key".length(), end);
+        }
+        return normalized
+                .replace("-----BEGIN PUBLIC KEY-----", "")
+                .replace("-----END PUBLIC KEY-----", "")
+                .replace("#", "")
+                .replaceAll("\\s+", "");
+    }
+
+    private String buildSignContent(Map<String, String> params) {
+        Map<String, String> sorted = new TreeMap<>(params);
+        String api = sorted.remove("api");
+        sorted.remove("sign");
+        StringBuilder content = new StringBuilder(firstNotBlank(api, ""));
+        content.append('?');
+        boolean hasParam = false;
+        for (Map.Entry<String, String> entry : sorted.entrySet()) {
+            if (StringUtils.isEmpty(entry.getValue())) {
+                continue;
+            }
+            if (hasParam) {
+                content.append('&');
+            }
+            hasParam = true;
+            content.append(entry.getKey()).append('=').append(entry.getValue());
+        }
+        return content.toString();
+    }
+
+    private boolean isPaidStatus(String status) {
+        return Arrays.asList("1", "2", "SUCCESS", "PAID", "PAY_SUCCESS", "TRADE_SUCCESS").contains(status);
+    }
+
+    private String firstNotBlank(String... values) {
+        for (String value : values) {
+            if (StringUtils.isNotEmpty(value)) {
+                return value;
+            }
+        }
+        return "";
+    }
+
+    private String truncate(String value, int maxLength) {
+        if (StringUtils.isEmpty(value) || value.length() <= maxLength) {
+            return value;
+        }
+        return value.substring(0, maxLength);
+    }
+}
diff --git a/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/mapper/IcbcPaymentFlowRecordMapper.java b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/mapper/IcbcPaymentFlowRecordMapper.java
new file mode 100644
index 0000000..438a9ee
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/src/main/java/com/ruoyi/payment/mapper/IcbcPaymentFlowRecordMapper.java
@@ -0,0 +1,7 @@
+package com.ruoyi.payment.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.payment.domain.IcbcPaymentFlowRecord;
+
+public interface IcbcPaymentFlowRecordMapper extends BaseMapper<IcbcPaymentFlowRecord> {
+}
diff --git a/ruoyi-service/ruoyi-payment/src/main/resources/bootstrap.yml b/ruoyi-service/ruoyi-payment/src/main/resources/bootstrap.yml
index 291aca3..165a3ef 100644
--- a/ruoyi-service/ruoyi-payment/src/main/resources/bootstrap.yml
+++ b/ruoyi-service/ruoyi-payment/src/main/resources/bootstrap.yml
@@ -9,7 +9,7 @@
   profiles:
     # 环境配置
 #    active: dev
-    active: prod
+    active: dev
 ---
 spring:
   config:
@@ -19,7 +19,7 @@
     nacos:
       discovery:
         # 服务注册地址
-        server-addr: 127.0.0.1:8848
+        server-addr: 192.168.110.80:8848
         service: ${spring.application.name}
         group: DEFAULT_GROUP
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
@@ -27,7 +27,7 @@
         password: nacos
       config:
         # 配置中心地址
-        server-addr: 127.0.0.1:8848
+        server-addr: 192.168.110.80:8848
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
         group: DEFAULT_GROUP
         name: ${spring.application.name}
diff --git a/ruoyi-service/ruoyi-payment/src/test/java/com/ruoyi/payment/icbc/service/impl/IcbcNotifyParserTest.java b/ruoyi-service/ruoyi-payment/src/test/java/com/ruoyi/payment/icbc/service/impl/IcbcNotifyParserTest.java
new file mode 100644
index 0000000..6de3884
--- /dev/null
+++ b/ruoyi-service/ruoyi-payment/src/test/java/com/ruoyi/payment/icbc/service/impl/IcbcNotifyParserTest.java
@@ -0,0 +1,27 @@
+package com.ruoyi.payment.icbc.service.impl;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class IcbcNotifyParserTest {
+
+    @Test
+    void usesIcbcOrderIdAsRefundTransactionId() {
+        Map<String, String> params = new HashMap<>();
+        params.put("out_trade_no", "CD202607110001");
+        params.put("order_id", "231011990056000522607080001058");
+        params.put("msg_id", "050200126189883020726302842");
+        params.put("attach", "1");
+        params.put("return_code", "0");
+
+        Map<String, String> result = IcbcNotifyParser.parseVerified(params);
+
+        assertEquals("CD202607110001", result.get("out_trade_no"));
+        assertEquals("231011990056000522607080001058", result.get("transaction_id"));
+        assertEquals("1", result.get("attach"));
+    }
+}
diff --git a/ruoyi-visual/ruoyi-monitor/src/main/resources/bootstrap.yml b/ruoyi-visual/ruoyi-monitor/src/main/resources/bootstrap.yml
index e2b56b8..81f8192 100644
--- a/ruoyi-visual/ruoyi-monitor/src/main/resources/bootstrap.yml
+++ b/ruoyi-visual/ruoyi-monitor/src/main/resources/bootstrap.yml
@@ -8,7 +8,7 @@
   profiles:
     # 环境配置
     #    active: dev
-    active: prod
+    active: dev
 ---
 spring:
   config:
@@ -18,7 +18,7 @@
     nacos:
       discovery:
         # 服务注册地址
-        server-addr: 127.0.0.1:8848
+        server-addr: 192.168.110.80:8848
         service: ${spring.application.name}
         group: DEFAULT_GROUP
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
@@ -26,7 +26,7 @@
         password: nacos
       config:
         # 配置中心地址
-        server-addr: 127.0.0.1:8848
+        server-addr: 192.168.110.80:8848
         namespace: b5290bc2-e3aa-4988-8a7d-9c07e4e073cb
         group: DEFAULT_GROUP
         name: ${spring.application.name}
diff --git a/sql/20260529_icbc_partner_profit_sharing.sql b/sql/20260529_icbc_partner_profit_sharing.sql
new file mode 100644
index 0000000..9c806f0
--- /dev/null
+++ b/sql/20260529_icbc_partner_profit_sharing.sql
@@ -0,0 +1,7 @@
+ALTER TABLE `t_partner`
+  ADD COLUMN `icbc_partner_id` varchar(64) NULL DEFAULT NULL COMMENT '工行合作商ID' AFTER `rate`,
+  ADD COLUMN `icbc_sub_mer_id` varchar(20) NULL DEFAULT NULL COMMENT '工行二级商户编号' AFTER `icbc_partner_id`,
+  ADD COLUMN `icbc_sub_mer_prtcl_no` varchar(20) NULL DEFAULT NULL COMMENT '工行二级商户协议编号' AFTER `icbc_sub_mer_id`,
+  ADD COLUMN `icbc_mer_acct` varchar(34) NULL DEFAULT NULL COMMENT '工行直接分账商户清算账号' AFTER `icbc_sub_mer_prtcl_no`,
+  ADD COLUMN `electricity_share_ratio` decimal(5, 2) NULL DEFAULT NULL COMMENT '电费分成比例' AFTER `icbc_mer_acct`,
+  ADD COLUMN `service_share_ratio` decimal(5, 2) NULL DEFAULT NULL COMMENT '服务费分成比例' AFTER `electricity_share_ratio`;
diff --git a/sql/20260530_charging_order_share_record.sql b/sql/20260530_charging_order_share_record.sql
new file mode 100644
index 0000000..3ddd606
--- /dev/null
+++ b/sql/20260530_charging_order_share_record.sql
@@ -0,0 +1,44 @@
+CREATE TABLE IF NOT EXISTS `t_charging_order_share_record` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `charging_order_id` bigint NULL DEFAULT NULL COMMENT '充电订单ID',
+  `charging_order_code` varchar(64) NOT NULL COMMENT '充电订单号',
+  `site_id` int NULL DEFAULT NULL COMMENT '站点ID',
+  `partner_id` int NULL DEFAULT NULL COMMENT '合作商ID',
+  `pay_channel` varchar(16) NOT NULL COMMENT '支付渠道:BALANCE/ICBC/WECHAT/ALIPAY/WHITELIST/OTHER',
+  `channel_pay_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '该渠道最终实收本金金额,元,不含赠送',
+  `channel_gift_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '该渠道赠送抵扣金额,元,不参与分账',
+  `channel_pay_ratio` decimal(10, 6) NOT NULL DEFAULT 0.000000 COMMENT '该渠道实收占订单最终实收比例',
+  `channel_electricity_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '该渠道分摊电费金额,元',
+  `channel_service_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '该渠道分摊服务费金额,元',
+  `electricity_share_ratio` decimal(5, 2) NULL DEFAULT NULL COMMENT '电费分成比例',
+  `service_share_ratio` decimal(5, 2) NULL DEFAULT NULL COMMENT '服务费分成比例',
+  `electricity_share_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '电费分账金额,元',
+  `service_share_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '服务费分账金额,元',
+  `share_amount` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '分账总金额,元',
+  `need_online_share` tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否需要线上分账',
+  `online_share_status` varchar(16) NOT NULL DEFAULT 'RECORDED' COMMENT '线上分账状态:RECORDED/PROCESSING/SUCCESS/FAIL/SKIP',
+  `seq_no` varchar(50) NULL DEFAULT NULL COMMENT '工行原交易消费订单号',
+  `busi_type` varchar(1) NULL DEFAULT NULL COMMENT '工行原交易消费订单类型:1外部订单号,2工行订单号',
+  `ori_mer_id` varchar(20) NULL DEFAULT NULL COMMENT '工行原交易商户编号',
+  `ori_trx_date` date NULL DEFAULT NULL COMMENT '工行原交易日期',
+  `sub_order_id` varchar(50) NULL DEFAULT NULL COMMENT '工行商户自定义子订单号',
+  `sub_mer_id` varchar(20) NULL DEFAULT NULL COMMENT '工行二级商户编号',
+  `sub_mer_prtcl_no` varchar(20) NULL DEFAULT NULL COMMENT '工行二级商户协议编号',
+  `mer_acct` varchar(34) NULL DEFAULT NULL COMMENT '工行直接分账商户清算账号',
+  `icbc_order_id` varchar(64) NULL DEFAULT NULL COMMENT '工行返回订单号',
+  `sub_order_seq` varchar(32) NULL DEFAULT NULL COMMENT '工行子订单序号',
+  `sub_order_trxid` varchar(64) NULL DEFAULT NULL COMMENT '工行子订单交易流水',
+  `rec_result` varchar(8) NULL DEFAULT NULL COMMENT '工行记录处理结果',
+  `request_json` text NULL COMMENT '线上分账请求报文',
+  `response_json` text NULL COMMENT '线上分账响应报文',
+  `fail_reason` varchar(512) NULL DEFAULT NULL COMMENT '失败或跳过原因',
+  `retry_count` int NOT NULL DEFAULT 0 COMMENT '重试次数',
+  `last_retry_time` datetime NULL DEFAULT NULL COMMENT '最近重试时间',
+  `create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
+  `del_flag` int NULL DEFAULT 0 COMMENT '删除状态(0=否,1=是)',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_order_channel` (`charging_order_code`, `pay_channel`),
+  KEY `idx_share_status` (`online_share_status`),
+  KEY `idx_share_sub_order_id` (`sub_order_id`),
+  KEY `idx_share_partner` (`partner_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='充电订单全渠道分账记录';
diff --git a/sql/20260711_icbc_payment_flow_record.sql b/sql/20260711_icbc_payment_flow_record.sql
new file mode 100644
index 0000000..86c1cba
--- /dev/null
+++ b/sql/20260711_icbc_payment_flow_record.sql
@@ -0,0 +1,22 @@
+CREATE TABLE IF NOT EXISTS `t_icbc_payment_flow_record` (
+  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `out_trade_no` varchar(64) DEFAULT NULL COMMENT '业务订单号',
+  `business_type` varchar(32) DEFAULT NULL COMMENT '业务类型',
+  `event_type` varchar(32) NOT NULL COMMENT '事件类型',
+  `payment_type` int DEFAULT NULL COMMENT '支付类型',
+  `amount` decimal(12,2) DEFAULT NULL COMMENT '金额(元)',
+  `icbc_order_id` varchar(64) DEFAULT NULL COMMENT '工行订单号',
+  `icbc_msg_id` varchar(64) DEFAULT NULL COMMENT '工行消息号',
+  `third_trade_no` varchar(64) DEFAULT NULL COMMENT '第三方支付流水号',
+  `status` varchar(32) DEFAULT NULL COMMENT '处理状态',
+  `request_json` longtext COMMENT '请求报文',
+  `response_json` longtext COMMENT '响应报文',
+  `error_message` varchar(1000) DEFAULT NULL COMMENT '异常信息',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  `del_flag` tinyint NOT NULL DEFAULT 0 COMMENT '删除标记',
+  PRIMARY KEY (`id`),
+  KEY `idx_icbc_flow_out_trade_no` (`out_trade_no`),
+  KEY `idx_icbc_flow_order_id` (`icbc_order_id`),
+  KEY `idx_icbc_flow_event_time` (`event_type`,`create_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工行支付流程审计记录';
diff --git a/sql/charging_pile_service.sql b/sql/charging_pile_service.sql
index 6381502..fae42cf 100644
--- a/sql/charging_pile_service.sql
+++ b/sql/charging_pile_service.sql
@@ -1897,6 +1897,12 @@
   `role_id` int NULL DEFAULT NULL COMMENT '角色id',
   `logo_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT 'logo地址',
   `rate` double(10, 2) NULL DEFAULT NULL COMMENT '费率',
+  `icbc_partner_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '工行合作商ID',
+  `icbc_sub_mer_id` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '工行二级商户编号',
+  `icbc_sub_mer_prtcl_no` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '工行二级商户协议编号',
+  `icbc_mer_acct` varchar(34) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '工行直接分账商户清算账号',
+  `electricity_share_ratio` decimal(5, 2) NULL DEFAULT NULL COMMENT '电费分成比例',
+  `service_share_ratio` decimal(5, 2) NULL DEFAULT NULL COMMENT '服务费分成比例',
   `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '注册地址',
   `phone_one` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '合作商电话1',
   `phone_two` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '合作商电话2',

--
Gitblit v1.7.1