From 64464568546b827fc5778a20efb347bf56ba34ac Mon Sep 17 00:00:00 2001
From: puzhibing <393733352@qq.com>
Date: 星期三, 30 九月 2026 11:05:05 +0800
Subject: [PATCH] 修改充电流程

---
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/support/ChargeAccountSupport.java |  159 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 159 insertions(+), 0 deletions(-)

diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/support/ChargeAccountSupport.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/support/ChargeAccountSupport.java
index b57c6fd..695e368 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/support/ChargeAccountSupport.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/support/ChargeAccountSupport.java
@@ -1,5 +1,6 @@
 package com.ruoyi.order.support;
 
+import com.alibaba.fastjson2.JSON;
 import com.ruoyi.account.api.feignClient.CompanyAllocateRecordClient;
 import com.ruoyi.chargingPile.api.dto.TBillingTemplateDTO;
 import com.ruoyi.chargingPile.api.feignClient.AccountingStrategyDetailClient;
@@ -8,6 +9,7 @@
 import com.ruoyi.chargingPile.api.model.TBillingTemplateDetail;
 import com.ruoyi.common.core.domain.R;
 import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.order.api.model.TChargingOrder;
 import com.ruoyi.other.api.domain.TGroupCompany;
 import com.ruoyi.other.api.dto.ChargeAuthorizationDTO;
 import com.ruoyi.other.api.dto.ChargeTimeRange;
@@ -163,6 +165,61 @@
 
         public String getFailReason() {
             return failReason;
+        }
+    }
+
+    /**
+     * 企业优惠快照载体(建单时固化 / 结算与补偿时解析的结果):
+     * 与既有 CompanyPreferentialDTO + TBillingTemplateDTO 调用点同构(getPreferentialType/getServiceDiscountRate/
+     * getBillingTemplateId 方法名保持一致),三链路优惠计算代码仅需把数据来源从 Feign 实时查询改为本解析结果,
+     * 计算逻辑不变。
+     */
+    public static class PreferentialSnapshot {
+        /** 优惠方式(1=不优惠,2=按比例优惠,3=按优惠单价收费;null=无优惠配置) */
+        private final Integer preferentialType;
+        /** 服务费优惠比例(%,方式2) */
+        private final BigDecimal serviceDiscountRate;
+        /** 计费模板ID(方式3) */
+        private final Integer billingTemplateId;
+        /** 方式3计费模板(订单快照反序列化或实时查询结果) */
+        private final TBillingTemplateDTO template;
+        /** 订单快照为空、本次为实时查询所得(结算链路需回填订单快照列,保证补偿重算口径一致) */
+        private final boolean fromRealtimeQuery;
+
+        public PreferentialSnapshot(Integer preferentialType, BigDecimal serviceDiscountRate, Integer billingTemplateId,
+                                    TBillingTemplateDTO template, boolean fromRealtimeQuery) {
+            this.preferentialType = preferentialType;
+            this.serviceDiscountRate = serviceDiscountRate;
+            this.billingTemplateId = billingTemplateId;
+            this.template = template;
+            this.fromRealtimeQuery = fromRealtimeQuery;
+        }
+
+        public Integer getPreferentialType() {
+            return preferentialType;
+        }
+
+        public BigDecimal getServiceDiscountRate() {
+            return serviceDiscountRate;
+        }
+
+        public Integer getBillingTemplateId() {
+            return billingTemplateId;
+        }
+
+        public TBillingTemplateDTO getTemplate() {
+            return template;
+        }
+
+        public boolean isFromRealtimeQuery() {
+            return fromRealtimeQuery;
+        }
+
+        /**
+         * 是否需要把本次解析结果回填到订单快照列(订单快照为空且实时解析到了配置)
+         */
+        public boolean needBackfill() {
+            return fromRealtimeQuery && preferentialType != null;
         }
     }
 
