@pisell/pisellos
Version:
一个可扩展的前端模块化SDK框架,支持插件系统
343 lines (341 loc) • 12.5 kB
JavaScript
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
});