@pisell/pisellos 2.2.141 → 2.2.143

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.
@@ -18,18 +18,29 @@ export function filterOrders(orders, filters) {
18
18
  list: [],
19
19
  count: 0,
20
20
  size: 0,
21
- skip: 0
21
+ skip: 0,
22
+ rejected: []
22
23
  };
23
24
  }
24
25
 
25
26
  // 默认分页参数
26
27
  var size = safeFilters.num || 20;
27
28
  var page = safeFilters.skip || 1;
29
+ var rejected = [];
28
30
 
29
31
  // 1. 过滤
30
32
  var filteredList = orders.filter(function (order) {
31
33
  if (safeFilters.keyword) {
32
34
  if (!JSON.stringify(order).toLowerCase().includes(safeFilters.keyword.toLowerCase())) {
35
+ rejected.push({
36
+ order_id: order.order_id,
37
+ order_number: order.order_number,
38
+ reason: {
39
+ field: 'keyword',
40
+ actual: '',
41
+ expected: safeFilters.keyword
42
+ }
43
+ });
33
44
  return false;
34
45
  }
35
46
  }
@@ -37,6 +48,15 @@ export function filterOrders(orders, filters) {
37
48
  // status - 订单状态
38
49
  if (safeFilters.status && safeFilters.status.length > 0) {
39
50
  if (!order.status || !safeFilters.status.includes(order.status)) {
51
+ rejected.push({
52
+ order_id: order.order_id,
53
+ order_number: order.order_number,
54
+ reason: {
55
+ field: 'status',
56
+ actual: order.status,
57
+ expected: safeFilters.status
58
+ }
59
+ });
40
60
  return false;
41
61
  }
42
62
  }
@@ -44,6 +64,15 @@ export function filterOrders(orders, filters) {
44
64
  // payment_status - 支付状态
45
65
  if (safeFilters.payment_status && safeFilters.payment_status.length > 0) {
46
66
  if (!order.payment_status || !safeFilters.payment_status.includes(order.payment_status)) {
67
+ rejected.push({
68
+ order_id: order.order_id,
69
+ order_number: order.order_number,
70
+ reason: {
71
+ field: 'payment_status',
72
+ actual: order.payment_status,
73
+ expected: safeFilters.payment_status
74
+ }
75
+ });
47
76
  return false;
48
77
  }
49
78
  }
@@ -51,6 +80,15 @@ export function filterOrders(orders, filters) {
51
80
  // shipping_status - 处理状态
52
81
  if (safeFilters.shipping_status && safeFilters.shipping_status.length > 0) {
53
82
  if (!order.shipping_status || !safeFilters.shipping_status.includes(order.shipping_status)) {
83
+ rejected.push({
84
+ order_id: order.order_id,
85
+ order_number: order.order_number,
86
+ reason: {
87
+ field: 'shipping_status',
88
+ actual: order.shipping_status,
89
+ expected: safeFilters.shipping_status
90
+ }
91
+ });
54
92
  return false;
55
93
  }
56
94
  }
@@ -58,6 +96,15 @@ export function filterOrders(orders, filters) {
58
96
  // customer_id - 客户ID(精确匹配)
59
97
  if (safeFilters.customer_id !== undefined && safeFilters.customer_id !== null) {
60
98
  if (String(order.customer_id) !== String(safeFilters.customer_id)) {
99
+ rejected.push({
100
+ order_id: order.order_id,
101
+ order_number: order.order_number,
102
+ reason: {
103
+ field: 'customer_id',
104
+ actual: order.customer_id,
105
+ expected: safeFilters.customer_id
106
+ }
107
+ });
61
108
  return false;
62
109
  }
63
110
  }
@@ -65,6 +112,15 @@ export function filterOrders(orders, filters) {
65
112
  // order_sales_channel - 销售渠道
66
113
  if (safeFilters.order_sales_channel && safeFilters.order_sales_channel.length > 0) {
67
114
  if (!order.order_sales_channel || !safeFilters.order_sales_channel.includes(order.order_sales_channel)) {
115
+ rejected.push({
116
+ order_id: order.order_id,
117
+ order_number: order.order_number,
118
+ reason: {
119
+ field: 'order_sales_channel',
120
+ actual: order.order_sales_channel,
121
+ expected: safeFilters.order_sales_channel
122
+ }
123
+ });
68
124
  return false;
69
125
  }
70
126
  }
@@ -72,12 +128,30 @@ export function filterOrders(orders, filters) {
72
128
  // payment_methods - 支付方式
73
129
  if (safeFilters.payment_methods && safeFilters.payment_methods.length > 0) {
74
130
  if (!order.payment_methods || !Array.isArray(order.payment_methods) || order.payment_methods.length === 0) {
131
+ rejected.push({
132
+ order_id: order.order_id,
133
+ order_number: order.order_number,
134
+ reason: {
135
+ field: 'payment_methods',
136
+ actual: order.payment_methods,
137
+ expected: safeFilters.payment_methods
138
+ }
139
+ });
75
140
  return false;
76
141
  }
77
142
  var hasMatch = order.payment_methods.some(function (pm) {
78
143
  return safeFilters.payment_methods.includes(pm);
79
144
  });
80
145
  if (!hasMatch) {
146
+ rejected.push({
147
+ order_id: order.order_id,
148
+ order_number: order.order_number,
149
+ reason: {
150
+ field: 'payment_methods',
151
+ actual: order.payment_methods,
152
+ expected: safeFilters.payment_methods
153
+ }
154
+ });
81
155
  return false;
82
156
  }
83
157
  }
@@ -86,9 +160,27 @@ export function filterOrders(orders, filters) {
86
160
  if (safeFilters.min_total_amount !== undefined || safeFilters.max_total_amount !== undefined) {
87
161
  var orderAmount = parseFloat(String(order.total_amount || 0));
88
162
  if (safeFilters.min_total_amount !== undefined && orderAmount < safeFilters.min_total_amount) {
163
+ rejected.push({
164
+ order_id: order.order_id,
165
+ order_number: order.order_number,
166
+ reason: {
167
+ field: 'min_total_amount',
168
+ actual: order.total_amount,
169
+ expected: safeFilters.min_total_amount
170
+ }
171
+ });
89
172
  return false;
90
173
  }
91
174
  if (safeFilters.max_total_amount !== undefined && orderAmount > safeFilters.max_total_amount) {
175
+ rejected.push({
176
+ order_id: order.order_id,
177
+ order_number: order.order_number,
178
+ reason: {
179
+ field: 'max_total_amount',
180
+ actual: order.total_amount,
181
+ expected: safeFilters.max_total_amount
182
+ }
183
+ });
92
184
  return false;
93
185
  }
94
186
  }
@@ -96,6 +188,15 @@ export function filterOrders(orders, filters) {
96
188
  // tag_ids - 标签(统一 String 比较,兼容 number/string)
97
189
  if (safeFilters.tag_ids && safeFilters.tag_ids.length > 0) {
98
190
  if (!order.tag_ids || !Array.isArray(order.tag_ids) || order.tag_ids.length === 0) {
191
+ rejected.push({
192
+ order_id: order.order_id,
193
+ order_number: order.order_number,
194
+ reason: {
195
+ field: 'tag_ids',
196
+ actual: order.tag_ids,
197
+ expected: safeFilters.tag_ids
198
+ }
199
+ });
99
200
  return false;
100
201
  }
101
202
  var filterTagSet = new Set(safeFilters.tag_ids.map(String));
@@ -103,6 +204,15 @@ export function filterOrders(orders, filters) {
103
204
  return filterTagSet.has(String(tag));
104
205
  });
105
206
  if (!_hasMatch) {
207
+ rejected.push({
208
+ order_id: order.order_id,
209
+ order_number: order.order_number,
210
+ reason: {
211
+ field: 'tag_ids',
212
+ actual: order.tag_ids,
213
+ expected: safeFilters.tag_ids
214
+ }
215
+ });
106
216
  return false;
107
217
  }
108
218
  }
@@ -110,6 +220,15 @@ export function filterOrders(orders, filters) {
110
220
  // shipping_type - 配送方式
111
221
  if (safeFilters.shipping_type && safeFilters.shipping_type.length > 0) {
112
222
  if (!order.shipping_type || !safeFilters.shipping_type.includes(order.shipping_type)) {
223
+ rejected.push({
224
+ order_id: order.order_id,
225
+ order_number: order.order_number,
226
+ reason: {
227
+ field: 'shipping_type',
228
+ actual: order.shipping_type,
229
+ expected: safeFilters.shipping_type
230
+ }
231
+ });
113
232
  return false;
114
233
  }
115
234
  }
@@ -117,6 +236,15 @@ export function filterOrders(orders, filters) {
117
236
  // delivery_zone_name - 配送区域(模糊匹配)
118
237
  if (safeFilters.delivery_zone_name !== undefined && safeFilters.delivery_zone_name !== null && safeFilters.delivery_zone_name !== '') {
119
238
  if (!order.delivery_zone_name || !order.delivery_zone_name.toLowerCase().includes(safeFilters.delivery_zone_name.toLowerCase())) {
239
+ rejected.push({
240
+ order_id: order.order_id,
241
+ order_number: order.order_number,
242
+ reason: {
243
+ field: 'delivery_zone_name',
244
+ actual: order.delivery_zone_name,
245
+ expected: safeFilters.delivery_zone_name
246
+ }
247
+ });
120
248
  return false;
121
249
  }
122
250
  }
@@ -124,6 +252,15 @@ export function filterOrders(orders, filters) {
124
252
  // location_id - 取货地点(精确匹配)
125
253
  if (safeFilters.location_id !== undefined && safeFilters.location_id !== null) {
126
254
  if (String(order.location_id) !== String(safeFilters.location_id)) {
255
+ rejected.push({
256
+ order_id: order.order_id,
257
+ order_number: order.order_number,
258
+ reason: {
259
+ field: 'location_id',
260
+ actual: order.location_id,
261
+ expected: safeFilters.location_id
262
+ }
263
+ });
127
264
  return false;
128
265
  }
129
266
  }
@@ -131,6 +268,15 @@ export function filterOrders(orders, filters) {
131
268
  // shipping_order_number - 物流单号(模糊匹配)
132
269
  if (safeFilters.shipping_order_number !== undefined && safeFilters.shipping_order_number !== null && safeFilters.shipping_order_number !== '') {
133
270
  if (!order.shipping_order_number || !order.shipping_order_number.toLowerCase().includes(safeFilters.shipping_order_number.toLowerCase())) {
271
+ rejected.push({
272
+ order_id: order.order_id,
273
+ order_number: order.order_number,
274
+ reason: {
275
+ field: 'shipping_order_number',
276
+ actual: order.shipping_order_number,
277
+ expected: safeFilters.shipping_order_number
278
+ }
279
+ });
134
280
  return false;
135
281
  }
136
282
  }
@@ -138,6 +284,15 @@ export function filterOrders(orders, filters) {
138
284
  // logistics_company_id - 物流公司
139
285
  if (safeFilters.logistics_company_id && safeFilters.logistics_company_id.length > 0) {
140
286
  if (!order.logistics_company_id) {
287
+ rejected.push({
288
+ order_id: order.order_id,
289
+ order_number: order.order_number,
290
+ reason: {
291
+ field: 'logistics_company_id',
292
+ actual: order.logistics_company_id,
293
+ expected: safeFilters.logistics_company_id
294
+ }
295
+ });
141
296
  return false;
142
297
  }
143
298
  var orderLogisticsId = String(order.logistics_company_id);
@@ -145,6 +300,15 @@ export function filterOrders(orders, filters) {
145
300
  return String(id) === orderLogisticsId;
146
301
  });
147
302
  if (!_hasMatch2) {
303
+ rejected.push({
304
+ order_id: order.order_id,
305
+ order_number: order.order_number,
306
+ reason: {
307
+ field: 'logistics_company_id',
308
+ actual: order.logistics_company_id,
309
+ expected: safeFilters.logistics_company_id
310
+ }
311
+ });
148
312
  return false;
149
313
  }
150
314
  }
@@ -152,6 +316,15 @@ export function filterOrders(orders, filters) {
152
316
  // zip - 邮编(模糊匹配)
153
317
  if (safeFilters.zip !== undefined && safeFilters.zip !== null && safeFilters.zip !== '') {
154
318
  if (!order.zip || !order.zip.toLowerCase().includes(safeFilters.zip.toLowerCase())) {
319
+ rejected.push({
320
+ order_id: order.order_id,
321
+ order_number: order.order_number,
322
+ reason: {
323
+ field: 'zip',
324
+ actual: order.zip,
325
+ expected: safeFilters.zip
326
+ }
327
+ });
155
328
  return false;
156
329
  }
157
330
  }
@@ -159,6 +332,15 @@ export function filterOrders(orders, filters) {
159
332
  // business_code - 业务类型(精确匹配)
160
333
  if (safeFilters.business_code) {
161
334
  if (!order.business_code || order.business_code !== safeFilters.business_code) {
335
+ rejected.push({
336
+ order_id: order.order_id,
337
+ order_number: order.order_number,
338
+ reason: {
339
+ field: 'business_code',
340
+ actual: order.business_code,
341
+ expected: safeFilters.business_code
342
+ }
343
+ });
162
344
  return false;
163
345
  }
164
346
  }
@@ -168,12 +350,34 @@ export function filterOrders(orders, filters) {
168
350
  var orderTime = order.created_at ? typeof order.created_at === 'number' ? order.created_at : new Date(String(order.created_at)).getTime() : 0;
169
351
  if (safeFilters.start_time) {
170
352
  var startTs = new Date(safeFilters.start_time).getTime();
171
- if (orderTime < startTs) return false;
353
+ if (orderTime < startTs) {
354
+ rejected.push({
355
+ order_id: order.order_id,
356
+ order_number: order.order_number,
357
+ reason: {
358
+ field: 'start_time',
359
+ actual: order.created_at,
360
+ expected: safeFilters.start_time
361
+ }
362
+ });
363
+ return false;
364
+ }
172
365
  }
173
366
  if (safeFilters.end_time) {
174
367
  // end_time 为当天结束(23:59:59.999)
175
368
  var endTs = new Date(safeFilters.end_time).getTime() + 86400000 - 1;
176
- if (orderTime > endTs) return false;
369
+ if (orderTime > endTs) {
370
+ rejected.push({
371
+ order_id: order.order_id,
372
+ order_number: order.order_number,
373
+ reason: {
374
+ field: 'end_time',
375
+ actual: order.created_at,
376
+ expected: safeFilters.end_time
377
+ }
378
+ });
379
+ return false;
380
+ }
177
381
  }
178
382
  }
179
383
  return true;
@@ -227,6 +431,7 @@ export function filterOrders(orders, filters) {
227
431
  list: paginatedList,
228
432
  count: totalCount,
229
433
  size: size,
230
- skip: page
434
+ skip: page,
435
+ rejected: rejected
231
436
  };
232
437
  }
@@ -136,7 +136,7 @@ export declare class BookingTicketImpl extends BaseModule implements Module {
136
136
  * 获取当前的客户搜索条件
137
137
  * @returns 当前搜索条件
138
138
  */
139
- getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "num" | "skip">;
139
+ getCurrentCustomerSearchParams(): Omit<import("../../modules").ShopGetCustomerListParams, "skip" | "num">;
140
140
  /**
141
141
  * 获取客户列表状态(包含滚动加载相关状态)
142
142
  * @returns 客户状态
@@ -45,6 +45,17 @@ var defaultStrategyMetadataCustom = {
45
45
  maxPassesPerItem: 0,
46
46
  deductOptionPrice: false
47
47
  };
48
+ var FIELD_REASON_CODE_MAP = {
49
+ orderTotalAmount: "order_total_amount_not_enough",
50
+ applicableProductTotalAmount: "applicable_product_total_amount_not_enough",
51
+ applicableProductCount: "applicable_product_count_not_enough"
52
+ };
53
+ function formatReason(template, value) {
54
+ if (!template)
55
+ return template;
56
+ const replacement = value === void 0 || value === null ? "" : String(value);
57
+ return template.replace(/\$\{value\}/g, replacement).replace(/\{value\}/g, replacement);
58
+ }
48
59
  var WalletPassEvaluator = class {
49
60
  constructor() {
50
61
  this.strategyConfigs = [];
@@ -101,7 +112,7 @@ var WalletPassEvaluator = class {
101
112
  * 评估可用的 vouchers
102
113
  */
103
114
  evaluate(input) {
104
- var _a, _b;
115
+ var _a, _b, _c, _d, _e;
105
116
  const { orderTotalAmount, products, vouchers, strategyConfigs } = input;
106
117
  const productIds = products.map((p) => p.product_id);
107
118
  const results = [];
@@ -176,7 +187,13 @@ var WalletPassEvaluator = class {
176
187
  });
177
188
  } else {
178
189
  let reasonCode = "not_meet_the_required_conditions";
179
- if (evaluationResult.code) {
190
+ let thresholdValue = void 0;
191
+ const failedField = (_c = evaluationResult.matched) == null ? void 0 : _c.failedField;
192
+ const failedRule = (_e = (_d = evaluationResult.matched) == null ? void 0 : _d.failedRules) == null ? void 0 : _e[0];
193
+ if (failedField && FIELD_REASON_CODE_MAP[failedField]) {
194
+ reasonCode = FIELD_REASON_CODE_MAP[failedField];
195
+ thresholdValue = failedRule == null ? void 0 : failedRule.value;
196
+ } else if (evaluationResult.code) {
180
197
  const codeMapping = {
181
198
  INSUFFICIENT_AMOUNT: "not_meet_the_required_conditions",
182
199
  USAGE_LIMIT_EXCEEDED: "usage_limit_reached",
@@ -186,6 +203,8 @@ var WalletPassEvaluator = class {
186
203
  };
187
204
  reasonCode = codeMapping[evaluationResult.code] || "not_meet_the_required_conditions";
188
205
  }
206
+ const rawText = this.getText(reasonCode);
207
+ const reason = thresholdValue !== void 0 ? formatReason(rawText, thresholdValue) : rawText;
189
208
  results.push({
190
209
  voucher,
191
210
  isApplicable: false,
@@ -193,7 +212,7 @@ var WalletPassEvaluator = class {
193
212
  maxDeduction: 0,
194
213
  deductTaxAndFee: false,
195
214
  applicableProductIds: [],
196
- reason: this.getText(reasonCode),
215
+ reason,
197
216
  reasonCode,
198
217
  strategyResult: evaluationResult
199
218
  });
@@ -29,7 +29,10 @@ var locales = {
29
29
  "usage_limit_reached": "已达到使用次数上限",
30
30
  "max_passes_per_item_reached": "该商品已达到卡券使用上限",
31
31
  "not_available_for_this_channel": "当前渠道不可使用",
32
- "not_valid_for_this_order_type": "当前订单类型不适用"
32
+ "not_valid_for_this_order_type": "当前订单类型不适用",
33
+ "order_total_amount_not_enough": "订单满 ${value} 才可用",
34
+ "applicable_product_total_amount_not_enough": "适用商品满 ${value} 才可用",
35
+ "applicable_product_count_not_enough": "至少购买 {value} 件适用商品才可用"
33
36
  },
34
37
  "en": {
35
38
  "not_meet_the_required_conditions": "Not meet the required conditions.",
@@ -37,7 +40,10 @@ var locales = {
37
40
  "usage_limit_reached": "Usage limit reached.",
38
41
  "max_passes_per_item_reached": "Max passes per item reached.",
39
42
  "not_available_for_this_channel": "Not available for this channel.",
40
- "not_valid_for_this_order_type": "Not valid for this order type."
43
+ "not_valid_for_this_order_type": "Not valid for this order type.",
44
+ "order_total_amount_not_enough": "Spend ${value} to use",
45
+ "applicable_product_total_amount_not_enough": "${value} on eligible items",
46
+ "applicable_product_count_not_enough": "Buy {value} eligible items to use"
41
47
  },
42
48
  "zh-HK": {
43
49
  "not_meet_the_required_conditions": "未達使用條件",
@@ -45,7 +51,10 @@ var locales = {
45
51
  "usage_limit_reached": "已達使用次數上限",
46
52
  "max_passes_per_item_reached": "該商品已達卡券使用上限",
47
53
  "not_available_for_this_channel": "當前渠道不可使用",
48
- "not_valid_for_this_order_type": "當前訂單類型不適用"
54
+ "not_valid_for_this_order_type": "當前訂單類型不適用",
55
+ "order_total_amount_not_enough": "訂單滿 ${value} 才可用",
56
+ "applicable_product_total_amount_not_enough": "適用商品滿 ${value} 才可用",
57
+ "applicable_product_count_not_enough": "至少購買 {value} 件適用商品才可用"
49
58
  }
50
59
  };
51
60
  // Annotate the CommonJS export names for ESM import in node:
@@ -27,17 +27,30 @@ export declare class StrategyEngine {
27
27
  *
28
28
  * @param group 条件组
29
29
  * @param context 运行时上下文
30
- * @returns 评估结果对象,包含条件是否满足和收集到的 actionIds
30
+ * @returns 评估结果对象,包含条件是否满足、收集到的 actionIds、以及失败的原子规则信息
31
31
  */
32
32
  private evaluateConditionGroup;
33
33
  /**
34
- * 评估条件组的逻辑运算
34
+ * 评估条件组的逻辑运算(带失败规则收集)
35
+ *
36
+ * 失败规则收集策略:
37
+ * - `and`:返回第一个失败的原子规则(嵌套 group 递归取其首个失败原子规则)
38
+ * - `or`:所有分支失败时,聚合每个分支的失败规则
39
+ * - `not`:被取反规则实际通过时,记录该规则
40
+ */
41
+ private evaluateGroupLogicDetailed;
42
+ /**
43
+ * 评估单个规则(带失败规则收集)
44
+ */
45
+ private evaluateRuleDetailed;
46
+ /**
47
+ * 评估原子规则(不收集失败信息,仅返回布尔结果)
35
48
  */
36
- private evaluateGroupLogic;
49
+ private evaluateAtomicRule;
37
50
  /**
38
- * 评估单个规则
51
+ * 构建失败规则信息
39
52
  */
40
- private evaluateRule;
53
+ private buildFailedRuleInfo;
41
54
  /**
42
55
  * 评估代码模式条件
43
56
  */
@@ -42,7 +42,7 @@ var StrategyEngine = class {
42
42
  * 评估策略
43
43
  */
44
44
  evaluate(config, context) {
45
- var _a;
45
+ var _a, _b;
46
46
  const startTime = Date.now();
47
47
  const trace = {
48
48
  steps: [],
@@ -105,7 +105,11 @@ var StrategyEngine = class {
105
105
  matched: {
106
106
  conditions: applicable,
107
107
  actionIds: evaluationResult.actionIds,
108
- details: {}
108
+ details: {},
109
+ ...applicable ? {} : {
110
+ failedField: (_a = evaluationResult.failedRules[0]) == null ? void 0 : _a.field,
111
+ failedRules: evaluationResult.failedRules
112
+ }
109
113
  },
110
114
  matchedActions: sortedActions,
111
115
  outputs: {},
@@ -119,7 +123,7 @@ var StrategyEngine = class {
119
123
  } catch (error) {
120
124
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
121
125
  if (this.options.enableTrace) {
122
- (_a = trace.errors) == null ? void 0 : _a.push({
126
+ (_b = trace.errors) == null ? void 0 : _b.push({
123
127
  step: "evaluation",
124
128
  error: errorMessage,
125
129
  timestamp: Date.now()
@@ -149,12 +153,15 @@ var StrategyEngine = class {
149
153
  *
150
154
  * @param group 条件组
151
155
  * @param context 运行时上下文
152
- * @returns 评估结果对象,包含条件是否满足和收集到的 actionIds
156
+ * @returns 评估结果对象,包含条件是否满足、收集到的 actionIds、以及失败的原子规则信息
153
157
  */
154
158
  evaluateConditionGroup(group, context) {
155
159
  const collectedActionIds = [];
156
- const isCurrentLayerSatisfied = this.evaluateGroupLogic(group, context);
157
- if (isCurrentLayerSatisfied) {
160
+ const { satisfied, failedRules } = this.evaluateGroupLogicDetailed(
161
+ group,
162
+ context
163
+ );
164
+ if (satisfied) {
158
165
  collectedActionIds.push(...group.actionIds);
159
166
  for (const rule of group.rules) {
160
167
  if (this.isConditionGroup(rule)) {
@@ -164,41 +171,98 @@ var StrategyEngine = class {
164
171
  }
165
172
  }
166
173
  return {
167
- satisfied: isCurrentLayerSatisfied,
168
- actionIds: collectedActionIds
174
+ satisfied,
175
+ actionIds: collectedActionIds,
176
+ failedRules
169
177
  };
170
178
  }
171
179
  /**
172
- * 评估条件组的逻辑运算
180
+ * 评估条件组的逻辑运算(带失败规则收集)
181
+ *
182
+ * 失败规则收集策略:
183
+ * - `and`:返回第一个失败的原子规则(嵌套 group 递归取其首个失败原子规则)
184
+ * - `or`:所有分支失败时,聚合每个分支的失败规则
185
+ * - `not`:被取反规则实际通过时,记录该规则
173
186
  */
174
- evaluateGroupLogic(group, context) {
187
+ evaluateGroupLogicDetailed(group, context) {
175
188
  const { operator, rules } = group;
176
189
  switch (operator) {
177
- case "and":
178
- return rules.every((rule) => this.evaluateRule(rule, context));
179
- case "or":
180
- return rules.some((rule) => this.evaluateRule(rule, context));
181
- case "not":
182
- if (rules.length === 0)
183
- return false;
184
- return !this.evaluateRule(rules[0], context);
190
+ case "and": {
191
+ for (const rule of rules) {
192
+ const ruleResult = this.evaluateRuleDetailed(rule, context);
193
+ if (!ruleResult.satisfied) {
194
+ return {
195
+ satisfied: false,
196
+ failedRules: ruleResult.failedRules
197
+ };
198
+ }
199
+ }
200
+ return { satisfied: true, failedRules: [] };
201
+ }
202
+ case "or": {
203
+ const aggregated = [];
204
+ for (const rule of rules) {
205
+ const ruleResult = this.evaluateRuleDetailed(rule, context);
206
+ if (ruleResult.satisfied) {
207
+ return { satisfied: true, failedRules: [] };
208
+ }
209
+ aggregated.push(...ruleResult.failedRules);
210
+ }
211
+ return { satisfied: false, failedRules: aggregated };
212
+ }
213
+ case "not": {
214
+ if (rules.length === 0) {
215
+ return { satisfied: false, failedRules: [] };
216
+ }
217
+ const ruleResult = this.evaluateRuleDetailed(rules[0], context);
218
+ if (ruleResult.satisfied) {
219
+ const failed = this.isConditionGroup(rules[0]) ? [] : [this.buildFailedRuleInfo(rules[0], context)];
220
+ return { satisfied: false, failedRules: failed };
221
+ }
222
+ return { satisfied: true, failedRules: [] };
223
+ }
185
224
  default:
186
225
  throw new Error(`Unknown operator: ${operator}`);
187
226
  }
188
227
  }
189
228
  /**
190
- * 评估单个规则
229
+ * 评估单个规则(带失败规则收集)
191
230
  */
192
- evaluateRule(rule, context) {
231
+ evaluateRuleDetailed(rule, context) {
193
232
  if (this.isConditionGroup(rule)) {
194
- return this.evaluateGroupLogic(rule, context);
233
+ return this.evaluateGroupLogicDetailed(rule, context);
195
234
  }
196
235
  const conditionRule = rule;
236
+ const satisfied = this.evaluateAtomicRule(conditionRule, context);
237
+ if (satisfied) {
238
+ return { satisfied: true, failedRules: [] };
239
+ }
240
+ return {
241
+ satisfied: false,
242
+ failedRules: [this.buildFailedRuleInfo(conditionRule, context)]
243
+ };
244
+ }
245
+ /**
246
+ * 评估原子规则(不收集失败信息,仅返回布尔结果)
247
+ */
248
+ evaluateAtomicRule(conditionRule, context) {
197
249
  if (conditionRule.type === "code" && conditionRule.code) {
198
250
  return this.evaluateCodeCondition(conditionRule.code, context);
199
251
  }
200
252
  return this.evaluateOperatorCondition(conditionRule, context);
201
253
  }
254
+ /**
255
+ * 构建失败规则信息
256
+ */
257
+ buildFailedRuleInfo(conditionRule, context) {
258
+ const actualValue = conditionRule.field ? this.getFieldValue(conditionRule.field, context) : void 0;
259
+ return {
260
+ field: conditionRule.field,
261
+ operator: conditionRule.operator,
262
+ value: conditionRule.value,
263
+ actualValue
264
+ };
265
+ }
202
266
  /**
203
267
  * 评估代码模式条件
204
268
  */