UNPKG

@pisell/pisellos

Version:

一个可扩展的前端模块化SDK框架,支持插件系统

407 lines (405 loc) 17.8 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/solution/ShopDiscount/utils.ts var utils_exports = {}; __export(utils_exports, { calculateOrderLevelDiscountAllocation: () => calculateOrderLevelDiscountAllocation, filterDiscountListByBookingTime: () => filterDiscountListByBookingTime, getDateIsInSchedule: () => getDateIsInSchedule, getDiscountAmount: () => getDiscountAmount, getDiscountListAmount: () => getDiscountListAmount, getDiscountListAmountTotal: () => getDiscountListAmountTotal, isAllNormalProduct: () => isAllNormalProduct, isNormalProductByDurationSchedule: () => isNormalProductByDurationSchedule, isOrderLevelFixedAmountDiscount: () => isOrderLevelFixedAmountDiscount, uniqueById: () => uniqueById }); module.exports = __toCommonJS(utils_exports); var import_decimal = __toESM(require("decimal.js")); var import_dayjs = __toESM(require("dayjs")); var uniqueById = (arr, key = "id") => { const seen = /* @__PURE__ */ new Set(); return arr.filter((item) => !seen.has(item[key]) && seen.add(item[key])); }; var isNormalProductByDurationSchedule = (item) => { var _a, _b; return !(item == null ? void 0 : item.duration) && !((_a = item == null ? void 0 : item.schedules) == null ? void 0 : _a.length) && !((_b = item == null ? void 0 : item["schedule.ids"]) == null ? void 0 : _b.length); }; var isAllNormalProduct = (items) => { return items.every((item) => isNormalProductByDurationSchedule(item)); }; var getDiscountAmount = (discount, total, price) => { var _a; if (discount.tag === "good_pass") { return new import_decimal.default(price).minus(new import_decimal.default(price || 0)).toNumber(); } const isFixedAmount = ((_a = discount == null ? void 0 : discount.metadata) == null ? void 0 : _a.discount_card_type) === "fixed_amount"; if (isFixedAmount) { return Math.max(new import_decimal.default(price).minus(new import_decimal.default((discount.amount ?? discount.par_value) || 0)).toNumber(), 0); } return new import_decimal.default(100).minus(discount.par_value || 0).div(100).mul(new import_decimal.default(price)).toNumber(); }; var getDiscountListAmountTotal = (discount) => { return discount.reduce((acc, cur) => { return new import_decimal.default(acc).plus(new import_decimal.default(cur.num || 1).mul(new import_decimal.default(cur.amount || 0))).toNumber(); }, new import_decimal.default(0)); }; var getDiscountListAmount = (discount) => { return discount.reduce((acc, cur) => { return new import_decimal.default(acc).plus(new import_decimal.default(cur.amount || 0)).toNumber(); }, new import_decimal.default(0)); }; var getDateIsInSchedule = (dateTime, scheduleList) => { if (!dateTime || !scheduleList || scheduleList.length === 0) { return false; } const targetDate = (0, import_dayjs.default)(dateTime); if (!targetDate.isValid()) { return false; } for (const schedule of scheduleList) { if (isTimeInScheduleItem(dateTime, schedule)) { return true; } } return false; }; var isTimeInScheduleItem = (dateTime, schedule) => { const targetDate = (0, import_dayjs.default)(dateTime); const targetDateString = dateTime.split(" ")[0]; const targetTimeString = dateTime.split(" ")[1] || "00:00:00"; switch (schedule.type) { case "standard": return isInStandardSchedule(targetDate, targetDateString, targetTimeString, schedule); case "time-slots": return isInTimeSlotsSchedule(targetDate, targetDateString, targetTimeString, schedule); case "designation": return isInDesignationSchedule(targetDate, targetDateString, targetTimeString, schedule); default: return false; } }; var isInStandardSchedule = (targetDate, targetDateString, targetTimeString, schedule) => { if (!schedule.start_time || !schedule.end_time) { return false; } const startDate = (0, import_dayjs.default)(schedule.start_time); const endDate = (0, import_dayjs.default)(schedule.end_time); const isInBasicRange = targetDate.isSameOrAfter(startDate) && targetDate.isSameOrBefore(endDate); if (schedule.repeat_type === "none") { return isInBasicRange; } return isInRepeatingSchedule(targetDate, targetDateString, targetTimeString, schedule, startDate, endDate); }; var isInTimeSlotsSchedule = (targetDate, targetDateString, targetTimeString, schedule) => { if (!schedule.start_time) { return false; } if (schedule.repeat_type === "none") { const scheduleDate = schedule.start_time.split(" ")[0]; if (targetDateString !== scheduleDate) { return false; } return checkTimeSlotsForDate(targetDate, targetDateString, schedule.time_slot); } return isInTimeSlotsRepeatingSchedule(targetDate, targetDateString, targetTimeString, schedule); }; var isInDesignationSchedule = (targetDate, targetDateString, targetTimeString, schedule) => { if (!schedule.designation) { return false; } for (const designation of schedule.designation) { const startDateTime = `${designation.start_date} ${designation.start_time}:00`; const endDateTime = `${designation.end_date} ${designation.end_time}:00`; const startDate = (0, import_dayjs.default)(startDateTime); const endDate = (0, import_dayjs.default)(endDateTime); if (targetDate.isSameOrAfter(startDate) && targetDate.isSameOrBefore(endDate)) { return true; } } return false; }; var isInRepeatingSchedule = (targetDate, targetDateString, targetTimeString, schedule, startDate, endDate) => { if (!schedule.repeat_rule) { return false; } const { repeat_rule, repeat_type } = schedule; const targetDateOnly = (0, import_dayjs.default)(targetDateString); if (repeat_rule.included_date && repeat_rule.included_date.length > 0) { for (const includedDate of repeat_rule.included_date) { const includeStartDate = (0, import_dayjs.default)(includedDate.start); const includeEndDate = (0, import_dayjs.default)(includedDate.end); if (targetDateOnly.isSameOrAfter(includeStartDate, "day") && targetDateOnly.isSameOrBefore(includeEndDate, "day")) { return true; } } } for (const excludedDate of repeat_rule.excluded_date) { const excludeStartDate = (0, import_dayjs.default)(excludedDate.start); const excludeEndDate = (0, import_dayjs.default)(excludedDate.end); if (targetDateOnly.isSameOrAfter(excludeStartDate, "day") && targetDateOnly.isSameOrBefore(excludeEndDate, "day")) { return false; } } if (repeat_rule.end.type === "date" && repeat_rule.end.end_date) { const ruleEndDate = (0, import_dayjs.default)(repeat_rule.end.end_date); if (targetDate.isAfter(ruleEndDate)) { return false; } } switch (repeat_type) { case "daily": return isInDailyRepeat(targetDate, startDate, endDate, repeat_rule); case "weekly": return isInWeeklyRepeat(targetDate, startDate, endDate, repeat_rule); default: return false; } }; var isInDailyRepeat = (targetDate, startDate, endDate, repeatRule) => { const daysDiff = targetDate.diff(startDate, "day"); if (daysDiff < 0) { return false; } if (daysDiff % repeatRule.frequency !== 0) { return false; } const targetTimeOfDay = targetDate.hour() * 3600 + targetDate.minute() * 60 + targetDate.second(); const startTimeOfDay = startDate.hour() * 3600 + startDate.minute() * 60 + startDate.second(); const endTimeOfDay = endDate.hour() * 3600 + endDate.minute() * 60 + endDate.second(); return targetTimeOfDay >= startTimeOfDay && targetTimeOfDay <= endTimeOfDay; }; var isInWeeklyRepeat = (targetDate, startDate, endDate, repeatRule) => { const targetDayOfWeek = targetDate.day(); if (!repeatRule.frequency_date.includes(targetDayOfWeek)) { return false; } const weeksDiff = targetDate.diff(startDate, "week"); if (weeksDiff < 0) { return false; } if (weeksDiff % repeatRule.frequency !== 0) { return false; } const targetTimeOfDay = targetDate.hour() * 3600 + targetDate.minute() * 60 + targetDate.second(); const startTimeOfDay = startDate.hour() * 3600 + startDate.minute() * 60 + startDate.second(); const endTimeOfDay = endDate.hour() * 3600 + endDate.minute() * 60 + endDate.second(); return targetTimeOfDay >= startTimeOfDay && targetTimeOfDay <= endTimeOfDay; }; var isInTimeSlotsRepeatingSchedule = (targetDate, targetDateString, targetTimeString, schedule) => { if (!schedule.repeat_rule || !schedule.start_time) { return false; } const { repeat_rule, repeat_type } = schedule; const startDate = (0, import_dayjs.default)(schedule.start_time); const targetDateOnly = (0, import_dayjs.default)(targetDateString); if (repeat_rule.included_date && repeat_rule.included_date.length > 0) { for (const includedDate of repeat_rule.included_date) { const includeStartDate = (0, import_dayjs.default)(includedDate.start); const includeEndDate = (0, import_dayjs.default)(includedDate.end); if (targetDateOnly.isSameOrAfter(includeStartDate, "day") && targetDateOnly.isSameOrBefore(includeEndDate, "day")) { return checkTimeSlotsForDate(targetDate, targetDateString, schedule.time_slot); } } } for (const excludedDate of repeat_rule.excluded_date) { const excludeStartDate = (0, import_dayjs.default)(excludedDate.start); const excludeEndDate = (0, import_dayjs.default)(excludedDate.end); if (targetDateOnly.isSameOrAfter(excludeStartDate, "day") && targetDateOnly.isSameOrBefore(excludeEndDate, "day")) { return false; } } if (repeat_rule.end.type === "date" && repeat_rule.end.end_date) { const ruleEndDate = (0, import_dayjs.default)(repeat_rule.end.end_date); if (targetDateOnly.isAfter(ruleEndDate, "day")) { return false; } } let isDateValid = false; switch (repeat_type) { case "daily": isDateValid = isInDailyRepeatForDate(targetDateOnly, startDate, repeat_rule); break; case "weekly": isDateValid = isInWeeklyRepeatForDate(targetDateOnly, startDate, repeat_rule); break; default: return false; } if (isDateValid) { return checkTimeSlotsForDate(targetDate, targetDateString, schedule.time_slot); } return false; }; var checkTimeSlotsForDate = (targetDate, targetDateString, timeSlots) => { for (const timeSlot of timeSlots) { const slotStart = `${targetDateString} ${timeSlot.start_time}:00`; const slotEnd = `${targetDateString} ${timeSlot.end_time}:00`; const slotStartDate = (0, import_dayjs.default)(slotStart); const slotEndDate = (0, import_dayjs.default)(slotEnd); if (targetDate.isSameOrAfter(slotStartDate) && targetDate.isSameOrBefore(slotEndDate)) { return true; } } return false; }; var isInDailyRepeatForDate = (targetDate, startDate, repeatRule) => { const daysDiff = targetDate.diff(startDate, "day"); if (daysDiff < 0) { return false; } return daysDiff % repeatRule.frequency === 0; }; var isInWeeklyRepeatForDate = (targetDate, startDate, repeatRule) => { const targetDayOfWeek = targetDate.day(); if (!repeatRule.frequency_date.includes(targetDayOfWeek)) { return false; } const weeksDiff = targetDate.diff(startDate, "week"); if (weeksDiff < 0) { return false; } return weeksDiff % repeatRule.frequency === 0; }; var filterDiscountListByBookingTime = (discountList, bookingTime) => { if (!bookingTime) { return discountList; } return discountList.filter((discount) => { var _a, _b; if (((_a = discount == null ? void 0 : discount.metadata) == null ? void 0 : _a.validity_type) === "custom_schedule_validity") { return getDateIsInSchedule(bookingTime, ((_b = discount.custom_schedule_snapshot) == null ? void 0 : _b.data) || []); } try { const isHasStart = !!discount.start_date && !!discount.start_time; const startDate = (0, import_dayjs.default)(`${discount.start_date} ${discount.start_time}`); const bookingTimeDayjs = (0, import_dayjs.default)(bookingTime); const isAfterStart = isHasStart ? bookingTimeDayjs.isSameOrAfter(startDate) : true; const isBeforeExpire = discount.expire_time ? bookingTimeDayjs.isSameOrBefore((0, import_dayjs.default)(discount.expire_time)) : true; return isAfterStart && isBeforeExpire; } catch (error) { return true; } }); }; var isOrderLevelFixedAmountDiscount = (discount) => { var _a, _b; return discount.tag === "product_discount_card" && ((_a = discount == null ? void 0 : discount.metadata) == null ? void 0 : _a.discount_card_type) === "fixed_amount" && ((_b = discount == null ? void 0 : discount.metadata) == null ? void 0 : _b.discount_calculation_mode) === "order_level"; }; var calculateOrderLevelDiscountAllocation = (discount, applicableProducts) => { const result = /* @__PURE__ */ new Map(); if (applicableProducts.length === 0) { return result; } const totalAmount = applicableProducts.reduce((acc, product) => { return new import_decimal.default(acc).plus(new import_decimal.default(product.amount).mul(product.quantity)).toNumber(); }, 0); if (totalAmount <= 0) { return result; } const fixedAmount = new import_decimal.default(discount.par_value || 0).toNumber(); const actualDiscountTotal = Math.min(fixedAmount, totalAmount); let allocatedTotal = new import_decimal.default(0); applicableProducts.forEach((product) => { const rawDiscountPerItem = new import_decimal.default(product.amount).div(totalAmount).mul(actualDiscountTotal); const discountAmountPerItem = rawDiscountPerItem.toDecimalPlaces(2, import_decimal.default.ROUND_DOWN).toNumber(); result.set(product.productId, { discountAmount: discountAmountPerItem, // 单价折扣金额 difference: 0 // 默认差值为0 }); allocatedTotal = allocatedTotal.plus( new import_decimal.default(discountAmountPerItem).mul(product.quantity) ); }); const totalDifference = new import_decimal.default(actualDiscountTotal).minus(allocatedTotal).toDecimalPlaces(2, import_decimal.default.ROUND_HALF_UP).toNumber(); if (totalDifference > 0) { const isSingleQuantity = (product) => { if (product.parentQuantity !== void 0) { return product.quantity === 1 && product.parentQuantity === 1; } return product.quantity === 1; }; const singleQuantityProducts = applicableProducts.filter(isSingleQuantity); if (singleQuantityProducts.length > 0) { const productsWithEnoughSpace = singleQuantityProducts.filter((product) => { const allocation = result.get(product.productId); if (!allocation) return false; const remainingSpace = new import_decimal.default(product.amount).minus(allocation.discountAmount).toNumber(); return remainingSpace >= totalDifference; }); if (productsWithEnoughSpace.length > 0) { productsWithEnoughSpace.sort((a, b) => b.amount - a.amount); const targetProduct = productsWithEnoughSpace[0]; const targetAllocation = result.get(targetProduct.productId); if (targetAllocation) { result.set(targetProduct.productId, { discountAmount: new import_decimal.default(targetAllocation.discountAmount).plus(totalDifference).toNumber(), difference: 0 // 不需要存储差值 }); } } else { singleQuantityProducts.sort((a, b) => b.amount - a.amount); const targetProduct = singleQuantityProducts[0]; const targetAllocation = result.get(targetProduct.productId); if (targetAllocation) { result.set(targetProduct.productId, { ...targetAllocation, difference: totalDifference }); } } } else { const lastProduct = applicableProducts[applicableProducts.length - 1]; const lastProductAllocation = result.get(lastProduct.productId); if (lastProductAllocation) { result.set(lastProduct.productId, { ...lastProductAllocation, difference: totalDifference }); } } } return result; }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { calculateOrderLevelDiscountAllocation, filterDiscountListByBookingTime, getDateIsInSchedule, getDiscountAmount, getDiscountListAmount, getDiscountListAmountTotal, isAllNormalProduct, isNormalProductByDurationSchedule, isOrderLevelFixedAmountDiscount, uniqueById });