@@ -335,6 +392,108 @@
     }
 
     /**
+     * 建单时固化企业优惠快照:读取当前生效的公司优惠设置(方式3连带加载计费模板明细),
+     * 供调用方写入订单 preferential_* 快照列;未配置或查询失败返回 null(结算时按实时查询兼容)。
+     */
+    public PreferentialSnapshot snapshotPreferential(Integer companyId) {
+        CompanyPreferentialDTO preferential = loadCompanyPreferential(companyId);
+        if (preferential == null) {
+            return null;
+        }
+        return new PreferentialSnapshot(preferential.getPreferentialType(), preferential.getServiceDiscountRate(),
+                preferential.getBillingTemplateId(), loadBillingTemplate(preferential), false);
+    }
+
+    /**
+     * 建单时把当前生效的企业优惠设置写入订单快照字段(含方式3模板明细JSON,未配置/查询失败时不落快照)
+     */
+    public void fillPreferentialSnapshot(TChargingOrder order, Integer companyId) {
+        if (order == null) {
+            return;
+        }
+        PreferentialSnapshot snapshot = snapshotPreferential(companyId);
+        if (snapshot == null || snapshot.getPreferentialType() == null) {
+            return;
+        }
+        order.setPreferentialType(snapshot.getPreferentialType());
+        order.setServiceDiscountRate(snapshot.getServiceDiscountRate());
+        order.setBillingTemplateId(snapshot.getBillingTemplateId());
+        order.setBillingTemplateSnapshot(serializeTemplateSnapshot(snapshot.getTemplate()));
+        order.setPreferentialSnapshotTime(LocalDateTime.now());
+    }
+
+    /**
+     * 结算/补偿时解析企业优惠:订单快照优先(建单后策略变更不影响本单口径),
+     * 快照为空(历史订单)才实时查询并标记回填;返回 null 表示无优惠配置(按不优惠结算)。
+     */
+    public PreferentialSnapshot resolvePreferential(TChargingOrder order) {
+        if (order != null && order.getPreferentialType() != null) {
+            TBillingTemplateDTO template = null;
+            if (Integer.valueOf(PREFERENTIAL_TYPE_TEMPLATE).equals(order.getPreferentialType())) {
+                template = parseTemplateSnapshot(order.getBillingTemplateSnapshot());
+                if (template == null && order.getBillingTemplateId() != null) {
+                    // 快照JSON缺失(历史订单/序列化失败)按模板ID实时查询兜底,尽量不倒退回原价
+                    template = loadTemplateById(order.getBillingTemplateId());
+                }
+            }
+            return new PreferentialSnapshot(order.getPreferentialType(), order.getServiceDiscountRate(),
+                    order.getBillingTemplateId(), template, false);
+        }
+        CompanyPreferentialDTO preferential = loadCompanyPreferential(order == null ? null : order.getCompanyId());
+        if (preferential == null) {
+            return null;
+        }
+        return new PreferentialSnapshot(preferential.getPreferentialType(), preferential.getServiceDiscountRate(),
+                preferential.getBillingTemplateId(), loadBillingTemplate(preferential), true);
+    }
+
+    /**
+     * 计费模板明细序列化为订单快照JSON(方式3建单固化:模板后续被改动/删除不影响本单结算与补偿口径)
+     */
+    public static String serializeTemplateSnapshot(TBillingTemplateDTO template) {
+        if (template == null) {
+            return null;
+        }
+        try {
+            return JSON.toJSONString(template);
+        } catch (Exception ex) {
+            log.error("[充电优惠] 计费模板快照序列化失败 templateId={}", template.getId(), ex);
+            return null;
+        }
+    }
+
+    /**
+     * 订单快照JSON反序列化为计费模板(失败返回 null,由调用方按模板ID实时查询或按原价兜底)
+     */
+    public static TBillingTemplateDTO parseTemplateSnapshot(String snapshotJson) {
+        if (StringUtils.isEmpty(snapshotJson)) {
+            return null;
+        }
+        try {
+            return JSON.parseObject(snapshotJson, TBillingTemplateDTO.class);
+        } catch (Exception ex) {
+            log.error("[充电优惠] 计费模板快照反序列化失败,按模板ID实时查询兜底", ex);
+            return null;
+        }
+    }
+
+    /**
+     * 按模板ID查询计费模板(仅方式3快照兜底使用,取不到时该段按原价结算)
+     */
+    public TBillingTemplateDTO loadTemplateById(Integer templateId) {
+        if (templateId == null) {
+            return null;
+        }
+        try {
+            R<TBillingTemplateDTO> result = billingTemplateClient.getTemplateDetail(templateId);
+            return result == null ? null : result.getData();
+        } catch (Exception ex) {
+            log.error("[充电优惠] 查询计费模板失败,按原价结算 templateId={}", templateId, ex);
+            return null;
+        }
+    }
+
+    /**
      * 服务费按比例优惠:最终单价 = 原价 × (1 - 折扣率/100),折扣率缺失或越界时按原价
      */
     public static BigDecimal applyServiceDiscountRate(BigDecimal servicePrice, BigDecimal discountRate) {

--
Gitblit v1.7.1