|
@@ -0,0 +1,202 @@
|
|
|
|
|
+package com.sckw.transport.service;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
|
|
+import com.sckw.core.common.enums.enums.ErrorCodeEnum;
|
|
|
|
|
+import com.sckw.core.exception.BusinessPlatfromException;
|
|
|
|
|
+import com.sckw.core.model.constant.Global;
|
|
|
|
|
+import com.sckw.core.web.context.LoginUserHolder;
|
|
|
|
|
+import com.sckw.transport.model.KwtParkingChargeStrategy;
|
|
|
|
|
+import com.sckw.transport.model.KwtParkingChargeStrategyUnit;
|
|
|
|
|
+import com.sckw.transport.model.KwtParkingWalletFee;
|
|
|
|
|
+import com.sckw.transport.model.param.ParkingWalletFeeEstimateQueryParam;
|
|
|
|
|
+import com.sckw.transport.model.param.ParkingWalletFeeEstimateResp;
|
|
|
|
|
+import com.sckw.transport.repository.KwtParkingChangeStrategyRepository;
|
|
|
|
|
+import com.sckw.transport.repository.KwtParkingChangeStrategyUnitRepository;
|
|
|
|
|
+import com.sckw.system.api.RemoteSystemService;
|
|
|
|
|
+import com.sckw.transport.repository.KwtParkingWalletFeeBalanceRepository;
|
|
|
|
|
+import com.sckw.transport.repository.KwtParkingWalletFeeRepository;
|
|
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.apache.commons.collections4.CollectionUtils;
|
|
|
|
|
+import org.apache.dubbo.config.annotation.DubboReference;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
|
+import java.math.RoundingMode;
|
|
|
|
|
+import java.util.Comparator;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Objects;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Author: donglang
|
|
|
|
|
+ * Time: 2026-01-05
|
|
|
|
|
+ * Des: 收费策略 Service
|
|
|
|
|
+ * Version: 1.0
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@Service
|
|
|
|
|
+@RequiredArgsConstructor
|
|
|
|
|
+public class ParkingWalletFeeService {
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ private static final BigDecimal ZERO_AMOUNT = BigDecimal.ZERO;
|
|
|
|
|
+ private static final int STRATEGY_OPEN = 1;
|
|
|
|
|
+
|
|
|
|
|
+ private final KwtParkingWalletFeeRepository parkingWalletFeeRepository;
|
|
|
|
|
+ private final KwtParkingWalletFeeBalanceRepository parkingWalletFeeBalanceRepository;
|
|
|
|
|
+ private final KwtParkingChangeStrategyRepository parkingChangeStrategyRepository;
|
|
|
|
|
+ private final KwtParkingChangeStrategyUnitRepository parkingChangeStrategyUnitRepository;
|
|
|
|
|
+
|
|
|
|
|
+ @DubboReference(version = "1.0.0", group = "design", check = false, timeout = 6000)
|
|
|
|
|
+ RemoteSystemService remoteSystemService;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 查询服务费余额、本次预计服务费与最大可购买数量
|
|
|
|
|
+ * 规则:
|
|
|
|
|
+ * 1. 服务费余额 = 采购方当前服务费余额
|
|
|
|
|
+ * 2. 本次预计服务费 = 采购数量 * 采购方当前配置的收费策略单价
|
|
|
|
|
+ * 3. 最大可购买数量 = 收费策略开启时,服务费余额 / 策略单价;未开启时不返回余额约束
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param param 查询参数
|
|
|
|
|
+ * @return 服务费余额、本次预计服务费与最大可购买数量
|
|
|
|
|
+ */
|
|
|
|
|
+ public ParkingWalletFeeEstimateResp queryEstimateServiceFee(ParkingWalletFeeEstimateQueryParam param) {
|
|
|
|
|
+ Long entId = LoginUserHolder.getEntId();
|
|
|
|
|
+ log.info("服务费预估开始,entId:{}, param:{}", entId, param);
|
|
|
|
|
+ if (param.getPurchaseQuantity() == null || param.getPurchaseQuantity().compareTo(ZERO_AMOUNT) < 0) {
|
|
|
|
|
+ log.warn("服务费预估参数异常,采购数量非法,entId:{}, purchaseQuantity:{}",
|
|
|
|
|
+ entId, param.getPurchaseQuantity());
|
|
|
|
|
+ throw new BusinessPlatfromException(ErrorCodeEnum.PARAM_ERROR, "采购数量不能为空且不能小于0");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 查询采购方当前服务费余额
|
|
|
|
|
+ BigDecimal serviceFeeBalance = queryServiceFeeBalance(entId);
|
|
|
|
|
+
|
|
|
|
|
+ // 查询当前企业生效中的收费策略(仅取状态开启的最新一条)
|
|
|
|
|
+ KwtParkingChargeStrategy currentStrategy = queryCurrentEnableStrategy(entId);
|
|
|
|
|
+ BigDecimal currentStrategyUnitFee = currentStrategy == null || currentStrategy.getMethod() == null
|
|
|
|
|
+ ? ZERO_AMOUNT : currentStrategy.getMethod();
|
|
|
|
|
+ log.info("服务费预估策略信息,entId:{}, strategyId:{}, strategyStatus:{}, unitFee:{}",
|
|
|
|
|
+ entId,
|
|
|
|
|
+ currentStrategy == null ? null : currentStrategy.getId(),
|
|
|
|
|
+ currentStrategy == null ? null : currentStrategy.getStatus(),
|
|
|
|
|
+ currentStrategyUnitFee);
|
|
|
|
|
+
|
|
|
|
|
+ // 本次预计服务费 = 采购数量 * 策略单价,金额统一保留2位
|
|
|
|
|
+ BigDecimal estimatedServiceFee = param.getPurchaseQuantity().multiply(currentStrategyUnitFee)
|
|
|
|
|
+ .setScale(2, RoundingMode.HALF_UP);
|
|
|
|
|
+
|
|
|
|
|
+ // 收费策略开关开启时,按服务费余额计算最大可购买数量
|
|
|
|
|
+ BigDecimal maxPurchaseQuantity = calculateMaxPurchaseQuantity(currentStrategy, currentStrategyUnitFee, serviceFeeBalance);
|
|
|
|
|
+ log.info("服务费预估计算完成,entId:{}, purchaseQuantity:{}, serviceFeeBalance:{}, estimatedServiceFee:{}, maxPurchaseQuantity:{}",
|
|
|
|
|
+ entId, param.getPurchaseQuantity(), serviceFeeBalance, estimatedServiceFee, maxPurchaseQuantity);
|
|
|
|
|
+
|
|
|
|
|
+ ParkingWalletFeeEstimateResp resp = new ParkingWalletFeeEstimateResp();
|
|
|
|
|
+ resp.setServiceFeeBalance(serviceFeeBalance);
|
|
|
|
|
+ resp.setEstimatedServiceFee(estimatedServiceFee);
|
|
|
|
|
+ resp.setMaxPurchaseQuantity(maxPurchaseQuantity);
|
|
|
|
|
+ log.info("服务费预估结束,entId:{}, resp:{}", entId, resp);
|
|
|
|
|
+ return resp;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 计算服务费余额支持的最大可购买数量
|
|
|
|
|
+ * 仅在收费策略开关开启且单价大于0时计算:服务费余额 / 策略单价(向下取整,保留4位小数)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param currentStrategy 当前收费策略
|
|
|
|
|
+ * @param currentStrategyUnitFee 策略单价
|
|
|
|
|
+ * @param serviceFeeBalance 服务费余额
|
|
|
|
|
+ * @return 最大可购买数量;策略未开启时不做余额约束,返回null
|
|
|
|
|
+ */
|
|
|
|
|
+ private BigDecimal calculateMaxPurchaseQuantity(KwtParkingChargeStrategy currentStrategy,
|
|
|
|
|
+ BigDecimal currentStrategyUnitFee,
|
|
|
|
|
+ BigDecimal serviceFeeBalance) {
|
|
|
|
|
+ if (currentStrategy == null || !Objects.equals(currentStrategy.getStatus(), STRATEGY_OPEN)
|
|
|
|
|
+ || currentStrategyUnitFee.compareTo(ZERO_AMOUNT) <= 0) {
|
|
|
|
|
+ log.info("最大可购买数量未计算,原因:{}",
|
|
|
|
|
+ currentStrategy == null ? "未匹配到收费策略" : "收费策略未开启或单价<=0");
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ BigDecimal maxPurchaseQuantity = serviceFeeBalance.divide(currentStrategyUnitFee, 4, RoundingMode.DOWN);
|
|
|
|
|
+ log.info("最大可购买数量计算完成,serviceFeeBalance:{}, unitFee:{}, maxPurchaseQuantity:{}",
|
|
|
|
|
+ serviceFeeBalance, currentStrategyUnitFee, maxPurchaseQuantity);
|
|
|
|
|
+ return maxPurchaseQuantity.max(ZERO_AMOUNT);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 查询企业当前生效的收费策略
|
|
|
|
|
+ * 逻辑:先查企业绑定的策略,再查策略详情,最后取“开启状态”且id最大(最新)的一条
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param proEntId 采购企业id
|
|
|
|
|
+ * @return 生效策略;未匹配到则返回null
|
|
|
|
|
+ */
|
|
|
|
|
+ private KwtParkingChargeStrategy queryCurrentEnableStrategy(Long proEntId) {
|
|
|
|
|
+ List<KwtParkingChargeStrategyUnit> strategyUnitList = parkingChangeStrategyUnitRepository.list(
|
|
|
|
|
+ Wrappers.<KwtParkingChargeStrategyUnit>lambdaQuery()
|
|
|
|
|
+ .eq(KwtParkingChargeStrategyUnit::getEntId, proEntId)
|
|
|
|
|
+ .eq(KwtParkingChargeStrategyUnit::getDelFlag, Global.NO)
|
|
|
|
|
+ );
|
|
|
|
|
+ log.info("查询企业策略绑定关系,proEntId:{}, bindCount:{}",
|
|
|
|
|
+ proEntId, strategyUnitList == null ? 0 : strategyUnitList.size());
|
|
|
|
|
+
|
|
|
|
|
+ if (CollectionUtils.isEmpty(strategyUnitList)) {
|
|
|
|
|
+ log.info("企业未绑定任何策略,proEntId:{}, strategyUnitList:[]", proEntId);
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ List<Long> strategyIds = strategyUnitList.stream()
|
|
|
|
|
+ .map(KwtParkingChargeStrategyUnit::getStrategyId)
|
|
|
|
|
+ .filter(Objects::nonNull)
|
|
|
|
|
+ .toList();
|
|
|
|
|
+ log.info("查询企业策略ID集合,proEntId:{}, strategyIds:{}", proEntId, strategyIds);
|
|
|
|
|
+ if (CollectionUtils.isEmpty(strategyIds)) {
|
|
|
|
|
+ log.info("企业绑定策略ID集合为空");
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ List<KwtParkingChargeStrategy> strategyList = parkingChangeStrategyRepository.list(
|
|
|
|
|
+ Wrappers.<KwtParkingChargeStrategy>lambdaQuery()
|
|
|
|
|
+ .in(KwtParkingChargeStrategy::getId, strategyIds)
|
|
|
|
|
+ .eq(KwtParkingChargeStrategy::getDelFlag, Global.NO)
|
|
|
|
|
+ .orderByDesc(KwtParkingChargeStrategy::getId)
|
|
|
|
|
+ );
|
|
|
|
|
+ log.info("查询策略详情完成,proEntId:{}, strategyCount:{}",
|
|
|
|
|
+ proEntId, strategyList == null ? 0 : strategyList.size());
|
|
|
|
|
+ if (CollectionUtils.isEmpty(strategyList)) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ KwtParkingChargeStrategy hitStrategy = strategyList.stream()
|
|
|
|
|
+ .filter(item -> Objects.equals(item.getStatus(), STRATEGY_OPEN))
|
|
|
|
|
+ .max(Comparator.comparing(KwtParkingChargeStrategy::getId))
|
|
|
|
|
+ .orElse(null);
|
|
|
|
|
+ log.info("匹配生效策略结果,proEntId:{}, hitStrategyId:{}",
|
|
|
|
|
+ proEntId, hitStrategy == null ? null : hitStrategy.getId());
|
|
|
|
|
+ return hitStrategy;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 查询企业当前可用服务费余额
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param proEntId 采购企业id
|
|
|
|
|
+ * @return 服务费余额,无记录时返回0
|
|
|
|
|
+ */
|
|
|
|
|
+ private BigDecimal queryServiceFeeBalance(Long proEntId) {
|
|
|
|
|
+ KwtParkingWalletFee walletFee = parkingWalletFeeRepository.getOne(
|
|
|
|
|
+ Wrappers.<KwtParkingWalletFee>lambdaQuery()
|
|
|
|
|
+ .eq(KwtParkingWalletFee::getProEntId, proEntId)
|
|
|
|
|
+ .eq(KwtParkingWalletFee::getDelFlag, Global.NO)
|
|
|
|
|
+ .orderByDesc(KwtParkingWalletFee::getId)
|
|
|
|
|
+ .last("limit 1"),
|
|
|
|
|
+ false
|
|
|
|
|
+ );
|
|
|
|
|
+ if (walletFee == null || walletFee.getServiceFeeBalance() == null) {
|
|
|
|
|
+ log.info("查询服务费余额为空,按0处理,proEntId:{}", proEntId);
|
|
|
|
|
+ return ZERO_AMOUNT;
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("查询服务费余额成功,proEntId:{}, serviceFeeBalance:{}",
|
|
|
|
|
+ proEntId, walletFee.getServiceFeeBalance());
|
|
|
|
|
+ return walletFee.getServiceFeeBalance();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+}
|