@pisell/pisellos 0.0.229 → 0.0.231
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/Order/index.d.ts +13 -2
- package/dist/modules/Order/index.js +84 -1
- package/dist/modules/Order/types.d.ts +43 -0
- package/dist/modules/Order/types.js +8 -0
- package/dist/modules/Payment/cash.d.ts +8 -0
- package/dist/modules/Payment/cash.js +20 -1
- package/dist/modules/Payment/cashRecommendationAlgorithm.d.ts +22 -0
- package/dist/modules/Payment/cashRecommendationAlgorithm.js +423 -0
- package/dist/modules/Payment/eftpos.js +1 -1
- package/dist/modules/Payment/index.d.ts +57 -94
- package/dist/modules/Payment/index.js +810 -1109
- package/dist/modules/Payment/mx51.d.ts +0 -0
- package/dist/modules/Payment/mx51.js +0 -0
- package/dist/modules/Payment/types.d.ts +358 -50
- package/dist/modules/Payment/types.js +103 -4
- package/dist/modules/Payment/utils.d.ts +17 -0
- package/dist/modules/Payment/utils.js +62 -0
- package/dist/modules/Payment/walletpass.d.ts +96 -0
- package/dist/modules/Payment/walletpass.js +492 -0
- package/dist/modules/Schedule/index.d.ts +9 -0
- package/dist/modules/Schedule/index.js +60 -0
- package/dist/solution/BookingTicket/index.d.ts +1 -1
- package/dist/solution/Checkout/appointmentDemo.json +1 -0
- package/dist/solution/Checkout/index.d.ts +415 -0
- package/dist/solution/Checkout/index.js +3622 -0
- package/dist/solution/Checkout/types.d.ts +737 -0
- package/dist/solution/Checkout/types.js +137 -0
- package/dist/solution/Checkout/utils/index.d.ts +73 -0
- package/dist/{modules/Payment/wallet.js → solution/Checkout/utils/index.js} +363 -70
- package/dist/solution/index.d.ts +1 -0
- package/dist/solution/index.js +2 -1
- package/lib/modules/Order/index.d.ts +13 -2
- package/lib/modules/Order/index.js +58 -1
- package/lib/modules/Order/types.d.ts +43 -0
- package/lib/modules/Payment/cash.d.ts +8 -0
- package/lib/modules/Payment/cash.js +14 -1
- package/lib/modules/Payment/cashRecommendationAlgorithm.d.ts +22 -0
- package/lib/modules/Payment/cashRecommendationAlgorithm.js +342 -0
- package/lib/modules/Payment/eftpos.js +1 -1
- package/lib/modules/Payment/index.d.ts +57 -94
- package/lib/modules/Payment/index.js +363 -525
- package/lib/modules/Payment/mx51.d.ts +0 -0
- package/lib/modules/Payment/mx51.js +0 -0
- package/lib/modules/Payment/types.d.ts +358 -50
- package/lib/modules/Payment/types.js +37 -2
- package/lib/modules/Payment/utils.d.ts +17 -0
- package/lib/modules/Payment/utils.js +67 -0
- package/lib/modules/Payment/walletpass.d.ts +96 -0
- package/lib/modules/Payment/walletpass.js +304 -0
- package/lib/modules/Schedule/index.d.ts +9 -0
- package/lib/modules/Schedule/index.js +36 -0
- package/lib/solution/BookingTicket/index.d.ts +1 -1
- package/lib/solution/Checkout/appointmentDemo.json +1 -0
- package/lib/solution/Checkout/index.d.ts +415 -0
- package/lib/solution/Checkout/index.js +2107 -0
- package/lib/solution/Checkout/types.d.ts +737 -0
- package/lib/solution/Checkout/types.js +80 -0
- package/lib/solution/Checkout/utils/index.d.ts +73 -0
- package/lib/solution/Checkout/utils/index.js +266 -0
- package/lib/solution/index.d.ts +1 -0
- package/lib/solution/index.js +3 -1
- package/package.json +1 -1
- package/dist/modules/Payment/wallet.d.ts +0 -11
- package/lib/modules/Payment/wallet.d.ts +0 -11
- package/lib/modules/Payment/wallet.js +0 -51
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
|
|
2
|
+
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
3
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
4
|
+
function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
|
|
5
|
+
function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
|
|
6
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
7
|
+
/**
|
|
8
|
+
* 现金支付推荐算法
|
|
9
|
+
*
|
|
10
|
+
* 核心原理:
|
|
11
|
+
* 1. 每个推荐金额都应该是独立的最优组合
|
|
12
|
+
* 2. 不推荐在已有最优解基础上添加额外面额的组合
|
|
13
|
+
* 3. 优先推荐使用不同数量币种的组合
|
|
14
|
+
* 4. 根据组合判断去重,避免扩展组合
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 常见国家货币面额配置
|
|
19
|
+
*/
|
|
20
|
+
export var CURRENCY_DENOMINATIONS = {
|
|
21
|
+
// 美元
|
|
22
|
+
'USD': [100, 50, 20, 10, 5, 2, 1, 0.5, 0.25, 0.1, 0.05, 0.01],
|
|
23
|
+
// 人民币
|
|
24
|
+
'CNY': [100, 50, 20, 10, 5, 1, 0.5, 0.1, 0.05, 0.01],
|
|
25
|
+
'RMB': [100, 50, 20, 10, 5, 1, 0.5, 0.1, 0.05, 0.01],
|
|
26
|
+
// 欧元
|
|
27
|
+
'EUR': [500, 200, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01],
|
|
28
|
+
// 日元
|
|
29
|
+
'JPY': [10000, 5000, 2000, 1000, 500, 100, 50, 10, 5, 1],
|
|
30
|
+
// 英镑
|
|
31
|
+
'GBP': [50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01],
|
|
32
|
+
// 澳元
|
|
33
|
+
'AUD': [100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1],
|
|
34
|
+
// 加元
|
|
35
|
+
'CAD': [100, 50, 20, 10, 5, 2, 1, 0.25, 0.1, 0.05],
|
|
36
|
+
// 港元
|
|
37
|
+
'HKD': [1000, 500, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1],
|
|
38
|
+
// 新台币
|
|
39
|
+
'TWD': [2000, 1000, 500, 200, 100, 50, 10, 5, 1],
|
|
40
|
+
// 韩元
|
|
41
|
+
'KRW': [50000, 10000, 5000, 1000, 500, 100, 50, 10, 5, 1],
|
|
42
|
+
// 新加坡元
|
|
43
|
+
'SGD': [10000, 1000, 100, 50, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05, 0.01],
|
|
44
|
+
// 瑞士法郎
|
|
45
|
+
'CHF': [1000, 200, 100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1, 0.05],
|
|
46
|
+
// 默认通用面额(如果币种不在列表中)
|
|
47
|
+
'DEFAULT': [100, 50, 20, 10, 5, 1, 0.5, 0.25, 0.1, 0.05, 0.01]
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 支付组合类型定义
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 最优支付金额推荐算法
|
|
56
|
+
* 推荐通过不同数量面额组合刚好足够支付的最小金额
|
|
57
|
+
*
|
|
58
|
+
* @param targetAmount 目标金额
|
|
59
|
+
* @param denominations 币种面值数组
|
|
60
|
+
* @returns 推荐支付金额数组
|
|
61
|
+
*/
|
|
62
|
+
export function recommendOptimalPayments(targetAmount, denominations) {
|
|
63
|
+
// 参数验证
|
|
64
|
+
if (targetAmount <= 0 || !isFinite(targetAmount) || isNaN(targetAmount)) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
if (!denominations || !Array.isArray(denominations) || denominations.length === 0) {
|
|
68
|
+
return [Math.ceil(targetAmount)];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 过滤有效面额并限制搜索空间以提高性能
|
|
72
|
+
var maxReasonableDenom = targetAmount * 5; // 适度限制搜索空间
|
|
73
|
+
var validDenoms = denominations.filter(function (denom) {
|
|
74
|
+
return denom > 0 && isFinite(denom) && !isNaN(denom) && denom <= maxReasonableDenom;
|
|
75
|
+
}).slice(0, 8); // 限制面额数量以提高性能
|
|
76
|
+
|
|
77
|
+
if (validDenoms.length === 0) {
|
|
78
|
+
// 如果没有合适的面额,返回最接近的整数金额
|
|
79
|
+
return [Math.ceil(targetAmount)];
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
// 处理精度问题,将金额转换为整数计算
|
|
83
|
+
var precision = 100; // 假设最小单位是0.01
|
|
84
|
+
var target = Math.round(targetAmount * precision);
|
|
85
|
+
var denoms = validDenoms.map(function (d) {
|
|
86
|
+
return Math.round(d * precision);
|
|
87
|
+
}).sort(function (a, b) {
|
|
88
|
+
return b - a;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// 存储不同的支付组合方案
|
|
92
|
+
var paymentOptions = [];
|
|
93
|
+
|
|
94
|
+
// 首先尝试找到精确匹配的组合
|
|
95
|
+
var exactCombination = findExactCombination(target, denoms, precision);
|
|
96
|
+
if (exactCombination) {
|
|
97
|
+
paymentOptions.push(exactCombination);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 为了提高效率,我们将按币种数量来生成组合
|
|
101
|
+
// 1币种组合:只用一种面额
|
|
102
|
+
for (var i = 0; i < Math.min(denoms.length, 6); i++) {
|
|
103
|
+
var denom = denoms[i];
|
|
104
|
+
var count = Math.ceil(target / denom);
|
|
105
|
+
var sum = count * denom;
|
|
106
|
+
if (sum >= target && count <= 12) {
|
|
107
|
+
// 限制硬币数量以提高性能
|
|
108
|
+
paymentOptions.push({
|
|
109
|
+
combination: Array(count).fill(denom / precision),
|
|
110
|
+
sum: sum / precision,
|
|
111
|
+
coinCount: count,
|
|
112
|
+
denomTypes: 1
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 2币种组合:使用两种面额(大幅限制搜索范围以提高性能)
|
|
118
|
+
for (var _i = 0; _i < Math.min(denoms.length - 1, 5); _i++) {
|
|
119
|
+
for (var j = _i + 1; j < Math.min(denoms.length, 6); j++) {
|
|
120
|
+
var denom1 = denoms[_i];
|
|
121
|
+
var denom2 = denoms[j];
|
|
122
|
+
|
|
123
|
+
// 大幅限制搜索范围,基于目标金额动态调整
|
|
124
|
+
var maxCount1 = Math.min(Math.ceil(target / denom1) + 1, 8);
|
|
125
|
+
var maxCount2 = Math.min(Math.ceil(target / denom2) + 1, 10);
|
|
126
|
+
for (var count1 = 1; count1 <= maxCount1; count1++) {
|
|
127
|
+
for (var count2 = 1; count2 <= maxCount2; count2++) {
|
|
128
|
+
var _sum = count1 * denom1 + count2 * denom2;
|
|
129
|
+
if (_sum >= target) {
|
|
130
|
+
var combination = [].concat(_toConsumableArray(Array(count1).fill(denom1 / precision)), _toConsumableArray(Array(count2).fill(denom2 / precision)));
|
|
131
|
+
paymentOptions.push({
|
|
132
|
+
combination: combination,
|
|
133
|
+
sum: _sum / precision,
|
|
134
|
+
coinCount: count1 + count2,
|
|
135
|
+
denomTypes: 2
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 3币种组合:使用三种面额(大幅限制搜索以提高效率)
|
|
144
|
+
for (var _i2 = 0; _i2 < Math.min(denoms.length - 2, 3); _i2++) {
|
|
145
|
+
for (var _j = _i2 + 1; _j < Math.min(denoms.length - 1, 4); _j++) {
|
|
146
|
+
for (var k = _j + 1; k < Math.min(denoms.length, 5); k++) {
|
|
147
|
+
var _denom = denoms[_i2];
|
|
148
|
+
var _denom2 = denoms[_j];
|
|
149
|
+
var denom3 = denoms[k];
|
|
150
|
+
|
|
151
|
+
// 大幅限制搜索范围
|
|
152
|
+
var _maxCount = Math.min(Math.ceil(target / _denom) + 1, 5);
|
|
153
|
+
var _maxCount2 = Math.min(Math.ceil(target / _denom2) + 1, 6);
|
|
154
|
+
var maxCount3 = Math.min(Math.ceil(target / denom3) + 1, 8);
|
|
155
|
+
for (var _count = 1; _count <= _maxCount; _count++) {
|
|
156
|
+
for (var _count2 = 1; _count2 <= _maxCount2; _count2++) {
|
|
157
|
+
for (var count3 = 1; count3 <= maxCount3; count3++) {
|
|
158
|
+
var _sum2 = _count * _denom + _count2 * _denom2 + count3 * denom3;
|
|
159
|
+
if (_sum2 >= target) {
|
|
160
|
+
var _combination = [].concat(_toConsumableArray(Array(_count).fill(_denom / precision)), _toConsumableArray(Array(_count2).fill(_denom2 / precision)), _toConsumableArray(Array(count3).fill(denom3 / precision)));
|
|
161
|
+
paymentOptions.push({
|
|
162
|
+
combination: _combination,
|
|
163
|
+
sum: _sum2 / precision,
|
|
164
|
+
coinCount: _count + _count2 + count3,
|
|
165
|
+
denomTypes: 3
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// 4币种组合:使用四种面额(极度限制搜索以提高性能)
|
|
176
|
+
for (var _i3 = 0; _i3 < Math.min(denoms.length - 3, 2); _i3++) {
|
|
177
|
+
for (var _j2 = _i3 + 1; _j2 < Math.min(denoms.length - 2, 3); _j2++) {
|
|
178
|
+
for (var _k = _j2 + 1; _k < Math.min(denoms.length - 1, 4); _k++) {
|
|
179
|
+
for (var l = _k + 1; l < Math.min(denoms.length, 5); l++) {
|
|
180
|
+
var _denom3 = denoms[_i3];
|
|
181
|
+
var _denom4 = denoms[_j2];
|
|
182
|
+
var _denom5 = denoms[_k];
|
|
183
|
+
var denom4 = denoms[l];
|
|
184
|
+
|
|
185
|
+
// 极度限制搜索范围
|
|
186
|
+
var _maxCount3 = Math.min(Math.ceil(target / _denom3) + 1, 3);
|
|
187
|
+
var _maxCount4 = Math.min(Math.ceil(target / _denom4) + 1, 4);
|
|
188
|
+
var _maxCount5 = Math.min(Math.ceil(target / _denom5) + 1, 5);
|
|
189
|
+
var maxCount4 = Math.min(Math.ceil(target / denom4) + 1, 6);
|
|
190
|
+
for (var _count3 = 1; _count3 <= _maxCount3; _count3++) {
|
|
191
|
+
for (var _count4 = 1; _count4 <= _maxCount4; _count4++) {
|
|
192
|
+
for (var _count5 = 1; _count5 <= _maxCount5; _count5++) {
|
|
193
|
+
for (var count4 = 1; count4 <= maxCount4; count4++) {
|
|
194
|
+
var _sum3 = _count3 * _denom3 + _count4 * _denom4 + _count5 * _denom5 + count4 * denom4;
|
|
195
|
+
if (_sum3 >= target) {
|
|
196
|
+
var _combination2 = [].concat(_toConsumableArray(Array(_count3).fill(_denom3 / precision)), _toConsumableArray(Array(_count4).fill(_denom4 / precision)), _toConsumableArray(Array(_count5).fill(_denom5 / precision)), _toConsumableArray(Array(count4).fill(denom4 / precision)));
|
|
197
|
+
paymentOptions.push({
|
|
198
|
+
combination: _combination2,
|
|
199
|
+
sum: _sum3 / precision,
|
|
200
|
+
coinCount: _count3 + _count4 + _count5 + count4,
|
|
201
|
+
denomTypes: 4
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// 如果没有找到任何组合,提供默认建议
|
|
214
|
+
if (paymentOptions.length === 0) {
|
|
215
|
+
// 找到大于等于目标金额的最小面额
|
|
216
|
+
var minValidDenom = validDenoms.find(function (denom) {
|
|
217
|
+
return denom >= targetAmount;
|
|
218
|
+
});
|
|
219
|
+
if (minValidDenom) {
|
|
220
|
+
return [minValidDenom];
|
|
221
|
+
} else {
|
|
222
|
+
// 使用最大面额的组合
|
|
223
|
+
var maxDenom = Math.max.apply(Math, _toConsumableArray(validDenoms));
|
|
224
|
+
var _count6 = Math.ceil(targetAmount / maxDenom);
|
|
225
|
+
return [_count6 * maxDenom];
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// 移除重复和扩展组合
|
|
230
|
+
var uniqueCombinations = removeDuplicateAndExtendedCombinations(paymentOptions, targetAmount);
|
|
231
|
+
|
|
232
|
+
// 按币种类型数量排序,然后按总硬币数量排序,最后按金额排序
|
|
233
|
+
uniqueCombinations.sort(function (a, b) {
|
|
234
|
+
if (a.denomTypes !== b.denomTypes) {
|
|
235
|
+
return a.denomTypes - b.denomTypes;
|
|
236
|
+
}
|
|
237
|
+
if (a.coinCount !== b.coinCount) {
|
|
238
|
+
return a.coinCount - b.coinCount;
|
|
239
|
+
}
|
|
240
|
+
return a.sum - b.sum;
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// 按金额去重并返回推荐的支付金额
|
|
244
|
+
var uniqueAmounts = new Set();
|
|
245
|
+
var finalResults = [];
|
|
246
|
+
for (var _i4 = 0, _uniqueCombinations = uniqueCombinations; _i4 < _uniqueCombinations.length; _i4++) {
|
|
247
|
+
var item = _uniqueCombinations[_i4];
|
|
248
|
+
var roundedAmount = Math.round(item.sum * 100) / 100; // 处理浮点数精度
|
|
249
|
+
if (!uniqueAmounts.has(roundedAmount) && finalResults.length < 10) {
|
|
250
|
+
uniqueAmounts.add(roundedAmount);
|
|
251
|
+
finalResults.push(roundedAmount);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return finalResults.sort(function (a, b) {
|
|
255
|
+
return a - b;
|
|
256
|
+
});
|
|
257
|
+
} catch (error) {
|
|
258
|
+
// 发生错误时返回安全的默认值
|
|
259
|
+
console.warn('推荐支付金额计算出错:', error);
|
|
260
|
+
// 返回最接近的整数金额作为兜底
|
|
261
|
+
var safeAmount = Math.ceil(targetAmount);
|
|
262
|
+
return [targetAmount, safeAmount];
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* 尝试找到精确匹配目标金额的组合
|
|
268
|
+
*/
|
|
269
|
+
function findExactCombination(target, denoms, precision) {
|
|
270
|
+
// 简单的深度优先搜索,限制搜索深度
|
|
271
|
+
function dfs(remaining, denomIndex, currentCombination) {
|
|
272
|
+
if (remaining === 0) {
|
|
273
|
+
return currentCombination;
|
|
274
|
+
}
|
|
275
|
+
if (remaining < 0 || denomIndex >= denoms.length || currentCombination.length > 10) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
var denom = denoms[denomIndex];
|
|
279
|
+
|
|
280
|
+
// 尝试使用当前面额 0 到 maxCount 次
|
|
281
|
+
var maxCount = Math.min(Math.floor(remaining / denom), 8);
|
|
282
|
+
for (var count = maxCount; count >= 0; count--) {
|
|
283
|
+
var newCombination = [].concat(_toConsumableArray(currentCombination), _toConsumableArray(Array(count).fill(denom)));
|
|
284
|
+
var result = dfs(remaining - count * denom, denomIndex + 1, newCombination);
|
|
285
|
+
if (result) {
|
|
286
|
+
return result;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
var exactMatch = dfs(target, 0, []);
|
|
292
|
+
if (exactMatch && exactMatch.length > 0) {
|
|
293
|
+
var denomTypes = new Set(exactMatch).size;
|
|
294
|
+
return {
|
|
295
|
+
combination: exactMatch.map(function (d) {
|
|
296
|
+
return d / precision;
|
|
297
|
+
}),
|
|
298
|
+
sum: target / precision,
|
|
299
|
+
coinCount: exactMatch.length,
|
|
300
|
+
denomTypes: denomTypes
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* 移除重复和扩展组合
|
|
308
|
+
* 核心原理:如果组合A包含组合B的所有硬币,并且还有额外硬币,则A是B的扩展,应该被移除
|
|
309
|
+
*/
|
|
310
|
+
function removeDuplicateAndExtendedCombinations(combinations, targetAmount) {
|
|
311
|
+
var result = [];
|
|
312
|
+
|
|
313
|
+
// 按币种类型数量、硬币数量、金额排序,确保较简单的组合在前面
|
|
314
|
+
combinations.sort(function (a, b) {
|
|
315
|
+
if (a.denomTypes !== b.denomTypes) {
|
|
316
|
+
return a.denomTypes - b.denomTypes;
|
|
317
|
+
}
|
|
318
|
+
if (a.coinCount !== b.coinCount) {
|
|
319
|
+
return a.coinCount - b.coinCount;
|
|
320
|
+
}
|
|
321
|
+
return a.sum - b.sum;
|
|
322
|
+
});
|
|
323
|
+
for (var i = 0; i < combinations.length; i++) {
|
|
324
|
+
var current = combinations[i];
|
|
325
|
+
var shouldSkip = false;
|
|
326
|
+
|
|
327
|
+
// 检查当前组合是否与已添加的组合重复或者是扩展
|
|
328
|
+
for (var j = 0; j < result.length; j++) {
|
|
329
|
+
var existing = result[j];
|
|
330
|
+
|
|
331
|
+
// 如果完全相同,跳过
|
|
332
|
+
if (isSameCombination(current.combination, existing.combination)) {
|
|
333
|
+
shouldSkip = true;
|
|
334
|
+
break;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// 如果当前组合是已存在组合的扩展,跳过
|
|
338
|
+
if (isExtensionOf(current.combination, existing.combination)) {
|
|
339
|
+
shouldSkip = true;
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// 检查是否有其他组合是当前组合的扩展,如果有,移除那些扩展
|
|
345
|
+
if (!shouldSkip) {
|
|
346
|
+
// 移除所有当前组合的扩展
|
|
347
|
+
for (var _j3 = result.length - 1; _j3 >= 0; _j3--) {
|
|
348
|
+
if (isExtensionOf(result[_j3].combination, current.combination)) {
|
|
349
|
+
result.splice(_j3, 1);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
result.push(current);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return result;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* 检查组合A是否是组合B的扩展(即A包含B的所有硬币,并且还有额外的硬币)
|
|
360
|
+
*/
|
|
361
|
+
function isExtensionOf(combinationA, combinationB) {
|
|
362
|
+
// 如果A的硬币数量少于等于B,A不可能是B的扩展
|
|
363
|
+
if (combinationA.length <= combinationB.length) {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// 统计每个面额的数量
|
|
368
|
+
var countA = {};
|
|
369
|
+
var countB = {};
|
|
370
|
+
combinationA.forEach(function (coin) {
|
|
371
|
+
countA[coin] = (countA[coin] || 0) + 1;
|
|
372
|
+
});
|
|
373
|
+
combinationB.forEach(function (coin) {
|
|
374
|
+
countB[coin] = (countB[coin] || 0) + 1;
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// 检查B的所有面额在A中是否都有足够的数量
|
|
378
|
+
for (var coin in countB) {
|
|
379
|
+
if (!countA[coin] || countA[coin] < countB[coin]) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// 检查A是否有B没有的额外硬币
|
|
385
|
+
var hasExtra = false;
|
|
386
|
+
for (var _coin in countA) {
|
|
387
|
+
if (countA[_coin] > (countB[_coin] || 0)) {
|
|
388
|
+
hasExtra = true;
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return hasExtra;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* 检查两个组合是否本质相同(相同的面额组合)
|
|
397
|
+
*/
|
|
398
|
+
function isSameCombination(combinationA, combinationB) {
|
|
399
|
+
if (combinationA.length !== combinationB.length) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
var countA = {};
|
|
403
|
+
var countB = {};
|
|
404
|
+
combinationA.forEach(function (coin) {
|
|
405
|
+
countA[coin] = (countA[coin] || 0) + 1;
|
|
406
|
+
});
|
|
407
|
+
combinationB.forEach(function (coin) {
|
|
408
|
+
countB[coin] = (countB[coin] || 0) + 1;
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
// 检查两个组合是否有相同的面额和数量
|
|
412
|
+
for (var coin in countA) {
|
|
413
|
+
if (countA[coin] !== (countB[coin] || 0)) {
|
|
414
|
+
return false;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
for (var _coin2 in countB) {
|
|
418
|
+
if (countB[_coin2] !== (countA[_coin2] || 0)) {
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
@@ -44,7 +44,7 @@ export var EftposPaymentImpl = /*#__PURE__*/function () {
|
|
|
44
44
|
voucher_id: ''
|
|
45
45
|
};
|
|
46
46
|
_context.next = 8;
|
|
47
|
-
return this.paymentModule.
|
|
47
|
+
return this.paymentModule.addPaymentItemAsync(orderUuid, paymentItem);
|
|
48
48
|
case 8:
|
|
49
49
|
case "end":
|
|
50
50
|
return _context.stop();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Module, PisellCore, ModuleOptions } from '../../types';
|
|
2
|
+
import { RequestPlugin } from '../../plugins';
|
|
2
3
|
import { BaseModule } from '../BaseModule';
|
|
3
|
-
import { PaymentModuleAPI, PaymentMethod, PaymentOrder, PaymentItem, PaymentItemInput, PaymentUpdateFields, PushOrderParams, CashPayment, EftposPayment,
|
|
4
|
+
import { PaymentModuleAPI, PaymentMethod, PaymentOrder, PaymentItem, PaymentItemInput, PaymentUpdateFields, PushOrderParams, CashPayment, EftposPayment, WalletPassPayment, RoundingRule, RoundingInterval } from './types';
|
|
4
5
|
export * from './types';
|
|
5
6
|
export { generateRequestUniqueId };
|
|
6
7
|
/**
|
|
@@ -12,40 +13,19 @@ declare function generateRequestUniqueId(): string;
|
|
|
12
13
|
/**
|
|
13
14
|
* 支付模块实现
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
-
* 1. 用户操作 → 调用支付API (pushPaymentAsync, submitPayAsync等)
|
|
17
|
-
* 2. 数据处理 → 更新本地IndexDB中的订单和支付项数据
|
|
18
|
-
* 3. 立即完成 → 本地订单状态立即更新为PaymentStatus.Finished(isSynced保持false)
|
|
19
|
-
* 4. 后台同步 → 网络请求推送到TasksManager异步同步到服务器
|
|
20
|
-
* 5. 同步成功 → 网络请求成功后,isSynced设为true
|
|
21
|
-
* 6. 自动重试 → TasksManager自动重试失败的网络请求,直到isSynced为true
|
|
22
|
-
*
|
|
23
|
-
* 数据状态说明:
|
|
24
|
-
* - PaymentStatus.Processing: 订单正在进行支付操作
|
|
25
|
-
* - PaymentStatus.PartiallyPaid + isSynced=false: 部分支付完成,等待服务器同步
|
|
26
|
-
* - PaymentStatus.PartiallyPaid + isSynced=true: 部分支付且已同步到服务器
|
|
27
|
-
* - PaymentStatus.Finished + isSynced=false: 本地支付完成,等待服务器同步
|
|
28
|
-
* - PaymentStatus.Finished + isSynced=true: 支付完成且已同步到服务器
|
|
29
|
-
*
|
|
30
|
-
* 这种设计的优势:
|
|
31
|
-
* - 即时反馈:支付操作后立即显示完成状态,用户体验更佳
|
|
32
|
-
* - 离线支持:即使网络断开也能正常操作,数据保存在本地
|
|
33
|
-
* - 同步追踪:通过isSynced清楚知道哪些数据还未同步到服务器
|
|
34
|
-
* - 自动重试:TasksManager会自动重试失败的网络请求
|
|
35
|
-
* - 异步处理:不阻塞用户界面,网络同步在后台进行
|
|
36
|
-
* - 数据一致性:通过事件系统确保数据状态同步
|
|
16
|
+
* 负责处理支付相关的数据管理和本地存储
|
|
37
17
|
*/
|
|
38
18
|
export declare class PaymentModule extends BaseModule implements Module, PaymentModuleAPI {
|
|
39
19
|
protected defaultName: string;
|
|
40
20
|
protected defaultVersion: string;
|
|
41
|
-
|
|
21
|
+
request: RequestPlugin;
|
|
42
22
|
private app;
|
|
43
23
|
private store;
|
|
44
24
|
private dbManager;
|
|
45
25
|
private logger;
|
|
46
26
|
cash: CashPayment;
|
|
47
27
|
eftpos: EftposPayment;
|
|
48
|
-
wallet:
|
|
28
|
+
wallet: WalletPassPayment;
|
|
49
29
|
constructor(name?: string, version?: string);
|
|
50
30
|
initialize(core: PisellCore, options: ModuleOptions): Promise<void>;
|
|
51
31
|
/**
|
|
@@ -86,33 +66,62 @@ export declare class PaymentModule extends BaseModule implements Module, Payment
|
|
|
86
66
|
*/
|
|
87
67
|
getOrderListAsync(): Promise<PaymentOrder[]>;
|
|
88
68
|
/**
|
|
89
|
-
*
|
|
69
|
+
* 根据订单UUID获取支付订单(新方法)
|
|
90
70
|
*/
|
|
91
|
-
|
|
71
|
+
getPaymentOrderByUuidAsync(orderUuid: string): Promise<PaymentOrder | null>;
|
|
92
72
|
/**
|
|
93
|
-
*
|
|
73
|
+
* 创建支付订单(新方法,专注支付数据)
|
|
94
74
|
*/
|
|
95
|
-
|
|
75
|
+
createPaymentOrderAsync(params: PushOrderParams): Promise<PaymentOrder>;
|
|
96
76
|
/**
|
|
97
|
-
*
|
|
77
|
+
* 删除支付订单(新方法)
|
|
98
78
|
*/
|
|
99
|
-
|
|
79
|
+
deletePaymentOrderAsync(orderUuid: string): Promise<void>;
|
|
100
80
|
/**
|
|
101
81
|
* 更新订单
|
|
102
82
|
*/
|
|
103
83
|
updateOrderAsync(orderUuid: string, params: Partial<PaymentOrder>): Promise<void>;
|
|
104
84
|
/**
|
|
105
|
-
*
|
|
85
|
+
* 基于UUID替换订单ID
|
|
86
|
+
*
|
|
87
|
+
* 此方法用于将本地虚拟订单ID替换为真实的订单ID。
|
|
88
|
+
* 当前端模拟下单流程完成后,后端返回真实订单ID时调用此方法。
|
|
89
|
+
*
|
|
90
|
+
* @param orderUuid 订单的UUID
|
|
91
|
+
* @param newOrderId 新的订单ID (来自后端)
|
|
92
|
+
* @returns 更新后的订单对象,如果订单不存在则返回null
|
|
93
|
+
*/
|
|
94
|
+
replaceOrderIdByUuidAsync(orderUuid: string, newOrderId: string): Promise<PaymentOrder | null>;
|
|
95
|
+
/**
|
|
96
|
+
* 获取支付项(新方法)
|
|
97
|
+
*
|
|
98
|
+
* @param orderUuid 订单UUID
|
|
99
|
+
* @param includeVoided 是否包含已撤销的支付项,默认为false
|
|
100
|
+
* @returns 支付项数组
|
|
106
101
|
*/
|
|
107
|
-
|
|
102
|
+
getPaymentItemsAsync(orderUuid: string, includeVoided?: boolean): Promise<PaymentItem[]>;
|
|
108
103
|
/**
|
|
109
|
-
*
|
|
104
|
+
* 获取所有支付项(包括已撤销的)
|
|
105
|
+
*
|
|
106
|
+
* @param orderUuid 订单UUID
|
|
107
|
+
* @returns 所有支付项数组(包括撤销的)
|
|
110
108
|
*/
|
|
111
|
-
|
|
109
|
+
getAllPaymentItemsAsync(orderUuid: string): Promise<PaymentItem[]>;
|
|
112
110
|
/**
|
|
113
|
-
*
|
|
111
|
+
* 为某个订单添加支付项(新方法)
|
|
112
|
+
*/
|
|
113
|
+
addPaymentItemAsync(orderUuid: string, paymentItem: PaymentItemInput): Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* 删除一个支付项 - 标记删除而非物理删除
|
|
114
116
|
*/
|
|
115
117
|
deletePaymentAsync(orderUuid: string, paymentUuid: string): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* 批量更新代金券类支付项(覆盖更新)
|
|
120
|
+
*
|
|
121
|
+
* 删除所有现有的带 voucher_id 的支付项,然后添加新的代金券支付项
|
|
122
|
+
* 这是一个覆盖式更新,确保代金券支付项的一致性
|
|
123
|
+
*/
|
|
124
|
+
updateVoucherPaymentItemsAsync(orderUuid: string, voucherPaymentItems: PaymentItemInput[]): Promise<void>;
|
|
116
125
|
/**
|
|
117
126
|
* 更新一个支付项
|
|
118
127
|
*/
|
|
@@ -127,10 +136,6 @@ export declare class PaymentModule extends BaseModule implements Module, Payment
|
|
|
127
136
|
* 提交单个订单的支付(推送到任务队列)
|
|
128
137
|
*/
|
|
129
138
|
private submitSingleOrderPayment;
|
|
130
|
-
/**
|
|
131
|
-
* 添加到同步任务队列
|
|
132
|
-
*/
|
|
133
|
-
private addToSyncQueue;
|
|
134
139
|
/**
|
|
135
140
|
* 获取订单剩余待付金额
|
|
136
141
|
*/
|
|
@@ -155,15 +160,6 @@ export declare class PaymentModule extends BaseModule implements Module, Payment
|
|
|
155
160
|
* 获取钱包支付方式
|
|
156
161
|
*/
|
|
157
162
|
getWalletPaymentMethod(): Promise<PaymentMethod | null>;
|
|
158
|
-
/**
|
|
159
|
-
* 注册任务处理函数
|
|
160
|
-
*/
|
|
161
|
-
private registerTaskHandlers;
|
|
162
|
-
/**
|
|
163
|
-
* 静态方法:处理支付同步任务(后台网络同步)
|
|
164
|
-
* 这个方法里不允许用 this,因为需要作为静态方法注入到任务队列里
|
|
165
|
-
*/
|
|
166
|
-
static syncPaymentTask(payload: any): Promise<any>;
|
|
167
163
|
/**
|
|
168
164
|
* 确保支付模块所需的数据库表已创建
|
|
169
165
|
*/
|
|
@@ -173,55 +169,22 @@ export declare class PaymentModule extends BaseModule implements Module, Payment
|
|
|
173
169
|
*/
|
|
174
170
|
getPartiallyPaidOrdersAsync(): Promise<PaymentOrder[]>;
|
|
175
171
|
/**
|
|
176
|
-
*
|
|
177
|
-
*/
|
|
178
|
-
getUnsyncedOrdersAsync(): Promise<PaymentOrder[]>;
|
|
179
|
-
/**
|
|
180
|
-
* 清理已完成且已同步的订单
|
|
181
|
-
* 注意:只清理完全支付的订单,部分支付的订单保留以便继续支付
|
|
182
|
-
*/
|
|
183
|
-
cleanupFinishedOrders(): Promise<void>;
|
|
184
|
-
/**
|
|
185
|
-
* 检测未同步的订单并重新加入任务队列
|
|
186
|
-
*
|
|
187
|
-
* 此方法专门用于处理因网络问题导致任务队列执行失败但队列被清空的情况。
|
|
188
|
-
* 它会检查本地 IndexDB 中所有未同步的订单(isSynced: false),
|
|
189
|
-
* 并将这些订单重新添加到任务队列中。
|
|
190
|
-
*
|
|
191
|
-
* 使用场景:
|
|
192
|
-
* - 网络恢复后重新检查未同步的订单
|
|
193
|
-
* - 应用启动时检查并重新排队未完成的支付
|
|
194
|
-
* - 用户手动重试前的预检查
|
|
195
|
-
*
|
|
196
|
-
* @returns Promise<number> 返回重新加入队列的订单数量
|
|
197
|
-
*/
|
|
198
|
-
recheckAndRequeueUnsyncedOrders(): Promise<number>;
|
|
199
|
-
/**
|
|
200
|
-
* 手动执行支付同步队列
|
|
201
|
-
* 外部可以调用此方法来触发支付同步任务的执行
|
|
202
|
-
*
|
|
203
|
-
* 注意:此方法只执行现有队列中的任务,不会检查未同步的订单。
|
|
204
|
-
* 如果需要检查未同步订单,请先调用 recheckAndRequeueUnsyncedOrders()
|
|
172
|
+
* 智能金额舍入
|
|
205
173
|
*
|
|
206
|
-
*
|
|
207
|
-
* ```typescript
|
|
208
|
-
* // 1. 先检查并重新排队未同步订单,然后执行队列
|
|
209
|
-
* const requeuedCount = await paymentModule.recheckAndRequeueUnsyncedOrders();
|
|
210
|
-
* if (requeuedCount > 0) {
|
|
211
|
-
* await paymentModule.runPaymentSyncQueue();
|
|
212
|
-
* }
|
|
174
|
+
* 根据指定的舍入间隔和规则对金额进行舍入处理
|
|
213
175
|
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
176
|
+
* @param originalAmount 原始金额
|
|
177
|
+
* @param interval 舍入间隔 (0.05, 0.1, 0.5, 1)
|
|
178
|
+
* @param rule 舍入规则 (standard, standard_down, always_up, always_down)
|
|
179
|
+
* @returns 舍入后的金额(保留两位小数的字符串)
|
|
217
180
|
*/
|
|
218
|
-
|
|
181
|
+
roundAmountAsync(originalAmount: number | string, interval: RoundingInterval | number, rule: RoundingRule | string): Promise<string>;
|
|
219
182
|
/**
|
|
220
|
-
*
|
|
221
|
-
* 用于处理因网络问题导致任务队列清空但订单未同步的情况
|
|
183
|
+
* 标准舍入处理(处理中点情况)
|
|
222
184
|
*
|
|
223
|
-
* @param
|
|
224
|
-
* @
|
|
185
|
+
* @param value 要舍入的值
|
|
186
|
+
* @param midpointUp 中点是否向上舍入
|
|
187
|
+
* @returns 舍入后的值
|
|
225
188
|
*/
|
|
226
|
-
private
|
|
189
|
+
private standardRound;
|
|
227
190
|
}
|