@nest-omni/core 4.1.3-20 → 4.1.3-23

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.
Files changed (107) hide show
  1. package/audit/audit.module.d.ts +1 -0
  2. package/audit/audit.module.js +5 -3
  3. package/audit/controllers/audit.controller.d.ts +3 -11
  4. package/audit/controllers/audit.controller.js +12 -19
  5. package/audit/decorators/audit-operation.decorator.d.ts +0 -7
  6. package/audit/decorators/audit-operation.decorator.js +0 -7
  7. package/audit/dto/audit-action-query.dto.d.ts +13 -0
  8. package/audit/dto/audit-action-query.dto.js +77 -0
  9. package/audit/dto/index.d.ts +1 -0
  10. package/audit/dto/index.js +1 -0
  11. package/audit/entities/entity-audit-log.entity.d.ts +1 -4
  12. package/audit/entities/entity-audit-log.entity.js +1 -17
  13. package/audit/entities/manual-operation-log.entity.d.ts +0 -2
  14. package/audit/entities/manual-operation-log.entity.js +0 -8
  15. package/audit/enums/audit.enums.d.ts +0 -8
  16. package/audit/enums/audit.enums.js +1 -10
  17. package/audit/examples/decorator-value-mapping.example.d.ts +70 -0
  18. package/audit/examples/decorator-value-mapping.example.js +414 -0
  19. package/audit/index.d.ts +1 -0
  20. package/audit/index.js +5 -1
  21. package/audit/interceptors/audit.interceptor.d.ts +1 -0
  22. package/audit/interceptors/audit.interceptor.js +19 -11
  23. package/audit/interfaces/audit.interfaces.d.ts +2 -17
  24. package/audit/services/audit-context.service.d.ts +9 -0
  25. package/audit/services/entity-audit.service.d.ts +65 -24
  26. package/audit/services/entity-audit.service.js +280 -93
  27. package/audit/services/manual-audit-log.service.d.ts +0 -1
  28. package/audit/services/manual-audit-log.service.js +1 -3
  29. package/audit/subscribers/entity-audit.subscriber.d.ts +1 -0
  30. package/audit/subscribers/entity-audit.subscriber.js +22 -5
  31. package/cache/cache.module.d.ts +7 -2
  32. package/cache/cache.module.js +9 -7
  33. package/cache/cache.service.d.ts +4 -4
  34. package/cache/cache.service.js +5 -5
  35. package/cache/entities/index.d.ts +1 -0
  36. package/cache/entities/index.js +17 -0
  37. package/cache/entities/typeorm-cache.entity.d.ts +71 -0
  38. package/cache/entities/typeorm-cache.entity.js +110 -0
  39. package/cache/index.d.ts +2 -1
  40. package/cache/index.js +19 -2
  41. package/cache/providers/index.d.ts +2 -1
  42. package/cache/providers/index.js +2 -1
  43. package/cache/providers/lrucache.provider.d.ts +76 -0
  44. package/cache/providers/lrucache.provider.js +226 -0
  45. package/cache/providers/typeorm-cache.provider.d.ts +211 -0
  46. package/cache/providers/typeorm-cache.provider.js +483 -0
  47. package/common/boilerplate.polyfill.d.ts +1 -0
  48. package/common/boilerplate.polyfill.js +17 -0
  49. package/common/helpers/validation-metadata-helper.d.ts +55 -0
  50. package/common/helpers/validation-metadata-helper.js +60 -0
  51. package/common/index.d.ts +1 -0
  52. package/common/index.js +4 -0
  53. package/decorators/field.decorators.d.ts +71 -2
  54. package/decorators/field.decorators.js +147 -18
  55. package/decorators/transform.decorators.d.ts +0 -2
  56. package/decorators/transform.decorators.js +0 -23
  57. package/filters/bad-request.filter.js +19 -4
  58. package/http-client/examples/axios-config-extended.example.d.ts +17 -0
  59. package/http-client/examples/axios-config-extended.example.js +313 -0
  60. package/http-client/examples/index.d.ts +2 -0
  61. package/http-client/examples/index.js +2 -0
  62. package/http-client/examples/ssl-certificate.example.d.ts +47 -0
  63. package/http-client/examples/ssl-certificate.example.js +431 -0
  64. package/http-client/index.d.ts +1 -1
  65. package/http-client/interfaces/http-client-config.interface.d.ts +73 -0
  66. package/http-client/services/http-client.service.js +46 -5
  67. package/http-client/utils/context-extractor.util.js +2 -0
  68. package/ip-filter/constants.d.ts +21 -0
  69. package/ip-filter/constants.js +24 -0
  70. package/ip-filter/decorators/index.d.ts +1 -0
  71. package/ip-filter/decorators/index.js +17 -0
  72. package/ip-filter/decorators/ip-filter.decorator.d.ts +58 -0
  73. package/ip-filter/decorators/ip-filter.decorator.js +79 -0
  74. package/ip-filter/guards/index.d.ts +1 -0
  75. package/ip-filter/guards/index.js +17 -0
  76. package/ip-filter/guards/ip-filter.guard.d.ts +62 -0
  77. package/ip-filter/guards/ip-filter.guard.js +174 -0
  78. package/ip-filter/index.d.ts +7 -0
  79. package/ip-filter/index.js +23 -0
  80. package/ip-filter/interfaces/index.d.ts +4 -0
  81. package/ip-filter/interfaces/index.js +20 -0
  82. package/ip-filter/interfaces/ip-filter-async-options.interface.d.ts +15 -0
  83. package/ip-filter/interfaces/ip-filter-async-options.interface.js +2 -0
  84. package/ip-filter/interfaces/ip-filter-metadata.interface.d.ts +26 -0
  85. package/ip-filter/interfaces/ip-filter-metadata.interface.js +2 -0
  86. package/ip-filter/interfaces/ip-filter-options.interface.d.ts +34 -0
  87. package/ip-filter/interfaces/ip-filter-options.interface.js +2 -0
  88. package/ip-filter/interfaces/ip-rule.interface.d.ts +36 -0
  89. package/ip-filter/interfaces/ip-rule.interface.js +2 -0
  90. package/ip-filter/ip-filter.module.d.ts +55 -0
  91. package/ip-filter/ip-filter.module.js +105 -0
  92. package/ip-filter/services/index.d.ts +1 -0
  93. package/ip-filter/services/index.js +17 -0
  94. package/ip-filter/services/ip-filter.service.d.ts +92 -0
  95. package/ip-filter/services/ip-filter.service.js +238 -0
  96. package/ip-filter/utils/index.d.ts +1 -0
  97. package/ip-filter/utils/index.js +17 -0
  98. package/ip-filter/utils/ip-utils.d.ts +61 -0
  99. package/ip-filter/utils/ip-utils.js +162 -0
  100. package/package.json +23 -24
  101. package/providers/context.provider.d.ts +9 -0
  102. package/providers/context.provider.js +13 -0
  103. package/setup/bootstrap.setup.d.ts +1 -1
  104. package/setup/bootstrap.setup.js +1 -1
  105. package/shared/service-registry.module.js +0 -1
  106. package/cache/providers/memory-cache.provider.d.ts +0 -69
  107. package/cache/providers/memory-cache.provider.js +0 -237
