fix: 代客下单修改

This commit is contained in:
2025-10-09 16:48:13 +08:00
parent 5cd5265ffb
commit 4b9fc0ad3f
11 changed files with 436 additions and 246 deletions

View File

@@ -1,33 +1,22 @@
import { BigNumber } from "bignumber.js";
import _ from "lodash";
/**
* 返回可以抵扣优惠券的商品列表,过滤掉赠品临时商品,价格从高到低排序
* @param arr 商品列表
* @param user 用户信息
*/
export function returnCanDikouGoods(arr, user) {
return arr
.filter((v) => {
return v.is_temporary != 1 && v.is_gift != 1;
})
.filter((v) => {
return v.num > 0;
})
.sort((a, b) => {
return returnGoodsPrice(b, user) - returnGoodsPrice(a, user);
});
}
/**
* 返回商品单价
* @param goods 商品
* @param user 用户信息
* @param {Object} shopInfo
*/
export function returnGoodsPrice(goods, user) {
export function returnGoodsPrice(goods, user, shopInfo) {
if (!goods) {
return 0;
}
if (goods.discount_sale_amount * 1 > 0) {
return goods.discount_sale_amount;
}
if (shopInfo && !shopInfo.isMemberPrice) {
return goods.salePrice;
}
if (user.isVip && goods.memberPrice * 1 <= goods.salePrice * 1 && goods.memberPrice * 1 > 0) {
return goods.memberPrice;
}
@@ -75,116 +64,183 @@ export function returnCoupType(coupon) {
* @param user 用户信息
*/
export function returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user) {
const types = [2, 4, 6];
// 收集已抵扣商品并关联对应的优惠券类型
const goodsCouponGoods = selCoupon
.filter((v) => v.type == 2)
.reduce((prve, cur) => {
prve.push(...cur.discount.hasDiscountGoodsArr);
return prve;
.filter((v) => types.includes(v.type))
.reduce((prev, cur) => {
// 给每个抵扣商品添加所属优惠券类型
const goodsWithType = cur.discount.hasDiscountGoodsArr.map((goods) => ({
...goods,
couponType: cur.type, // 记录该商品是被哪种类型的优惠券抵扣的
}));
prev.push(...goodsWithType);
return prev;
}, []);
const arr = _.cloneDeep(canDikouGoodsArr)
.map((v) => {
const findCart = goodsCouponGoods.find((carts) => carts.id == v.id);
if (findCart) {
v.num -= findCart.num;
// 根据优惠券类型判断扣减数量
if ([4, 6].includes(findCart.couponType)) {
// 类型4第二件半价或6买一送一数量减2
v.num -= 2;
} else {
// 其他类型如类型2商品券按原逻辑扣减对应数量
v.num -= findCart.num;
}
}
return v;
})
.filter((v) => v.num > 0);
.filter((v) => v.num > 0); // 过滤掉数量<=0的商品
return arr;
}
/**
* 判断优惠券是否可使用
* 判断优惠券是否可使用并返回不可用原因
*
* @param {Object} args - 函数参数集合
* @param {Array} args.canDikouGoodsArr - 可参与抵扣的商品列表每个元素包含商品信息productIdnum等
* @param {Array} args.canDikouGoodsArr - 可参与抵扣的商品列表
* @param {Object} args.coupon - 优惠券信息对象
* @param {boolean} args.coupon.use - 优惠券是否启用true为启用false为禁用
* @param {Array} args.coupon.useFoods - 优惠券适用的商品ID列表空数组表示适用全部商品
* @param {boolean} args.coupon.use - 优惠券是否启用
* @param {Array} args.coupon.useFoods - 优惠券适用的商品ID列表
* @param {number} args.coupon.fullAmount - 优惠券使用门槛金额
* @param {number} args.coupon.type - 优惠券类型1:满减券, 2:商品券, 3:折扣券, 4:第二件半价券, 6:买一送一券
* @param {number} args.goodsOrderPrice - 订单中所有商品的总金额未筛选时的初始金额
* @param {Object} args.user - 用户信息对象用于计算商品价格如会员价等
* @param {Object} args.user - 用户信息对象用于计算商品价格如会员价等
* @param {number} args.coupon.type - 优惠券类型
* @param {number} args.goodsOrderPrice - 订单中所有商品的总金额
* @param {Object} args.user - 用户信息对象
* @param {Object} args.selCoupon - 已经选择的优惠券信息对象
* @returns {boolean} - 优惠券是否可用true可用false不可用
* @param {Object} args.shopInfo
* @returns {Object} - { canUse: boolean, reason: string } 可用状态及不可用原因
*/
export function returnCouponCanUse(args) {
let { canDikouGoodsArr, coupon, goodsOrderPrice, user, selCoupon } = args;
let { canDikouGoodsArr, coupon, goodsOrderPrice, user, selCoupon, shopInfo } = args;
// 优惠券未启用
if (!coupon.use) {
return false;
return {
canUse: false,
reason: "优惠券未启用",
};
}
canDikouGoodsArr = returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user);
// 计算门槛金额
let fullAmount = goodsOrderPrice;
//是否抵扣全部商品
const isDikouAll = coupon.useFoods.length === 0;
let canCalcGoodsArr = [];
// 订单里参与门槛计算的商品
if (!isDikouAll) {
canDikouGoodsArr = returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user, shopInfo);
//优惠券指定门槛商品列表
let canCalcGoodsArr = [...canDikouGoodsArr];
//部分商品参与门槛计算
if (coupon.thresholdFoods.length) {
canCalcGoodsArr = canDikouGoodsArr.filter((v) => {
return coupon.useFoods.find((food) => food.id == v.productId);
return coupon.thresholdFoods.find((food) => food.id == v.productId);
});
fullAmount = canCalcGoodsArr.reduce((pre, cur) => {
return pre + returnGoodsPrice(cur, user) * cur.num;
return pre + returnGoodsPrice(cur, user, shopInfo) * cur.num;
}, 0);
}
//没有符合商品
if (!isDikouAll && canCalcGoodsArr.length == 0) {
return false;
// 是否全部商品可用
const isDikouAll = coupon.useFoods.length === 0;
// 订单可用商品列表
let canUseGoodsArr = [];
if (!isDikouAll) {
canUseGoodsArr = canDikouGoodsArr.filter((v) => {
return coupon.useFoods.find((food) => food.id == v.productId);
});
}
if (user.isVip && !coupon.vipPriceShare) {
return {
canUse: false,
reason: "非会员可用",
};
}
if (selCoupon.length > 0 && !selCoupon[0].otherCouponShare) {
return {
canUse: false,
reason: "当前选中的券不可与其他券同享",
};
}
if (selCoupon.length > 0 && !coupon.otherCouponShare) {
return {
canUse: false,
reason: "当前选中的券不可与其他券同享",
};
}
// 满减券和折扣券计算门槛金额是否满足
if ([1, 3].includes(coupon.type)) {
if (canCalcGoodsArr.length <= 0) {
return {
canUse: false,
reason: "没有可参与计算门槛的商品",
};
}
// 不满足门槛金额
if (fullAmount < coupon.fullAmount) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
}
// 商品兑换券,第二件半价和买一送一判断是否有可用商品
if ([2, 4, 5].includes(coupon.type)) {
// 没有符合条件的商品
if (!isDikouAll && canCalcGoodsArr.length === 0) {
return {
canUse: false,
reason: "没有符合条件的商品",
};
}
}
//商品兑换券是否达到门槛金额
if (coupon.type == 2 && goodsOrderPrice < coupon.fullAmount) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
//不满足门槛金额
if (fullAmount < coupon.fullAmount) {
console.log("不满足门槛金额");
return false;
}
if (coupon.type == 2) {
//商品券
return isDikouAll || canCalcGoodsArr.length > 0;
}
if (coupon.type == 1) {
//满减券
return fullAmount >= coupon.fullAmount;
}
if (coupon.type == 6) {
//买一送一券
// 买一送一券特殊验证
if (coupon.type === 6) {
let canUse = false;
if (isDikouAll) {
canUse = canDikouGoodsArr.some((v) => v.num >= 2);
} else if (canCalcGoodsArr.length > 0) {
canUse = canCalcGoodsArr.some((v) => v.num >= 2);
}
if (canCalcGoodsArr.length > 0) {
canUse = canDikouGoodsArr
.filter((v) => {
return coupon.useFoods.find((food) => food.id == v.productId);
})
.some((v) => v.num >= 2);
if (!canUse) {
return {
canUse: false,
reason: "需要购买至少2件相同的商品才能使用",
};
}
return canUse;
}
if (coupon.type == 4) {
//第二件半价券
// 第二件半价券特殊验证
if (coupon.type === 4) {
let canUse = false;
if (isDikouAll) {
canUse = canDikouGoodsArr.some((v) => v.num >= 2);
} else if (canCalcGoodsArr.length > 0) {
canUse = canCalcGoodsArr.some((v) => v.num >= 2);
}
if (canCalcGoodsArr.length > 0) {
canUse = canDikouGoodsArr
.filter((v) => {
return coupon.useFoods.find((food) => food.id == v.productId);
})
.some((v) => v.num >= 2);
if (!canUse) {
return {
canUse: false,
reason: "需要购买至少2件相同的商品才能使用",
};
}
return canUse;
}
if (coupon.type == 3) {
//折扣券
return true;
}
// 所有条件都满足
return {
canUse: true,
reason: "",
};
}
/**
@@ -192,8 +248,9 @@ export function returnCouponCanUse(args) {
* @param discountGoodsArr 可抵扣商品列表
* @param discountNum 抵扣数量
* @param user 用户信息
* @param {Object} shopInfo 店铺信息
*/
export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user) {
export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user, shopInfo) {
let hasCountNum = 0;
let discountPrice = 0;
let hasDiscountGoodsArr = [];
@@ -204,31 +261,38 @@ export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user) {
const goods = discountGoodsArr[i];
const shengyuNum = discountNum - hasCountNum;
const num = Math.min(goods.num, shengyuNum);
discountPrice += returnGoodsPrice(goods, user) * num;
discountPrice += returnGoodsPrice(goods, user, shopInfo) * num;
hasCountNum += num;
hasDiscountGoodsArr.push({ ...goods, num });
hasDiscountGoodsArr.push({
...goods,
num,
});
}
return { discountPrice, hasDiscountGoodsArr };
return {
discountPrice,
hasDiscountGoodsArr,
};
}
/**
* 计算优惠券抵扣金额
* @param canDikouGoodsArr 可抵扣商品列表
* @param arr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表
* @param shopInfo 店铺信息
*/
export function returnCouponDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice, selCoupon) {
canDikouGoodsArr = returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user);
export function returnCouponDiscount(arr, coupon, user, goodsOrderPrice, selCoupon, shopInfo) {
const canDikouGoodsArr = returnCanDikouGoodsArr(arr, selCoupon, user);
if (coupon.type == 2) {
return returnCouponProductDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice);
return returnCouponProductDiscount(canDikouGoodsArr, coupon, user, shopInfo);
}
if (coupon.type == 6) {
return returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice);
return returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopInfo);
}
if (coupon.type == 4) {
return returnSecoendDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice);
return returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo);
}
if (coupon.type == 3) {
return returnCouponZhekouDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice, selCoupon);
@@ -253,21 +317,28 @@ export function returnCouponZhekouDiscount(
) {
const { discountRate, maxDiscountAmount } = coupon;
// 计算商品优惠券折扣总和使用BigNumber避免精度问题
const goodsCouponDiscount = selCoupon
.filter((v) => v.type == 2)
.reduce((prve, cur) => {
return prve + cur.discount.discountPrice;
}, 0);
goodsOrderPrice -= goodsCouponDiscount;
return new BigNumber(prve).plus(new BigNumber(cur.discount.discountPrice));
}, new BigNumber(0));
// 使用bignumber处理高精度计算
// 1. 计算折扣率百分比转小数discountRate / 100
const discountRatio = new BigNumber(discountRate).dividedBy(100);
// 2. 计算优惠比例1 - 折扣率例如8折的优惠比例是 1 - 0.8 = 0.2
const discountAmountRatio = new BigNumber(1).minus(discountRatio);
// 3. 计算折扣金额:商品订单金额 × 优惠比例
let discountPrice = new BigNumber(goodsOrderPrice).times(discountAmountRatio).toNumber();
if (maxDiscountAmount != 0) {
// 将商品订单价格转换为BigNumber并减去优惠券折扣
const adjustedGoodsOrderPrice = new BigNumber(goodsOrderPrice).minus(goodsCouponDiscount);
console.log("adjustedGoodsOrderPrice", adjustedGoodsOrderPrice.toNumber());
// 计算优惠比例:(100 - 折扣率) / 100
const discountAmountRatio = new BigNumber(100).minus(discountRate).dividedBy(100);
// 计算折扣金额:调整后的商品订单金额 × 优惠比例
let discountPrice = adjustedGoodsOrderPrice
.times(discountAmountRatio)
.decimalPlaces(2, BigNumber.ROUND_FLOOR)
.toNumber();
// 应用最大折扣金额限制
if (maxDiscountAmount !== 0) {
discountPrice = discountPrice >= maxDiscountAmount ? maxDiscountAmount : discountPrice;
}
@@ -282,13 +353,12 @@ export function returnCouponZhekouDiscount(
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
*/
export function returnCouponProductDiscount(canDikouGoodsArr, coupon, user) {
export function returnCouponProductDiscount(canDikouGoodsArr, coupon, user, shopInfo) {
const { useFoods, discountNum, useRule } = coupon;
//抵扣商品数组
let discountGoodsArr = [];
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
@@ -311,8 +381,7 @@ export function returnCouponProductDiscount(canDikouGoodsArr, coupon, user) {
discountGoodsArr = discountSelGoodsArr.slice(0, discountNum);
}
}
const result = calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user);
console.log(result);
const result = calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user, shopInfo);
return result;
}
@@ -321,8 +390,9 @@ export function returnCouponProductDiscount(canDikouGoodsArr, coupon, user) {
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
*/
function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user) {
function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopInfo) {
const { useFoods, useRule } = coupon;
//抵扣商品
let discountGoods = undefined;
@@ -338,18 +408,18 @@ function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user) {
} else {
//符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) => useFoods.find((food) => food.id == v.productId));
console.log(canUseGoods1);
if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else {
discountGoods = canUseGoods1.slice(0, 1);
}
}
console.log("discountGoods");
console.log(discountGoods);
const discountPrice = returnGoodsPrice(discountGoods, user);
const discountPrice = returnGoodsPrice(discountGoods, user, shopInfo);
const hasDiscountGoodsArr = [discountGoods];
return { discountPrice, hasDiscountGoodsArr };
return {
discountPrice,
hasDiscountGoodsArr,
};
}
/**
@@ -357,8 +427,9 @@ function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user) {
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
*/
function returnSecoendDiscount(canDikouGoodsArr, coupon, user) {
function returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo) {
const { useFoods, useRule } = coupon;
//抵扣商品
let discountGoods = undefined;
@@ -374,14 +445,15 @@ function returnSecoendDiscount(canDikouGoodsArr, coupon, user) {
} else {
//符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) => useFoods.find((food) => food.id == v.productId));
console.log(canUseGoods1);
if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else {
discountGoods = canUseGoods1.slice(0, 1);
}
}
const discountPrice = returnGoodsPrice(discountGoods, user);
console.log("returnSecoendDiscount:discountGoods", discountGoods);
const discountPrice = returnGoodsPrice(discountGoods[0], user, shopInfo);
console.log("returnSecoendDiscount:discountPrice", discountPrice);
const hasDiscountGoodsArr = [discountGoods];
//返回半价价格
return {
@@ -389,3 +461,23 @@ function returnSecoendDiscount(canDikouGoodsArr, coupon, user) {
hasDiscountGoodsArr,
};
}
/**
* 返回可以抵扣优惠券的商品列表,过滤掉赠品临时商品,价格从高到低排序
* @param arr 商品列表
* @param user 用户信息
* @param shopInfo 店铺信息
*/
export function returnCanDikouGoods(arr, user, shopInfo) {
const result = arr
.filter((v) => {
return v.is_temporary != 1 && v.is_gift != 1;
})
.filter((v) => {
return v.num > 0;
})
.sort((a, b) => {
return returnGoodsPrice(b, user, shopInfo) - returnGoodsPrice(a, user, shopInfo);
});
return result;
}

View File

@@ -60,8 +60,8 @@ export interface BackendCoupon {
id?: number; // 自增主键int64
shopId?: number; // 店铺IDint64
syncId?: number; // 同步Idint64
couponType?: number; // 优惠券类型1-满减券2-商品兑换券3-折扣券4-第二件半价券5-消费送券6-买一送一券7-固定价格券8-免配送费券
title?: string; // 券名称
type?: number; // 优惠券类型1-满减券2-商品兑换券3-折扣券4-第二件半价券5-消费送券6-买一送一券7-固定价格券8-免配送费券
name?: string; // 券名称
useShopType?: string; // 可用门店类型only-仅本店all-所有门店custom-指定门店
useShops?: string; // 可用门店(逗号分隔字符串,如"1,2,3"
useType?: string; // 可使用类型dine堂食/pickup自取/deliv配送/express快递
@@ -228,6 +228,7 @@ export interface OrderExtraConfig {
userPoints: number; // 用户当前积分(用于积分抵扣)
isMember: boolean; // 用户是否会员(用于会员优惠)
memberDiscountRate?: number; // 会员折扣率如0.95=95折无会员价时用
newUserDiscount?: number; // 新用户减免金额默认0
}
/** 订单费用汇总(修改:补充商家减免类型和明细) */
@@ -251,6 +252,9 @@ export interface OrderCostSummary {
finalPayAmount: number; // 最终实付金额
couponUsed?: Coupon; // 实际使用的优惠券
pointUsed: number; // 实际使用的积分
newUserDiscount: number; // 新用户减免金额默认0
dinnerType?: 'dine-in' | 'take-out'; // 就餐类型(堂食/自取/配送/快递)
}
// ============================ 2. 基础工具函数核心修正所有商品ID匹配用product_id ============================
@@ -269,15 +273,15 @@ export function convertBackendCouponToToolCoupon(
currentTime: Date = new Date()
): Coupon | null {
// 1. 基础校验必选字段缺失直接返回null
if (!backendCoupon.id || backendCoupon.type === undefined || !backendCoupon.title) {
if (!backendCoupon.id || backendCoupon.type === undefined) {
console.warn('优惠券必选字段缺失', backendCoupon);
return null;
}
// 2. 转换券类型:后端数字枚举 → 工具库字符串枚举
const couponType = mapBackendCouponTypeToTool(backendCoupon.couponType);
const couponType = mapBackendCouponTypeToTool(backendCoupon.type);
if (!couponType) {
console.warn(`不支持的优惠券类型:${backendCoupon.couponType}券ID${backendCoupon.id}`);
console.warn(`不支持的优惠券类型:${backendCoupon.type}券ID${backendCoupon.id}`);
return null;
}
@@ -286,16 +290,17 @@ export function convertBackendCouponToToolCoupon(
? [] // 空字符串/undefined → 全部商品按商品ID匹配
: backendCoupon.foods.split(',').map(id => id.trim()); // 逗号分隔 → 指定商品ID数组
const useType = backendCoupon?.useType?.split(',')?.map(v => v.replace(/[\[\]]/g, '').replace(/""/g, '"').replace(/["']/g, '')) || [];
// 4. 计算基础公共字段(含多维度可用性校验)
const baseCoupon: BaseCoupon = {
id: backendCoupon.id,
type: couponType,
name: backendCoupon.title,
name: backendCoupon.name || '',
available: isCouponAvailable(backendCoupon, currentStoreId, dinnerType, currentTime),
useShops: getApplicableStoreIds(backendCoupon, currentStoreId),
discountShare: backendCoupon.discountShare === 1,
vipPriceShare: backendCoupon.vipPriceShare === 1,
useType: backendCoupon.useType ? backendCoupon.useType.split(',') : [],
useType: useType,
isValid: isCouponInValidPeriod(backendCoupon, currentTime),
applicableProductIds: applicableProductIds,
};
@@ -365,7 +370,7 @@ function isCouponAvailable(
currentTime: Date = new Date()
): boolean {
// 1. 状态校验必须启用status=1
if (backendCoupon.status !== 1) return false;
if (backendCoupon.status === 0) return false;
// 3. 有效期校验:必须在有效期内
if (!isCouponInValidPeriod(backendCoupon, currentTime)) return false;
@@ -377,13 +382,13 @@ function isCouponAvailable(
if (!isCouponInDailyTimeRange(backendCoupon, currentTime)) return false;
// 6. 每周周期校验当前星期几需在可用周期内useDays非空时生效
if (!isCouponInWeekDays(backendCoupon, currentTime)) return false;
// if (!isCouponInWeekDays(backendCoupon, currentTime)) return false;
// 7. 门店匹配校验:当前门店需在适用门店范围内
if (!isStoreMatch(backendCoupon, currentStoreId)) return false;
// if (!isStoreMatch(backendCoupon, currentStoreId)) return false;
// 8. 就餐类型校验:当前就餐类型需在可用类型范围内
if (!isDinnerTypeMatch(backendCoupon, dinnerType)) return false;
// if (!isDinnerTypeMatch(backendCoupon, dinnerType)) return false;
return true;
}
@@ -1188,81 +1193,29 @@ export function calcCouponDeduction(
usedCoupon?: Coupon;
excludedProductIds: string[]; // 排除的商品ID列表商品ID
} {
// 1. 后端优惠券转工具库Coupon过滤无效/不支持的券)
const toolCoupons = backendCoupons
.map(coupon => convertBackendCouponToToolCoupon(
coupon,
config.currentStoreId,
config.dinnerType,
config.currentTime
))
.filter(Boolean) as Coupon[];
if (toolCoupons.length === 0) {
return {
deductionAmount: 0,
productCouponDeduction: 0,
fullCouponDeduction: 0,
excludedProductIds: []
};
}
console.log('toolCoupons', toolCoupons)
// 2. 优惠券互斥逻辑:兑换券与其他券互斥,优先选最优
const exchangeCoupons = toolCoupons.filter(c => c.type === CouponType.EXCHANGE);
const nonExchangeCoupons = toolCoupons.filter(c => c.type !== CouponType.EXCHANGE);
const goodsCoupon = backendCoupons.filter(v => v.type == 2)
const discountCoupon = backendCoupons.filter(v => v.type != 2)
// 3. 计算非兑换券最优抵扣传递已抵扣商品ID避免重复统计细分字段
let nonExchangeResult: CouponResult = {
deductionAmount: 0,
deductionAmount: discountCoupon.reduce((prve, cur): number => {
return prve + (cur.discountAmount || 0)
}, 0),
excludedProductIds: [],
usedCoupon: undefined,
productCouponDeduction: 0,
fullCouponDeduction: 0
};
if (nonExchangeCoupons.length > 0) {
nonExchangeResult = nonExchangeCoupons.reduce((best, coupon) => {
const strategy = getCouponStrategy(coupon.type);
const result = strategy.calculate(coupon, goodsList, {
...config,
excludedProductIds: best.excludedProductIds // 传递已排除的商品ID商品ID
});
const currentResult: CouponResult = {
deductionAmount: result.deductionAmount,
excludedProductIds: result.excludedProductIds,
usedCoupon: coupon,
// 按策略返回的字段赋值细分抵扣
productCouponDeduction: result.productCouponDeduction || 0,
fullCouponDeduction: result.fullCouponDeduction || 0
};
// 按总抵扣金额选择最优
return new BigNumber(currentResult.deductionAmount).isGreaterThan(best.deductionAmount)
? currentResult
: best;
}, nonExchangeResult);
}
// 4. 计算兑换券抵扣排除非兑换券已抵扣的商品ID统计商品券细分
let exchangeResult: ExchangeCalculationResult = {
deductionAmount: 0,
deductionAmount: goodsCoupon.reduce((prve, cur): number => {
return prve + (cur.discountAmount || 0)
}, 0),
excludedProductIds: [],
productCouponDeduction: 0
};
if (exchangeCoupons.length > 0) {
exchangeResult = exchangeCoupons.reduce((best, coupon) => {
const strategy = getCouponStrategy(coupon.type);
const result = strategy.calculate(coupon, goodsList, {
...config,
excludedProductIds: [...nonExchangeResult.excludedProductIds, ...best.excludedProductIds] // 合并排除的商品ID
});
return new BigNumber(result.deductionAmount).isGreaterThan(best.deductionAmount)
? {
deductionAmount: result.deductionAmount,
excludedProductIds: result.excludedProductIds,
productCouponDeduction: result.productCouponDeduction || 0 // 兑换券属于商品券
}
: best;
}, exchangeResult);
}
// 5. 汇总结果:兑换券与非兑换券不可同时使用,取抵扣金额大的
const exchangeBn = new BigNumber(exchangeResult.deductionAmount);
@@ -1270,9 +1223,9 @@ export function calcCouponDeduction(
const isExchangeBetter = exchangeBn.isGreaterThan(nonExchangeBn);
return {
deductionAmount: truncateToTwoDecimals(isExchangeBetter ? exchangeResult.deductionAmount : nonExchangeResult.deductionAmount),
productCouponDeduction: isExchangeBetter ? exchangeResult.productCouponDeduction : nonExchangeResult.productCouponDeduction,
fullCouponDeduction: isExchangeBetter ? 0 : nonExchangeResult.fullCouponDeduction, // 兑换券与满减券互斥满减券抵扣置0
deductionAmount: exchangeBn.plus(nonExchangeBn).toNumber(),
productCouponDeduction: exchangeResult.deductionAmount,
fullCouponDeduction: nonExchangeResult.deductionAmount, // 兑换券与满减券互斥满减券抵扣置0
usedCoupon: isExchangeBetter ? undefined : nonExchangeResult.usedCoupon,
excludedProductIds: isExchangeBetter ? exchangeResult.excludedProductIds : nonExchangeResult.excludedProductIds
};
@@ -1289,6 +1242,7 @@ export function calcTotalPackFee(
goodsList: BaseCartItem[],
dinnerType: 'dine-in' | 'take-out'
): number {
if (dinnerType !== 'take-out') return 0;
let total = new BigNumber(0);
for (const goods of goodsList) {
@@ -1298,7 +1252,7 @@ export function calcTotalPackFee(
// 计算单个商品打包数量外卖全打包堂食按配置称重商品≤1
let packNum = dinnerType === 'take-out'
? availableNum
: (goods.packNumber || 0);
: (0);
if (goods.product_type === GoodsType.WEIGHT) {
packNum = Math.min(packNum, 1);
}
@@ -1415,8 +1369,11 @@ export function calculateOrderCostSummary(
);
// 3. 其他费用计算(原有逻辑不变)
// 新客立减
const newUserDiscount = config.newUserDiscount || 0;
const packFee = calcTotalPackFee(goodsList, dinnerType);
const seatFee = calcSeatFee(config.seatFeeConfig);
let seatFee = calcSeatFee(config.seatFeeConfig);
seatFee = dinnerType === 'dine-in' ? seatFee : 0; // 外卖不收餐位费
const additionalFee = Math.max(0, config.additionalFee);
// 4. 积分抵扣(原有逻辑不变,先于商家减免计算)
@@ -1475,6 +1432,7 @@ export function calculateOrderCostSummary(
const finalPayAmount = new BigNumber(goodsOriginalAmount) // 商品原价总和
.minus(goodsDiscountAmount) // 减去商品折扣
.minus(couponDeductionAmount) // 减去优惠券抵扣
.minus(newUserDiscount) // 新客立减
.minus(pointDeductionAmount) // 减去积分抵扣
.minus(merchantReductionActualAmount) // 减去商家实际减免金额
.plus(seatFee) // 加上餐位费(不参与减免)
@@ -1503,7 +1461,9 @@ export function calculateOrderCostSummary(
additionalFee,
finalPayAmount: truncateToTwoDecimals(finalPayAmountNonNegative),
couponUsed: usedCoupon,
pointUsed: usedPoints
pointUsed: usedPoints,
newUserDiscount,
dinnerType
};
}
@@ -1522,6 +1482,7 @@ export const OrderPriceCalculator = {
isWeightGoods,
// 优惠券转换
convertBackendCouponToToolCoupon,
mapBackendCouponTypeToTool,
// 商品价格计算
calcSingleGoodsRealPrice,
calcGoodsOriginalAmount,