订单结算修改,增加限时折扣

This commit is contained in:
2025-10-24 16:13:40 +08:00
parent 5aa469a7b7
commit 26532150b5
36 changed files with 4989 additions and 1885 deletions

1507
utils/goods copy.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +1,55 @@
import {
BigNumber
} from "bignumber.js";
import { BigNumber } from "bignumber.js";
import _ from "lodash";
/**
* 返回商品单价
* @param goods 商品
* @param user 用户信息
* @param {Object} shopInfo
* @param {Object} shopInfo 店铺信息
* @param {boolean} limitTimeDiscount - 限时折扣
*/
export function returnGoodsPrice(goods, user, shopInfo) {
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;
}
return goods.salePrice;
export function returnGoodsPrice(goods, user, shopInfo, limitTimeDiscount) {
if (!goods) {
return 0;
}
// 商家改价
if (goods.discount_sale_amount * 1 > 0) {
return goods.discount_sale_amount;
}
// 限时折扣
if (limitTimeDiscount && limitTimeDiscount.id) {
if (
limitTimeDiscount.foodType == 1 &&
limitTimeDiscount.discountPriority == "limit-time"
) {
return new BigNumber(goods.salePrice)
.times(limitTimeDiscount.discountRate / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
}
const canUseFoods = limitTimeDiscount.foods.split(",");
if (
limitTimeDiscount.foodType == 2 &&
limitTimeDiscount.discountPriority == "limit-time" &&
canUseFoods.includes(`${goods.productId}`)
) {
return new BigNumber(goods.salePrice)
.times(limitTimeDiscount.discountRate / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
}
}
if (shopInfo && !shopInfo.isMemberPrice) {
return goods.salePrice;
}
if (
user.isVip &&
goods.memberPrice * 1 <= goods.salePrice * 1 &&
goods.memberPrice * 1 > 0
) {
return goods.memberPrice;
}
return goods.salePrice;
}
/**
@@ -29,15 +57,15 @@ export function returnGoodsPrice(goods, user, shopInfo) {
* @param arr 商品列表
*/
export function returnGoodsGroupMap(arr) {
let map = {};
arr.forEach((v) => {
const key = v.productId + "_" + v.skuId;
if (!map[key]) {
map[key] = [];
}
map[key].push(v);
});
return map;
let map = {};
arr.forEach((v) => {
const key = v.productId + "_" + v.skuId;
if (!map[key]) {
map[key] = [];
}
map[key].push(v);
});
return map;
}
/**
@@ -45,17 +73,17 @@ export function returnGoodsGroupMap(arr) {
* @param coupon
*/
export function returnCoupType(coupon) {
const couponTypes = {
1: "满减券",
2: "商品券",
3: "折扣券",
4: "第二件半价券",
5: "消费送券",
6: "买一送一券",
7: "固定价格券",
8: "免配送费券",
};
return couponTypes[coupon.type] || "未知类型";
const couponTypes = {
1: "满减券",
2: "商品券",
3: "折扣券",
4: "第二件半价券",
5: "消费送券",
6: "买一送一券",
7: "固定价格券",
8: "免配送费券",
};
return couponTypes[coupon.type] || "未知类型";
}
/**
@@ -65,22 +93,37 @@ export function returnCoupType(coupon) {
* @param user 用户信息
*/
export function returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user) {
const goodsCouponGoods = selCoupon
.filter((v) => v.type == 2)
.reduce((prve, cur) => {
prve.push(...cur.discount.hasDiscountGoodsArr);
return prve;
}, []);
const arr = _.cloneDeep(canDikouGoodsArr)
.map((v) => {
const findCart = goodsCouponGoods.find((carts) => carts.id == v.id);
if (findCart) {
v.num -= findCart.num;
}
return v;
})
.filter((v) => v.num > 0);
return arr;
const types = [2, 4, 6];
// 收集已抵扣商品并关联对应的优惠券类型
const goodsCouponGoods = selCoupon
.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) {
// 根据优惠券类型判断扣减数量
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); // 过滤掉数量<=0的商品
return arr;
}
/**
@@ -97,139 +140,208 @@ export function returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user) {
* @param {Object} args.user - 用户信息对象
* @param {Object} args.selCoupon - 已经选择的优惠券信息对象
* @param {Object} args.shopInfo
* @param {boolean} args.limitTimeDiscount - 限时折扣
* @returns {Object} - { canUse: boolean, reason: string } 可用状态及不可用原因
*/
export function returnCouponCanUse(args) {
let {
canDikouGoodsArr,
coupon,
goodsOrderPrice,
user,
selCoupon,
shopInfo
} = args;
let {
canDikouGoodsArr,
coupon,
goodsOrderPrice,
user,
selCoupon,
shopInfo,
isMemberPrice,
limitTimeDiscount,
} = args;
// 优惠券未启用
if (!coupon.use) {
return {
canUse: false,
reason: coupon.noUseRestrictions || "不在可用时间段内",
};
}
if(limitTimeDiscount&&limitTimeDiscount.id&&!coupon.discountShare){
return {
canUse: false,
reason: coupon.noUseRestrictions || "不可与限时折扣同享",
};
}
// 优惠券未启用
if (!coupon.use) {
return {
canUse: false,
reason: "优惠券未启用"
};
}
// 计算门槛金额
let fullAmount = goodsOrderPrice;
canDikouGoodsArr = returnCanDikouGoodsArr(
canDikouGoodsArr,
selCoupon,
user,
shopInfo,
limitTimeDiscount
);
//优惠券指定门槛商品列表
let canCalcGoodsArr = [...canDikouGoodsArr];
//部分商品参与门槛计算
if (coupon.thresholdFoods.length) {
canCalcGoodsArr = canDikouGoodsArr.filter((v) => {
return coupon.thresholdFoods.find((food) => food.id == v.productId);
});
fullAmount = canCalcGoodsArr.reduce((pre, cur) => {
return (
pre + returnGoodsPrice(cur, user, shopInfo, limitTimeDiscount) * cur.num
);
}, 0);
}
canDikouGoodsArr = returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user,shopInfo);
// 计算门槛金额
let fullAmount = goodsOrderPrice;
// 是否抵扣全部商品
const isDikouAll = coupon.useFoods.length === 0;
let canCalcGoodsArr = [];
// 订单里参与门槛计算的商品
if (!isDikouAll) {
canCalcGoodsArr = canDikouGoodsArr.filter((v) => {
return coupon.useFoods.find((food) => food.id == v.productId);
});
fullAmount = canCalcGoodsArr.reduce((pre, cur) => {
return pre + returnGoodsPrice(cur, user,shopInfo) * cur.num;
}, 0);
}
// 是否全部商品可用
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 (coupon.type == 2 && fullAmount < coupon.fullAmount) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
// 没有符合条件的商品
if (!isDikouAll && canCalcGoodsArr.length === 0) {
return {
canUse: false,
reason: "没有符合条件的商品"
};
}
// 没有符合条件的商品
if (isDikouAll && canDikouGoodsArr.length === 0) {
return {
canUse: false,
reason: "没有符合条件的商品",
};
}
if (!isDikouAll && canUseGoodsArr.length === 0) {
return {
canUse: false,
reason: "没有符合条件的商品",
};
}
}
//商品兑换券是否达到门槛金额
if (coupon.type == 2 && goodsOrderPrice < coupon.fullAmount) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
// 不满足门槛金额
if (fullAmount < coupon.fullAmount) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`
};
}
// 买一送一券特殊验证
if (coupon.type === 6) {
let canUse = false;
if (isDikouAll) {
canUse = canDikouGoodsArr.some((v) => v.num >= 2);
} else if (canUseGoodsArr.length > 0) {
canUse = canUseGoodsArr.some((v) => v.num >= 2);
}
// 商品券特殊验证
if (coupon.type === 2) {
if (!(isDikouAll || canCalcGoodsArr.length > 0)) {
return {
canUse: false,
reason: "没有符合条件的商品可抵扣"
};
}
}
if (!canUse) {
return {
canUse: false,
reason: "需要购买至少2件相同的商品才能使用",
};
}
}
// 买一送一券特殊验证
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 (coupon.type === 4) {
let canUse = false;
if (isDikouAll) {
canUse = canDikouGoodsArr.some((v) => v.num >= 2);
} else if (canUseGoodsArr.length > 0) {
canUse = canUseGoodsArr.some((v) => v.num >= 2);
}
if (!canUse) {
return {
canUse: false,
reason: "需要购买至少2件相同的商品才能使用",
};
}
}
if (!canUse) {
return {
canUse: false,
reason: "需要购买至少2件相同的商品才能使用"
};
}
}
// 第二件半价券特殊验证
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 (!canUse) {
return {
canUse: false,
reason: "需要购买至少2件相同的商品才能使用"
};
}
}
// 所有条件都满足
return {
canUse: true,
reason: ""
};
// 所有条件都满足
return {
canUse: true,
reason: "",
};
}
/**
* 计算抵扣商品金额
* @param discountGoodsArr 可抵扣商品列表
* @param discountNum 抵扣数量
* @param user 用户信息
* @param {Object} shopInfo 店铺信息
* @param {Object} shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user,shopInfo) {
let hasCountNum = 0;
let discountPrice = 0;
let hasDiscountGoodsArr = [];
for (let i = 0; i < discountGoodsArr.length; i++) {
if (hasCountNum >= discountNum) {
break;
}
const goods = discountGoodsArr[i];
const shengyuNum = discountNum - hasCountNum;
const num = Math.min(goods.num, shengyuNum);
discountPrice += returnGoodsPrice(goods, user,shopInfo) * num;
hasCountNum += num;
hasDiscountGoodsArr.push({
...goods,
num
});
}
return {
discountPrice,
hasDiscountGoodsArr
};
export function calcDiscountGoodsArrPrice(
discountGoodsArr,
discountNum,
user,
shopInfo,limitTimeDiscount
) {
let hasCountNum = 0;
let discountPrice = 0;
let hasDiscountGoodsArr = [];
for (let i = 0; i < discountGoodsArr.length; i++) {
if (hasCountNum >= discountNum) {
break;
}
const goods = discountGoodsArr[i];
const shengyuNum = discountNum - hasCountNum;
const num = Math.min(goods.num, shengyuNum);
discountPrice += returnGoodsPrice(goods, user, shopInfo,limitTimeDiscount) * num;
hasCountNum += num;
hasDiscountGoodsArr.push({
...goods,
num,
});
}
return {
discountPrice,
hasDiscountGoodsArr,
};
}
/**
@@ -239,23 +351,51 @@ export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user,sh
* @param user 用户信息
* @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function returnCouponDiscount(arr, coupon, user, goodsOrderPrice, selCoupon) {
console.log('arr', arr);
const canDikouGoodsArr = returnCanDikouGoodsArr(arr, selCoupon, user);
console.log('canDikouGoodsArr', canDikouGoodsArr);
if (coupon.type == 2) {
return returnCouponProductDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice);
}
if (coupon.type == 6) {
return returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice);
}
if (coupon.type == 4) {
return returnSecoendDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice);
}
if (coupon.type == 3) {
return returnCouponZhekouDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice, selCoupon);
}
export function returnCouponDiscount(
arr,
coupon,
user,
goodsOrderPrice,
selCoupon,
shopInfo,limitTimeDiscount
) {
arr = returnCanDikouGoods(arr, user, shopInfo);
const canDikouGoodsArr = returnCanDikouGoodsArr(arr, selCoupon, user);
if (coupon.type == 2) {
return returnCouponProductDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
}
if (coupon.type == 6) {
const result = returnCouponBuyOneGiveOneDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
return result;
}
if (coupon.type == 4) {
return returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo,limitTimeDiscount);
}
if (coupon.type == 3) {
return returnCouponZhekouDiscount(
canDikouGoodsArr,
coupon,
user,
goodsOrderPrice,
selCoupon,
limitTimeDiscount
);
}
}
/**
@@ -265,41 +405,53 @@ export function returnCouponDiscount(arr, coupon, user, goodsOrderPrice, selCoup
* @param user 用户信息
* @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表
* @param limitTimeDiscount 限时折扣
*
*/
export function returnCouponZhekouDiscount(
canDikouGoodsArr,
coupon,
user,
goodsOrderPrice,
selCoupon
canDikouGoodsArr,
coupon,
user,
goodsOrderPrice,
selCoupon,limitTimeDiscount
) {
const {
discountRate,
maxDiscountAmount
} = coupon;
const { discountRate, maxDiscountAmount } = coupon;
const goodsCouponDiscount = selCoupon
.filter((v) => v.type == 2)
.reduce((prve, cur) => {
return prve + cur.discount.discountPrice;
}, 0);
goodsOrderPrice -= goodsCouponDiscount;
// 使用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) {
discountPrice = discountPrice >= maxDiscountAmount ? maxDiscountAmount : discountPrice;
}
// 计算商品优惠券折扣总和使用BigNumber避免精度问题
const goodsCouponDiscount = selCoupon
.filter((v) => v.type == 2)
.reduce((prve, cur) => {
return new BigNumber(prve).plus(
new BigNumber(cur.discount.discountPrice)
);
}, new BigNumber(0));
return {
discountPrice, // 折扣抵扣金额(即优惠的金额)
hasDiscountGoodsArr: [],
};
// 将商品订单价格转换为BigNumber并减去优惠券折扣
const adjustedGoodsOrderPrice = new BigNumber(goodsOrderPrice).minus(
goodsCouponDiscount
);
// 计算优惠比例:(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;
}
return {
discountPrice, // 折扣抵扣金额(即优惠的金额)
hasDiscountGoodsArr: [],
};
}
/**
@@ -307,39 +459,45 @@ export function returnCouponZhekouDiscount(
* @param canDikouGoodsArr 可抵扣商品列表
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function returnCouponProductDiscount(canDikouGoodsArr, coupon, user) {
const {
useFoods,
discountNum,
useRule
} = coupon;
//抵扣商品数组
let discountGoodsArr = [];
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
discountGoodsArr = canDikouGoodsArr
.slice(canDikouGoodsArr.length - discountNum, canDikouGoodsArr.length)
.reverse();
} else {
discountGoodsArr = canDikouGoodsArr.slice(0, discountNum);
}
} else {
//抵扣选中商品
const discountSelGoodsArr = canDikouGoodsArr.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") {
discountGoodsArr = discountSelGoodsArr
.slice(discountSelGoodsArr.length - discountNum, discountSelGoodsArr.length)
.reverse();
} else {
discountGoodsArr = discountSelGoodsArr.slice(0, discountNum);
}
}
const result = calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user);
return result;
export function returnCouponProductDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
) {
const { useFoods, discountNum, useRule } = coupon;
//抵扣商品数组
let discountGoodsArr = [];
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
discountGoodsArr = canDikouGoodsArr.slice(discountNum * -1).reverse();
} else {
discountGoodsArr = canDikouGoodsArr.slice(0, discountNum);
}
} else {
//抵扣选中商品
const discountSelGoodsArr = canDikouGoodsArr.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") {
discountGoodsArr = discountSelGoodsArr.slice(discountNum * -1).reverse();
} else {
discountGoodsArr = discountSelGoodsArr.slice(0, discountNum);
}
}
const result = calcDiscountGoodsArrPrice(
discountGoodsArr,
discountNum,
user,
shopInfo,
limitTimeDiscount
);
return result;
}
// 返回买一送一券抵扣详情
@@ -348,38 +506,47 @@ export function returnCouponProductDiscount(canDikouGoodsArr, coupon, user) {
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user,shopInfo) {
const {
useFoods,
useRule
} = coupon;
//抵扣商品
let discountGoods = undefined;
//符合买一送一条件的商品
const canUseGoods = canDikouGoodsArr.filter((v) => v.num >= 2);
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
discountGoods = canUseGoods[canUseGoods.length - 1];
} else {
discountGoods = canUseGoods.slice(0, 1);
}
} else {
//符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) => useFoods.find((food) => food.id == v.productId));
if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else {
discountGoods = canUseGoods1.slice(0, 1);
}
}
const discountPrice = returnGoodsPrice(discountGoods, user,shopInfo);
const hasDiscountGoodsArr = [discountGoods];
return {
discountPrice,
hasDiscountGoodsArr
};
function returnCouponBuyOneGiveOneDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,limitTimeDiscount
) {
const { useFoods, useRule } = coupon;
//抵扣商品
let discountGoods = undefined;
//符合买一送一条件的商品
const canUseGoods = canDikouGoodsArr.filter((v) => v.num >= 2);
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
discountGoods = canUseGoods[canUseGoods.length - 1];
} else {
discountGoods = canUseGoods[0];
}
} else {
//符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else {
discountGoods = canUseGoods1[0];
}
}
let discountPrice = 0;
let hasDiscountGoodsArr = [];
if (discountGoods) {
discountPrice = returnGoodsPrice(discountGoods, user, shopInfo,limitTimeDiscount);
hasDiscountGoodsArr = [discountGoods];
}
return {
discountPrice: discountPrice <= 0 ? 0 : discountPrice,
hasDiscountGoodsArr,
};
}
/**
@@ -388,39 +555,46 @@ function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user,shopIn
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
function returnSecoendDiscount(canDikouGoodsArr, coupon, user,shopInfo) {
const {
useFoods,
useRule
} = coupon;
//抵扣商品
let discountGoods = undefined;
//符合买一送一条件的商品
const canUseGoods = canDikouGoodsArr.filter((v) => v.num >= 2);
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
discountGoods = canUseGoods[canUseGoods.length - 1];
} else {
discountGoods = canUseGoods.slice(0, 1);
}
} else {
//符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) => useFoods.find((food) => food.id == v.productId));
if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else {
discountGoods = canUseGoods1.slice(0, 1);
}
}
const discountPrice = returnGoodsPrice(discountGoods, user,shopInfo);
const hasDiscountGoodsArr = [discountGoods];
//返回半价价格
return {
discountPrice: new BigNumber(discountPrice).dividedBy(2).toNumber(),
hasDiscountGoodsArr,
};
function returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo,limitTimeDiscount) {
const { useFoods, useRule } = coupon;
//抵扣商品
let discountGoods = undefined;
//符合条件的商品
const canUseGoods = canDikouGoodsArr.filter((v) => v.num >= 2);
//抵扣全部商品
if (useFoods.length === 0) {
if (useRule == "price_asc") {
discountGoods = canUseGoods[canUseGoods.length - 1];
} else {
discountGoods = canUseGoods[0];
}
} else {
//符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else {
discountGoods = canUseGoods1[0];
}
}
let discountPrice = 0;
let hasDiscountGoodsArr = [];
if (discountGoods) {
discountPrice = returnGoodsPrice(discountGoods, user, shopInfo,limitTimeDiscount);
hasDiscountGoodsArr = [discountGoods];
}
//返回半价价格
return {
discountPrice:
discountPrice <= 0
? 0
: new BigNumber(discountPrice).dividedBy(2).toNumber(),
hasDiscountGoodsArr,
};
}
/**
@@ -428,17 +602,21 @@ function returnSecoendDiscount(canDikouGoodsArr, coupon, user,shopInfo) {
* @param arr 商品列表
* @param user 用户信息
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
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, use, shopInfo) - returnGoodsPrice(a, user, shopInfo);
});
return result;
}
export function returnCanDikouGoods(arr, user, shopInfo,limitTimeDiscount) {
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,limitTimeDiscount) -
returnGoodsPrice(a, user, shopInfo,limitTimeDiscount)
);
});
return result;
}

View File

@@ -5,19 +5,48 @@ import _ from "lodash";
* 返回商品单价
* @param goods 商品
* @param user 用户信息
* @param {Object} shopInfo
* @param {Object} shopInfo 店铺信息
* @param {boolean} limitTimeDiscount - 限时折扣
*/
export function returnGoodsPrice(goods, user, shopInfo) {
export function returnGoodsPrice(goods, user, shopInfo, limitTimeDiscount) {
if (!goods) {
return 0;
}
// 商家改价
if (goods.discount_sale_amount * 1 > 0) {
return goods.discount_sale_amount;
return goods.salePrice;
}
// 限时折扣
if (limitTimeDiscount && limitTimeDiscount.id) {
if (
limitTimeDiscount.foodType == 1 &&
limitTimeDiscount.discountPriority == "limit-time"
) {
return new BigNumber(goods.salePrice)
.times(limitTimeDiscount.discountRate / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
}
const canUseFoods = limitTimeDiscount.foods.split(",");
if (
limitTimeDiscount.foodType == 2 &&
limitTimeDiscount.discountPriority == "limit-time" &&
canUseFoods.includes(`${goods.productId}`)
) {
return new BigNumber(goods.salePrice)
.times(limitTimeDiscount.discountRate / 100)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
}
}
if (shopInfo && !shopInfo.isMemberPrice) {
return goods.salePrice;
}
if (user.isVip && goods.memberPrice * 1 <= goods.salePrice * 1 && goods.memberPrice * 1 > 0) {
if (
user.isVip &&
goods.memberPrice * 1 <= goods.salePrice * 1 &&
goods.memberPrice * 1 > 0
) {
return goods.memberPrice;
}
return goods.salePrice;
@@ -63,7 +92,9 @@ export function returnCoupType(coupon) {
* @param selCoupon 已选择的优惠券列表
* @param user 用户信息
*/
export function returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user) {
export function returnCanDikouGoodsArr(args) {
const { canDikouGoodsArr, selCoupon, user, shopInfo, limitTimeDiscount } =
args;
const types = [2, 4, 6];
// 收集已抵扣商品并关联对应的优惠券类型
const goodsCouponGoods = selCoupon
@@ -92,11 +123,53 @@ export function returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user) {
}
return v;
})
.filter((v) => v.num > 0); // 过滤掉数量<=0的商品
.filter((v) => {
const canUseNum=v.num-(v.returnNum||0)
if(canUseNum <= 0||v.is_temporary==1||v.is_gift==1){
return false
}
return true
}); // 过滤掉数量<=0的商品,赠菜,临时菜
return arr;
}
/**
* 返回商品是否享用了会员价/会员折扣
* @param {*} goods
*/
function returnGoodsIsUseVipPrice(shopInfo,user,goods) {
if(goods.is_time_discount){
return false
}
if(shopInfo.isMemberPrice!=1||user.isVip!=1){
return false
}
if(shopInfo.isMemberPrice==1&&user.isVip==1){
if(goods.memberPrice<=0){
return false
}
return true
}
}
/**
* 返回可以计算抵扣金额的商品列表
*/
function returnCanCalcGoodsList(canCalcGoodsArr, coupon, shopInfo, user) {
return canCalcGoodsArr.filter((goods) => {
if (!coupon.discountShare && goods.is_time_discount) {
return false;
}
if(!coupon.vipPriceShare&& returnGoodsIsUseVipPrice(shopInfo,user,goods)){
return false;
}
return true;
});
}
/**
* 判断优惠券是否可使用,并返回不可用原因
*
@@ -111,10 +184,20 @@ export function returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user) {
* @param {Object} args.user - 用户信息对象
* @param {Object} args.selCoupon - 已经选择的优惠券信息对象
* @param {Object} args.shopInfo
* @param {boolean} args.limitTimeDiscount - 限时折扣
* @returns {Object} - { canUse: boolean, reason: string } 可用状态及不可用原因
*/
export function returnCouponCanUse(args) {
let { canDikouGoodsArr, coupon, goodsOrderPrice, user, selCoupon, shopInfo } = args;
let {
canDikouGoodsArr,
coupon,
goodsOrderPrice,
user,
selCoupon,
shopInfo,
isMemberPrice,
limitTimeDiscount,
} = args;
// 优惠券未启用
if (!coupon.use) {
return {
@@ -122,10 +205,27 @@ export function returnCouponCanUse(args) {
reason: coupon.noUseRestrictions || "不在可用时间段内",
};
}
if (
limitTimeDiscount &&
limitTimeDiscount.id &&
limitTimeDiscount.foodType == 1 &&
!coupon.discountShare
) {
return {
canUse: false,
reason: coupon.noUseRestrictions || "不可与限时折扣同享",
};
}
// 计算门槛金额
let fullAmount = goodsOrderPrice;
canDikouGoodsArr = returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user, shopInfo);
canDikouGoodsArr = returnCanDikouGoodsArr({
canDikouGoodsArr,
selCoupon,
user,
shopInfo,
limitTimeDiscount,
});
//优惠券指定门槛商品列表
let canCalcGoodsArr = [...canDikouGoodsArr];
//部分商品参与门槛计算
@@ -133,10 +233,18 @@ export function returnCouponCanUse(args) {
canCalcGoodsArr = canDikouGoodsArr.filter((v) => {
return coupon.thresholdFoods.find((food) => food.id == v.productId);
});
fullAmount = canCalcGoodsArr.reduce((pre, cur) => {
return pre + returnGoodsPrice(cur, user, shopInfo) * cur.num;
}, 0);
}
canCalcGoodsArr = returnCanCalcGoodsList(
canCalcGoodsArr,
coupon,
shopInfo,
user
);
fullAmount = canCalcGoodsArr.reduce((pre, cur) => {
return (
pre + returnGoodsPrice(cur, user, shopInfo, limitTimeDiscount) * cur.num
);
}, 0);
// 是否全部商品可用
const isDikouAll = coupon.useFoods.length === 0;
@@ -147,12 +255,12 @@ export function returnCouponCanUse(args) {
return coupon.useFoods.find((food) => food.id == v.productId);
});
}
if (user.isVip && !coupon.vipPriceShare) {
return {
canUse: false,
reason: "非会员可用",
};
}
// if (user.isVip && !coupon.vipPriceShare) {
// return {
// canUse: false,
// reason: "非会员可用",
// };
// }
if (selCoupon.length > 0 && !selCoupon[0].otherCouponShare) {
return {
canUse: false,
@@ -183,15 +291,8 @@ export function returnCouponCanUse(args) {
}
// 商品兑换券,第二件半价和买一送一判断是否有可用商品
if ([2, 4, 5].includes(coupon.type)) {
if (coupon.type == 2 && fullAmount < coupon.fullAmount) {
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
// 没有符合条件的商品
if (isDikouAll && canDikouGoodsArr.length === 0) {
// 没有符合条件的商品
if (isDikouAll && canDikouGoodsArr.length === 0) {
return {
canUse: false,
reason: "没有符合条件的商品",
@@ -203,6 +304,23 @@ export function returnCouponCanUse(args) {
reason: "没有符合条件的商品",
};
}
if (coupon.type == 2 ) {
if(canCalcGoodsArr.length<=0){
return {
canUse: false,
reason: "没有符合计算门槛条件的商品",
};
}
if(fullAmount < coupon.fullAmount){
return {
canUse: false,
reason: `${coupon.fullAmount}元可用,当前可参与金额${fullAmount}`,
};
}
}
}
//商品兑换券是否达到门槛金额
if (coupon.type == 2 && goodsOrderPrice < coupon.fullAmount) {
@@ -258,8 +376,15 @@ export function returnCouponCanUse(args) {
* @param discountNum 抵扣数量
* @param user 用户信息
* @param {Object} shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user, shopInfo) {
export function calcDiscountGoodsArrPrice(
discountGoodsArr,
discountNum,
user,
shopInfo,
limitTimeDiscount
) {
let hasCountNum = 0;
let discountPrice = 0;
let hasDiscountGoodsArr = [];
@@ -270,7 +395,8 @@ export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user, s
const goods = discountGoodsArr[i];
const shengyuNum = discountNum - hasCountNum;
const num = Math.min(goods.num, shengyuNum);
discountPrice += returnGoodsPrice(goods, user, shopInfo) * num;
discountPrice +=
returnGoodsPrice(goods, user, shopInfo, limitTimeDiscount) * num;
hasCountNum += num;
hasDiscountGoodsArr.push({
@@ -293,22 +419,62 @@ export function calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user, s
* @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function returnCouponDiscount(arr, coupon, user, goodsOrderPrice, selCoupon, shopInfo) {
export function returnCouponDiscount(
arr,
coupon,
user,
goodsOrderPrice,
selCoupon,
shopInfo,
limitTimeDiscount
) {
arr = returnCanDikouGoods(arr, user, shopInfo);
const canDikouGoodsArr = returnCanDikouGoodsArr(arr, selCoupon, user);
const canDikouGoodsArr = returnCanDikouGoodsArr({
canDikouGoodsArr: arr,
selCoupon,
user,
shopInfo,
limitTimeDiscount,
});
if (coupon.type == 2) {
return returnCouponProductDiscount(canDikouGoodsArr, coupon, user, shopInfo);
return returnCouponProductDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
}
if (coupon.type == 6) {
const result = returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopInfo);
const result = returnCouponBuyOneGiveOneDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
return result;
}
if (coupon.type == 4) {
return returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo);
return returnSecoendDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
);
}
if (coupon.type == 3) {
return returnCouponZhekouDiscount(canDikouGoodsArr, coupon, user, goodsOrderPrice, selCoupon);
return returnCouponZhekouDiscount(
canDikouGoodsArr,
coupon,
user,
goodsOrderPrice,
selCoupon,
limitTimeDiscount
);
}
}
@@ -319,6 +485,7 @@ export function returnCouponDiscount(arr, coupon, user, goodsOrderPrice, selCoup
* @param user 用户信息
* @param goodsOrderPrice 商品订单金额
* @param selCoupon 已选择的优惠券列表
* @param limitTimeDiscount 限时折扣
*
*/
export function returnCouponZhekouDiscount(
@@ -326,7 +493,8 @@ export function returnCouponZhekouDiscount(
coupon,
user,
goodsOrderPrice,
selCoupon
selCoupon,
limitTimeDiscount
) {
const { discountRate, maxDiscountAmount } = coupon;
@@ -334,14 +502,20 @@ export function returnCouponZhekouDiscount(
const goodsCouponDiscount = selCoupon
.filter((v) => v.type == 2)
.reduce((prve, cur) => {
return new BigNumber(prve).plus(new BigNumber(cur.discount.discountPrice));
return new BigNumber(prve).plus(
new BigNumber(cur.discount.discountPrice)
);
}, new BigNumber(0));
// 将商品订单价格转换为BigNumber并减去优惠券折扣
const adjustedGoodsOrderPrice = new BigNumber(goodsOrderPrice).minus(goodsCouponDiscount);
const adjustedGoodsOrderPrice = new BigNumber(goodsOrderPrice).minus(
goodsCouponDiscount
);
// 计算优惠比例:(100 - 折扣率) / 100
const discountAmountRatio = new BigNumber(100).minus(discountRate).dividedBy(100);
const discountAmountRatio = new BigNumber(100)
.minus(discountRate)
.dividedBy(100);
// 计算折扣金额:调整后的商品订单金额 × 优惠比例
let discountPrice = adjustedGoodsOrderPrice
@@ -351,7 +525,8 @@ export function returnCouponZhekouDiscount(
// 应用最大折扣金额限制
if (maxDiscountAmount !== 0) {
discountPrice = discountPrice >= maxDiscountAmount ? maxDiscountAmount : discountPrice;
discountPrice =
discountPrice >= maxDiscountAmount ? maxDiscountAmount : discountPrice;
}
return {
@@ -366,8 +541,15 @@ export function returnCouponZhekouDiscount(
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function returnCouponProductDiscount(canDikouGoodsArr, coupon, user, shopInfo) {
export function returnCouponProductDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
) {
const { useFoods, discountNum, useRule } = coupon;
//抵扣商品数组
let discountGoodsArr = [];
@@ -389,7 +571,13 @@ export function returnCouponProductDiscount(canDikouGoodsArr, coupon, user, shop
discountGoodsArr = discountSelGoodsArr.slice(0, discountNum);
}
}
const result = calcDiscountGoodsArrPrice(discountGoodsArr, discountNum, user, shopInfo);
const result = calcDiscountGoodsArrPrice(
discountGoodsArr,
discountNum,
user,
shopInfo,
limitTimeDiscount
);
return result;
}
@@ -399,8 +587,15 @@ export function returnCouponProductDiscount(canDikouGoodsArr, coupon, user, shop
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopInfo) {
function returnCouponBuyOneGiveOneDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
) {
const { useFoods, useRule } = coupon;
//抵扣商品
let discountGoods = undefined;
@@ -415,7 +610,9 @@ function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopI
}
} else {
//符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) => useFoods.find((food) => food.id == v.productId));
const canUseGoods1 = canUseGoods.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else {
@@ -425,7 +622,12 @@ function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopI
let discountPrice = 0;
let hasDiscountGoodsArr = [];
if (discountGoods) {
discountPrice = returnGoodsPrice(discountGoods, user, shopInfo);
discountPrice = returnGoodsPrice(
discountGoods,
user,
shopInfo,
limitTimeDiscount
);
hasDiscountGoodsArr = [discountGoods];
}
return {
@@ -440,8 +642,15 @@ function returnCouponBuyOneGiveOneDiscount(canDikouGoodsArr, coupon, user, shopI
* @param coupon 优惠券
* @param user 用户信息
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
function returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo) {
function returnSecoendDiscount(
canDikouGoodsArr,
coupon,
user,
shopInfo,
limitTimeDiscount
) {
const { useFoods, useRule } = coupon;
//抵扣商品
let discountGoods = undefined;
@@ -456,7 +665,9 @@ function returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo) {
}
} else {
//符合抵扣条件的商品
const canUseGoods1 = canUseGoods.filter((v) => useFoods.find((food) => food.id == v.productId));
const canUseGoods1 = canUseGoods.filter((v) =>
useFoods.find((food) => food.id == v.productId)
);
if (useRule == "price_asc") {
discountGoods = canUseGoods1[canUseGoods1.length - 1];
} else {
@@ -466,12 +677,20 @@ function returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo) {
let discountPrice = 0;
let hasDiscountGoodsArr = [];
if (discountGoods) {
discountPrice = returnGoodsPrice(discountGoods, user, shopInfo);
discountPrice = returnGoodsPrice(
discountGoods,
user,
shopInfo,
limitTimeDiscount
);
hasDiscountGoodsArr = [discountGoods];
}
//返回半价价格
return {
discountPrice: discountPrice <= 0 ? 0 : new BigNumber(discountPrice).dividedBy(2).toNumber(),
discountPrice:
discountPrice <= 0
? 0
: new BigNumber(discountPrice).dividedBy(2).toNumber(),
hasDiscountGoodsArr,
};
}
@@ -481,8 +700,9 @@ function returnSecoendDiscount(canDikouGoodsArr, coupon, user, shopInfo) {
* @param arr 商品列表
* @param user 用户信息
* @param shopInfo 店铺信息
* @param limitTimeDiscount 限时折扣
*/
export function returnCanDikouGoods(arr, user, shopInfo) {
export function returnCanDikouGoods(arr, user, shopInfo, limitTimeDiscount) {
const result = arr
.filter((v) => {
return v.is_temporary != 1 && v.is_gift != 1;
@@ -491,7 +711,10 @@ export function returnCanDikouGoods(arr, user, shopInfo) {
return v.num > 0;
})
.sort((a, b) => {
return returnGoodsPrice(b, user, shopInfo) - returnGoodsPrice(a, user, shopInfo);
return (
returnGoodsPrice(b, user, shopInfo, limitTimeDiscount) -
returnGoodsPrice(a, user, shopInfo, limitTimeDiscount)
);
});
return result;
}

View File

@@ -234,6 +234,40 @@ export interface FreeDineConfig {
useShopType?: string; //all 全部 part部分
shopIdList?: number[]; //可用门店id
}
//限时折扣配置
export interface TimeLimitDiscountConfig {
/**
* 折扣优先级 limit-time/vip-price
*/
discountPriority: string;
/**
* 折扣% 范围1-99
*/
discountRate: number;
/**
* 参与商品
*/
foods: string;
/**
* 参与商品 1全部 2部分
*/
foodType: number;
/**
* 自增主键
*/
id: number;
/**
* 店铺ID
*/
shopId: number;
/**
* 可使用类型:堂食 dine-in 外带 take-out 外卖 take-away 配送 post
*/
useType: string;
[property: string]: any;
}
/** 订单额外费用配置 */
export interface OrderExtraConfig {
// merchantReduction: number; // 商家减免金额默认0
@@ -251,6 +285,7 @@ export interface OrderExtraConfig {
currentDinnerType: "dine-in" | "take-out" | "take-away" | "post"; // 当前就餐类型匹配useType
isFreeDine?: boolean; //是否霸王餐
freeDineConfig?: FreeDineConfig;
limitTimeDiscount?: TimeLimitDiscountConfig; //限时折扣
}
/** 订单费用汇总(修改:补充商家减免类型和明细) */
@@ -283,10 +318,12 @@ export interface OrderCostSummary {
config: OrderExtraConfig; // 订单额外费用配置
//满减活动
fullReduction: {
usedFullReductionActivityFullAmount: number; // 计算出的满减活动的门槛金额
usedActivity?: FullReductionActivity; // 实际使用的满减活动
usedThreshold?: FullReductionThreshold; // 实际使用的满减阈值(多门槛中选最优)
actualAmount: number; // 满减实际减免金额(元)
};
vipDiscountAmount: number; //会员折扣减免金额
// 订单原支付金额
orderOriginFinalPayAmount: number; //订单原金额(包含打包费+餐位费)
}
@@ -399,6 +436,188 @@ function isDinnerTypeMatch(
//满减活动的就餐类型和当前券类型字段值不一样暂时返回true
return true;
}
//判断商品是否可以使用限时折扣
export function returnCanUseLimitTimeDiscount(
goods: BaseCartItem,
limitTimeDiscount: TimeLimitDiscountConfig | null | undefined,
useVipPrice: boolean,
idKey = "product_id"
) {
if (!limitTimeDiscount || !limitTimeDiscount.id) {
return false;
}
const canUseFoods = (limitTimeDiscount.foods || "").split(",");
if (limitTimeDiscount.discountPriority == "limit-time") {
if (
limitTimeDiscount.foodType == 1 ||
canUseFoods.includes("" + goods[idKey])
) {
return true;
}
}
if (limitTimeDiscount.discountPriority == "vip-price") {
if (!useVipPrice) {
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) {
//使用会员价
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
): number {
if (!fullReductionActivitie) {
return 0;
}
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;
console.log("amount", amount);
}
/**
* 筛选最优满减活动(对齐后端逻辑:状态→时间→周期→时段→就餐类型→排序→修改时间)
* @param activities 后端返回的满减活动列表
@@ -424,11 +643,16 @@ export function filterOptimalFullReductionActivity(
activity.thresholds?.length // 至少有一个满减阈值
);
});
console.log("baseEligible", baseEligible);
if (!baseEligible.length) return undefined;
// 第二步:时间筛选(有效期内+周期内+时段内)
const timeEligible = baseEligible.filter((activity) => {
// 1. 校验有效期validStartTime ~ validEndTime
if (activity.useTimeType == "all") {
return true;
}
if (!activity.validStartTime || !activity.validEndTime) return false;
const startDate = new Date(activity.validStartTime);
const endDate = new Date(activity.validEndTime);
@@ -665,11 +889,12 @@ export function calcCouponThresholdAmount(
*/
export function calcSingleGoodsRealPrice(
goods: BaseCartItem,
config: Pick<OrderExtraConfig, "isMember" | "memberDiscountRate"> & {
activity?: ActivityConfig; // 商品参与的营销活动如限时折扣按商品ID匹配
}
config: Pick<
OrderExtraConfig,
"isMember" | "memberDiscountRate" | "limitTimeDiscount"
>
): number {
const { isMember, memberDiscountRate, activity } = config;
const { isMember, memberDiscountRate, limitTimeDiscount: activity } = config;
//如果是增菜价格为0
if (goods.is_gift) {
@@ -680,6 +905,7 @@ 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(
@@ -687,12 +913,35 @@ export function calcSingleGoodsRealPrice(
);
// 3. 优先级3营销活动折扣如限时折扣需按商品ID匹配活动
const isActivityApplicable = activity
? (activity.applicableProductIds || []).includes(String(goods.product_id)) // 核心修正用商品ID匹配活动
: false;
let isActivityApplicable = false;
if (activity) {
if (activity.foodType == 1) {
isActivityApplicable = true;
} else {
const canUseGoods = activity.foods?.split(",") || [];
if (canUseGoods.find((v) => v == String(goods.product_id))) {
isActivityApplicable = true;
}
}
}
if (!activity || !isActivityApplicable) {
return memberPrice.toNumber();
}
//限时折扣优先或者会员价优先但是不是会员或者未开启会员价格时限时折扣优先
if (
activity.discountPriority == "limit-time" ||
(activity.discountPriority == "vip-price" && !isMember)
) {
//限时折扣优先
return truncateToTwoDecimals(
new BigNumber(goods.salePrice)
.multipliedBy(activity.discountRate / 100)
.toNumber()
);
}
if (activity.discountPriority == "vip-price" && isMember) {
return memberPrice.toNumber();
}
// 处理活动与会员的同享/不同享逻辑
if (activity.vipPriceShare) {
@@ -751,26 +1000,17 @@ export function calcGoodsOriginalAmount(goodsList: BaseCartItem[]): number {
*/
export function calcGoodsRealAmount(
goodsList: BaseCartItem[],
config: Pick<OrderExtraConfig, "isMember" | "memberDiscountRate">,
activities: ActivityConfig[] = []
config: Pick<
OrderExtraConfig,
"isMember" | "memberDiscountRate" | "limitTimeDiscount"
>
): number {
let total = new BigNumber(0);
for (const goods of goodsList) {
const availableNum = Math.max(0, goods.number - (goods.returnNum || 0));
if (availableNum <= 0) continue;
// 匹配商品参与的营销活动按商品ID匹配优先商品自身配置
const activity =
goods.activityInfo ??
activities.find(
(act) =>
(act.applicableProductIds || []).includes(String(goods.product_id)) // 核心修正用商品ID匹配活动
);
const realPrice = new BigNumber(
calcSingleGoodsRealPrice(goods, { ...config, activity })
);
const realPrice = new BigNumber(calcSingleGoodsRealPrice(goods, config));
total = total.plus(realPrice.multipliedBy(availableNum));
}
@@ -967,14 +1207,13 @@ export function calcTotalPackFee(
for (const goods of goodsList) {
const packNumber = goods.packNumber ? goods.packNumber * 1 : 0;
let availableNum = Math.max(0, goods.number - (goods.returnNum || 0));
if (availableNum === 0) continue;
// 计算单个商品打包数量外卖全打包堂食按配置称重商品≤1
let packNum = Math.min(availableNum, packNumber);
if(dinnerType === "take-out"){
packNum=availableNum
if (dinnerType === "take-out") {
packNum = availableNum;
}
if (goods.product_type === GoodsType.WEIGHT) {
packNum = Math.min(packNum, 1);
@@ -1066,7 +1305,12 @@ export function calculateOrderCostSummary(
currentTime: Date = new Date()
): OrderCostSummary {
//是否使用霸王餐,霸王餐配置
const { isFreeDine, freeDineConfig } = config;
const {
isFreeDine,
freeDineConfig,
limitTimeDiscount,
fullReductionActivities,
} = config;
// ------------------------------ 1. 基础费用计算 ------------------------------
const goodsOriginalAmount = calcGoodsOriginalAmount(goodsList); // 商品原价总和
@@ -1076,13 +1320,17 @@ export function calculateOrderCostSummary(
{
isMember: config.isMember,
memberDiscountRate: config.memberDiscountRate,
},
activities
limitTimeDiscount: config.limitTimeDiscount,
}
);
const goodsDiscountAmount = calcGoodsDiscountAmount(
goodsOriginalAmount,
goodsRealAmount
); // 商品折扣金额
//会员折扣
let vipDiscountAmount = 0;
const newUserDiscount = config.newUserDiscount || 0; // 新客立减
// ------------------------------ 2. 满减活动计算(核心步骤) ------------------------------
@@ -1104,7 +1352,14 @@ export function calculateOrderCostSummary(
const additionalFee = Math.max(0, config.additionalFee); // 附加费
// 2.2 计算满减基数(先扣新客立减)
let baseAfterNewUserDiscount = new BigNumber(goodsRealAmount)
let baseAfterNewUserDiscount = new BigNumber(
limitTimeDiscount &&
limitTimeDiscount.id &&
usedFullReductionActivity &&
!usedFullReductionActivity.discountShare
? goodsRealAmount
: goodsRealAmount
)
.minus(newUserDiscount)
.plus(packFee)
.plus(seatFee)
@@ -1114,10 +1369,19 @@ export function calculateOrderCostSummary(
baseAfterNewUserDiscount > 0 ? baseAfterNewUserDiscount : 0;
// 2.3 选择最优满减阈值(多门槛场景)
let usedFullReductionActivityFullAmount = 0;
if (usedFullReductionActivity) {
//计算当前满减活动的门槛金额
usedFullReductionActivityFullAmount = calcFullReductionActivityFullAmount(
goodsList,
usedFullReductionActivity,
config.limitTimeDiscount,
config.isMember
);
usedFullReductionThreshold = selectOptimalThreshold(
usedFullReductionActivity.thresholds,
baseAfterNewUserDiscount,
usedFullReductionActivityFullAmount,
goodsOriginalAmount,
goodsRealAmount,
usedFullReductionActivity.discountShare || 0 // 与限时折扣同享规则
@@ -1182,27 +1446,23 @@ export function calculateOrderCostSummary(
maxPointDeductionLimit =
maxPointDeductionLimit > 0 ? maxPointDeductionLimit : 0;
const pointResult = calcPointDeduction(
config.userPoints,
config.pointDeductionRule,
maxPointDeductionLimit
);
console.log("积分抵扣结果:", pointResult);
pointDeductionAmount = pointResult.deductionAmount;
usedPoints = pointResult.usedPoints;
// 若满减与积分不同享pointsShare=1积分抵扣为0
if (usedFullReductionActivity && !usedFullReductionActivity.pointsShare) {
console.log("满减与积分不同享:积分抵扣为0");
pointDeductionAmount = 0;
usedPoints = 0;
}
//使用霸王餐
if (isFreeDine && freeDineConfig && freeDineConfig.enable) {
console.log("使用霸王餐");
fullReductionAmount=0;
fullReductionAmount = 0;
//不与优惠券同享
if (!freeDineConfig.withCoupon) {
couponDeductionAmount = 0;
@@ -1319,10 +1579,12 @@ export function calculateOrderCostSummary(
scoreMaxMoney,
// 满减活动明细(后端字段)
fullReduction: {
usedFullReductionActivityFullAmount: usedFullReductionActivityFullAmount,
usedActivity: usedFullReductionActivity,
usedThreshold: usedFullReductionThreshold,
actualAmount: truncateToTwoDecimals(fullReductionAmount),
},
vipDiscountAmount: vipDiscountAmount, //会员折扣减免金额
merchantReduction: {
type: merchantReductionConfig.type,
originalConfig: merchantReductionConfig,

152
utils/order-utils.js Normal file
View File

@@ -0,0 +1,152 @@
import BigNumber from "bignumber.js";
//判断商品是否可以使用限时折扣
export function canUseLimitTimeDiscount(
goods,
limitTimeDiscountRes,
shopInfo,
shopUserInfo,
idKey = "id"
) {
shopInfo=shopInfo||{}
shopUserInfo=shopUserInfo||{}
if (!limitTimeDiscountRes || !limitTimeDiscountRes.id) {
return false;
}
const canUseFoods = (limitTimeDiscountRes.foods || "").split(",");
if (limitTimeDiscountRes.discountPriority == "limit-time") {
if (
limitTimeDiscountRes.foodType == 1 ||
canUseFoods.includes("" + goods[idKey])
) {
return true;
}
}
if (
limitTimeDiscountRes.discountPriority == "vip-price"
) {
if(shopUserInfo.isVip != 1 || shopInfo.isMemberPrice != 1){
return true;
}
}
return false;
}
/**
* 返回商品显示价格
* @params {*} args 参数对象
* @params {*} args.goods 商品对象
* @params {*} args.shopInfo 店铺信息
* @params {*} args.limitTimeDiscountRes 限时折扣信息
* @params {*} args.shopUserInfo 店铺用户信息
* @returns
*/
export function returnPrice(args) {
let {
goods,
shopInfo,
limitTimeDiscountRes,
shopUserInfo,
idKey = "product_id",
} = args;
const canUseFoods = (limitTimeDiscountRes.foods || "").split(",");
shopInfo=shopInfo||{}
shopUserInfo=shopUserInfo||{}
if (shopInfo.isMemberPrice == 1 && shopUserInfo.isVip == 1) {
const memberPrice = goods.memberPrice || goods.salePrice;
//是会员而且启用会员价
if (limitTimeDiscountRes) {
//使用限时折扣
//限时折扣优先
if (limitTimeDiscountRes.discountPriority == "limit-time") {
if (
limitTimeDiscountRes.foodType == 1 ||
canUseFoods.includes("" + goods[idKey])
) {
return returnLimitPrice({
price: goods.salePrice,
limitTimeDiscountRes,
});
} else {
return memberPrice;
}
}
if (limitTimeDiscountRes.discountPriority == "vip-price") {
//会员优先
return memberPrice;
}
} else {
//是会员没有限时折扣
return memberPrice;
}
} else {
// console.log('不是会员或者没有启用会员价',goods,limitTimeDiscountRes);
//不是会员或者没有启用会员价
if (
limitTimeDiscountRes &&
limitTimeDiscountRes.id &&
(limitTimeDiscountRes.foodType == 1 ||
canUseFoods.includes("" + goods[idKey]))
) {
const price = returnLimitPrice({
price: goods.salePrice,
limitTimeDiscountRes,
goods: goods,
});
return price;
} else {
return goods.salePrice;
}
}
}
/**
* 返回限时折扣价格
* @params {*} args 参数对象
* @params {*} args.limitTimeDiscountRes 限时折扣信息
* @params {*} args.price 商品价格
* @param {*} args.goods 商品对象
* @returns
*/
export function returnLimitPrice(args) {
const { limitTimeDiscountRes, price, goods } = args;
const discountRate = new BigNumber(
limitTimeDiscountRes.discountRate
).dividedBy(100);
const result = BigNumber(price)
.times(discountRate)
.decimalPlaces(2, BigNumber.ROUND_UP)
.toNumber();
return result;
}
/**
* 判断是否返回会员价
* @param {*} args 参数对象
* @param {*} args.shopInfo 店铺信息
* @param {*} args.shopUserInfo 店铺用户信息
* @returns
*/
export function canReturnMemberPrice(args) {
const { shopInfo, shopUserInfo } = args;
if (shopInfo.isMemberPrice == 1 && shopUserInfo.isVip == 1) {
return true;
} else {
return false;
}
}
/**
* 返回会员价格
* @param {*} goods
* @returns
*/
export function returnMemberPrice(goods) {
return goods.memberPrice || goods.salePrice;
}