@@ -21,7 +21,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
21
21
  });
22
22
  };
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.EntityAuditService = void 0;
24
+ exports.EntityAuditService = exports.AUDIT_CONSTANTS = void 0;
25
+ exports.registerEntityClass = registerEntityClass;
26
+ exports.getEntityClass = getEntityClass;
25
27
  const common_1 = require("@nestjs/common");
26
28
  const typeorm_1 = require("@nestjs/typeorm");
27
29
  const typeorm_2 = require("typeorm");
@@ -31,14 +33,46 @@ const enums_1 = require("../enums");
31
33
  const audit_context_service_1 = require("./audit-context.service");
32
34
  const audit_strategy_service_1 = require("./audit-strategy.service");
33
35
  const multi_database_service_1 = require("./multi-database.service");
36
+ const operation_description_service_1 = require("./operation-description.service");
34
37
  const dto_1 = require("../../common/dto");
35
38
  const entity_audit_decorator_1 = require("../decorators/entity-audit.decorator");
36
39
  const transaction_1 = require("@nest-omni/transaction");
40
+ /**
41
+ * 审计配置常量
42
+ */
43
+ exports.AUDIT_CONSTANTS = {
44
+ /** 深度比较的最大深度限制 */
45
+ MAX_DIFF_DEPTH: 5,
46
+ /** 深度比较的最大键数量限制 */
47
+ MAX_KEYS: 100,
48
+ /** 描述的最大长度 */
49
+ MAX_DESCRIPTION_LENGTH: 1000,
50
+ /** 哈希截断长度 */
51
+ HASH_TRUNCATE_LENGTH: 64,
52
+ /** 部分掩码显示的字符数 */
53
+ PARTIAL_MASK_CHARS: 2,
54
+ };
55
+ /**
56
+ * 实体类注册表 - 用于在运行时通过 entityType 字符串获取实体类引用
57
+ */
58
+ const ENTITY_CLASS_REGISTRY = new Map();
59
+ /**
60
+ * 注册实体类到注册表
61
+ */
62
+ function registerEntityClass(entityType, entityClass) {
63
+ ENTITY_CLASS_REGISTRY.set(entityType, entityClass);
64
+ }
65
+ /**
66
+ * 获取实体类
67
+ */
68
+ function getEntityClass(entityType) {
69
+ return ENTITY_CLASS_REGISTRY.get(entityType);
70
+ }
37
71
  /**
38
72
  * 实体审计服务
39
73
  */
