问题修复,下单优化,订单结算问题修复

This commit is contained in:
2025-10-31 16:30:47 +08:00
parent 09fc28de06
commit 31d91f7de5
10 changed files with 379 additions and 148 deletions

View File

@@ -268,6 +268,12 @@ export interface TimeLimitDiscountConfig {
[property: string]: any;
}
//用户信息
interface ShopUserInfo {
isVip: number | null; //是否会员
discount: number | null; //用户折扣
isMemberPrice: number | null; //会员折扣与会员价是否同时使用
}
/** 订单额外费用配置 */
export interface OrderExtraConfig {
// merchantReduction: number; // 商家减免金额默认0
@@ -286,6 +292,7 @@ export interface OrderExtraConfig {
isFreeDine?: boolean; //是否霸王餐
freeDineConfig?: FreeDineConfig;
limitTimeDiscount?: TimeLimitDiscountConfig; //限时折扣
shopUserInfo: ShopUserInfo; // 用户信息
}
/** 订单费用汇总(修改:补充商家减免类型和明细) */
@@ -318,10 +325,12 @@ export interface OrderCostSummary {
config: OrderExtraConfig; // 订单额外费用配置
//满减活动
fullReduction: {
usedFullReductionActivityFullAmount: number; // 计算出的满减活动的门槛金额
usedActivity?: FullReductionActivity; // 实际使用的满减活动
usedThreshold?: FullReductionThreshold; // 实际使用的满减阈值(多门槛中选最优)
actualAmount: number; // 满减实际减免金额(元)
};
vipDiscountAmount: number; //会员折扣减免金额
// 订单原支付金额
orderOriginFinalPayAmount: number; //订单原金额(包含打包费+餐位费)
}
@@ -437,58 +446,190 @@ function isDinnerTypeMatch(
//判断商品是否可以使用限时折扣
export function returnCanUseLimitTimeDiscount(
goods:BaseCartItem,
limitTimeDiscountRes:TimeLimitDiscountConfig|null|undefined,
useVipPrice:boolean,
goods: BaseCartItem,
limitTimeDiscount: TimeLimitDiscountConfig | null | undefined,
useVipPrice: boolean,
idKey = "product_id"
) {
if (!limitTimeDiscountRes || !limitTimeDiscountRes.id) {
if (!limitTimeDiscount || !limitTimeDiscount.id) {
return false;
}
const canUseFoods = (limitTimeDiscountRes.foods || "").split(",");
if (limitTimeDiscountRes.discountPriority == "limit-time") {
if (
limitTimeDiscountRes.foodType == 1 ||
canUseFoods.includes("" + goods[idKey])
) {
const canUseFoods = (limitTimeDiscount.foods || "").split(",");
const goodsCanUse =
limitTimeDiscount.foodType == 1 || canUseFoods.includes("" + goods[idKey]);
if (!goodsCanUse) {
return false;
}
if (limitTimeDiscount.discountPriority == "limit-time") {
return true;
}
if (limitTimeDiscount.discountPriority == "vip-price") {
if (!useVipPrice) {
return true;
}
}
if (
limitTimeDiscountRes.discountPriority == "vip-price"
) {
if(!useVipPrice){
return true;
if (useVipPrice && goods.hasOwnProperty("memberPrice")) {
if (goods.memberPrice && goods.memberPrice * 1 <= 0) {
return true;
}
}
}
return false;
}
function returnMemberPrice(useVipPrice: boolean, goods: BaseCartItem) {
if (useVipPrice) {
return goods.memberPrice || goods.salePrice;
} else {
return goods.salePrice;
}
}
/**
* 返回商品限时折扣价格
*/
function returnLimitPrice(
goods: BaseCartItem,
limitTimeDiscount: TimeLimitDiscountConfig | null | undefined,
useVipPrice
) {
if (!limitTimeDiscount) {
return 0;
}
const discountRate = new BigNumber(limitTimeDiscount.discountRate).dividedBy(
100
);
const canuseLimit = returnCanUseLimitTimeDiscount(
goods,
limitTimeDiscount,
useVipPrice
);
if (canuseLimit) {
//可以使用限时折扣
if (limitTimeDiscount.discountPriority == "limit-time") {
//限时价优先
const result = BigNumber(goods.salePrice)
.times(discountRate)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
return result;
}
if (limitTimeDiscount.discountPriority == "vip-price") {
//会员价优先
if (useVipPrice && goods.memberPrice && goods.memberPrice * 1 > 0) {
//使用会员价
return returnMemberPrice(useVipPrice, goods);
} else {
//不使用会员价
const result = BigNumber(goods.salePrice)
.times(discountRate)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
return result;
}
}
} else {
//不可以使用限时折扣
//会员价优先
if (useVipPrice) {
//使用会员价
return returnMemberPrice(useVipPrice, goods);
} else {
return goods.salePrice;
}
}
}
/**
* 计算商品计算门槛时的金额
*/
export function returnCalcPrice(
goods: BaseCartItem,
fullReductionActivitie: FullReductionActivity | undefined,
limitTimeDiscount: TimeLimitDiscountConfig | null | undefined,
useVipPrice: boolean,
idKey = "product_id"
) {
if (goods.discountSaleAmount && goods.discountSaleAmount * 1 > 0) {
return goods.salePrice;
}
//限时折扣和满减活动都有
if (fullReductionActivitie && limitTimeDiscount) {
if (
fullReductionActivitie.discountShare == 1 &&
fullReductionActivitie.vipPriceShare == 1
) {
//与限时折扣同享,与会员价不同享
return returnLimitPrice(goods, limitTimeDiscount, useVipPrice);
}
if (
fullReductionActivitie.discountShare != 1 &&
fullReductionActivitie.vipPriceShare == 1
) {
//与限时折扣不同享,与会员价同享
return returnMemberPrice(useVipPrice, goods);
}
if (fullReductionActivitie.vipPriceShare != 1) {
//与会员价不同享
return goods.salePrice;
}
return goods.salePrice;
}
//只有满减活动
if (fullReductionActivitie) {
if (fullReductionActivitie.vipPriceShare == 1) {
return returnMemberPrice(useVipPrice, goods);
} else {
return goods.salePrice;
}
}
//只有限时折扣
if (limitTimeDiscount) {
return returnLimitPrice(goods, limitTimeDiscount, useVipPrice);
}
if (useVipPrice) {
return returnMemberPrice(useVipPrice, goods);
}
return goods.salePrice;
}
/**
* 计算满减活动门槛
*/
export function calcFullReductionActivityFullAmount(goodsList: BaseCartItem[],fullReductionActivitie:FullReductionActivity|undefined,limitTimeDiscount:TimeLimitDiscountConfig|null|undefined
,useVipPrice:boolean
) {
if(!fullReductionActivitie){
return 0
export function calcFullReductionActivityFullAmount(
goodsList: BaseCartItem[],
fullReductionActivitie: FullReductionActivity | undefined,
limitTimeDiscount: TimeLimitDiscountConfig | null | undefined,
useVipPrice: boolean,
seatFee: number,
packFee: number
): number {
if (!fullReductionActivitie) {
return 0;
}
let amount=0;
console.log('calcFullReductionActivityFullAmount')
console.log('fullReductionActivitie',fullReductionActivitie)
console.log('limitTimeDiscount',limitTimeDiscount)
for(let goods of goodsList){
const canuseLimit=returnCanUseLimitTimeDiscount(goods,limitTimeDiscount,useVipPrice,"product_id")
console.log('canuseLimit',canuseLimit)
if(!fullReductionActivitie.discountShare){
const availableNum = Math.max(0, goods.number - (goods.returnNum || 0));
amount+=goods.salePrice*availableNum
let amount = 0;
for (let goods of goodsList) {
const availableNum = Math.max(0, goods.number - (goods.returnNum || 0));
if (goods.is_temporary || goods.is_gift || availableNum <= 0) {
//临时菜,赠菜,数量<=0的商品不计算
continue;
}
const calcPrice = returnCalcPrice(
goods,
fullReductionActivitie,
limitTimeDiscount,
useVipPrice,
"product_id"
);
if (calcPrice !== undefined) {
amount += calcPrice * availableNum;
}
}
return amount + seatFee + packFee;
console.log("amount", amount);
}
/**
@@ -516,6 +657,8 @@ export function filterOptimalFullReductionActivity(
activity.thresholds?.length // 至少有一个满减阈值
);
});
console.log("baseEligible", baseEligible);
if (!baseEligible.length) return undefined;
// 第二步:时间筛选(有效期内+周期内+时段内)
@@ -766,6 +909,7 @@ export function calcSingleGoodsRealPrice(
>
): number {
const { isMember, memberDiscountRate, limitTimeDiscount: activity } = config;
console.log("activity", activity);
//如果是增菜价格为0
if (goods.is_gift) {
@@ -776,7 +920,6 @@ export function calcSingleGoodsRealPrice(
if (goods.discountSaleAmount && goods.discountSaleAmount > 0) {
return truncateToTwoDecimals(goods.discountSaleAmount);
}
console.log("calcSingleGoodsRealPrice:goods", goods);
// 2. 优先级2会员价含会员折扣率SKU会员价优先
const memberPrice = new BigNumber(
@@ -798,15 +941,18 @@ export function calcSingleGoodsRealPrice(
if (!activity || !isActivityApplicable) {
return memberPrice.toNumber();
}
console.log("isMember", isMember);
//限时折扣优先或者会员价优先但是不是会员或者未开启会员价格时限时折扣优先
if (
activity.discountPriority == "limit-time" ||
(activity.discountPriority == "vip-price" && !isMember)
(activity.discountPriority == "vip-price" && !isMember) ||
(activity.discountPriority == "vip-price" && isMember && !goods.memberPrice)
) {
//限时折扣优先
return truncateToTwoDecimals(
new BigNumber(goods.salePrice)
.multipliedBy(activity.discountRate / 100)
.times(activity.discountRate / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber()
);
}
@@ -881,6 +1027,7 @@ export function calcGoodsRealAmount(
for (const goods of goodsList) {
const availableNum = Math.max(0, goods.number - (goods.returnNum || 0));
if (availableNum <= 0) continue;
console.log("goods", goods);
const realPrice = new BigNumber(calcSingleGoodsRealPrice(goods, config));
total = total.plus(realPrice.multipliedBy(availableNum));
}
@@ -929,11 +1076,15 @@ export function selectOptimalThreshold(
const validThresholds = thresholds.filter((threshold) => {
const fullAmount = new BigNumber(threshold.fullAmount || 0);
const discountAmount = new BigNumber(threshold.discountAmount || 0);
console.log("fullAmount", fullAmount);
console.log("discountAmount", discountAmount);
return (
fullAmount.isLessThanOrEqualTo(thresholdBase) &&
discountAmount.isGreaterThan(0)
);
});
console.log("validThresholds", validThresholds);
if (!validThresholds.length) return undefined;
@@ -1154,6 +1305,24 @@ export function calcPointDeduction(
};
}
function calcVipDiscountAmount(
goodsRealAmount: number,
shopUserInfo: ShopUserInfo
): number {
if (!shopUserInfo.isVip || shopUserInfo.discount === 0) return 0;
if (shopUserInfo.isVip == 1 && shopUserInfo.isMemberPrice != 1) {
return 0;
}
console.log("goodsRealAmount", goodsRealAmount);
console.log("discount", (100 - (shopUserInfo.discount || 0)) / 100);
return truncateToTwoDecimals(
new BigNumber(goodsRealAmount)
.times((100 - (shopUserInfo.discount || 0)) / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber()
);
}
// ============================ 6. 订单总费用汇总与实付金额计算(核心入口,补充细分字段) ============================
/**
* 计算订单所有费用子项并汇总(核心入口函数)
@@ -1175,14 +1344,15 @@ export function calculateOrderCostSummary(
cartOrder: Record<string, number> = {},
currentTime: Date = new Date()
): OrderCostSummary {
//是否使用霸王餐,霸王餐配置
const {
isFreeDine,
freeDineConfig,
limitTimeDiscount,
fullReductionActivities,
shopUserInfo,
} = config;
console.log("shopUserInfo", shopUserInfo);
// ------------------------------ 1. 基础费用计算 ------------------------------
const goodsOriginalAmount = calcGoodsOriginalAmount(goodsList); // 商品原价总和
@@ -1199,12 +1369,28 @@ export function calculateOrderCostSummary(
goodsOriginalAmount,
goodsRealAmount
); // 商品折扣金额
const newUserDiscount = config.newUserDiscount || 0; // 新客立减
// 其他费用计算(原有逻辑不变) ------------------------------
const packFee = calcTotalPackFee(goodsList, dinnerType); // 打包费
let seatFee = calcSeatFee(config.seatFeeConfig); // 餐位费
seatFee = dinnerType === "dine-in" ? seatFee : 0; // 外卖不收餐位费
const additionalFee = Math.max(0, config.additionalFee); // 附加费
// ------------------------------ 2. 满减活动计算(核心步骤) ------------------------------
let usedFullReductionActivity: FullReductionActivity | undefined;
let usedFullReductionThreshold: FullReductionThreshold | undefined;
let fullReductionAmount = 0;
let usedFullReductionActivityFullAmount = calcFullReductionActivityFullAmount(
goodsList,
usedFullReductionActivity,
config.limitTimeDiscount,
config.isMember,
seatFee,
packFee
);
// 2.1 筛选最优满减活动(后端活动列表、当前店铺、就餐类型、时间)
usedFullReductionActivity = filterOptimalFullReductionActivity(
config.fullReductionActivities,
@@ -1213,17 +1399,6 @@ export function calculateOrderCostSummary(
currentTime
);
calcFullReductionActivityFullAmount(goodsList,usedFullReductionActivity,config.limitTimeDiscount,config.isMember);
console.log("usedFullReductionActivity", usedFullReductionActivity);
// 其他费用计算(原有逻辑不变) ------------------------------
const packFee = calcTotalPackFee(goodsList, dinnerType); // 打包费
let seatFee = calcSeatFee(config.seatFeeConfig); // 餐位费
seatFee = dinnerType === "dine-in" ? seatFee : 0; // 外卖不收餐位费
const additionalFee = Math.max(0, config.additionalFee); // 附加费
// 2.2 计算满减基数(先扣新客立减)
let baseAfterNewUserDiscount = new BigNumber(
limitTimeDiscount &&
@@ -1243,9 +1418,19 @@ export function calculateOrderCostSummary(
// 2.3 选择最优满减阈值(多门槛场景)
if (usedFullReductionActivity) {
//计算当前满减活动的门槛金额
usedFullReductionActivityFullAmount = calcFullReductionActivityFullAmount(
goodsList,
usedFullReductionActivity,
config.limitTimeDiscount,
config.isMember,
seatFee,
packFee
);
usedFullReductionThreshold = selectOptimalThreshold(
usedFullReductionActivity.thresholds,
baseAfterNewUserDiscount,
usedFullReductionActivityFullAmount,
goodsOriginalAmount,
goodsRealAmount,
usedFullReductionActivity.discountShare || 0 // 与限时折扣同享规则
@@ -1258,6 +1443,7 @@ export function calculateOrderCostSummary(
usedFullReductionThreshold
);
}
// ------------------------------ 3. 优惠券抵扣(适配满减同享规则) ------------------------------
let couponDeductionAmount = 0;
let productCouponDeduction = 0;
@@ -1279,6 +1465,7 @@ export function calculateOrderCostSummary(
currentTime,
}
);
console.log("couponResult", couponResult);
couponDeductionAmount = couponResult.deductionAmount;
productCouponDeduction = couponResult.productCouponDeduction;
fullCouponDeduction = couponResult.fullCouponDeduction;
@@ -1286,7 +1473,10 @@ export function calculateOrderCostSummary(
excludedProductIds = couponResult.excludedProductIds;
// 若满减与优惠券同享couponShare=1才计算优惠券否则优惠券抵扣为0
if (usedFullReductionActivity && !usedFullReductionActivity.couponShare) {
if (
usedFullReductionThreshold &&
(!usedFullReductionActivity || !usedFullReductionActivity.couponShare)
) {
couponDeductionAmount = 0;
productCouponDeduction = 0;
fullCouponDeduction = 0;
@@ -1315,20 +1505,20 @@ export function calculateOrderCostSummary(
config.pointDeductionRule,
maxPointDeductionLimit
);
console.log("积分抵扣结果:", pointResult);
pointDeductionAmount = pointResult.deductionAmount;
usedPoints = pointResult.usedPoints;
// 若满减与积分不同享pointsShare=1积分抵扣为0
if (usedFullReductionActivity && !usedFullReductionActivity.pointsShare) {
console.log("满减与积分不同享:积分抵扣为0");
if (
usedFullReductionThreshold &&
(!usedFullReductionActivity || !usedFullReductionActivity.pointsShare)
) {
pointDeductionAmount = 0;
usedPoints = 0;
}
//使用霸王餐
if (isFreeDine && freeDineConfig && freeDineConfig.enable) {
console.log("使用霸王餐");
fullReductionAmount = 0;
//不与优惠券同享
if (!freeDineConfig.withCoupon) {
@@ -1386,9 +1576,21 @@ export function calculateOrderCostSummary(
truncateToTwoDecimals(merchantReductionActualAmount)
);
// 会员折扣减免
const vipDiscountAmount = calcVipDiscountAmount(
new BigNumber(goodsRealAmount)
.minus(couponDeductionAmount)
.plus(packFee)
.plus(seatFee)
.minus(newUserDiscount)
.minus(fullReductionAmount)
.toNumber(),
shopUserInfo
); // 会员折扣减免金额
// ------------------------------ 6. 最终实付金额计算 ------------------------------
const finalPayAmountBn = new BigNumber(goodsRealAmount)
.minus(newUserDiscount)
.minus(vipDiscountAmount)
.minus(fullReductionAmount)
.minus(couponDeductionAmount)
.minus(pointDeductionAmount)
@@ -1416,6 +1618,7 @@ export function calculateOrderCostSummary(
.plus(couponDeductionAmount)
.plus(pointDeductionAmount)
.plus(merchantReductionActualAmount)
.plus(vipDiscountAmount)
.toNumber()
);
//积分可抵扣最大金额 最终支付金额+积分抵扣-商家减免
@@ -1424,8 +1627,6 @@ export function calculateOrderCostSummary(
.minus(merchantReductionActualAmount)
.toNumber();
console.log("限时折扣:", config.limitTimeDiscount);
// ------------------------------ 8. 返回完整结果 ------------------------------
return {
goodsTotal: goodsList.reduce(
@@ -1448,10 +1649,12 @@ export function calculateOrderCostSummary(
scoreMaxMoney,
// 满减活动明细(后端字段)
fullReduction: {
usedFullReductionActivityFullAmount: usedFullReductionActivityFullAmount,
usedActivity: usedFullReductionActivity,
usedThreshold: usedFullReductionThreshold,
actualAmount: truncateToTwoDecimals(fullReductionAmount),
},
vipDiscountAmount: vipDiscountAmount, //会员折扣减免金额
merchantReduction: {
type: merchantReductionConfig.type,
originalConfig: merchantReductionConfig,