@nest-omni/core 3.1.2-8 → 3.1.2-9

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 (91) hide show
  1. package/audit/audit.module.d.ts +10 -0
  2. package/audit/audit.module.js +254 -0
  3. package/audit/controllers/audit.controller.d.ts +20 -0
  4. package/audit/controllers/audit.controller.js +142 -0
  5. package/audit/controllers/index.d.ts +1 -0
  6. package/audit/controllers/index.js +17 -0
  7. package/audit/decorators/audit-controller.decorator.d.ts +5 -0
  8. package/audit/decorators/audit-controller.decorator.js +17 -0
  9. package/audit/decorators/audit-operation.decorator.d.ts +7 -0
  10. package/audit/decorators/audit-operation.decorator.js +25 -0
  11. package/audit/decorators/entity-audit.decorator.d.ts +10 -0
  12. package/audit/decorators/entity-audit.decorator.js +70 -0
  13. package/audit/decorators/index.d.ts +3 -0
  14. package/audit/decorators/index.js +19 -0
  15. package/audit/dto/audit-log-query.dto.d.ts +14 -0
  16. package/audit/dto/audit-log-query.dto.js +95 -0
  17. package/audit/dto/begin-transaction.dto.d.ts +3 -0
  18. package/audit/dto/begin-transaction.dto.js +22 -0
  19. package/audit/dto/compare-entities.dto.d.ts +6 -0
  20. package/audit/dto/compare-entities.dto.js +44 -0
  21. package/audit/dto/index.d.ts +5 -0
  22. package/audit/dto/index.js +21 -0
  23. package/audit/dto/pre-check-restore.dto.d.ts +5 -0
  24. package/audit/dto/pre-check-restore.dto.js +32 -0
  25. package/audit/dto/restore-entity.dto.d.ts +9 -0
  26. package/audit/dto/restore-entity.dto.js +53 -0
  27. package/audit/entities/entity-audit-log.entity.d.ts +23 -0
  28. package/audit/entities/entity-audit-log.entity.js +110 -0
  29. package/audit/entities/entity-transaction.entity.d.ts +21 -0
  30. package/audit/entities/entity-transaction.entity.js +80 -0
  31. package/audit/entities/index.d.ts +4 -0
  32. package/audit/entities/index.js +20 -0
  33. package/audit/entities/manual-operation-log.entity.d.ts +13 -0
  34. package/audit/entities/manual-operation-log.entity.js +65 -0
  35. package/audit/entities/operation-template.entity.d.ts +11 -0
  36. package/audit/entities/operation-template.entity.js +65 -0
  37. package/audit/enums/audit.enums.d.ts +42 -0
  38. package/audit/enums/audit.enums.js +53 -0
  39. package/audit/enums/index.d.ts +1 -0
  40. package/audit/enums/index.js +17 -0
  41. package/audit/index.d.ts +12 -0
  42. package/audit/index.js +48 -0
  43. package/audit/interceptors/audit.interceptor.d.ts +12 -0
  44. package/audit/interceptors/audit.interceptor.js +95 -0
  45. package/audit/interceptors/index.d.ts +1 -0
  46. package/audit/interceptors/index.js +17 -0
  47. package/audit/interfaces/audit.interfaces.d.ts +180 -0
  48. package/audit/interfaces/audit.interfaces.js +2 -0
  49. package/audit/interfaces/index.d.ts +1 -0
  50. package/audit/interfaces/index.js +17 -0
  51. package/audit/services/audit-context.service.d.ts +10 -0
  52. package/audit/services/audit-context.service.js +55 -0
  53. package/audit/services/audit-strategy.service.d.ts +19 -0
  54. package/audit/services/audit-strategy.service.js +89 -0
  55. package/audit/services/entity-audit.service.d.ts +65 -0
  56. package/audit/services/entity-audit.service.js +626 -0
  57. package/audit/services/index.d.ts +6 -0
  58. package/audit/services/index.js +22 -0
  59. package/audit/services/multi-database.service.d.ts +10 -0
  60. package/audit/services/multi-database.service.js +59 -0
  61. package/audit/services/operation-description.service.d.ts +21 -0
  62. package/audit/services/operation-description.service.js +213 -0
  63. package/audit/services/transaction-audit.service.d.ts +22 -0
  64. package/audit/services/transaction-audit.service.js +201 -0
  65. package/audit/subscribers/entity-audit.subscriber.d.ts +14 -0
  66. package/audit/subscribers/entity-audit.subscriber.js +136 -0
  67. package/audit/subscribers/index.d.ts +1 -0
  68. package/audit/subscribers/index.js +17 -0
  69. package/http-client/examples/advanced-usage.example.js +14 -1
  70. package/http-client/examples/auth-with-waiting-lock.example.d.ts +17 -0
  71. package/http-client/examples/auth-with-waiting-lock.example.js +336 -0
  72. package/http-client/examples/basic-usage.example.d.ts +1 -9
  73. package/http-client/examples/basic-usage.example.js +4 -14
  74. package/http-client/examples/multi-api-configuration.example.js +4 -4
  75. package/http-client/http-client.module.js +7 -4
  76. package/http-client/services/api-client-registry.service.d.ts +2 -1
  77. package/http-client/services/api-client-registry.service.js +2 -1
  78. package/http-client/services/http-client.service.d.ts +18 -1
  79. package/http-client/services/http-client.service.js +123 -3
  80. package/http-client/services/http-log-query.service.d.ts +20 -0
  81. package/http-client/services/http-log-query.service.js +176 -0
  82. package/http-client/services/http-replay.service.d.ts +58 -0
  83. package/http-client/services/http-replay.service.js +266 -0
  84. package/http-client/services/log-cleanup.service.js +2 -2
  85. package/http-client/services/logging.service.js +1 -1
  86. package/http-client/utils/request-id.util.d.ts +4 -0
  87. package/http-client/utils/request-id.util.js +34 -0
  88. package/index.d.ts +1 -0
  89. package/index.js +1 -0
  90. package/package.json +1 -1
  91. package/setup/bootstrap.setup.js +5 -1
