|
@@ -345,7 +345,7 @@ public class ParkingWalletFeeService {
|
|
|
* 业务规则:
|
|
* 业务规则:
|
|
|
* 1. 服务费余额取当前登录采购企业的可用服务费余额;
|
|
* 1. 服务费余额取当前登录采购企业的可用服务费余额;
|
|
|
* 2. 收费策略总开关关闭时,不计算预计服务费,最大可购买数量返回 null,表示不做余额约束;
|
|
* 2. 收费策略总开关关闭时,不计算预计服务费,最大可购买数量返回 null,表示不做余额约束;
|
|
|
- * 3. 收费策略总开关开启时,按企业绑定的当前收费策略计算预计服务费;
|
|
|
|
|
|
|
+ * 3. 收费策略总开关开启时,按企业绑定的当前收费策略计算预计服务费;无企业策略时使用开关表 default_fee;
|
|
|
* 4. 策略 method 为 0.00 时,实际单价使用 kwt_parking_strategy_switch.default_fee;
|
|
* 4. 策略 method 为 0.00 时,实际单价使用 kwt_parking_strategy_switch.default_fee;
|
|
|
* 5. 最大可购买数量 = 服务费余额 / 实际策略单价,向下取整,避免超出余额。
|
|
* 5. 最大可购买数量 = 服务费余额 / 实际策略单价,向下取整,避免超出余额。
|
|
|
*
|
|
*
|
|
@@ -372,7 +372,7 @@ public class ParkingWalletFeeService {
|
|
|
// 收费策略总开关统一以 kwt_parking_strategy_switch.status 为准,策略表 status 不再作为计费开关。
|
|
// 收费策略总开关统一以 kwt_parking_strategy_switch.status 为准,策略表 status 不再作为计费开关。
|
|
|
boolean chargeStrategySwitchOpen = isChargeStrategySwitchOpen();
|
|
boolean chargeStrategySwitchOpen = isChargeStrategySwitchOpen();
|
|
|
|
|
|
|
|
- // 查询当前企业绑定的最新收费策略;若 method 为 0.00,resolveStrategyUnitFee 会使用开关表 default_fee。
|
|
|
|
|
|
|
+ // 查询当前企业绑定的最新收费策略;无企业策略或 method 为 0.00 时,resolveStrategyUnitFee 会使用开关表 default_fee。
|
|
|
KwtParkingChargeStrategy currentStrategy = queryCurrentEnableStrategy(entId);
|
|
KwtParkingChargeStrategy currentStrategy = queryCurrentEnableStrategy(entId);
|
|
|
BigDecimal currentStrategyUnitFee = resolveStrategyUnitFee(currentStrategy);
|
|
BigDecimal currentStrategyUnitFee = resolveStrategyUnitFee(currentStrategy);
|
|
|
log.info("服务费预估策略匹配完成,企业id:{}, 开关状态:{}, 策略id:{}, 策略原始单价:{}, 实际计费单价:{}",
|
|
log.info("服务费预估策略匹配完成,企业id:{}, 开关状态:{}, 策略id:{}, 策略原始单价:{}, 实际计费单价:{}",
|
|
@@ -389,8 +389,8 @@ public class ParkingWalletFeeService {
|
|
|
log.info("服务费预估金额计算完成,企业id:{}, 采购数量:{}, 实际计费单价:{}, 预计服务费:{}",
|
|
log.info("服务费预估金额计算完成,企业id:{}, 采购数量:{}, 实际计费单价:{}, 预计服务费:{}",
|
|
|
entId, param.getPurchaseQuantity(), currentStrategyUnitFee, estimatedServiceFee);
|
|
entId, param.getPurchaseQuantity(), currentStrategyUnitFee, estimatedServiceFee);
|
|
|
|
|
|
|
|
- // 开关开启且存在有效策略单价时,按服务费余额计算最大可购买数量;否则返回 null。
|
|
|
|
|
- BigDecimal maxPurchaseQuantity = calculateMaxPurchaseQuantity(currentStrategy, currentStrategyUnitFee, serviceFeeBalance);
|
|
|
|
|
|
|
+ // 开关开启且存在有效单价时,按服务费余额计算最大可购买数量;否则返回 null。
|
|
|
|
|
+ BigDecimal maxPurchaseQuantity = calculateMaxPurchaseQuantity(currentStrategyUnitFee, serviceFeeBalance);
|
|
|
log.info("服务费预估计算完成,企业id:{}, 采购数量:{}, 服务费余额:{}, 预计服务费:{}, 最大可购买数量:{}",
|
|
log.info("服务费预估计算完成,企业id:{}, 采购数量:{}, 服务费余额:{}, 预计服务费:{}, 最大可购买数量:{}",
|
|
|
entId, param.getPurchaseQuantity(), serviceFeeBalance, estimatedServiceFee, maxPurchaseQuantity);
|
|
entId, param.getPurchaseQuantity(), serviceFeeBalance, estimatedServiceFee, maxPurchaseQuantity);
|
|
|
|
|
|
|
@@ -474,6 +474,7 @@ public class ParkingWalletFeeService {
|
|
|
result.setApplyChargeStrategy(Global.YES);
|
|
result.setApplyChargeStrategy(Global.YES);
|
|
|
result.setChargeStrategyId(currentStrategy.getId());
|
|
result.setChargeStrategyId(currentStrategy.getId());
|
|
|
result.setChargeStrategyDesc(buildStrategyMethodDesc(currentStrategy));
|
|
result.setChargeStrategyDesc(buildStrategyMethodDesc(currentStrategy));
|
|
|
|
|
+ result.setChargeStrategyAmount(unitFee);
|
|
|
result.setFreezeAmount(freezeAmount);
|
|
result.setFreezeAmount(freezeAmount);
|
|
|
result.setServiceFeeBalance(walletFee.getServiceFeeBalance());
|
|
result.setServiceFeeBalance(walletFee.getServiceFeeBalance());
|
|
|
log.info("贸易订单应用收费策略完成,orderNo:{}, result:{}", param.getOrderNo(), result);
|
|
log.info("贸易订单应用收费策略完成,orderNo:{}, result:{}", param.getOrderNo(), result);
|
|
@@ -706,8 +707,8 @@ public class ParkingWalletFeeService {
|
|
|
/**
|
|
/**
|
|
|
* 解析订单完结服务费扣减信息
|
|
* 解析订单完结服务费扣减信息
|
|
|
* <p>
|
|
* <p>
|
|
|
- * 扣减金额 = 运输净重量 × 采购方当前最优收费策略单价;
|
|
|
|
|
- * 最优策略:企业已绑定且开启的策略中,单价最低的一条(对采购方最优惠)。
|
|
|
|
|
|
|
+ * 扣减金额 = 运输净重量 × 收费策略单价;
|
|
|
|
|
+ * 单价优先取订单下单快照(chargeStrategyAmount),无快照时再取采购方当前最优策略;
|
|
|
* 若无法匹配策略,则回退为下单冻结金额。
|
|
* 若无法匹配策略,则回退为下单冻结金额。
|
|
|
*
|
|
*
|
|
|
* @param param 结算参数(actualQuantity 为运输净重量)
|
|
* @param param 结算参数(actualQuantity 为运输净重量)
|
|
@@ -716,6 +717,17 @@ public class ParkingWalletFeeService {
|
|
|
*/
|
|
*/
|
|
|
private SettleConsumeInfo resolveSettleConsumeInfo(ParkingWalletFeeFreezeParam param, BigDecimal freezeAmount) {
|
|
private SettleConsumeInfo resolveSettleConsumeInfo(ParkingWalletFeeFreezeParam param, BigDecimal freezeAmount) {
|
|
|
BigDecimal transportNetWeight = param.getActualQuantity() == null ? ZERO_AMOUNT : param.getActualQuantity();
|
|
BigDecimal transportNetWeight = param.getActualQuantity() == null ? ZERO_AMOUNT : param.getActualQuantity();
|
|
|
|
|
+
|
|
|
|
|
+ // 优先使用订单下单时快照的收费策略单价,避免策略表变更后扣减金额变化
|
|
|
|
|
+ BigDecimal snapshotUnitFee = param.getChargeStrategyAmount();
|
|
|
|
|
+ if (snapshotUnitFee != null && snapshotUnitFee.compareTo(ZERO_AMOUNT) > 0) {
|
|
|
|
|
+ BigDecimal consumeAmount = transportNetWeight.multiply(snapshotUnitFee).setScale(2, RoundingMode.HALF_UP);
|
|
|
|
|
+ log.info("按订单快照单价计算扣减,orderNo:{}, transportNetWeight:{}, snapshotUnitFee:{}, consumeAmount:{}",
|
|
|
|
|
+ param.getOrderNo(), transportNetWeight, snapshotUnitFee, consumeAmount);
|
|
|
|
|
+ return new SettleConsumeInfo(consumeAmount, transportNetWeight, param.getChargeStrategyId(),
|
|
|
|
|
+ null, buildSnapshotStrategyDesc(snapshotUnitFee));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
KwtParkingChargeStrategy strategy = queryOptimalEnableStrategy(param.getProEntId());
|
|
KwtParkingChargeStrategy strategy = queryOptimalEnableStrategy(param.getProEntId());
|
|
|
if (strategy == null && param.getChargeStrategyId() != null) {
|
|
if (strategy == null && param.getChargeStrategyId() != null) {
|
|
|
strategy = parkingChangeStrategyRepository.getById(param.getChargeStrategyId());
|
|
strategy = parkingChangeStrategyRepository.getById(param.getChargeStrategyId());
|
|
@@ -884,6 +896,13 @@ public class ParkingWalletFeeService {
|
|
|
return unitFee.stripTrailingZeros().toPlainString() + "元/月";
|
|
return unitFee.stripTrailingZeros().toPlainString() + "元/月";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ private String buildSnapshotStrategyDesc(BigDecimal snapshotUnitFee) {
|
|
|
|
|
+ if (snapshotUnitFee == null || snapshotUnitFee.compareTo(ZERO_AMOUNT) <= 0) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ return snapshotUnitFee.stripTrailingZeros().toPlainString() + "元";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 写入服务费钱包日志(kwt_parking_wallet_fee_balance)
|
|
* 写入服务费钱包日志(kwt_parking_wallet_fee_balance)
|
|
|
*/
|
|
*/
|
|
@@ -933,23 +952,19 @@ public class ParkingWalletFeeService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 计算服务费余额支持的最大可购买数量
|
|
|
|
|
- * 仅在收费策略开关开启且单价大于0时计算:服务费余额 / 策略单价(向下取整,保留4位小数)
|
|
|
|
|
|
|
+ * 计算服务费余额支持的最大可购买数量。
|
|
|
|
|
+ * 仅在收费策略总开关开启且实际单价大于 0 时计算:服务费余额 / 单价(向下取整)。
|
|
|
*
|
|
*
|
|
|
- * @param currentStrategy 当前收费策略
|
|
|
|
|
- * @param currentStrategyUnitFee 策略单价
|
|
|
|
|
- * @param serviceFeeBalance 服务费余额
|
|
|
|
|
- * @return 最大可购买数量;策略未开启时不做余额约束,返回null
|
|
|
|
|
|
|
+ * @param currentStrategyUnitFee 实际计费单价(含 default_fee 兜底)
|
|
|
|
|
+ * @param serviceFeeBalance 服务费余额
|
|
|
|
|
+ * @return 最大可购买数量;开关未开启或单价无效时返回 null
|
|
|
*/
|
|
*/
|
|
|
- private BigDecimal calculateMaxPurchaseQuantity(KwtParkingChargeStrategy currentStrategy,
|
|
|
|
|
- BigDecimal currentStrategyUnitFee,
|
|
|
|
|
|
|
+ private BigDecimal calculateMaxPurchaseQuantity(BigDecimal currentStrategyUnitFee,
|
|
|
BigDecimal serviceFeeBalance) {
|
|
BigDecimal serviceFeeBalance) {
|
|
|
boolean strategySwitchOpen = isChargeStrategySwitchOpen();
|
|
boolean strategySwitchOpen = isChargeStrategySwitchOpen();
|
|
|
- if (!strategySwitchOpen || currentStrategy == null
|
|
|
|
|
- || currentStrategyUnitFee.compareTo(ZERO_AMOUNT) <= 0) {
|
|
|
|
|
|
|
+ if (!strategySwitchOpen || currentStrategyUnitFee.compareTo(ZERO_AMOUNT) <= 0) {
|
|
|
log.info("最大可购买数量未计算,原因:{}",
|
|
log.info("最大可购买数量未计算,原因:{}",
|
|
|
- !strategySwitchOpen ? "收费策略开关未开启"
|
|
|
|
|
- : currentStrategy == null ? "未匹配到收费策略" : "策略单价<=0");
|
|
|
|
|
|
|
+ !strategySwitchOpen ? "收费策略开关未开启" : "实际计费单价<=0");
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
//向下取整
|
|
//向下取整
|
|
@@ -980,10 +995,20 @@ public class ParkingWalletFeeService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 解析策略实际计费单价:method 为 0.00 时使用开关表 default_fee
|
|
|
|
|
|
|
+ * 解析策略实际计费单价。
|
|
|
|
|
+ * <ul>
|
|
|
|
|
+ * <li>无企业策略且收费策略总开关开启:使用 {@code kwt_parking_strategy_switch.default_fee}</li>
|
|
|
|
|
+ * <li>有企业策略且 method 为 0.00:使用开关表 default_fee</li>
|
|
|
|
|
+ * <li>有企业策略且 method > 0:使用策略 method</li>
|
|
|
|
|
+ * </ul>
|
|
|
*/
|
|
*/
|
|
|
private BigDecimal resolveStrategyUnitFee(KwtParkingChargeStrategy strategy) {
|
|
private BigDecimal resolveStrategyUnitFee(KwtParkingChargeStrategy strategy) {
|
|
|
if (strategy == null) {
|
|
if (strategy == null) {
|
|
|
|
|
+ if (isChargeStrategySwitchOpen()) {
|
|
|
|
|
+ BigDecimal defaultFee = resolveDefaultFee();
|
|
|
|
|
+ log.info("未匹配到企业收费策略,收费策略开关已开启,使用默认单价:{}", defaultFee);
|
|
|
|
|
+ return defaultFee;
|
|
|
|
|
+ }
|
|
|
return ZERO_AMOUNT;
|
|
return ZERO_AMOUNT;
|
|
|
}
|
|
}
|
|
|
BigDecimal method = strategy.getMethod() == null ? ZERO_AMOUNT : strategy.getMethod();
|
|
BigDecimal method = strategy.getMethod() == null ? ZERO_AMOUNT : strategy.getMethod();
|