UNPKG

@pisell/pisellos

Version:

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

343 lines (341 loc) 12.5 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; 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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/modules/Payment/cashRecommendationAlgorithm.ts var cashRecommendationAlgorithm_exports = {}; __export(cashRecommendationAlgorithm_exports, { CURRENCY_DENOMINATIONS: () => CURRENCY_DENOMINATIONS, recommendOptimalPayments: () => recommendOptimalPayments }); module.exports = __toCommonJS(cashRecommendationAlgorithm_exports); var CURRENCY_DENOMINATIONS = { // 美元 "USD": [100, 50, 20, 10, 5, 1, 0.5, 0.25, 0.1, 0.05, 0.01], // 人民币 "CNY": [100, 50, 20, 10, 5, 1, 0.5, 0.1, 0.05, 0.01], "RMB": [100, 50, 20, 10, 5, 1, 0.5, 0.1, 0.05, 0.01], // 欧元 "EUR": [500, 200, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01], // 日元 "JPY": [1e4, 5e3, 2e3, 1e3, 500, 100, 50, 10, 5, 1], // 英镑 "GBP": [50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01], // 澳元 "AUD": [100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1], // 加元 "CAD": [100, 50, 20, 10, 5, 2, 1, 0.25, 0.1, 0.05], // 港元 "HKD": [1e3, 500, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1], // 新台币 "TWD": [2e3, 1e3, 500, 200, 100, 50, 10, 5, 1], // 韩元 "KRW": [5e4, 1e4, 5e3, 1e3, 500, 100, 50, 10, 5, 1], // 新加坡元 "SGD": [1e4, 1e3, 100, 50, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.01], // 瑞士法郎 "CHF": [1e3, 200, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05], // 默认通用面额(如果币种不在列表中) "DEFAULT": [100, 50, 20, 10, 5, 1, 0.5, 0.25, 0.1, 0.05, 0.01] }; function recommendOptimalPayments(targetAmount, denominations) { if (targetAmount <= 0 || !isFinite(targetAmount) || isNaN(targetAmount)) { return []; } if (!denominations || !Array.isArray(denominations) || denominations.length === 0) { return [Math.ceil(targetAmount)]; } const maxReasonableDenom = targetAmount * 5; const validDenoms = denominations.filter((denom) => denom > 0 && isFinite(denom) && !isNaN(denom) && denom <= maxReasonableDenom).slice(0, 8); if (validDenoms.length === 0) { return [Math.ceil(targetAmount)]; } try { const precision = 100; const target = Math.round(targetAmount * precision); const denoms = validDenoms.map((d) => Math.round(d * precision)).sort((a, b) => b - a); const paymentOptions = []; const exactCombination = findExactCombination(target, denoms, precision); if (exactCombination) { paymentOptions.push(exactCombination); } for (let i = 0; i < Math.min(denoms.length, 6); i++) { const denom = denoms[i]; const count = Math.ceil(target / denom); const sum = count * denom; if (sum >= target && count <= 12) { paymentOptions.push({ combination: Array(count).fill(denom / precision), sum: sum / precision, coinCount: count, denomTypes: 1 }); } } for (let i = 0; i < Math.min(denoms.length - 1, 5); i++) { for (let j = i + 1; j < Math.min(denoms.length, 6); j++) { const denom1 = denoms[i]; const denom2 = denoms[j]; const maxCount1 = Math.min(Math.ceil(target / denom1) + 1, 8); const maxCount2 = Math.min(Math.ceil(target / denom2) + 1, 10); for (let count1 = 1; count1 <= maxCount1; count1++) { for (let count2 = 1; count2 <= maxCount2; count2++) { const sum = count1 * denom1 + count2 * denom2; if (sum >= target) { const combination = [ ...Array(count1).fill(denom1 / precision), ...Array(count2).fill(denom2 / precision) ]; paymentOptions.push({ combination, sum: sum / precision, coinCount: count1 + count2, denomTypes: 2 }); } } } } } for (let i = 0; i < Math.min(denoms.length - 2, 3); i++) { for (let j = i + 1; j < Math.min(denoms.length - 1, 4); j++) { for (let k = j + 1; k < Math.min(denoms.length, 5); k++) { const denom1 = denoms[i]; const denom2 = denoms[j]; const denom3 = denoms[k]; const maxCount1 = Math.min(Math.ceil(target / denom1) + 1, 5); const maxCount2 = Math.min(Math.ceil(target / denom2) + 1, 6); const maxCount3 = Math.min(Math.ceil(target / denom3) + 1, 8); for (let count1 = 1; count1 <= maxCount1; count1++) { for (let count2 = 1; count2 <= maxCount2; count2++) { for (let count3 = 1; count3 <= maxCount3; count3++) { const sum = count1 * denom1 + count2 * denom2 + count3 * denom3; if (sum >= target) { const combination = [ ...Array(count1).fill(denom1 / precision), ...Array(count2).fill(denom2 / precision), ...Array(count3).fill(denom3 / precision) ]; paymentOptions.push({ combination, sum: sum / precision, coinCount: count1 + count2 + count3, denomTypes: 3 }); } } } } } } } for (let i = 0; i < Math.min(denoms.length - 3, 2); i++) { for (let j = i + 1; j < Math.min(denoms.length - 2, 3); j++) { for (let k = j + 1; k < Math.min(denoms.length - 1, 4); k++) { for (let l = k + 1; l < Math.min(denoms.length, 5); l++) { const denom1 = denoms[i]; const denom2 = denoms[j]; const denom3 = denoms[k]; const denom4 = denoms[l]; const maxCount1 = Math.min(Math.ceil(target / denom1) + 1, 3); const maxCount2 = Math.min(Math.ceil(target / denom2) + 1, 4); const maxCount3 = Math.min(Math.ceil(target / denom3) + 1, 5); const maxCount4 = Math.min(Math.ceil(target / denom4) + 1, 6); for (let count1 = 1; count1 <= maxCount1; count1++) { for (let count2 = 1; count2 <= maxCount2; count2++) { for (let count3 = 1; count3 <= maxCount3; count3++) { for (let count4 = 1; count4 <= maxCount4; count4++) { const sum = count1 * denom1 + count2 * denom2 + count3 * denom3 + count4 * denom4; if (sum >= target) { const combination = [ ...Array(count1).fill(denom1 / precision), ...Array(count2).fill(denom2 / precision), ...Array(count3).fill(denom3 / precision), ...Array(count4).fill(denom4 / precision) ]; paymentOptions.push({ combination, sum: sum / precision, coinCount: count1 + count2 + count3 + count4, denomTypes: 4 }); } } } } } } } } } if (paymentOptions.length === 0) { const minValidDenom = validDenoms.find((denom) => denom >= targetAmount); if (minValidDenom) { return [minValidDenom]; } else { const maxDenom = Math.max(...validDenoms); const count = Math.ceil(targetAmount / maxDenom); return [count * maxDenom]; } } const uniqueCombinations = removeDuplicateAndExtendedCombinations(paymentOptions, targetAmount); uniqueCombinations.sort((a, b) => { if (a.denomTypes !== b.denomTypes) { return a.denomTypes - b.denomTypes; } if (a.coinCount !== b.coinCount) { return a.coinCount - b.coinCount; } return a.sum - b.sum; }); const uniqueAmounts = /* @__PURE__ */ new Set(); const finalResults = []; for (const item of uniqueCombinations) { const roundedAmount = Math.round(item.sum * 100) / 100; if (!uniqueAmounts.has(roundedAmount) && finalResults.length < 10) { uniqueAmounts.add(roundedAmount); finalResults.push(roundedAmount); } } return finalResults.sort((a, b) => a - b); } catch (error) { console.warn("推荐支付金额计算出错:", error); const safeAmount = Math.ceil(targetAmount); return [targetAmount, safeAmount]; } } function findExactCombination(target, denoms, precision) { function dfs(remaining, denomIndex, currentCombination) { if (remaining === 0) { return currentCombination; } if (remaining < 0 || denomIndex >= denoms.length || currentCombination.length > 10) { return null; } const denom = denoms[denomIndex]; const maxCount = Math.min(Math.floor(remaining / denom), 8); for (let count = maxCount; count >= 0; count--) { const newCombination = [...currentCombination, ...Array(count).fill(denom)]; const result = dfs(remaining - count * denom, denomIndex + 1, newCombination); if (result) { return result; } } return null; } const exactMatch = dfs(target, 0, []); if (exactMatch && exactMatch.length > 0) { const denomTypes = new Set(exactMatch).size; return { combination: exactMatch.map((d) => d / precision), sum: target / precision, coinCount: exactMatch.length, denomTypes }; } return null; } function removeDuplicateAndExtendedCombinations(combinations, targetAmount) { const result = []; combinations.sort((a, b) => { if (a.denomTypes !== b.denomTypes) { return a.denomTypes - b.denomTypes; } if (a.coinCount !== b.coinCount) { return a.coinCount - b.coinCount; } return a.sum - b.sum; }); for (let i = 0; i < combinations.length; i++) { const current = combinations[i]; let shouldSkip = false; for (let j = 0; j < result.length; j++) { const existing = result[j]; if (isSameCombination(current.combination, existing.combination)) { shouldSkip = true; break; } if (isExtensionOf(current.combination, existing.combination)) { shouldSkip = true; break; } } if (!shouldSkip) { for (let j = result.length - 1; j >= 0; j--) { if (isExtensionOf(result[j].combination, current.combination)) { result.splice(j, 1); } } result.push(current); } } return result; } function isExtensionOf(combinationA, combinationB) { if (combinationA.length <= combinationB.length) { return false; } const countA = {}; const countB = {}; combinationA.forEach((coin) => { countA[coin] = (countA[coin] || 0) + 1; }); combinationB.forEach((coin) => { countB[coin] = (countB[coin] || 0) + 1; }); for (const coin in countB) { if (!countA[coin] || countA[coin] < countB[coin]) { return false; } } let hasExtra = false; for (const coin in countA) { if (countA[coin] > (countB[coin] || 0)) { hasExtra = true; break; } } return hasExtra; } function isSameCombination(combinationA, combinationB) { if (combinationA.length !== combinationB.length) { return false; } const countA = {}; const countB = {}; combinationA.forEach((coin) => { countA[coin] = (countA[coin] || 0) + 1; }); combinationB.forEach((coin) => { countB[coin] = (countB[coin] || 0) + 1; }); for (const coin in countA) { if (countA[coin] !== (countB[coin] || 0)) { return false; } } for (const coin in countB) { if (countB[coin] !== (countA[coin] || 0)) { return false; } } return true; } // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { CURRENCY_DENOMINATIONS, recommendOptimalPayments });