40
74
  let EntityAuditService = class EntityAuditService {
41
- constructor(auditLogRepository, transactionRepository, manualOperationRepository, entityManager, contextService, multiDbService, auditStrategy = new audit_strategy_service_1.DefaultAuditStrategy(), config, auditConnectionName, actionSummaryRepository) {
75
+ constructor(auditLogRepository, transactionRepository, manualOperationRepository, entityManager, contextService, multiDbService, auditStrategy = new audit_strategy_service_1.DefaultAuditStrategy(), config, auditConnectionName, actionSummaryRepository, descriptionService) {
42
76
  this.auditLogRepository = auditLogRepository;
43
77
  this.transactionRepository = transactionRepository;
44
78
  this.manualOperationRepository = manualOperationRepository;
@@ -48,6 +82,7 @@ let EntityAuditService = class EntityAuditService {
48
82
  this.auditStrategy = auditStrategy;
49
83
  this.config = config;
50
84
  this.actionSummaryRepository = actionSummaryRepository;
85
+ this.descriptionService = descriptionService;
51
86
  this.auditConnectionName = auditConnectionName || 'default';
52
87
  }
53
88
  /**
@@ -55,7 +90,6 @@ let EntityAuditService = class EntityAuditService {
55
90
  */
56
91
  logEntityChange(entityType_1, entityId_1, operation_1, oldValue_1, newValue_1) {
57
92
  return __awaiter(this, arguments, void 0, function* (entityType, entityId, operation, oldValue, newValue, metadata = {}) {
58
- var _a, _b;
59
93
  // 检查是否应该记录
60
94
  if (!this.auditStrategy.shouldRecord(entityType, operation)) {
61
95
  return null;
@@ -72,9 +106,19 @@ let EntityAuditService = class EntityAuditService {
72
106
  const filteredNewValue = this.filterAndMaskFields(newValue, fieldFilter);
73
107
  // 计算变更字段
74
108
  const changedFields = this.calculateChangedFields(filteredOldValue, filteredNewValue);
75
- const changedFieldPaths = this.calculateChangedFieldPaths(filteredOldValue, filteredNewValue);
109
+ // 生成字段级别的变更详情(前后对比)
110
+ // CREATE 操作不需要 changeDetails,因为只有新值没有旧值
111
+ const changeDetails = operation === enums_1.AuditOperation.CREATE
112
+ ? undefined
113
+ : this.generateChangeDetails(entityType, filteredOldValue, filteredNewValue, changedFields);
76
114
  // 获取上下文信息
77
115
  const context = yield this.contextService.getCurrentContext();
116
+ // 准备模板键和参数
117
+ const templateKey = metadata.operationTemplateKey || `${entityType}.${operation.toLowerCase()}`;
118
+ const descriptionParams = Object.assign({ entityType,
119
+ entityId, operation: operation.toLowerCase(), changedFields: changedFields.join(', '), changedFieldsCount: changedFields.length }, metadata.descriptionParams);
120
+ // 生成描述(优先使用模板)
121
+ const description = yield this.generateDescriptionFromTemplate(templateKey, descriptionParams, operation, entityType, entityId, changedFields);
78
122
  // 创建审计日志
79
123
  const auditLog = this.auditLogRepository.create({
80
124
  entityType,
@@ -83,16 +127,15 @@ let EntityAuditService = class EntityAuditService {
83
127
  oldValue: filteredOldValue,
84
128
  newValue: filteredNewValue,
85
129
  changedFields,
86
- changedFieldPaths: changedFieldPaths.join(','),
130
+ changeDetails,
87
131
  userId: context.userId || metadata.userId,
88
132
  username: context.username || metadata.username,
89
133
  requestId: context.requestId || metadata.requestId,
90
134
  requestIp: context.requestIp || metadata.requestIp,
91
135
  userAgent: context.userAgent || metadata.userAgent,
92
- description: this.generateDescription(operation, entityType, entityId, changedFields),
93
- hashChain: ((_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.security) === null || _b === void 0 ? void 0 : _b.hashChainEnabled)
94
- ? yield this.generateHashChain(entityType, entityId, filteredNewValue)
95
- : undefined,
136
+ description,
137
+ operationTemplateKey: templateKey,
138
+ descriptionParams,
96
139
  // 新增:审计动作关联字段
97
140
  auditActionId: metadata.auditActionId,
98
141
  auditActionName: metadata.auditActionName,
@@ -201,7 +244,9 @@ let EntityAuditService = class EntityAuditService {
201
244
  }
202
245
  // 获取当前实体状态
203
246
  const repository = this.entityManager.getRepository(entityType);
204
- const currentEntity = yield repository.findOne({ where: { id: entityId } });
247
+ const currentEntity = yield repository.findOne({
248
+ where: { id: entityId },
249
+ });
205
250
  if (!currentEntity) {
206
251
  return {
207
252
  canRestore: false,
@@ -287,34 +332,91 @@ let EntityAuditService = class EntityAuditService {
287
332
  });
288
333
  }
289
334
  /**
290
- * 计算变更字段
335
+ * 计算变更字段(支持嵌套路径)
291
336
  */
292
337
  calculateChangedFields(oldValue, newValue) {
293
- const changedFields = [];
294
- // 获取所有字段
295
- const allFields = new Set([...Object.keys(oldValue || {}), ...Object.keys(newValue || {})]);
296
- // 比较每个字段
297
- for (const field of allFields) {
298
- const oldVal = oldValue === null || oldValue === void 0 ? void 0 : oldValue[field];
299
- const newVal = newValue === null || newValue === void 0 ? void 0 : newValue[field];
300
- if (!this.deepEqual(oldVal, newVal)) {
301
- changedFields.push(field);
302
- }
303
- }
304
- return changedFields;
338
+ // 使用 deepDiff 获取所有变更,包括嵌套字段
339
+ const diffChanges = this.deepDiff(oldValue, newValue);
340
+ // 将路径数组转换为点号分隔的字符串
341
+ return diffChanges.map(change => change.path.join('.'));
305
342
  }
306
343
  /**
307
- * 计算变更字段路径
344
+ * 生成字段级别的变更详情(前后对比,支持嵌套路径和装饰器值映射)
308
345
  */
309
- calculateChangedFieldPaths(oldValue, newValue) {
310
- const changedPaths = [];
311
- // 使用深度比较获取变更路径
312
- const diff = this.deepDiff(oldValue, newValue);
313
- // 提取路径
314
- for (const change of diff) {
315
- changedPaths.push(change.path.join('.'));
346
+ generateChangeDetails(entityType, oldValue, newValue, changedFields) {
347
+ const changeDetails = [];
348
+ // 使用 deepDiff 获取详细变更信息
349
+ const diffChanges = this.deepDiff(oldValue, newValue);
350
+ // 尝试获取实体类(用于装饰器值映射)
351
+ const entityClass = getEntityClass(entityType);
352
+ for (const change of diffChanges) {
353
+ const pathStr = change.path.join('.');
354
+ const oldVal = change.oldValue;
355
+ const newVal = change.newValue;
356
+ const fieldName = change.path[0]; // 顶层字段名
357
+ // 确定变更类型
358
+ let changeType;
359
+ if (change.type === 'ADD') {
360
+ changeType = enums_1.ChangeType.ADDED;
361
+ }
362
+ else if (change.type === 'REMOVE') {
363
+ changeType = enums_1.ChangeType.REMOVED;
364
+ }
365
+ else {
366
+ changeType = enums_1.ChangeType.MODIFIED;
367
+ }
368
+ // 尝试从装饰器获取字段标签(支持多语言)
369
+ let fieldLabels;
370
+ let displayOldValue;
371
+ let displayNewValue;
372
+ if (entityClass) {
373
+ try {
374
+ // 获取字段标签(多语言)
375
+ const supportedLanguages = ['zh', 'en', 'ja'];
376
+ fieldLabels = {};
377
+ for (const lang of supportedLanguages) {
378
+ const label = (0, entity_audit_decorator_1.getFieldLabel)(entityClass, pathStr, lang);
379
+ if (label && label !== pathStr) {
380
+ fieldLabels[lang] = label;
381
+ }
382
+ }
383
+ // 获取字段值的标签(多语言值映射)
384
+ if (oldVal !== null && oldVal !== undefined) {
385
+ const oldValueLabel = (0, entity_audit_decorator_1.getFieldValueLabel)(entityClass, fieldName, oldVal, 'zh');
386
+ if (oldValueLabel !== oldVal) {
387
+ // 如果有映射值,创建多语言映射
388
+ displayOldValue = {};
389
+ for (const lang of supportedLanguages) {
390
+ displayOldValue[lang] = (0, entity_audit_decorator_1.getFieldValueLabel)(entityClass, fieldName, oldVal, lang);
391
+ }
392
+ }
393
+ }
394
+ if (newVal !== null && newVal !== undefined) {
395
+ const newValueLabel = (0, entity_audit_decorator_1.getFieldValueLabel)(entityClass, fieldName, newVal, 'zh');
396
+ if (newValueLabel !== newVal) {
397
+ // 如果有映射值,创建多语言映射
398
+ displayNewValue = {};
399
+ for (const lang of supportedLanguages) {
400
+ displayNewValue[lang] = (0, entity_audit_decorator_1.getFieldValueLabel)(entityClass, fieldName, newVal, lang);
401
+ }
402
+ }
403
+ }
404
+ }
405
+ catch (error) {
406
+ // 忽略错误,使用默认值
407
+ }
408
+ }
409
+ changeDetails.push({
410
+ fieldName: pathStr,
411
+ fieldLabels: Object.keys(fieldLabels || {}).length > 0 ? fieldLabels : undefined,
412
+ oldValue: oldVal,
413
+ newValue: newVal,
414
+ displayOldValue,
415
+ displayNewValue,
416
+ changeType,
417
+ });
316
418
  }
317
- return changedPaths;
419
+ return changeDetails;
318
420
  }
319
421
  /**
320
422
  * 深度比较对象
@@ -346,9 +448,9 @@ let EntityAuditService = class EntityAuditService {
346
448
  * 深度差异比较
347
449
  */
348
450
  deepDiff(oldObj, newObj, path = [], visited = new WeakSet()) {
349
- var _a;
451
+ var _a, _b;
350
452
  const changes = [];
351
- const maxDepth = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.maxDiffDepth) || 5;
453
+ const maxDepth = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.maxDiffDepth) !== null && _b !== void 0 ? _b : exports.AUDIT_CONSTANTS.MAX_DIFF_DEPTH;
352
454
  // 防止过深
353
455
  if (path.length > maxDepth) {
354
456
  return changes;
@@ -405,6 +507,16 @@ let EntityAuditService = class EntityAuditService {
405
507
  }
406
508
  // 处理对象
407
509
  const allKeys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
510
+ // 添加键数量限制,防止内存溢出
511
+ if (allKeys.size > exports.AUDIT_CONSTANTS.MAX_KEYS) {
512
+ changes.push({
513
+ path,
514
+ type: 'CHANGE',
515
+ oldValue: `[Object with too many keys: ${allKeys.size}]`,
516
+ newValue: `[Object with too many keys: ${allKeys.size}]`,
517
+ });
518
+ return changes;
519
+ }
408
520
  for (const key of allKeys) {
409
521
  const oldVal = oldObj[key];
410
522
  const newVal = newObj[key];
@@ -511,35 +623,13 @@ let EntityAuditService = class EntityAuditService {
511
623
  */
512
624
  partialMaskValue(value) {
513
625
  const strValue = String(value);
514
- if (strValue.length <= 4) {
626
+ const maskChars = exports.AUDIT_CONSTANTS.PARTIAL_MASK_CHARS;
627
+ if (strValue.length <= maskChars * 2) {
515
628
  return '*'.repeat(strValue.length);
516
629
  }
517
- return (strValue.substring(0, 2) +
518
- '*'.repeat(strValue.length - 4) +
519
- strValue.substring(strValue.length - 2));
520
- }
521
- /**
522
- * 生成哈希链
523
- */
524
- generateHashChain(entityType, entityId, data) {
525
- return __awaiter(this, void 0, void 0, function* () {
526
- var _a, _b, _c;
527
- // 获取上一个审计记录的哈希
528
- const lastLog = yield this.auditLogRepository.findOne({
529
- where: { entityType, entityId },
530
- order: { createdAt: 'DESC' },
531
- });
532
- const previousHash = ((_a = lastLog === null || lastLog === void 0 ? void 0 : lastLog.hashChain) === null || _a === void 0 ? void 0 : _a.currentHash) || '';
533
- // 计算当前数据的哈希
534
- const currentData = JSON.stringify(data);
535
- const hashAlgorithm = ((_c = (_b = this.config) === null || _b === void 0 ? void 0 : _b.security) === null || _c === void 0 ? void 0 : _c.hashAlgorithm) || 'sha256';
536
- const currentHash = (0, crypto_1.createHash)(hashAlgorithm).update(currentData + previousHash).digest('hex');
537
- return {
538
- previousHash,
539
- currentHash,
540
- algorithm: hashAlgorithm,
541
- };
542
- });
630
+ return (strValue.substring(0, maskChars) +
631
+ '*'.repeat(strValue.length - maskChars * 2) +
632
+ strValue.substring(strValue.length - maskChars));
543
633
  }
544
634
  /**
545
635
  * 生成描述(支持多语言)
@@ -550,6 +640,28 @@ let EntityAuditService = class EntityAuditService {
550
640
  * @param language 语言(默认中文)
551
641
  * @param entityClass 实体类(可选,用于获取实体标签)
552
642
  */
643
+ /**
644
+ * 使用模板生成描述(优先使用 OperationDescriptionService)
645
+ */
646
+ generateDescriptionFromTemplate(templateKey, descriptionParams, operation, entityType, entityId, changedFields) {
647
+ return __awaiter(this, void 0, void 0, function* () {
648
+ // 优先尝试使用 OperationDescriptionService
649
+ if (this.descriptionService) {
650
+ try {
651
+ const templateDescription = yield this.descriptionService.generateDescription(templateKey, descriptionParams, 'zh');
652
+ // 如果成功获取到模板描述且不是原始的 templateKey,则使用它
653
+ if (templateDescription && templateDescription !== templateKey) {
654
+ return templateDescription;
655
+ }
656
+ }
657
+ catch (error) {
658
+ // 模板服务失败,降级到原有方式
659
+ }
660
+ }
661
+ // 降级:使用原有的描述生成逻辑
662
+ return this.generateDescription(operation, entityType, entityId, changedFields);
663
+ });
664
+ }
553
665
  generateDescription(operation, entityType, entityId, changedFields, language = 'zh', entityClass) {
554
666
  const operationText = {
555
667
  [enums_1.AuditOperation.CREATE]: '创建',
@@ -611,7 +723,7 @@ let EntityAuditService = class EntityAuditService {
611
723
  */
612
724
  logEntityChangeWithTemplate(data) {
613
725
  return __awaiter(this, void 0, void 0, function* () {
614
- var _a, _b, _c, _d, _e, _f, _g;
726
+ var _a, _b, _c, _d, _e;
615
727
  // 检查是否应该记录
616
728
  if (!this.auditStrategy.shouldRecord(data.entityType, data.operation)) {
617
729
  return null;
@@ -628,7 +740,6 @@ let EntityAuditService = class EntityAuditService {
628
740
  const filteredNewValue = this.filterAndMaskFields(data.newValue || {}, fieldFilter);
629
741
  // 计算变更字段
630
742
  const changedFields = data.changedFields || this.calculateChangedFields(filteredOldValue, filteredNewValue);
631
- const changedFieldPaths = this.calculateChangedFieldPaths(filteredOldValue, filteredNewValue);
632
743
  // 获取上下文信息
633
744
  const context = yield this.contextService.getCurrentContext();
634
745
  // 如果没有提供操作模板键,使用默认模板
@@ -643,21 +754,16 @@ let EntityAuditService = class EntityAuditService {
643
754
  oldValue: filteredOldValue,
644
755
  newValue: filteredNewValue,
645
756
  changedFields,
646
- changedFieldPaths: changedFieldPaths.join(','),
647
757
  userId: context.userId || ((_a = data.metadata) === null || _a === void 0 ? void 0 : _a.userId),
648
758
  username: context.username || ((_b = data.metadata) === null || _b === void 0 ? void 0 : _b.username),
649
759
  requestId: context.requestId || ((_c = data.metadata) === null || _c === void 0 ? void 0 : _c.requestId),
650
760
  requestIp: context.requestIp || ((_d = data.metadata) === null || _d === void 0 ? void 0 : _d.requestIp),
651
761
  userAgent: context.userAgent || ((_e = data.metadata) === null || _e === void 0 ? void 0 : _e.userAgent),
652
762
  description: this.generateDescription(data.operation, data.entityType, data.entityId, changedFields),
653
- hashChain: ((_g = (_f = this.config) === null || _f === void 0 ? void 0 : _f.security) === null || _g === void 0 ? void 0 : _g.hashChainEnabled)
654
- ? yield this.generateHashChain(data.entityType, data.entityId, filteredNewValue)
655
- : undefined,
656
763
  // 新增字段
657
764
  operationTemplateKey,
658
765
  descriptionParams,
659
766
  changeDetails: data.changeDetails,
660
- rollbackActions: data.rollbackActions,
661
767
  });
662
768
  // 保存变更日志
663
769
  const savedLog = yield this.auditLogRepository.save(auditLog);
@@ -680,7 +786,6 @@ let EntityAuditService = class EntityAuditService {
680
786
  userId: data.userId || context.userId,
681
787
  username: data.username || context.username,
682
788
  requestIp: data.requestIp || context.requestIp,
683
- rollbackActions: data.rollbackActions || [],
684
789
  });
685
790
  return yield this.manualOperationRepository.save(manualLog);
686
791
  });
@@ -822,7 +927,12 @@ let EntityAuditService = class EntityAuditService {
822
927
  queryBuilder.andWhere('action.username LIKE :username', { username: `%${options.username}%` });
823
928
  }
824
929
  if (options.actionName) {
825
- queryBuilder.andWhere('action.actionName = :actionName', { actionName: options.actionName });
930
+ // 支持模糊匹配动作名
931
+ queryBuilder.andWhere('action.actionName LIKE :actionName', { actionName: `%${options.actionName}%` });
932
+ }
933
+ // 新增:全局关键词搜索(搜索多个字段)
934
+ if (options.keyword) {
935
+ queryBuilder.andWhere('(action.username LIKE :keyword OR action.actionName LIKE :keyword OR action.description LIKE :keyword)', { keyword: `%${options.keyword}%` });
826
936
  }
827
937
  if (options.success !== undefined) {
828
938
  queryBuilder.andWhere('action.success = :success', { success: options.success });
@@ -850,9 +960,11 @@ let EntityAuditService = class EntityAuditService {
850
960
  }
851
961
  /**
852
962
  * 查询单个审计动作的详细信息(包含关联的实体变更)
963
+ * @param actionId 动作ID
964
+ * @param language 语言代码 (zh/en/ja),默认 zh
853
965
  */
854
- getAuditActionDetail(actionId) {
855
- return __awaiter(this, void 0, void 0, function* () {
966
+ getAuditActionDetail(actionId_1) {
967
+ return __awaiter(this, arguments, void 0, function* (actionId, language = 'zh') {
856
968
  if (!this.actionSummaryRepository) {
857
969
  throw new Error('AuditActionSummaryEntity repository not available');
858
970
  }
@@ -863,29 +975,32 @@ let EntityAuditService = class EntityAuditService {
863
975
  if (!summary) {
864
976
  throw new Error(`Audit action with id ${actionId} not found`);
865
977
  }
978
+ // 动态生成多语言描述
979
+ const description = yield this.generateLocalizedDescription(summary.operationTemplateKey, summary.actionName, summary.descriptionParams, summary.success, language);
866
980
  // 查询关联的实体变更
867
981
  const entityChanges = yield this.auditLogRepository.find({
868
982
  where: { auditActionId: actionId },
869
983
  order: { sequenceInAction: 'ASC' },
870
984
  });
871
- // 分析每个实体的详细变化
985
+ // 分析每个实体的详细变化(支持多语言)
872
986
  const detailedChanges = entityChanges.map((change) => {
873
- const fieldChanges = this.analyzeFieldChanges(change.oldValue || {}, change.newValue || {}, change.operation);
987
+ // 使用数据库中存储的 changeDetails,并根据语言转换
988
+ const changeDetails = this.localizeChangeDetails(change.changeDetails, language);
874
989
  return {
875
990
  id: change.id,
876
991
  entityType: change.entityType,
877
992
  entityId: change.entityId,
878
993
  operation: change.operation,
879
- operationLabel: this.getOperationLabel(change.operation),
994
+ operationLabel: this.getOperationLabel(change.operation, language),
880
995
  description: change.description,
881
996
  sequenceInAction: change.sequenceInAction,
882
997
  createdAt: change.createdAt,
883
998
  // 数据快照
884
999
  oldValue: change.oldValue,
885
1000
  newValue: change.newValue,
886
- // 详细的字段变化分析
887
- fieldChanges,
888
- changedFieldsCount: fieldChanges.length,
1001
+ // 详细的字段变化分析 (使用本地化的 changeDetails)
1002
+ changeDetails,
1003
+ changedFieldsCount: (changeDetails === null || changeDetails === void 0 ? void 0 : changeDetails.length) || 0,
889
1004
  changedFields: change.changedFields,
890
1005
  };
891
1006
  });
@@ -902,12 +1017,12 @@ let EntityAuditService = class EntityAuditService {
902
1017
  })),
903
1018
  };
904
1019
  return {
905
- // 汇总信息
1020
+ // 汇总信息(使用本地化描述)
906
1021
  summary: {
907
1022
  id: summary.id,
908
1023
  actionName: summary.actionName,
909
1024
  operationTemplateKey: summary.operationTemplateKey,
910
- description: summary.description,
1025
+ description,
911
1026
  descriptionParams: summary.descriptionParams,
912
1027
  success: summary.success,
913
1028
  errorMessage: summary.errorMessage,
@@ -932,6 +1047,88 @@ let EntityAuditService = class EntityAuditService {
932
1047
  };
933
1048
  });
934
1049
  }
1050
+ /**
1051
+ * 生成本地化的描述文本
1052
+ */
1053
+ generateLocalizedDescription(templateKey, actionName, params, success, language) {
1054
+ return __awaiter(this, void 0, void 0, function* () {
1055
+ var _a;
1056
+ let template;
1057
+ // 1. 尝试从模板服务获取多语言模板
1058
+ if (templateKey && this.descriptionService) {
1059
+ try {
1060
+ template = yield this.descriptionService.getTemplate(templateKey, language);
1061
+ }
1062
+ catch (error) {
1063
+ // 如果获取模板失败,继续使用默认逻辑
1064
+ }
1065
+ }
1066
+ // 2. 如果没有模板,使用默认格式
1067
+ if (!template) {
1068
+ const actionLabels = {
1069
+ zh: { success: '执行操作', fail: '执行操作失败' },
1070
+ en: { success: 'Executed operation', fail: 'Operation failed' },
1071
+ ja: { success: '操作を実行', fail: '操作が失敗しました' },
1072
+ };
1073
+ const label = ((_a = actionLabels[language]) === null || _a === void 0 ? void 0 : _a[success ? 'success' : 'fail']) || actionLabels.zh[success ? 'success' : 'fail'];
1074
+ template = `${label}: ${actionName}`;
1075
+ }
1076
+ // 3. 执行模板替换
1077
+ return this.replaceTemplatePlaceholders(template, params);
1078
+ });
1079
+ }
1080
+ /**
1081
+ * 替换模板中的占位符
1082
+ * @param template 模板字符串,如 "创建了产品 {name},价格 ¥{price}"
1083
+ * @param params 参数对象,如 { name: "iPhone", price: 999 }
1084
+ * @returns 替换后的字符串
1085
+ */
1086
+ replaceTemplatePlaceholders(template, params) {
1087
+ let result = template;
1088
+ // 匹配 {paramName} 格式的占位符
1089
+ const placeholderPattern = /\{([^}]+)\}/g;
1090
+ result = result.replace(placeholderPattern, (match, paramName) => {
1091
+ // 支持嵌套属性访问,如 {user.name}
1092
+ const value = this.getNestedValue(params, paramName);
1093
+ return value !== undefined && value !== null ? String(value) : match;
1094
+ });
1095
+ return result;
1096
+ }
1097
+ /**
1098
+ * 获取嵌套对象的值
1099
+ * @param obj 对象
1100
+ * @param path 路径,如 "user.name" 或 "user.profile.age"
1101
+ * @returns 值或 undefined
1102
+ */
1103
+ getNestedValue(obj, path) {
1104
+ return path.split('.').reduce((current, key) => {
1105
+ return current === null || current === void 0 ? void 0 : current[key];
1106
+ }, obj);
1107
+ }
1108
+ /**
1109
+ * 本地化 changeDetails(根据语言转换 fieldLabel 和 displayValue)
1110
+ */
1111
+ localizeChangeDetails(changeDetails, language) {
1112
+ if (!changeDetails)
1113
+ return undefined;
1114
+ return changeDetails.map((detail) => {
1115
+ var _a, _b, _c, _d, _e;
1116
+ return (Object.assign(Object.assign({}, detail), { fieldLabel: ((_a = detail.fieldLabels) === null || _a === void 0 ? void 0 : _a[language]) || detail.fieldName, displayOldValue: (_c = (_b = detail.displayOldValue) === null || _b === void 0 ? void 0 : _b[language]) !== null && _c !== void 0 ? _c : detail.oldValue, displayNewValue: (_e = (_d = detail.displayNewValue) === null || _d === void 0 ? void 0 : _d[language]) !== null && _e !== void 0 ? _e : detail.newValue }));
1117
+ });
1118
+ }
1119
+ /**
1120
+ * 获取操作标签(支持多语言)
1121
+ */
1122
+ getOperationLabel(operation, language = 'zh') {
1123
+ var _a, _b;
1124
+ const labels = {
1125
+ CREATE: { zh: '创建', en: 'Create', ja: '作成' },
1126
+ UPDATE: { zh: '更新', en: 'Update', ja: '更新' },
1127
+ DELETE: { zh: '删除', en: 'Delete', ja: '削除' },
1128
+ RESTORE: { zh: '恢复', en: 'Restore', ja: '復元' },
1129
+ };
1130
+ return ((_a = labels[operation]) === null || _a === void 0 ? void 0 : _a[language]) || ((_b = labels[operation]) === null || _b === void 0 ? void 0 : _b.zh) || operation;
1131
+ }
935
1132
  /**
936
1133
  * 分析字段变化(支持多语言)
937
1134
  * @param oldValue 旧值
@@ -1030,18 +1227,6 @@ let EntityAuditService = class EntityAuditService {
1030
1227
  }
1031
1228
  return String(value);
1032
1229
  }
1033
- /**
1034
- * 获取操作标签
1035
- */
1036
- getOperationLabel(operation) {
1037
- const labels = {
1038
- [enums_1.AuditOperation.CREATE]: '创建',
1039
- [enums_1.AuditOperation.UPDATE]: '更新',
1040
- [enums_1.AuditOperation.DELETE]: '删除',
1041
- [enums_1.AuditOperation.RESTORE]: '恢复',
1042
- };
1043
- return labels[operation] || operation;
1044
- }
1045
1230
  /**
1046
1231
  * 按字段分组统计
1047
1232
  */
@@ -1068,10 +1253,12 @@ exports.EntityAuditService = EntityAuditService = __decorate([
1068
1253
  __param(8, (0, common_1.Inject)('AUDIT_CONNECTION_NAME')),
1069
1254
  __param(9, (0, common_1.Optional)()),
1070
1255
  __param(9, (0, typeorm_1.InjectRepository)(entities_1.AuditActionSummaryEntity)),
1256
+ __param(10, (0, common_1.Optional)()),
1071
1257
  __metadata("design:paramtypes", [typeorm_2.Repository,
1072
1258
  typeorm_2.Repository,
1073
1259
  typeorm_2.Repository,
1074
1260
  typeorm_2.EntityManager,
1075
1261
  audit_context_service_1.AuditContextService,
1076
- multi_database_service_1.MultiDatabaseService, Object, Object, String, typeorm_2.Repository])
1262
+ multi_database_service_1.MultiDatabaseService, Object, Object, String, typeorm_2.Repository,
1263
+ operation_description_service_1.OperationDescriptionService])
1077
1264
  ], EntityAuditService);
@@ -7,7 +7,6 @@ import { AuditTransactionStatus } from '../enums';
7
7
  * 手动审计日志服务
8
8
  *
9
9
  * 提供标准的依赖注入方式记录操作日志
10
- * @deprecated 静态方法已废弃,请使用依赖注入方式
11
10
  */
12
11
  export declare class ManualAuditLogService {
13
12
  private readonly manualLogRepository;
@@ -33,7 +33,6 @@ const crypto_1 = require("crypto");
33
33
  * 手动审计日志服务
34
34
  *
35
35
  * 提供标准的依赖注入方式记录操作日志
36
- * @deprecated 静态方法已废弃,请使用依赖注入方式
37
36
  */
38
37
  let ManualAuditLogService = class ManualAuditLogService {
39
38
  constructor(manualLogRepository, transactionRepository, contextService, dataSource) {
@@ -99,7 +98,7 @@ let ManualAuditLogService = class ManualAuditLogService {
99
98
  */
100
99
  logOperation(options) {
101
100
  return __awaiter(this, void 0, void 0, function* () {
102
- const { templateKey, description, descriptionTemplate, descriptionParams, userId, username, requestIp, rollbackActions, transactionId, autoCreateTransaction = true, } = options;
101
+ const { templateKey, description, descriptionTemplate, descriptionParams, userId, username, requestIp, transactionId, autoCreateTransaction = true, } = options;
103
102
  // 验证参数:至少提供一种模式
104
103
  if (!templateKey && !description && !descriptionTemplate) {
105
104
  throw new Error('ManualAuditLogService.log() requires one of: templateKey, description, or descriptionTemplate');
@@ -135,7 +134,6 @@ let ManualAuditLogService = class ManualAuditLogService {
135
134
  userId: finalUserId,
136
135
  username: finalUsername,
137
136
  requestIp: finalRequestIp,
138
- rollbackActions: rollbackActions || [],
139
137
  });
140
138
  return this.manualLogRepository.save(manualLog);
141
139
  });
@@ -9,6 +9,7 @@ export declare class EntityAuditSubscriber implements EntitySubscriberInterface<
9
9
  private readonly entityAuditService;
10
10
  private readonly contextService;
11
11
  private readonly auditStrategy;
12
+ private readonly logger;
12
13
  constructor(entityAuditService: EntityAuditService, contextService: AuditContextService, auditStrategy: DefaultAuditStrategy, dataSource: DataSource);
13
14
  /**
14
15
  * 在实体插入后记录审计日志