@@ -0,0 +1,626 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
15
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
16
+ return new (P || (P = Promise))(function (resolve, reject) {
17
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
18
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
19
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
20
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
21
+ });
22
+ };
23
+ Object.defineProperty(exports, "__esModule", { value: true });
24
+ exports.EntityAuditService = void 0;
25
+ const common_1 = require("@nestjs/common");
26
+ const typeorm_1 = require("@nestjs/typeorm");
27
+ const typeorm_2 = require("typeorm");
28
+ const crypto_1 = require("crypto");
29
+ const entities_1 = require("../entities");
30
+ const enums_1 = require("../enums");
31
+ const audit_context_service_1 = require("./audit-context.service");
32
+ const audit_strategy_service_1 = require("./audit-strategy.service");
33
+ const multi_database_service_1 = require("./multi-database.service");
34
+ const dto_1 = require("../../common/dto");
35
+ let EntityAuditService = class EntityAuditService {
36
+ constructor(auditLogRepository, transactionRepository, manualOperationRepository, entityManager, contextService, multiDbService, auditStrategy = new audit_strategy_service_1.DefaultAuditStrategy(), config, auditConnectionName) {
37
+ this.auditLogRepository = auditLogRepository;
38
+ this.transactionRepository = transactionRepository;
39
+ this.manualOperationRepository = manualOperationRepository;
40
+ this.entityManager = entityManager;
41
+ this.contextService = contextService;
42
+ this.multiDbService = multiDbService;
43
+ this.auditStrategy = auditStrategy;
44
+ this.config = config;
45
+ this.auditConnectionName = auditConnectionName || 'default';
46
+ }
47
+ logEntityChange(entityType_1, entityId_1, operation_1, oldValue_1, newValue_1) {
48
+ return __awaiter(this, arguments, void 0, function* (entityType, entityId, operation, oldValue, newValue, metadata = {}) {
49
+ var _a, _b;
50
+ if (!this.auditStrategy.shouldRecord(entityType, operation)) {
51
+ return null;
52
+ }
53
+ const recordStrategy = this.auditStrategy.getRecordStrategy(entityType, operation);
54
+ if (recordStrategy === enums_1.RecordStrategy.DISABLED) {
55
+ return null;
56
+ }
57
+ const fieldFilter = this.auditStrategy.getFieldFilter(entityType);
58
+ const filteredOldValue = this.filterAndMaskFields(oldValue, fieldFilter);
59
+ const filteredNewValue = this.filterAndMaskFields(newValue, fieldFilter);
60
+ const changedFields = this.calculateChangedFields(filteredOldValue, filteredNewValue);
61
+ const changedFieldPaths = this.calculateChangedFieldPaths(filteredOldValue, filteredNewValue);
62
+ const context = yield this.contextService.getCurrentContext();
63
+ const auditLog = this.auditLogRepository.create({
64
+ entityType,
65
+ entityId,
66
+ operation,
67
+ oldValue: filteredOldValue,
68
+ newValue: filteredNewValue,
69
+ changedFields,
70
+ changedFieldPaths: changedFieldPaths.join(','),
71
+ userId: context.userId || metadata.userId,
72
+ username: context.username || metadata.username,
73
+ requestId: context.requestId || metadata.requestId,
74
+ requestIp: context.requestIp || metadata.requestIp,
75
+ userAgent: context.userAgent || metadata.userAgent,
76
+ description: this.generateDescription(operation, entityType, entityId, changedFields),
77
+ hashChain: ((_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.security) === null || _b === void 0 ? void 0 : _b.hashChainEnabled)
78
+ ? yield this.generateHashChain(entityType, entityId, filteredNewValue)
79
+ : undefined,
80
+ });
81
+ const savedLog = yield this.auditLogRepository.save(auditLog);
82
+ return savedLog;
83
+ });
84
+ }
85
+ getAuditLogs(query) {
86
+ return __awaiter(this, void 0, void 0, function* () {
87
+ const page = query.page || 1;
88
+ const limit = query.limit || 20;
89
+ const sortBy = query.sortBy || 'createdAt';
90
+ const sortOrder = query.sortOrder || 'DESC';
91
+ const queryBuilder = this.auditLogRepository.createQueryBuilder('log');
92
+ if (query.entityType) {
93
+ queryBuilder.andWhere('log.entityType = :entityType', { entityType: query.entityType });
94
+ }
95
+ if (query.entityId) {
96
+ queryBuilder.andWhere('log.entityId = :entityId', { entityId: query.entityId });
97
+ }
98
+ if (query.operation) {
99
+ queryBuilder.andWhere('log.operation = :operation', { operation: query.operation });
100
+ }
101
+ if (query.userId) {
102
+ queryBuilder.andWhere('log.userId = :userId', { userId: query.userId });
103
+ }
104
+ if (query.startDate && query.endDate) {
105
+ queryBuilder.andWhere('log.createdAt BETWEEN :startDate AND :endDate', {
106
+ startDate: query.startDate,
107
+ endDate: query.endDate,
108
+ });
109
+ }
110
+ if (query.search) {
111
+ queryBuilder.andWhere('(log.description LIKE :search OR log.changedFieldPaths LIKE :search)', {
112
+ search: `%${query.search}%`,
113
+ });
114
+ }
115
+ queryBuilder.orderBy(`log.${sortBy}`, sortOrder);
116
+ const skip = (page - 1) * limit;
117
+ queryBuilder.skip(skip).take(limit);
118
+ const [data, total] = yield queryBuilder.getManyAndCount();
119
+ const pageOptionsDto = new dto_1.PageOptionsDto();
120
+ Object.assign(pageOptionsDto, { page, pageSize: limit });
121
+ const pageMetaDto = new dto_1.PageMetaDto({
122
+ pageOptionsDto,
123
+ itemCount: total,
124
+ });
125
+ return new dto_1.PageDto(data, pageMetaDto);
126
+ });
127
+ }
128
+ compareEntities(entityType, entityId, fromLogId, toLogId) {
129
+ return __awaiter(this, void 0, void 0, function* () {
130
+ let fromData = {};
131
+ let toData = {};
132
+ if (fromLogId) {
133
+ const fromLog = yield this.auditLogRepository.findOne({ where: { id: fromLogId } });
134
+ fromData = (fromLog === null || fromLog === void 0 ? void 0 : fromLog.newValue) || {};
135
+ }
136
+ if (toLogId) {
137
+ const toLog = yield this.auditLogRepository.findOne({ where: { id: toLogId } });
138
+ toData = (toLog === null || toLog === void 0 ? void 0 : toLog.newValue) || {};
139
+ }
140
+ else {
141
+ const latestLog = yield this.auditLogRepository.findOne({
142
+ where: { entityType, entityId },
143
+ order: { createdAt: 'DESC' },
144
+ });
145
+ toData = (latestLog === null || latestLog === void 0 ? void 0 : latestLog.newValue) || {};
146
+ }
147
+ return this.compareSnapshotData(fromData, toData);
148
+ });
149
+ }
150
+ preCheckRestore(entityType, entityId, auditLogId) {
151
+ return __awaiter(this, void 0, void 0, function* () {
152
+ const auditLog = yield this.auditLogRepository.findOne({ where: { id: auditLogId } });
153
+ if (!auditLog) {
154
+ return {
155
+ canRestore: false,
156
+ conflicts: [
157
+ {
158
+ field: 'auditLog',
159
+ reason: '审计日志不存在',
160
+ currentValue: null,
161
+ targetValue: null,
162
+ },
163
+ ],
164
+ warnings: [],
165
+ };
166
+ }
167
+ const repository = this.entityManager.getRepository(entityType);
168
+ const currentEntity = yield repository.findOne({ where: { id: entityId } });
169
+ if (!currentEntity) {
170
+ return {
171
+ canRestore: false,
172
+ conflicts: [
173
+ {
174
+ field: 'entity',
175
+ reason: '实体不存在',
176
+ currentValue: null,
177
+ targetValue: auditLog.oldValue,
178
+ },
179
+ ],
180
+ warnings: [],
181
+ };
182
+ }
183
+ const conflicts = this.detectConflicts(currentEntity, auditLog.oldValue);
184
+ return {
185
+ canRestore: conflicts.length === 0,
186
+ conflicts,
187
+ warnings: conflicts.length > 0 ? ['存在字段冲突,可能需要强制恢复'] : [],
188
+ };
189
+ });
190
+ }
191
+ restoreEntity(entityType_1, entityId_1, auditLogId_1) {
192
+ return __awaiter(this, arguments, void 0, function* (entityType, entityId, auditLogId, options = {}) {
193
+ const auditLog = yield this.auditLogRepository.findOne({ where: { id: auditLogId } });
194
+ if (!auditLog) {
195
+ return {
196
+ success: false,
197
+ message: '审计日志不存在',
198
+ };
199
+ }
200
+ const preCheckResult = yield this.preCheckRestore(entityType, entityId, auditLogId);
201
+ if (!preCheckResult.canRestore && !options.force) {
202
+ return {
203
+ success: false,
204
+ conflicts: preCheckResult.conflicts,
205
+ message: '恢复操作存在冲突,请使用强制恢复或解决冲突后重试',
206
+ };
207
+ }
208
+ try {
209
+ const connectionName = options.connectionName || 'default';
210
+ const dataSource = yield this.multiDbService.getDataSource(connectionName);
211
+ const repository = dataSource.getRepository(entityType);
212
+ const restoreData = Object.assign({}, auditLog.oldValue);
213
+ if (options.includeFields && options.includeFields.length > 0) {
214
+ Object.keys(restoreData).forEach((key) => {
215
+ if (!options.includeFields.includes(key)) {
216
+ delete restoreData[key];
217
+ }
218
+ });
219
+ }
220
+ if (options.excludeFields && options.excludeFields.length > 0) {
221
+ options.excludeFields.forEach((field) => {
222
+ delete restoreData[field];
223
+ });
224
+ }
225
+ if (!options.dryRun) {
226
+ yield repository.update({ id: entityId }, restoreData);
227
+ yield this.logEntityChange(entityType, entityId, enums_1.AuditOperation.RESTORE, auditLog.newValue, restoreData);
228
+ }
229
+ return {
230
+ success: true,
231
+ message: options.dryRun ? '预演成功' : '恢复成功',
232
+ restoredEntities: [{ entityType, entityId }],
233
+ };
234
+ }
235
+ catch (error) {
236
+ return {
237
+ success: false,
238
+ message: `恢复失败: ${error.message}`,
239
+ };
240
+ }
241
+ });
242
+ }
243
+ calculateChangedFields(oldValue, newValue) {
244
+ const changedFields = [];
245
+ const allFields = new Set([...Object.keys(oldValue || {}), ...Object.keys(newValue || {})]);
246
+ for (const field of allFields) {
247
+ const oldVal = oldValue === null || oldValue === void 0 ? void 0 : oldValue[field];
248
+ const newVal = newValue === null || newValue === void 0 ? void 0 : newValue[field];
249
+ if (!this.deepEqual(oldVal, newVal)) {
250
+ changedFields.push(field);
251
+ }
252
+ }
253
+ return changedFields;
254
+ }
255
+ calculateChangedFieldPaths(oldValue, newValue) {
256
+ const changedPaths = [];
257
+ const diff = this.deepDiff(oldValue, newValue);
258
+ for (const change of diff) {
259
+ changedPaths.push(change.path.join('.'));
260
+ }
261
+ return changedPaths;
262
+ }
263
+ deepEqual(a, b) {
264
+ if (a === b)
265
+ return true;
266
+ if (a == null || b == null)
267
+ return false;
268
+ if (typeof a !== typeof b)
269
+ return false;
270
+ if (typeof a !== 'object')
271
+ return a === b;
272
+ if (Array.isArray(a) !== Array.isArray(b))
273
+ return false;
274
+ const keysA = Object.keys(a);
275
+ const keysB = Object.keys(b);
276
+ if (keysA.length !== keysB.length)
277
+ return false;
278
+ for (const key of keysA) {
279
+ if (!keysB.includes(key))
280
+ return false;
281
+ if (!this.deepEqual(a[key], b[key]))
282
+ return false;
283
+ }
284
+ return true;
285
+ }
286
+ deepDiff(oldObj, newObj, path = []) {
287
+ var _a;
288
+ const changes = [];
289
+ const maxDepth = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.maxDiffDepth) || 5;
290
+ if (path.length > maxDepth) {
291
+ return changes;
292
+ }
293
+ if (typeof oldObj !== 'object' || oldObj === null) {
294
+ if (oldObj !== newObj) {
295
+ changes.push({
296
+ path,
297
+ type: 'CHANGE',
298
+ oldValue: oldObj,
299
+ newValue: newObj,
300
+ });
301
+ }
302
+ return changes;
303
+ }
304
+ if (Array.isArray(oldObj)) {
305
+ if (!Array.isArray(newObj)) {
306
+ changes.push({
307
+ path,
308
+ type: 'CHANGE',
309
+ oldValue: oldObj,
310
+ newValue: newObj,
311
+ });
312
+ return changes;
313
+ }
314
+ if (oldObj.length !== newObj.length) {
315
+ changes.push({
316
+ path,
317
+ type: 'CHANGE',
318
+ oldValue: oldObj,
319
+ newValue: newObj,
320
+ });
321
+ return changes;
322
+ }
323
+ for (let i = 0; i < oldObj.length; i++) {
324
+ changes.push(...this.deepDiff(oldObj[i], newObj[i], [...path, i.toString()]));
325
+ }
326
+ return changes;
327
+ }
328
+ const allKeys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
329
+ for (const key of allKeys) {
330
+ const oldVal = oldObj[key];
331
+ const newVal = newObj[key];
332
+ if (!(key in oldObj)) {
333
+ changes.push({
334
+ path: [...path, key],
335
+ type: 'ADD',
336
+ oldValue: undefined,
337
+ newValue: newVal,
338
+ });
339
+ }
340
+ else if (!(key in newObj)) {
341
+ changes.push({
342
+ path: [...path, key],
343
+ type: 'REMOVE',
344
+ oldValue: oldVal,
345
+ newValue: undefined,
346
+ });
347
+ }
348
+ else {
349
+ changes.push(...this.deepDiff(oldVal, newVal, [...path, key]));
350
+ }
351
+ }
352
+ return changes;
353
+ }
354
+ compareSnapshotData(fromData, toData) {
355
+ const changes = this.deepDiff(fromData, toData);
356
+ return {
357
+ changes,
358
+ summary: {
359
+ added: changes.filter((c) => c.type === 'ADD').length,
360
+ removed: changes.filter((c) => c.type === 'REMOVE').length,
361
+ changed: changes.filter((c) => c.type === 'CHANGE').length,
362
+ },
363
+ };
364
+ }
365
+ filterAndMaskFields(data, fieldFilter, path = []) {
366
+ if (typeof data !== 'object' || data === null) {
367
+ return data;
368
+ }
369
+ const result = {};
370
+ for (const [key, value] of Object.entries(data)) {
371
+ const currentPath = [...path, key];
372
+ if (!fieldFilter.shouldIncludeField(key, currentPath)) {
373
+ continue;
374
+ }
375
+ if (fieldFilter.shouldMaskField(key, currentPath)) {
376
+ const maskingStrategy = fieldFilter.getMaskingStrategy(key, currentPath);
377
+ result[key] = this.applyMasking(value, maskingStrategy);
378
+ }
379
+ else if (typeof value === 'object' && value !== null) {
380
+ result[key] = this.filterAndMaskFields(value, fieldFilter, currentPath);
381
+ }
382
+ else {
383
+ result[key] = value;
384
+ }
385
+ }
386
+ return result;
387
+ }
388
+ applyMasking(value, strategy) {
389
+ if (value == null)
390
+ return value;
391
+ switch (strategy) {
392
+ case enums_1.MaskingStrategy.NONE:
393
+ return value;
394
+ case enums_1.MaskingStrategy.HASH:
395
+ return this.hashValue(value);
396
+ case enums_1.MaskingStrategy.MASK:
397
+ return this.maskValue(value);
398
+ case enums_1.MaskingStrategy.PARTIAL:
399
+ return this.partialMaskValue(value);
400
+ default:
401
+ return '***MASKED***';
402
+ }
403
+ }
404
+ hashValue(value) {
405
+ const strValue = String(value);
406
+ return (0, crypto_1.createHash)('sha256').update(strValue).digest('hex');
407
+ }
408
+ maskValue(value) {
409
+ const strValue = String(value);
410
+ return '*'.repeat(strValue.length);
411
+ }
412
+ partialMaskValue(value) {
413
+ const strValue = String(value);
414
+ if (strValue.length <= 4) {
415
+ return '*'.repeat(strValue.length);
416
+ }
417
+ return (strValue.substring(0, 2) +
418
+ '*'.repeat(strValue.length - 4) +
419
+ strValue.substring(strValue.length - 2));
420
+ }
421
+ generateHashChain(entityType, entityId, data) {
422
+ return __awaiter(this, void 0, void 0, function* () {
423
+ var _a, _b, _c;
424
+ const lastLog = yield this.auditLogRepository.findOne({
425
+ where: { entityType, entityId },
426
+ order: { createdAt: 'DESC' },
427
+ });
428
+ const previousHash = ((_a = lastLog === null || lastLog === void 0 ? void 0 : lastLog.hashChain) === null || _a === void 0 ? void 0 : _a.currentHash) || '';
429
+ const currentData = JSON.stringify(data);
430
+ const hashAlgorithm = ((_c = (_b = this.config) === null || _b === void 0 ? void 0 : _b.security) === null || _c === void 0 ? void 0 : _c.hashAlgorithm) || 'sha256';
431
+ const currentHash = (0, crypto_1.createHash)(hashAlgorithm).update(currentData + previousHash).digest('hex');
432
+ return {
433
+ previousHash,
434
+ currentHash,
435
+ algorithm: hashAlgorithm,
436
+ };
437
+ });
438
+ }
439
+ generateDescription(operation, entityType, entityId, changedFields) {
440
+ const operationText = {
441
+ [enums_1.AuditOperation.CREATE]: '创建',
442
+ [enums_1.AuditOperation.UPDATE]: '更新',
443
+ [enums_1.AuditOperation.DELETE]: '删除',
444
+ [enums_1.AuditOperation.RESTORE]: '恢复',
445
+ };
446
+ const text = operationText[operation] || operation;
447
+ if (operation === enums_1.AuditOperation.UPDATE && changedFields.length > 0) {
448
+ return `${text} ${entityType}(${entityId}), 变更字段: ${changedFields.join(', ')}`;
449
+ }
450
+ return `${text} ${entityType}(${entityId})`;
451
+ }
452
+ detectConflicts(currentEntity, targetValue) {
453
+ const conflicts = [];
454
+ for (const [key, targetVal] of Object.entries(targetValue)) {
455
+ const currentVal = currentEntity[key];
456
+ if (!this.deepEqual(currentVal, targetVal)) {
457
+ conflicts.push({
458
+ field: key,
459
+ reason: '字段值已被修改',
460
+ currentValue: currentVal,
461
+ targetValue: targetVal,
462
+ });
463
+ }
464
+ }
465
+ return conflicts;
466
+ }
467
+ logEntityChangeWithTemplate(data) {
468
+ return __awaiter(this, void 0, void 0, function* () {
469
+ var _a, _b, _c, _d, _e, _f, _g;
470
+ if (!this.auditStrategy.shouldRecord(data.entityType, data.operation)) {
471
+ return null;
472
+ }
473
+ const recordStrategy = this.auditStrategy.getRecordStrategy(data.entityType, data.operation);
474
+ if (recordStrategy === enums_1.RecordStrategy.DISABLED) {
475
+ return null;
476
+ }
477
+ const fieldFilter = this.auditStrategy.getFieldFilter(data.entityType);
478
+ const filteredOldValue = this.filterAndMaskFields(data.oldValue || {}, fieldFilter);
479
+ const filteredNewValue = this.filterAndMaskFields(data.newValue || {}, fieldFilter);
480
+ const changedFields = data.changedFields || this.calculateChangedFields(filteredOldValue, filteredNewValue);
481
+ const changedFieldPaths = this.calculateChangedFieldPaths(filteredOldValue, filteredNewValue);
482
+ const context = yield this.contextService.getCurrentContext();
483
+ const operationTemplateKey = data.operationTemplateKey || `${data.entityType}.${data.operation}`;
484
+ const descriptionParams = data.descriptionParams || Object.assign(Object.assign(Object.assign({}, filteredOldValue), filteredNewValue), { changedFields, changedFieldsCount: changedFields.length });
485
+ const auditLog = this.auditLogRepository.create({
486
+ entityType: data.entityType,
487
+ entityId: data.entityId,
488
+ operation: data.operation,
489
+ oldValue: filteredOldValue,
490
+ newValue: filteredNewValue,
491
+ changedFields,
492
+ changedFieldPaths: changedFieldPaths.join(','),
493
+ userId: context.userId || ((_a = data.metadata) === null || _a === void 0 ? void 0 : _a.userId),
494
+ username: context.username || ((_b = data.metadata) === null || _b === void 0 ? void 0 : _b.username),
495
+ requestId: context.requestId || ((_c = data.metadata) === null || _c === void 0 ? void 0 : _c.requestId),
496
+ requestIp: context.requestIp || ((_d = data.metadata) === null || _d === void 0 ? void 0 : _d.requestIp),
497
+ userAgent: context.userAgent || ((_e = data.metadata) === null || _e === void 0 ? void 0 : _e.userAgent),
498
+ description: this.generateDescription(data.operation, data.entityType, data.entityId, changedFields),
499
+ hashChain: ((_g = (_f = this.config) === null || _f === void 0 ? void 0 : _f.security) === null || _g === void 0 ? void 0 : _g.hashChainEnabled)
500
+ ? yield this.generateHashChain(data.entityType, data.entityId, filteredNewValue)
501
+ : undefined,
502
+ operationTemplateKey,
503
+ descriptionParams,
504
+ changeDetails: data.changeDetails,
505
+ rollbackActions: data.rollbackActions,
506
+ });
507
+ const savedLog = yield this.auditLogRepository.save(auditLog);
508
+ return savedLog;
509
+ });
510
+ }
511
+ logManualOperation(data) {
512
+ return __awaiter(this, void 0, void 0, function* () {
513
+ const context = yield this.contextService.getCurrentContext();
514
+ const manualLog = this.manualOperationRepository.create({
515
+ transactionId: data.transactionId,
516
+ operationTemplateKey: data.operationTemplateKey,
517
+ descriptionParams: data.descriptionParams,
518
+ userId: data.userId || context.userId,
519
+ username: data.username || context.username,
520
+ requestIp: data.requestIp || context.requestIp,
521
+ rollbackActions: data.rollbackActions || [],
522
+ });
523
+ return yield this.manualOperationRepository.save(manualLog);
524
+ });
525
+ }
526
+ withTransaction(fn, options) {
527
+ return __awaiter(this, void 0, void 0, function* () {
528
+ var _a;
529
+ const context = yield this.contextService.getCurrentContext();
530
+ const transaction = this.transactionRepository.create({
531
+ description: ((_a = options === null || options === void 0 ? void 0 : options.descriptionParams) === null || _a === void 0 ? void 0 : _a.description) || 'Transaction',
532
+ status: 'PENDING',
533
+ entities: [],
534
+ userId: (options === null || options === void 0 ? void 0 : options.userId) || context.userId,
535
+ username: (options === null || options === void 0 ? void 0 : options.username) || context.username,
536
+ requestIp: (options === null || options === void 0 ? void 0 : options.requestIp) || context.requestIp,
537
+ operationTemplateKey: options === null || options === void 0 ? void 0 : options.operationTemplateKey,
538
+ descriptionParams: options === null || options === void 0 ? void 0 : options.descriptionParams,
539
+ metadata: options === null || options === void 0 ? void 0 : options.metadata,
540
+ });
541
+ const savedTransaction = yield this.transactionRepository.save(transaction);
542
+ const transactionId = savedTransaction.id;
543
+ try {
544
+ yield this.contextService.setContext(Object.assign(Object.assign({}, context), { transactionId }));
545
+ const result = yield fn(transactionId);
546
+ yield this.transactionRepository.update(transactionId, {
547
+ status: 'COMMITTED',
548
+ completedAt: new Date(),
549
+ });
550
+ return result;
551
+ }
552
+ catch (error) {
553
+ yield this.transactionRepository.update(transactionId, {
554
+ status: 'ROLLED_BACK',
555
+ completedAt: new Date(),
556
+ });
557
+ throw error;
558
+ }
559
+ });
560
+ }
561
+ getCurrentTransactionId() {
562
+ return __awaiter(this, void 0, void 0, function* () {
563
+ const context = yield this.contextService.getCurrentContext();
564
+ return context.transactionId || null;
565
+ });
566
+ }
567
+ setCurrentTransactionId(transactionId) {
568
+ return __awaiter(this, void 0, void 0, function* () {
569
+ const context = yield this.contextService.getCurrentContext();
570
+ yield this.contextService.setContext(Object.assign(Object.assign({}, context), { transactionId }));
571
+ });
572
+ }
573
+ beginTransaction(userId, userName, ip, operationTemplateKey, descriptionParams) {
574
+ return __awaiter(this, void 0, void 0, function* () {
575
+ const context = yield this.contextService.getCurrentContext();
576
+ const transaction = this.transactionRepository.create({
577
+ description: (descriptionParams === null || descriptionParams === void 0 ? void 0 : descriptionParams.description) || 'Transaction',
578
+ status: 'pending',
579
+ entities: [],
580
+ userId: userId || context.userId,
581
+ username: userName || context.username,
582
+ requestIp: ip || context.requestIp,
583
+ operationTemplateKey,
584
+ descriptionParams,
585
+ });
586
+ const savedTransaction = yield this.transactionRepository.save(transaction);
587
+ return savedTransaction.id;
588
+ });
589
+ }
590
+ commitTransaction(transactionId) {
591
+ return __awaiter(this, void 0, void 0, function* () {
592
+ yield this.transactionRepository.update(transactionId, {
593
+ status: 'committed',
594
+ completedAt: new Date(),
595
+ });
596
+ });
597
+ }
598
+ rollbackTransaction(transactionId) {
599
+ return __awaiter(this, void 0, void 0, function* () {
600
+ yield this.transactionRepository.update(transactionId, {
601
+ status: 'rolled_back',
602
+ completedAt: new Date(),
603
+ });
604
+ });
605
+ }
606
+ };
607
+ exports.EntityAuditService = EntityAuditService;
608
+ exports.EntityAuditService = EntityAuditService = __decorate([
609
+ (0, common_1.Injectable)(),
610
+ __param(0, (0, typeorm_1.InjectRepository)(entities_1.EntityAuditLogEntity)),
611
+ __param(1, (0, typeorm_1.InjectRepository)(entities_1.EntityTransactionEntity)),
612
+ __param(2, (0, typeorm_1.InjectRepository)(entities_1.ManualOperationLogEntity)),
613
+ __param(3, (0, common_1.Inject)('AUDIT_ENTITY_MANAGER')),
614
+ __param(6, (0, common_1.Optional)()),
615
+ __param(6, (0, common_1.Inject)('AUDIT_STRATEGY')),
616
+ __param(7, (0, common_1.Optional)()),
617
+ __param(7, (0, common_1.Inject)('AUDIT_CONFIG')),
618
+ __param(8, (0, common_1.Optional)()),
619
+ __param(8, (0, common_1.Inject)('AUDIT_CONNECTION_NAME')),
620
+ __metadata("design:paramtypes", [typeorm_2.Repository,
621
+ typeorm_2.Repository,
622
+ typeorm_2.Repository,
623
+ typeorm_2.EntityManager,
624
+ audit_context_service_1.AuditContextService,
625
+ multi_database_service_1.MultiDatabaseService, Object, Object, String])
626
+ ], EntityAuditService);
@@ -0,0 +1,6 @@
1
+ export * from './audit-context.service';
2
+ export * from './audit-strategy.service';
3
+ export * from './entity-audit.service';
4
+ export * from './transaction-audit.service';
5
+ export * from './multi-database.service';
6
+ export * from './operation-description.service';
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./audit-context.service"), exports);
18
+ __exportStar(require("./audit-strategy.service"), exports);
19
+ __exportStar(require("./entity-audit.service"), exports);
20
+ __exportStar(require("./transaction-audit.service"), exports);
21
+ __exportStar(require("./multi-database.service"), exports);
22
+ __exportStar(require("./operation-description.service"), exports);
@@ -0,0 +1,10 @@
1
+ import { DataSource } from 'typeorm';
2
+ import { ModuleRef } from '@nestjs/core';
3
+ export declare class MultiDatabaseService {
4
+ private readonly moduleRef;
5
+ private readonly config?;
6
+ constructor(moduleRef: ModuleRef, config?: any);
7
+ getDataSource(connectionName?: string): Promise<DataSource>;
8
+ getMonitoredConnections(): string[];
9
+ getAuditConnection(): string;
10
+ }