開始使用
TIP
建議使用 Web Worker 運行此模組,以免運算卡住主頁面,導致體驗不佳。
安裝
在根目錄建立 .npmrc
加入以下資訊
shell@inner-products:registry=http://npm.bgmotion.com.tw安裝套件
shellnpm i @inner-products/bgm-promotion-module
如何使用
簡易使用順序為:
1. 準備商品與待用優惠資料
例如:
ts
import {
Item,
Promotion
} from '@inner-products/bgm-promotion-module'
/** 購物車商品 */
const items: Item[] = [
{
commodity: {
// key 很重要,模組使用 key 來區分商品
key: '1',
// name 用於方便識別,不影響計算
name: 'A-30ml',
price: 140,
},
quantity: 5
},
{
commodity: {
key: '2',
name: 'A-50ml',
price: 300,
},
quantity: 1
}
]
/** 可用優惠 */
const promotions: Promotion[] = [
{
key: '1',
type: 'commodity-offer',
name: '周年慶大放送',
description: '買 5 瓶 A 30ml 送 1 瓶 A 50ml',
constraints: [],
contents: [
{
condition: [
{
list: [
{
key: '1',
name: 'A-30ml',
price: 140,
}
],
comparison: {
logic: 'greater-than-equal-to',
target: 'purchase-quantity',
value: 5
}
}
],
result: {
commodities: [
{
key: '2',
name: 'A-50ml',
price: 300,
}
],
quantity: 1
}
},
],
}
]2. 取得最佳優惠結果
預設取得前 3 名,可以指定名次。
ts
/** 購物車商品 */
const items: Item[] = []
/** 可用優惠 */
const promotions: Promotion[] = []
const rankedResults = getPromotionsRanking({ items, promotions })
// 限制數量
const rankedResultsTop2 = getPromotionsRanking({
items,
promotions,
topN: 2,
})
// 設定 autoOffsetMode 可以讓贈品自動抵銷商品
const offsetResults = getPromotionsRanking({
items,
promotions,
autoOffsetMode: 'multiple-types-from-highest',
})
// 預設會保證相同輸入會有相同輸出,若希望結果不太一樣,可以將 reproducibility 設為 false
// 不建議這麼做就是惹。(○´・д・)ノ
const irreproducibilityResults = getPromotionsRanking({
items,
promotions,
reproducibility: false,
})
// 可以使用 checkPromotionResult 檢查優惠結果
rankedResults.forEach((result) => {
const errorMessage = checkPromotionResult(result)
})💡 自動抵銷模式
細節請見文件
優惠結果為:
ts
/** 優惠結果 */
export interface PromotionResult<Data = undefined> {
/** 原始購物清單 */
originalItems: Item<Data>[];
/** 剩餘的商品。沒有商品優惠可用,剩下來的商品 */
remainingItems: Item<Data>[];
/** 套用優惠的商品 */
promotedList: PromotedListItem<Data>[];
/** 可用贈品。 */
giveawayList: GiveawayListItem<Data>[];
/** 被贈品抵銷的商品。此區商品要被視為 0 元 */
offsetList: OffsetListItem<Data>[];
/** 所有已使用的優惠,彙整自 promotedList.promotion */
usedPromotions: Promotion[];
}
/** 已套用優惠結果的商品 */
export interface PromotedListItem<Data = undefined> {
/** 符合條件之商品與優惠結果 */
appliedList: Array<{
item: Item<Data>;
/** 優惠結果 */
result?: SelectableGiveaway<Data> | Arithmetic;
}>;
/** 原始優惠 */
promotion: Promotion;
/** 套用的優惠內容 */
promotedContent: Promotion['contents'][number];
/** 匹配的 Decision */
matchedDecision?: PromotionDecision<Data>;
}
/** 可用贈品。
*
* 集結 promotedList.appliedList.result 中的 SelectableGiveaway 資料。
*
* 不包含以自動抵銷的贈品,自動抵銷的贈品在 offsetList 中。
*/
export interface GiveawayListItem<Data = undefined> {
/** 贈品 */
item: SelectableGiveaway<Data>;
/** 原始優惠 */
promotion: Promotion;
/** 套用的優惠內容 */
promotedContent: Promotion['contents'][number];
/** 匹配的 Decision */
matchedDecision?: PromotionDecision<Data>;
}
/** 被贈品抵銷的商品 */
export interface OffsetListItem<Data = undefined> {
/** 商品 */
item: Item<Data>;
/** 原始優惠 */
promotion: Promotion;
/** 套用的優惠內容 */
promotedContent: Promotion['contents'][number];
/** 匹配的 Decision */
matchedDecision?: PromotionDecision<Data>;
}
/** 已排名後的優惠結果 */
export interface RankedPromotionResult<Data> extends PromotionResult<Data> {
/** 統計資料 */
analyticalData: AnalyticalData;
}3. 內容解析
以下功能可以進行結果解析
計算優惠結果金額:getAnalyticalData
💡 排名結果已有此資料
RankedPromotionResult 內已包含 analyticalData,不用再次進行計算
ts
/** 優惠結果 */
const promotionResult: PromotionResult = {}
const analyticalData = getAnalyticalData(promotionResult)analyticalData 結果為:
ts
export interface AnalyticalData {
/** 原始總價 */
originalTotalPrice: number;
/** 滿額累積,用於判斷滿額優惠是否可用
*
* 去除不計入滿額贈優惠後的金額總和
*/
spendTotalPrice: number;
/** 商品總價。包含剩餘商品和商品優惠(commodity-offer) */
commodityTotalPrice: number;
/** commodityTotalPrice 加入訂單優惠(order-offer)後的價格 */
orderTotalPrice: number;
/** 包含全部優惠(commodity-offer、order-offer、minimum-spend-offer)總價 */
totalPrice: number;
}取得每個商品優惠後的價格:getItemizedResult
ts
const promotionResult: PromotionResult = {}
const result = getItemizedResult(promotionResult)result 結果為:
ts
export interface ItemizedResult<Data = undefined> {
list: Array<{
/** 商品 */
commodity: Commodity<Data>;
quantity: number;
/** 優惠後的商品單價 */
promotedPrice: number;
/** 優惠來源 */
sources: Array<{
promotion: Promotion;
promotedContent: Promotion['contents'][number];
/** 匹配的 Decision */
matchedDecision?: PromotionDecision<Data>;
}>;
}>;
}5. 推薦更多商品
推薦模組會依照目前商品與待用優惠,推薦可再追加多少商品或金額,即可達成指定優惠
ts
const results = getRecommendResults({
items,
promotions,
})results 結果為:
ts
/** 推薦商品與數量
*
* 來自 promotedContent.condition 內容
*/
type RecommendResultContentList<
Data = undefined,
> = Array<{
/** 候選商品 */
commodities: Commodity<Data>[];
/** 原始的比對條件 */
comparison: Comparison;
/** 推薦數量 */
quantity: number;
}>
interface RecommendResult<Data = undefined> {
/** 推薦列表。可以同時使用、不會互斥的優惠 */
recommendedList: Array<{
/** 與 recommendedItems 一起搭配的商品項目
*
* 如果條件是 price,則此欄位為空
*/
bundledItems: Item<Data>[];
/** 推薦內容。可能為商品或金額。 */
content: {
/** 推薦商品與數量 */
list: RecommendResultContentList<Data>;
} | {
/** 還差多少金額
*
* 金額目前只實作差多少,不實作其他情境
*/
price: number;
};
/** 差一點就能達成的條件 */
promotedContent: Promotion['contents'][number];
promotion: Promotion;
}>;
}檢查結果有效性
檢查結果是否有效,可用來確認資料是否被竄改
檢查優惠結果重現性:checkParamsAndRankedResult
檢查優惠結果是否與指定參數結果一致。
ts
/** 購物車商品 */
const items: Item[] = []
/** 可用優惠 */
const promotions: Promotion[] = []
const rankedResults = getPromotionsRanking({ items, promotions })
// 結果是否相同
const isSame = checkParamsAndRankedResult({
items,
promotions,
results: rankedResults,
})