费用管理优化

localizer
ljw 2025-10-29 17:27:40 +08:00
parent 5a95ce7441
commit da713ef56e
5 changed files with 252 additions and 64 deletions

View File

@ -0,0 +1,19 @@
package com.njzscloud.supervisory.expense.contant;
import com.njzscloud.common.core.ienum.DictStr;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* billing_type
*
*/
@Getter
@RequiredArgsConstructor
public enum BillingType implements DictStr {
FIXED("fixed", "固定计费"),
ELASTICITY("elasticity", "弹性计费"),
;
private final String val;
private final String txt;
}

View File

@ -0,0 +1,19 @@
package com.njzscloud.supervisory.expense.contant;
import com.njzscloud.common.core.ienum.DictStr;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* goods_scope
*
*/
@Getter
@RequiredArgsConstructor
public enum GoodsScope implements DictStr {
ALL("all", "所有产品"),
GOODS("goods", "指定产品"),
;
private final String val;
private final String txt;
}

View File

@ -3,6 +3,7 @@ package com.njzscloud.supervisory.expense.pojo.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.njzscloud.common.mp.support.handler.j.JsonTypeHandler;
import com.njzscloud.supervisory.biz.constant.BizObj;
import com.njzscloud.supervisory.expense.contant.GoodsScope;
import com.njzscloud.supervisory.expense.contant.Scope;
import lombok.Getter;
import lombok.Setter;
@ -80,6 +81,42 @@ public class ExpenseItemsConfigEntity {
@TableField(typeHandler = JsonTypeHandler.class)
private List<String> companyIds;
/**
* ;goods_scope all -> goods ->
*/
private GoodsScope goodsScope;
/**
* ids
*/
@TableField(typeHandler = JsonTypeHandler.class)
private List<String> goodsIds;
/**
* fixed -> elasticity ->
*/
private String billingType;
/**
* ;
*/
private BigDecimal basicWeight;
/**
* ;
*/
private BigDecimal basicPrice;
/**
* ;
*/
private BigDecimal everyWeight;
/**
* ;
*/
private BigDecimal everyPrice;
/**
*
*/

View File

@ -3,6 +3,7 @@ package com.njzscloud.supervisory.order.pojo.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.njzscloud.common.mp.support.handler.j.JsonTypeHandler;
import com.njzscloud.supervisory.biz.constant.BizObj;
import com.njzscloud.supervisory.expense.contant.GoodsScope;
import com.njzscloud.supervisory.expense.contant.Scope;
import lombok.Getter;
import lombok.Setter;
@ -85,6 +86,42 @@ public class OrderExpenseItemsEntity {
@TableField(typeHandler = JsonTypeHandler.class)
private List<String> companyIds;
/**
* ;goods_scope all -> goods ->
*/
private GoodsScope goodsScope;
/**
* ids
*/
@TableField(typeHandler = JsonTypeHandler.class)
private List<String> goodsIds;
/**
* fixed -> elasticity ->
*/
private String billingType;
/**
* ;
*/
private BigDecimal basicWeight;
/**
* ;
*/
private BigDecimal basicPrice;
/**
* ;
*/
private BigDecimal everyWeight;
/**
* ;
*/
private BigDecimal everyPrice;
/**
*
*/

View File

@ -33,7 +33,9 @@ import com.njzscloud.supervisory.biz.service.TruckLocationTrackService;
import com.njzscloud.supervisory.constant.Constant;
import com.njzscloud.supervisory.device.pojo.entity.DeviceLocalizerEntity;
import com.njzscloud.supervisory.device.service.DeviceInfoService;
import com.njzscloud.supervisory.expense.contant.BillingType;
import com.njzscloud.supervisory.expense.contant.ExpenseItemCategory;
import com.njzscloud.supervisory.expense.contant.GoodsScope;
import com.njzscloud.supervisory.expense.contant.Scope;
import com.njzscloud.supervisory.expense.pojo.entity.ExpenseItemsConfigEntity;
import com.njzscloud.supervisory.expense.service.ExpenseItemsConfigService;
@ -130,7 +132,7 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
OrderGoodsEntity entity = orderGoodsService.getById(orderGoodsId);
// 添加付费项,计算费用,需要称重在更新
settleForTransCompany(this.getById(orderInfoEntity.getId()), entity, 0);
settleForTransCompany(this.getById(orderInfoEntity.getId()), entity);
Long orderId = orderInfoEntity.getId();
OrderCategory orderCategory = addOrderInfoParam.getOrderCategory();
@ -1085,33 +1087,89 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
}
}
public void updateOrderItems(Long orderId, Integer settleWeight) {
List<OrderExpenseItemsEntity> extraItems = orderExpenseItemsService.list(Wrappers.lambdaQuery(OrderExpenseItemsEntity.class)
.eq(OrderExpenseItemsEntity::getOrderId, orderId)
.eq(OrderExpenseItemsEntity::getDeleted, Boolean.FALSE));
BigDecimal totalDiscountMoney = BigDecimal.ZERO;
BigDecimal totalReviseMoney = BigDecimal.ZERO;
BigDecimal totalSettleMoney = BigDecimal.ZERO;
BigDecimal totalTotalMoney = BigDecimal.ZERO;
for (OrderExpenseItemsEntity item : extraItems) {
item.setQuantity(MoneyStrategy.Che.getVal().equals(item.getMoneyStrategy()) ? 1 : settleWeight);
/**
*
* /
*
* @param item
* @return
*/
private BigDecimal calculateItemTotalMoney(OrderExpenseItemsEntity item) {
// 判断是否为清运服务费的弹性计费
if (ExpenseItemCategory.QingYunFuWuFei.getVal().equals(item.getExpenseItemCategory())
&& MoneyStrategy.Dun.getVal().equals(item.getMoneyStrategy())
&& BillingType.ELASTICITY.getVal().equals(item.getBillingType())) {
// 弹性计费逻辑
// 1. 将数量(公斤)转换为吨
BigDecimal actualWeight = new BigDecimal(item.getQuantity() == null ? 0 : item.getQuantity())
.divide(new BigDecimal("1000"), 6, BigDecimal.ROUND_HALF_UP);
// 2. 获取弹性计费参数
BigDecimal basicWeight = item.getBasicWeight();
BigDecimal basicPrice = item.getBasicPrice();
BigDecimal everyWeight = item.getEveryWeight();
BigDecimal everyPrice = item.getEveryPrice();
// 3. 计算总金额
if (actualWeight.compareTo(basicWeight) <= 0) {
// 实际重量小于等于基础磅重,使用基础价格
return basicPrice;
} else {
// 实际重量大于基础磅重,计算超出部分的档次
BigDecimal exceedWeight = actualWeight.subtract(basicWeight);
// 计算需要多少档(向上取整)
int tiers = exceedWeight.divide(everyWeight, 0, BigDecimal.ROUND_UP).intValue();
// 总金额 = 基础价格 + 档次 * 每档单价
return basicPrice.add(everyPrice.multiply(new BigDecimal(tiers)));
}
} else {
// 固定计费逻辑: total_money = quantity * unit_price
BigDecimal quantity = new BigDecimal(item.getQuantity() == null ? 0 : item.getQuantity());
// 如果计费策略是按吨数量需要除以1000
if (MoneyStrategy.Dun.getVal().equals(item.getMoneyStrategy())) {
quantity = quantity.divide(new BigDecimal("1000"), 6, BigDecimal.ROUND_HALF_UP);
}
return item.getUnitPrice().multiply(quantity);
}
}
BigDecimal totalMoney = item.getUnitPrice().multiply(quantity);
// settle_money = total_money + discount_money + revise_moneydiscount、revise 可为正负)
/**
*
*
* @param item
*/
private void calculateAndSetItemMoney(OrderExpenseItemsEntity item) {
// 计算总金额
BigDecimal totalMoney = calculateItemTotalMoney(item);
// settle_money = total_money + discount_money + revise_moneydiscount、revise 可为正负)
BigDecimal discount = item.getDiscountMoney() == null ? BigDecimal.ZERO : item.getDiscountMoney();
BigDecimal revise = item.getReviseMoney() == null ? BigDecimal.ZERO : item.getReviseMoney();
BigDecimal settle = totalMoney.add(discount).add(revise);
item.setTotalMoney(totalMoney).setSettleMoney(settle);
}
/**
*
*
* @param orderId ID
* @param items
*/
private void updateOrderTotalMoney(Long orderId, List<OrderExpenseItemsEntity> items) {
BigDecimal totalDiscountMoney = BigDecimal.ZERO;
BigDecimal totalReviseMoney = BigDecimal.ZERO;
BigDecimal totalSettleMoney = BigDecimal.ZERO;
BigDecimal totalTotalMoney = BigDecimal.ZERO;
// 汇总各项金额
for (OrderExpenseItemsEntity item : items) {
BigDecimal discount = item.getDiscountMoney() == null ? BigDecimal.ZERO : item.getDiscountMoney();
BigDecimal revise = item.getReviseMoney() == null ? BigDecimal.ZERO : item.getReviseMoney();
BigDecimal settle = totalMoney.add(discount).add(revise);
item.setTotalMoney(totalMoney).setSettleMoney(settle);
totalDiscountMoney = totalDiscountMoney.add(discount);
totalReviseMoney = totalReviseMoney.add(revise);
totalSettleMoney = totalSettleMoney.add(settle);
totalTotalMoney = totalTotalMoney.add(totalMoney);
orderExpenseItemsService.updateById(item);
totalSettleMoney = totalSettleMoney.add(item.getSettleMoney());
totalTotalMoney = totalTotalMoney.add(item.getTotalMoney());
}
// 更新订单表的优惠金额; 手动修正金额;结算金额;总金额;
@ -1125,6 +1183,41 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
.update();
}
/**
*
*
* @param orderId ID
* @param items
*/
private void calculateItemsAndUpdateOrder(Long orderId, List<OrderExpenseItemsEntity> items) {
// 计算各付费项金额
for (OrderExpenseItemsEntity item : items) {
calculateAndSetItemMoney(item);
}
// 更新订单汇总金额
updateOrderTotalMoney(orderId, items);
}
public void updateOrderItems(Long orderId, Integer settleWeight) {
List<OrderExpenseItemsEntity> extraItems = orderExpenseItemsService.list(Wrappers.lambdaQuery(OrderExpenseItemsEntity.class)
.eq(OrderExpenseItemsEntity::getOrderId, orderId)
.eq(OrderExpenseItemsEntity::getDeleted, Boolean.FALSE));
// 更新数量:按车计费数量=1否则=称重数量
for (OrderExpenseItemsEntity item : extraItems) {
item.setQuantity(MoneyStrategy.Che.getVal().equals(item.getMoneyStrategy()) ? 1 : settleWeight);
// 重新计算金额totalMoney、settleMoney
calculateAndSetItemMoney(item);
}
// 批量更新付费项包含quantity和所有金额字段
orderExpenseItemsService.updateBatchById(extraItems);
// 汇总并更新订单总金额
updateOrderTotalMoney(orderId, extraItems);
}
/**
*
* 1. ExpenseItemCategory=ChanPin
@ -1132,12 +1225,9 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
* 3. total_money=quantity*unit_pricesettle_money=total_money+discount_money+revise_money
*/
@Transactional(rollbackFor = Exception.class)
public void settleForTransCompany(OrderInfoEntity orderInfoEntity, OrderGoodsEntity orderGoods, Integer settleWeight) {
public void settleForTransCompany(OrderInfoEntity orderInfoEntity, OrderGoodsEntity orderGoods) {
Long orderId = orderInfoEntity.getId();
// 数量口径:计费策略=按车(Che) 时数量=1否则数量=settleWeight
int quantityByStrategy = (orderGoods.getMoneyStrategy() == MoneyStrategy.Che) ? 1 : settleWeight;
// 第一步:复制产品信息为付费项记录
BigDecimal unitPrice = orderGoods.getUnitPrice();
OrderExpenseItemsEntity productItem = null;
@ -1152,7 +1242,7 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
.setMoneyStrategy(orderGoods.getMoneyStrategy() == null ? null : orderGoods.getMoneyStrategy().getVal())
.setMoneyConfigId(orderGoods.getMoneyConfigId())
.setTaxRate(orderGoods.getTaxRate())
.setQuantity(quantityByStrategy);
.setQuantity((orderGoods.getMoneyStrategy() == MoneyStrategy.Che) ? 1 : 0);
}
@ -1162,17 +1252,28 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
.eq(ExpenseItemsConfigEntity::getDeleted, Boolean.FALSE));
Long transCompanyId = orderInfoEntity.getTransCompanyId();
Long goodsId = orderGoods.getOriginGoodsId();
List<OrderExpenseItemsEntity> extraItems = configs.stream()
.filter(cfg -> {
Scope scope = cfg.getScope();
if (scope == Scope.ALL) return true;
if (scope == Scope.CUSTOMER_TYPE) {
GoodsScope goodsScope = cfg.getGoodsScope();
List<String> companyIds = cfg.getCompanyIds();
List<String> goodsIds = cfg.getGoodsIds();
boolean isComTrue = companyIds != null && transCompanyId != null && companyIds.contains(String.valueOf(transCompanyId));
boolean isGoodsTrue = goodsIds != null && goodsId != null && goodsIds.contains(String.valueOf(goodsId));
if (scope == Scope.ALL && goodsScope == GoodsScope.ALL) return true;
if (scope == Scope.ALL && goodsScope == GoodsScope.GOODS) {
return isGoodsTrue;
}
/*if (scope == Scope.CUSTOMER_TYPE) {
BizObj bizObj = cfg.getBizObj();
return bizObj == BizObj.QiYe || bizObj == BizObj.GeTi;
}*/
if (scope == Scope.CUSTOMER && goodsScope == GoodsScope.ALL) {
return isComTrue;
}
if (scope == Scope.CUSTOMER) {
List<String> companyIds = cfg.getCompanyIds();
return companyIds != null && transCompanyId != null && companyIds.contains(String.valueOf(transCompanyId));
if (scope == Scope.CUSTOMER && goodsScope == GoodsScope.GOODS) {
return isComTrue && isGoodsTrue;
}
return false;
})
@ -1189,8 +1290,15 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
.setScope(cfg.getScope())
.setBizObj(cfg.getBizObj())
.setCompanyIds(cfg.getCompanyIds())
.setGoodsScope(cfg.getGoodsScope())
.setGoodsIds(cfg.getGoodsIds())
.setBillingType(cfg.getBillingType())
.setBasicWeight(cfg.getBasicWeight())
.setBasicPrice(cfg.getBasicPrice())
.setEveryWeight(cfg.getEveryWeight())
.setEveryPrice(cfg.getEveryPrice())
.setTaxRate(cfg.getTaxRate())
.setQuantity(MoneyStrategy.Che.getVal().equals(cfg.getMoneyStrategy()) ? 1 : settleWeight))
.setQuantity(MoneyStrategy.Che.getVal().equals(cfg.getMoneyStrategy()) ? 1 : 0))
.collect(Collectors.toList());
// 第三步:合并集合并计算金额,然后批量落库
@ -1198,44 +1306,12 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
if (extraItems.isEmpty()) {
return;
}
BigDecimal totalDiscountMoney = BigDecimal.ZERO;
BigDecimal totalReviseMoney = BigDecimal.ZERO;
BigDecimal totalSettleMoney = BigDecimal.ZERO;
BigDecimal totalTotalMoney = BigDecimal.ZERO;
for (OrderExpenseItemsEntity item : extraItems) {
// total_money = quantity * unit_price 如果计费策略是按吨总金额计算时数量需要÷1000
BigDecimal quantity = new BigDecimal(item.getQuantity() == null ? 0 : item.getQuantity());
// 如果计费策略是按吨数量需要除以1000
if (MoneyStrategy.Dun.getVal().equals(item.getMoneyStrategy())) {
quantity = quantity.divide(new BigDecimal("1000"), 6, BigDecimal.ROUND_HALF_UP);
}
BigDecimal totalMoney = item.getUnitPrice().multiply(quantity);
// settle_money = total_money + discount_money + revise_moneydiscount、revise 可为正负)
BigDecimal discount = item.getDiscountMoney() == null ? BigDecimal.ZERO : item.getDiscountMoney();
BigDecimal revise = item.getReviseMoney() == null ? BigDecimal.ZERO : item.getReviseMoney();
BigDecimal settle = totalMoney.add(discount).add(revise);
item.setTotalMoney(totalMoney).setSettleMoney(settle);
totalDiscountMoney = totalDiscountMoney.add(discount);
totalReviseMoney = totalReviseMoney.add(revise);
totalSettleMoney = totalSettleMoney.add(settle);
totalTotalMoney = totalTotalMoney.add(totalMoney);
}
// 批量保存付费项
orderExpenseItemsService.saveBatch(extraItems);
// 更新订单表的优惠金额; 手动修正金额;结算金额;总金额;
this.lambdaUpdate()
.eq(OrderInfoEntity::getId, orderId)
.set(OrderInfoEntity::getDiscountMoney, totalDiscountMoney)
.set(OrderInfoEntity::getReviseMoney, totalReviseMoney)
.set(OrderInfoEntity::getSettleMoney, totalSettleMoney)
.set(OrderInfoEntity::getTotalMoney, totalTotalMoney)
.set(OrderInfoEntity::getPaymentStatus, PaymentStatus.WeiZhiFu)
.update();
// 计算付费项金额并更新订单汇总
calculateItemsAndUpdateOrder(orderId, extraItems);
}
public OrderPagingResult pendingOrder(String licensePlate) {