import { BigNumber } from "bignumber.js"; import _ from "lodash"; /** * 返回商品单价 * @param goods 商品 * @param user 用户信息 * @param {Object} shopInfo */ 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; } return goods.salePrice; } /** * 返回商品分组 * @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; } /** * 优惠券类型:1-满减券,2-商品兑换券,3-折扣券,4-第二件半价券,5-消费送券,6-买一送一券,7-固定价格券,8-免配送费券 * @param coupon */ export function returnCoupType(coupon) { const couponTypes = { 1: "满减券", 2: "商品券", 3: "折扣券", 4: "第二件半价券", 5: "消费送券", 6: "买一送一券", 7: "固定价格券", 8: "免配送费券", }; return couponTypes[coupon.type] || "未知类型"; } /** * 返回商品券抵扣后的商品列表 * @param canDikouGoodsArr 可抵扣商品列表 * @param selCoupon 已选择的优惠券列表 * @param user 用户信息 */ export function returnCanDikouGoodsArr(canDikouGoodsArr, selCoupon, user) { 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; } /** * 判断优惠券是否可使用,并返回不可用原因 * * @param {Object} args - 函数参数集合 * @param {Array} args.canDikouGoodsArr - 可参与抵扣的商品列表 * @param {Object} args.coupon - 优惠券信息对象 * @param {boolean} args.coupon.use - 优惠券是否启用 * @param {Array} args.coupon.useFoods - 优惠券适用的商品ID列表 * @param {number} args.coupon.fullAmount - 优惠券使用门槛金额 * @param {number} args.coupon.type - 优惠券类型 * @param {number} args.goodsOrderPrice - 订单中所有商品的总金额 * @param {Object} args.user - 用户信息对象 * @param {Object} args.selCoupon - 已经选择的优惠券信息对象 * @param {Object} args.shopInfo * @returns {Object} - { canUse: boolean, reason: string } 可用状态及不可用原因 */ export function returnCouponCanUse(args) { let { canDikouGoodsArr, coupon, goodsOrderPrice, user, selCoupon, shopInfo } = args; // 优惠券未启用 if (!coupon.use) { return { canUse: false, reason: "优惠券未启用" }; } 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); } // 没有符合条件的商品 if (!isDikouAll && canCalcGoodsArr.length === 0) { return { canUse: false, reason: "没有符合条件的商品" }; } // 不满足门槛金额 if (fullAmount < coupon.fullAmount) { return { canUse: false, reason: `满${coupon.fullAmount}元可用,当前可参与金额${fullAmount}元` }; } // 商品券特殊验证 if (coupon.type === 2) { if (!(isDikouAll || canCalcGoodsArr.length === 0)) { return { canUse: false, reason: "没有符合条件的商品可抵扣" }; } if(canDikouGoodsArr.length<=0){ return { canUse: false, reason: "没有符合条件的商品可抵扣" }; } } // 买一送一券特殊验证 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 (!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: "" }; } /** * 计算抵扣商品金额 * @param discountGoodsArr 可抵扣商品列表 * @param discountNum 抵扣数量 * @param user 用户信息 * @param {Object} shopInfo 店铺信息 */ 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 }; } /** * 计算优惠券抵扣金额 * @param arr 可抵扣商品列表 * @param coupon 优惠券 * @param user 用户信息 * @param goodsOrderPrice 商品订单金额 * @param selCoupon 已选择的优惠券列表 */ export function returnCouponDiscount(arr, coupon, user, goodsOrderPrice, selCoupon) { 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); } } /** * 折扣券抵扣金额 * @param canDikouGoodsArr 可抵扣商品列表 * @param coupon 优惠券 * @param user 用户信息 * @param goodsOrderPrice 商品订单金额 * @param selCoupon 已选择的优惠券列表 * */ export function returnCouponZhekouDiscount( canDikouGoodsArr, coupon, user, goodsOrderPrice, selCoupon ) { 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).decimalPlaces(2, BigNumber.ROUND_FLOOR).toNumber(); if (maxDiscountAmount != 0) { discountPrice = discountPrice >= maxDiscountAmount ? maxDiscountAmount : discountPrice; } return { discountPrice, // 折扣抵扣金额(即优惠的金额) hasDiscountGoodsArr: [], }; } /** * 商品券抵扣金额 * @param canDikouGoodsArr 可抵扣商品列表 * @param coupon 优惠券 * @param user 用户信息 */ 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; } // 返回买一送一券抵扣详情 /** * @param canDikouGoodsArr 可抵扣商品列表 * @param coupon 优惠券 * @param user 用户信息 * @param shopInfo 店铺信息 */ 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 }; } /** * 返回第二件半价券抵扣详情 * @param canDikouGoodsArr 可抵扣商品列表 * @param coupon 优惠券 * @param user 用户信息 * @param shopInfo 店铺信息 */ 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, }; } /** * 返回可以抵扣优惠券的商品列表,过滤掉赠品、临时商品,价格从高到低排序 * @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; }