@nest-omni/core 4.1.3-10 → 4.1.3-12
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/audit/audit.module.js +42 -2
- package/audit/controllers/audit.controller.d.ts +64 -0
- package/audit/controllers/audit.controller.js +50 -0
- package/audit/decorators/audit-action.decorator.d.ts +74 -0
- package/audit/decorators/audit-action.decorator.js +42 -0
- package/audit/decorators/audit-controller.decorator.d.ts +1 -1
- package/audit/decorators/audit-controller.decorator.js +2 -2
- package/audit/decorators/entity-audit.decorator.d.ts +78 -2
- package/audit/decorators/entity-audit.decorator.js +145 -4
- package/audit/decorators/index.d.ts +2 -0
- package/audit/decorators/index.js +2 -0
- package/audit/entities/audit-action-summary.entity.d.ts +23 -0
- package/audit/entities/audit-action-summary.entity.js +101 -0
- package/audit/entities/entity-audit-log.entity.d.ts +8 -0
- package/audit/entities/entity-audit-log.entity.js +54 -2
- package/audit/entities/entity-transaction.entity.d.ts +8 -2
- package/audit/entities/entity-transaction.entity.js +39 -3
- package/audit/entities/index.d.ts +3 -0
- package/audit/entities/index.js +3 -0
- package/audit/entities/manual-operation-log.entity.js +8 -1
- package/audit/enums/audit.enums.d.ts +22 -6
- package/audit/enums/audit.enums.js +27 -9
- package/audit/index.d.ts +4 -1
- package/audit/index.js +25 -2
- package/audit/interceptors/audit-action.interceptor.d.ts +38 -0
- package/audit/interceptors/audit-action.interceptor.js +215 -0
- package/audit/interceptors/index.d.ts +1 -0
- package/audit/interceptors/index.js +1 -0
- package/audit/interfaces/audit.interfaces.d.ts +145 -2
- package/audit/services/audit-action.service.d.ts +141 -0
- package/audit/services/audit-action.service.js +244 -0
- package/audit/services/audit-context.service.d.ts +82 -0
- package/audit/services/audit-context.service.js +170 -0
- package/audit/services/entity-audit.service.d.ts +174 -4
- package/audit/services/entity-audit.service.js +515 -14
- package/audit/services/index.d.ts +3 -0
- package/audit/services/index.js +3 -0
- package/audit/services/manual-audit-log.service.d.ts +24 -23
- package/audit/services/manual-audit-log.service.js +32 -53
- package/audit/services/operation-description.service.d.ts +13 -3
- package/audit/services/operation-description.service.js +161 -24
- package/audit/services/transaction-audit.service.js +3 -3
- package/audit/subscribers/entity-audit.subscriber.d.ts +4 -0
- package/audit/subscribers/entity-audit.subscriber.js +47 -0
- package/file-upload/controllers/file-access.controller.d.ts +23 -0
- package/file-upload/controllers/file-access.controller.js +128 -0
- package/file-upload/decorators/csv-data.decorator.d.ts +44 -0
- package/file-upload/decorators/csv-data.decorator.js +131 -0
- package/file-upload/decorators/excel-data.decorator.d.ts +44 -0
- package/file-upload/decorators/excel-data.decorator.js +125 -0
- package/file-upload/decorators/file-upload.decorator.d.ts +83 -0
- package/file-upload/decorators/file-upload.decorator.js +172 -0
- package/file-upload/decorators/index.d.ts +4 -0
- package/file-upload/decorators/index.js +20 -0
- package/file-upload/decorators/process.decorator.d.ts +40 -0
- package/file-upload/decorators/process.decorator.js +52 -0
- package/file-upload/dto/create-file.dto.d.ts +24 -0
- package/file-upload/dto/create-file.dto.js +112 -0
- package/file-upload/dto/find-files.dto.d.ts +15 -0
- package/file-upload/dto/find-files.dto.js +76 -0
- package/file-upload/dto/index.d.ts +4 -0
- package/file-upload/dto/index.js +20 -0
- package/file-upload/dto/pagination.dto.d.ts +7 -0
- package/file-upload/dto/pagination.dto.js +39 -0
- package/file-upload/dto/update-file.dto.d.ts +16 -0
- package/file-upload/dto/update-file.dto.js +71 -0
- package/file-upload/entities/file-metadata.entity.d.ts +22 -0
- package/file-upload/entities/file-metadata.entity.js +84 -0
- package/file-upload/entities/file.entity.d.ts +129 -0
- package/file-upload/entities/file.entity.js +384 -0
- package/file-upload/entities/index.d.ts +2 -0
- package/file-upload/entities/index.js +18 -0
- package/file-upload/enums/file-type.enum.d.ts +72 -0
- package/file-upload/enums/file-type.enum.js +212 -0
- package/file-upload/exceptions/file-upload.exception.d.ts +57 -0
- package/file-upload/exceptions/file-upload.exception.js +120 -0
- package/file-upload/exceptions/index.d.ts +1 -0
- package/file-upload/exceptions/index.js +17 -0
- package/file-upload/file-upload.module.d.ts +89 -0
- package/file-upload/file-upload.module.js +264 -0
- package/file-upload/index.d.ts +26 -0
- package/file-upload/index.js +59 -0
- package/file-upload/interceptors/file-upload.interceptor.d.ts +48 -0
- package/file-upload/interceptors/file-upload.interceptor.js +434 -0
- package/file-upload/interceptors/index.d.ts +1 -0
- package/file-upload/interceptors/index.js +17 -0
- package/file-upload/interfaces/custom-file-type.interface.d.ts +72 -0
- package/file-upload/interfaces/custom-file-type.interface.js +2 -0
- package/file-upload/interfaces/file-buffer.interface.d.ts +72 -0
- package/file-upload/interfaces/file-buffer.interface.js +2 -0
- package/file-upload/interfaces/file-entity.interface.d.ts +142 -0
- package/file-upload/interfaces/file-entity.interface.js +28 -0
- package/file-upload/interfaces/file-metadata.interface.d.ts +21 -0
- package/file-upload/interfaces/file-metadata.interface.js +2 -0
- package/file-upload/interfaces/file-upload-options.interface.d.ts +117 -0
- package/file-upload/interfaces/file-upload-options.interface.js +2 -0
- package/file-upload/interfaces/index.d.ts +7 -0
- package/file-upload/interfaces/index.js +24 -0
- package/file-upload/interfaces/storage-provider.interface.d.ts +239 -0
- package/file-upload/interfaces/storage-provider.interface.js +2 -0
- package/file-upload/interfaces/upload-options.interface.d.ts +19 -0
- package/file-upload/interfaces/upload-options.interface.js +2 -0
- package/file-upload/providers/index.d.ts +2 -0
- package/file-upload/providers/index.js +18 -0
- package/file-upload/providers/local-storage.provider.d.ts +98 -0
- package/file-upload/providers/local-storage.provider.js +484 -0
- package/file-upload/providers/s3-storage.provider.d.ts +87 -0
- package/file-upload/providers/s3-storage.provider.js +455 -0
- package/file-upload/services/file-signature-validator.service.d.ts +118 -0
- package/file-upload/services/file-signature-validator.service.js +376 -0
- package/file-upload/services/file.service.d.ts +190 -0
- package/file-upload/services/file.service.js +609 -0
- package/file-upload/services/index.d.ts +4 -0
- package/file-upload/services/index.js +20 -0
- package/file-upload/services/malicious-file-detector.service.d.ts +274 -0
- package/file-upload/services/malicious-file-detector.service.js +1035 -0
- package/file-upload/services/mime-registry.service.d.ts +47 -0
- package/file-upload/services/mime-registry.service.js +167 -0
- package/file-upload/utils/checksum.util.d.ts +28 -0
- package/file-upload/utils/checksum.util.js +65 -0
- package/file-upload/utils/dynamic-import.util.d.ts +50 -0
- package/file-upload/utils/dynamic-import.util.js +144 -0
- package/file-upload/utils/filename.util.d.ts +59 -0
- package/file-upload/utils/filename.util.js +184 -0
- package/file-upload/utils/filepath.util.d.ts +70 -0
- package/file-upload/utils/filepath.util.js +152 -0
- package/file-upload/utils/index.d.ts +4 -0
- package/file-upload/utils/index.js +20 -0
- package/http-client/http-client.module.js +1 -5
- package/index.d.ts +3 -1
- package/index.js +4 -1
- package/package.json +4 -5
- package/redis-lock/lock-heartbeat.service.d.ts +2 -2
- package/redis-lock/lock-heartbeat.service.js +4 -4
- package/redis-lock/redis-lock.service.d.ts +18 -0
- package/redis-lock/redis-lock.service.js +38 -8
- package/setup/bootstrap.setup.d.ts +1 -0
- package/setup/bootstrap.setup.js +1 -0
- package/setup/schedule.decorator.js +18 -8
- package/shared/index.d.ts +1 -1
- package/shared/index.js +1 -1
- package/shared/{serviceRegistryModule.js → service-registry.module.js} +9 -16
- package/shared/services/index.d.ts +0 -1
- package/shared/services/index.js +0 -1
- package/transaction/__tests__/mocks.d.ts +9 -0
- package/transaction/__tests__/mocks.js +33 -0
- package/transaction/base-service-transaction.d.ts +99 -0
- package/transaction/base-service-transaction.js +286 -0
- package/transaction/cls-compatibility.service.d.ts +55 -0
- package/transaction/cls-compatibility.service.js +127 -0
- package/transaction/data-source-registry.d.ts +91 -0
- package/transaction/data-source-registry.js +349 -0
- package/transaction/database-adapter.d.ts +44 -0
- package/transaction/database-adapter.js +240 -0
- package/transaction/decorators/entity-datasource.decorator.d.ts +62 -0
- package/transaction/decorators/entity-datasource.decorator.js +105 -0
- package/transaction/index.d.ts +14 -0
- package/transaction/index.js +57 -0
- package/transaction/logging-transactional.interceptor.d.ts +18 -0
- package/transaction/logging-transactional.interceptor.js +163 -0
- package/transaction/transaction-context.service.d.ts +137 -0
- package/transaction/transaction-context.service.js +411 -0
- package/transaction/transaction-manager.d.ts +230 -0
- package/transaction/transaction-manager.js +1001 -0
- package/transaction/transaction-synchronization.d.ts +171 -0
- package/transaction/transaction-synchronization.js +380 -0
- package/transaction/transaction.errors.d.ts +91 -0
- package/transaction/transaction.errors.js +206 -0
- package/transaction/transaction.module.d.ts +30 -0
- package/transaction/transaction.module.js +98 -0
- package/transaction/transactional.decorator.d.ts +82 -0
- package/transaction/transactional.decorator.js +319 -0
- package/transaction/typeorm-module-wrapper.d.ts +96 -0
- package/transaction/typeorm-module-wrapper.js +197 -0
- package/validators/file-mimetype.validator.d.ts +0 -2
- package/validators/file-mimetype.validator.js +4 -6
- package/validators/is-exists.validator.d.ts +2 -5
- package/validators/is-exists.validator.js +4 -6
- package/validators/is-unique.validator.d.ts +2 -5
- package/validators/is-unique.validator.js +6 -11
- package/shared/services/validator.service.d.ts +0 -3
- package/shared/services/validator.service.js +0 -20
- /package/shared/{serviceRegistryModule.d.ts → service-registry.module.d.ts} +0 -0
|
@@ -32,18 +32,21 @@ const audit_context_service_1 = require("./audit-context.service");
|
|
|
32
32
|
const audit_strategy_service_1 = require("./audit-strategy.service");
|
|
33
33
|
const multi_database_service_1 = require("./multi-database.service");
|
|
34
34
|
const dto_1 = require("../../common/dto");
|
|
35
|
+
const entity_audit_decorator_1 = require("../decorators/entity-audit.decorator");
|
|
35
36
|
/**
|
|
36
37
|
* 实体审计服务
|
|
37
38
|
*/
|
|
38
39
|
let EntityAuditService = class EntityAuditService {
|
|
39
|
-
constructor(auditLogRepository, transactionRepository, entityManager, contextService, multiDbService, auditStrategy = new audit_strategy_service_1.DefaultAuditStrategy(), config, auditConnectionName) {
|
|
40
|
+
constructor(auditLogRepository, transactionRepository, manualOperationRepository, entityManager, contextService, multiDbService, auditStrategy = new audit_strategy_service_1.DefaultAuditStrategy(), config, auditConnectionName, actionSummaryRepository) {
|
|
40
41
|
this.auditLogRepository = auditLogRepository;
|
|
41
42
|
this.transactionRepository = transactionRepository;
|
|
43
|
+
this.manualOperationRepository = manualOperationRepository;
|
|
42
44
|
this.entityManager = entityManager;
|
|
43
45
|
this.contextService = contextService;
|
|
44
46
|
this.multiDbService = multiDbService;
|
|
45
47
|
this.auditStrategy = auditStrategy;
|
|
46
48
|
this.config = config;
|
|
49
|
+
this.actionSummaryRepository = actionSummaryRepository;
|
|
47
50
|
this.auditConnectionName = auditConnectionName || 'default';
|
|
48
51
|
}
|
|
49
52
|
/**
|
|
@@ -89,6 +92,10 @@ let EntityAuditService = class EntityAuditService {
|
|
|
89
92
|
hashChain: ((_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.security) === null || _b === void 0 ? void 0 : _b.hashChainEnabled)
|
|
90
93
|
? yield this.generateHashChain(entityType, entityId, filteredNewValue)
|
|
91
94
|
: undefined,
|
|
95
|
+
// 新增:审计动作关联字段
|
|
96
|
+
auditActionId: metadata.auditActionId,
|
|
97
|
+
auditActionName: metadata.auditActionName,
|
|
98
|
+
sequenceInAction: metadata.sequenceInAction || 0,
|
|
92
99
|
});
|
|
93
100
|
// 保存变更日志
|
|
94
101
|
const savedLog = yield this.auditLogRepository.save(auditLog);
|
|
@@ -337,7 +344,7 @@ let EntityAuditService = class EntityAuditService {
|
|
|
337
344
|
/**
|
|
338
345
|
* 深度差异比较
|
|
339
346
|
*/
|
|
340
|
-
deepDiff(oldObj, newObj, path = []) {
|
|
347
|
+
deepDiff(oldObj, newObj, path = [], visited = new WeakSet()) {
|
|
341
348
|
var _a;
|
|
342
349
|
const changes = [];
|
|
343
350
|
const maxDepth = ((_a = this.config) === null || _a === void 0 ? void 0 : _a.maxDiffDepth) || 5;
|
|
@@ -357,6 +364,19 @@ let EntityAuditService = class EntityAuditService {
|
|
|
357
364
|
}
|
|
358
365
|
return changes;
|
|
359
366
|
}
|
|
367
|
+
// 检测循环引用
|
|
368
|
+
if (visited.has(oldObj)) {
|
|
369
|
+
// 检测到循环引用,停止递归
|
|
370
|
+
return changes;
|
|
371
|
+
}
|
|
372
|
+
visited.add(oldObj);
|
|
373
|
+
// 如果newObj也是对象,也加入访问集合
|
|
374
|
+
if (typeof newObj === 'object' && newObj !== null) {
|
|
375
|
+
if (visited.has(newObj)) {
|
|
376
|
+
return changes;
|
|
377
|
+
}
|
|
378
|
+
visited.add(newObj);
|
|
379
|
+
}
|
|
360
380
|
// 处理数组
|
|
361
381
|
if (Array.isArray(oldObj)) {
|
|
362
382
|
if (!Array.isArray(newObj)) {
|
|
@@ -378,7 +398,7 @@ let EntityAuditService = class EntityAuditService {
|
|
|
378
398
|
return changes;
|
|
379
399
|
}
|
|
380
400
|
for (let i = 0; i < oldObj.length; i++) {
|
|
381
|
-
changes.push(...this.deepDiff(oldObj[i], newObj[i], [...path, i.toString()]));
|
|
401
|
+
changes.push(...this.deepDiff(oldObj[i], newObj[i], [...path, i.toString()], visited));
|
|
382
402
|
}
|
|
383
403
|
return changes;
|
|
384
404
|
}
|
|
@@ -404,7 +424,7 @@ let EntityAuditService = class EntityAuditService {
|
|
|
404
424
|
});
|
|
405
425
|
}
|
|
406
426
|
else {
|
|
407
|
-
changes.push(...this.deepDiff(oldVal, newVal, [...path, key]));
|
|
427
|
+
changes.push(...this.deepDiff(oldVal, newVal, [...path, key], visited));
|
|
408
428
|
}
|
|
409
429
|
}
|
|
410
430
|
return changes;
|
|
@@ -521,9 +541,15 @@ let EntityAuditService = class EntityAuditService {
|
|
|
521
541
|
});
|
|
522
542
|
}
|
|
523
543
|
/**
|
|
524
|
-
*
|
|
544
|
+
* 生成描述(支持多语言)
|
|
545
|
+
* @param operation 操作类型
|
|
546
|
+
* @param entityType 实体类型
|
|
547
|
+
* @param entityId 实体ID
|
|
548
|
+
* @param changedFields 变更字段列表
|
|
549
|
+
* @param language 语言(默认中文)
|
|
550
|
+
* @param entityClass 实体类(可选,用于获取实体标签)
|
|
525
551
|
*/
|
|
526
|
-
generateDescription(operation, entityType, entityId, changedFields) {
|
|
552
|
+
generateDescription(operation, entityType, entityId, changedFields, language = 'zh', entityClass) {
|
|
527
553
|
const operationText = {
|
|
528
554
|
[enums_1.AuditOperation.CREATE]: '创建',
|
|
529
555
|
[enums_1.AuditOperation.UPDATE]: '更新',
|
|
@@ -531,10 +557,32 @@ let EntityAuditService = class EntityAuditService {
|
|
|
531
557
|
[enums_1.AuditOperation.RESTORE]: '恢复',
|
|
532
558
|
};
|
|
533
559
|
const text = operationText[operation] || operation;
|
|
560
|
+
// 尝试获取实体的多语言标签
|
|
561
|
+
let entityLabel = entityType;
|
|
562
|
+
if (entityClass) {
|
|
563
|
+
try {
|
|
564
|
+
entityLabel = (0, entity_audit_decorator_1.getEntityLabel)(entityClass, language);
|
|
565
|
+
}
|
|
566
|
+
catch (error) {
|
|
567
|
+
// 忽略错误,使用默认的 entityType
|
|
568
|
+
}
|
|
569
|
+
}
|
|
534
570
|
if (operation === enums_1.AuditOperation.UPDATE && changedFields.length > 0) {
|
|
535
|
-
|
|
571
|
+
// 对于更新操作,显示哪些字段发生了变化
|
|
572
|
+
const fieldLabels = changedFields.map(field => {
|
|
573
|
+
if (entityClass) {
|
|
574
|
+
try {
|
|
575
|
+
return (0, entity_audit_decorator_1.getFieldLabel)(entityClass, field, language);
|
|
576
|
+
}
|
|
577
|
+
catch (error) {
|
|
578
|
+
return field;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return field;
|
|
582
|
+
});
|
|
583
|
+
return `${text}${entityLabel}(${entityId}), 变更字段: ${fieldLabels.join('、')}`;
|
|
536
584
|
}
|
|
537
|
-
return `${text}
|
|
585
|
+
return `${text}${entityLabel}(${entityId})`;
|
|
538
586
|
}
|
|
539
587
|
/**
|
|
540
588
|
* 检测冲突
|
|
@@ -554,22 +602,475 @@ let EntityAuditService = class EntityAuditService {
|
|
|
554
602
|
}
|
|
555
603
|
return conflicts;
|
|
556
604
|
}
|
|
605
|
+
// ========== 新增方法:支持模板和手动操作 ==========
|
|
606
|
+
/**
|
|
607
|
+
* 增强的记录实体变更(支持模板和结构化变更详情)
|
|
608
|
+
* @param data 审计数据
|
|
609
|
+
* @returns 审计日志实体
|
|
610
|
+
*/
|
|
611
|
+
logEntityChangeWithTemplate(data) {
|
|
612
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
613
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
614
|
+
// 检查是否应该记录
|
|
615
|
+
if (!this.auditStrategy.shouldRecord(data.entityType, data.operation)) {
|
|
616
|
+
return null;
|
|
617
|
+
}
|
|
618
|
+
// 获取记录策略
|
|
619
|
+
const recordStrategy = this.auditStrategy.getRecordStrategy(data.entityType, data.operation);
|
|
620
|
+
if (recordStrategy === enums_1.RecordStrategy.DISABLED) {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
// 获取字段过滤器
|
|
624
|
+
const fieldFilter = this.auditStrategy.getFieldFilter(data.entityType);
|
|
625
|
+
// 过滤和脱敏字段
|
|
626
|
+
const filteredOldValue = this.filterAndMaskFields(data.oldValue || {}, fieldFilter);
|
|
627
|
+
const filteredNewValue = this.filterAndMaskFields(data.newValue || {}, fieldFilter);
|
|
628
|
+
// 计算变更字段
|
|
629
|
+
const changedFields = data.changedFields || this.calculateChangedFields(filteredOldValue, filteredNewValue);
|
|
630
|
+
const changedFieldPaths = this.calculateChangedFieldPaths(filteredOldValue, filteredNewValue);
|
|
631
|
+
// 获取上下文信息
|
|
632
|
+
const context = yield this.contextService.getCurrentContext();
|
|
633
|
+
// 如果没有提供操作模板键,使用默认模板
|
|
634
|
+
const operationTemplateKey = data.operationTemplateKey || `${data.entityType}.${data.operation}`;
|
|
635
|
+
// 如果没有提供描述参数,使用实体数据
|
|
636
|
+
const descriptionParams = data.descriptionParams || Object.assign(Object.assign(Object.assign({}, filteredOldValue), filteredNewValue), { changedFields, changedFieldsCount: changedFields.length });
|
|
637
|
+
// 创建审计日志
|
|
638
|
+
const auditLog = this.auditLogRepository.create({
|
|
639
|
+
entityType: data.entityType,
|
|
640
|
+
entityId: data.entityId,
|
|
641
|
+
operation: data.operation,
|
|
642
|
+
oldValue: filteredOldValue,
|
|
643
|
+
newValue: filteredNewValue,
|
|
644
|
+
changedFields,
|
|
645
|
+
changedFieldPaths: changedFieldPaths.join(','),
|
|
646
|
+
userId: context.userId || ((_a = data.metadata) === null || _a === void 0 ? void 0 : _a.userId),
|
|
647
|
+
username: context.username || ((_b = data.metadata) === null || _b === void 0 ? void 0 : _b.username),
|
|
648
|
+
requestId: context.requestId || ((_c = data.metadata) === null || _c === void 0 ? void 0 : _c.requestId),
|
|
649
|
+
requestIp: context.requestIp || ((_d = data.metadata) === null || _d === void 0 ? void 0 : _d.requestIp),
|
|
650
|
+
userAgent: context.userAgent || ((_e = data.metadata) === null || _e === void 0 ? void 0 : _e.userAgent),
|
|
651
|
+
description: this.generateDescription(data.operation, data.entityType, data.entityId, changedFields),
|
|
652
|
+
hashChain: ((_g = (_f = this.config) === null || _f === void 0 ? void 0 : _f.security) === null || _g === void 0 ? void 0 : _g.hashChainEnabled)
|
|
653
|
+
? yield this.generateHashChain(data.entityType, data.entityId, filteredNewValue)
|
|
654
|
+
: undefined,
|
|
655
|
+
// 新增字段
|
|
656
|
+
operationTemplateKey,
|
|
657
|
+
descriptionParams,
|
|
658
|
+
changeDetails: data.changeDetails,
|
|
659
|
+
rollbackActions: data.rollbackActions,
|
|
660
|
+
});
|
|
661
|
+
// 保存变更日志
|
|
662
|
+
const savedLog = yield this.auditLogRepository.save(auditLog);
|
|
663
|
+
return savedLog;
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* 记录手动操作
|
|
668
|
+
* @param data 手动操作数据
|
|
669
|
+
* @returns 手动操作日志实体
|
|
670
|
+
*/
|
|
671
|
+
logManualOperation(data) {
|
|
672
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
673
|
+
// 获取上下文信息
|
|
674
|
+
const context = yield this.contextService.getCurrentContext();
|
|
675
|
+
const manualLog = this.manualOperationRepository.create({
|
|
676
|
+
transactionId: data.transactionId,
|
|
677
|
+
operationTemplateKey: data.operationTemplateKey,
|
|
678
|
+
descriptionParams: data.descriptionParams,
|
|
679
|
+
userId: data.userId || context.userId,
|
|
680
|
+
username: data.username || context.username,
|
|
681
|
+
requestIp: data.requestIp || context.requestIp,
|
|
682
|
+
rollbackActions: data.rollbackActions || [],
|
|
683
|
+
});
|
|
684
|
+
return yield this.manualOperationRepository.save(manualLog);
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* 在事务中执行操作
|
|
689
|
+
* @param fn 执行函数
|
|
690
|
+
* @param options 事务选项
|
|
691
|
+
* @returns 执���结果
|
|
692
|
+
*/
|
|
693
|
+
withTransaction(fn, options) {
|
|
694
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
695
|
+
var _a;
|
|
696
|
+
// 获取上下文信息
|
|
697
|
+
const context = yield this.contextService.getCurrentContext();
|
|
698
|
+
// 创建事务
|
|
699
|
+
const transaction = this.transactionRepository.create({
|
|
700
|
+
description: ((_a = options === null || options === void 0 ? void 0 : options.descriptionParams) === null || _a === void 0 ? void 0 : _a.description) || 'Transaction',
|
|
701
|
+
status: 'PENDING',
|
|
702
|
+
entities: [],
|
|
703
|
+
userId: (options === null || options === void 0 ? void 0 : options.userId) || context.userId,
|
|
704
|
+
username: (options === null || options === void 0 ? void 0 : options.username) || context.username,
|
|
705
|
+
requestIp: (options === null || options === void 0 ? void 0 : options.requestIp) || context.requestIp,
|
|
706
|
+
operationTemplateKey: options === null || options === void 0 ? void 0 : options.operationTemplateKey,
|
|
707
|
+
descriptionParams: options === null || options === void 0 ? void 0 : options.descriptionParams,
|
|
708
|
+
metadata: options === null || options === void 0 ? void 0 : options.metadata,
|
|
709
|
+
});
|
|
710
|
+
const savedTransaction = yield this.transactionRepository.save(transaction);
|
|
711
|
+
const transactionId = savedTransaction.id;
|
|
712
|
+
try {
|
|
713
|
+
// 设置事务ID到上下文
|
|
714
|
+
yield this.contextService.setContext(Object.assign(Object.assign({}, context), { transactionId }));
|
|
715
|
+
// 执行操作
|
|
716
|
+
const result = yield fn(transactionId);
|
|
717
|
+
// 提交事务
|
|
718
|
+
yield this.transactionRepository.update(transactionId, {
|
|
719
|
+
status: 'COMMITTED',
|
|
720
|
+
completedAt: new Date(),
|
|
721
|
+
});
|
|
722
|
+
return result;
|
|
723
|
+
}
|
|
724
|
+
catch (error) {
|
|
725
|
+
// 回滚事务
|
|
726
|
+
yield this.transactionRepository.update(transactionId, {
|
|
727
|
+
status: 'ROLLED_BACK',
|
|
728
|
+
completedAt: new Date(),
|
|
729
|
+
});
|
|
730
|
+
throw error;
|
|
731
|
+
}
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* 获取当前事务ID
|
|
736
|
+
* @returns 当前事务ID或null
|
|
737
|
+
*/
|
|
738
|
+
getCurrentTransactionId() {
|
|
739
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
740
|
+
const context = yield this.contextService.getCurrentContext();
|
|
741
|
+
return context.transactionId || null;
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* 设置当前事务ID
|
|
746
|
+
* @param transactionId 事务ID
|
|
747
|
+
*/
|
|
748
|
+
setCurrentTransactionId(transactionId) {
|
|
749
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
750
|
+
const context = yield this.contextService.getCurrentContext();
|
|
751
|
+
yield this.contextService.setContext(Object.assign(Object.assign({}, context), { transactionId }));
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* 开始事务
|
|
756
|
+
* @param userId 用户ID
|
|
757
|
+
* @param userName 用户名
|
|
758
|
+
* @param ip IP地址
|
|
759
|
+
* @param operationTemplateKey 操作模板键
|
|
760
|
+
* @param descriptionParams 描述参数
|
|
761
|
+
* @returns 事务ID
|
|
762
|
+
*/
|
|
763
|
+
beginTransaction(userId, userName, ip, operationTemplateKey, descriptionParams) {
|
|
764
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
765
|
+
// 获取上下文信息
|
|
766
|
+
const context = yield this.contextService.getCurrentContext();
|
|
767
|
+
// 创建事务
|
|
768
|
+
const transaction = this.transactionRepository.create({
|
|
769
|
+
description: (descriptionParams === null || descriptionParams === void 0 ? void 0 : descriptionParams.description) || 'Transaction',
|
|
770
|
+
status: 'pending',
|
|
771
|
+
entities: [],
|
|
772
|
+
userId: userId || context.userId,
|
|
773
|
+
username: userName || context.username,
|
|
774
|
+
requestIp: ip || context.requestIp,
|
|
775
|
+
operationTemplateKey,
|
|
776
|
+
descriptionParams,
|
|
777
|
+
});
|
|
778
|
+
const savedTransaction = yield this.transactionRepository.save(transaction);
|
|
779
|
+
return savedTransaction.id;
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* 提交事务
|
|
784
|
+
* @param transactionId 事务ID
|
|
785
|
+
*/
|
|
786
|
+
commitTransaction(transactionId) {
|
|
787
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
788
|
+
yield this.transactionRepository.update(transactionId, {
|
|
789
|
+
status: 'committed',
|
|
790
|
+
completedAt: new Date(),
|
|
791
|
+
});
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* 回滚事务
|
|
796
|
+
* @param transactionId 事务ID
|
|
797
|
+
*/
|
|
798
|
+
rollbackTransaction(transactionId) {
|
|
799
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
800
|
+
yield this.transactionRepository.update(transactionId, {
|
|
801
|
+
status: 'rolled_back',
|
|
802
|
+
completedAt: new Date(),
|
|
803
|
+
});
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* 查询审计动作汇总记录
|
|
808
|
+
*/
|
|
809
|
+
getAuditActions(options) {
|
|
810
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
811
|
+
if (!this.actionSummaryRepository) {
|
|
812
|
+
throw new Error('AuditActionSummaryEntity repository not available');
|
|
813
|
+
}
|
|
814
|
+
const { page = 1, limit = 20 } = options;
|
|
815
|
+
const queryBuilder = this.actionSummaryRepository.createQueryBuilder('action');
|
|
816
|
+
// 应用过滤条件
|
|
817
|
+
if (options.userId) {
|
|
818
|
+
queryBuilder.andWhere('action.userId = :userId', { userId: options.userId });
|
|
819
|
+
}
|
|
820
|
+
if (options.username) {
|
|
821
|
+
queryBuilder.andWhere('action.username LIKE :username', { username: `%${options.username}%` });
|
|
822
|
+
}
|
|
823
|
+
if (options.actionName) {
|
|
824
|
+
queryBuilder.andWhere('action.actionName = :actionName', { actionName: options.actionName });
|
|
825
|
+
}
|
|
826
|
+
if (options.success !== undefined) {
|
|
827
|
+
queryBuilder.andWhere('action.success = :success', { success: options.success });
|
|
828
|
+
}
|
|
829
|
+
if (options.startTime && options.endTime) {
|
|
830
|
+
queryBuilder.andWhere('action.createdAt BETWEEN :startTime AND :endTime', {
|
|
831
|
+
startTime: options.startTime,
|
|
832
|
+
endTime: options.endTime,
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
// 排序
|
|
836
|
+
queryBuilder.orderBy('action.createdAt', 'DESC');
|
|
837
|
+
// 分页
|
|
838
|
+
const skip = (page - 1) * limit;
|
|
839
|
+
queryBuilder.skip(skip).take(limit);
|
|
840
|
+
const [data, total] = yield queryBuilder.getManyAndCount();
|
|
841
|
+
const pageOptionsDto = new dto_1.PageOptionsDto();
|
|
842
|
+
Object.assign(pageOptionsDto, { page, pageSize: limit });
|
|
843
|
+
const pageMetaDto = new dto_1.PageMetaDto({
|
|
844
|
+
pageOptionsDto,
|
|
845
|
+
itemCount: total,
|
|
846
|
+
});
|
|
847
|
+
return new dto_1.PageDto(data, pageMetaDto);
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* 查询单个审计动作的详细信息(包含关联的实体变更)
|
|
852
|
+
*/
|
|
853
|
+
getAuditActionDetail(actionId) {
|
|
854
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
855
|
+
if (!this.actionSummaryRepository) {
|
|
856
|
+
throw new Error('AuditActionSummaryEntity repository not available');
|
|
857
|
+
}
|
|
858
|
+
// 查询汇总信息
|
|
859
|
+
const summary = yield this.actionSummaryRepository.findOne({
|
|
860
|
+
where: { id: actionId },
|
|
861
|
+
});
|
|
862
|
+
if (!summary) {
|
|
863
|
+
throw new Error(`Audit action with id ${actionId} not found`);
|
|
864
|
+
}
|
|
865
|
+
// 查询关联的实体变更
|
|
866
|
+
const entityChanges = yield this.auditLogRepository.find({
|
|
867
|
+
where: { auditActionId: actionId },
|
|
868
|
+
order: { sequenceInAction: 'ASC' },
|
|
869
|
+
});
|
|
870
|
+
// 分析每个实体的详细变化
|
|
871
|
+
const detailedChanges = entityChanges.map((change) => {
|
|
872
|
+
const fieldChanges = this.analyzeFieldChanges(change.oldValue || {}, change.newValue || {}, change.operation);
|
|
873
|
+
return {
|
|
874
|
+
id: change.id,
|
|
875
|
+
entityType: change.entityType,
|
|
876
|
+
entityId: change.entityId,
|
|
877
|
+
operation: change.operation,
|
|
878
|
+
operationLabel: this.getOperationLabel(change.operation),
|
|
879
|
+
description: change.description,
|
|
880
|
+
sequenceInAction: change.sequenceInAction,
|
|
881
|
+
createdAt: change.createdAt,
|
|
882
|
+
// 数据快照
|
|
883
|
+
oldValue: change.oldValue,
|
|
884
|
+
newValue: change.newValue,
|
|
885
|
+
// 详细的字段变化分析
|
|
886
|
+
fieldChanges,
|
|
887
|
+
changedFieldsCount: fieldChanges.length,
|
|
888
|
+
changedFields: change.changedFields,
|
|
889
|
+
};
|
|
890
|
+
});
|
|
891
|
+
// 统计信息
|
|
892
|
+
const statistics = {
|
|
893
|
+
totalChanges: entityChanges.length,
|
|
894
|
+
totalFieldChanges: detailedChanges.reduce((sum, c) => sum + c.changedFieldsCount, 0),
|
|
895
|
+
byEntityType: this.groupBy(entityChanges, 'entityType'),
|
|
896
|
+
byOperation: this.groupBy(entityChanges, 'operation'),
|
|
897
|
+
operationSequence: entityChanges.map((c) => ({
|
|
898
|
+
sequence: c.sequenceInAction,
|
|
899
|
+
entityType: c.entityType,
|
|
900
|
+
operation: c.operation,
|
|
901
|
+
})),
|
|
902
|
+
};
|
|
903
|
+
return {
|
|
904
|
+
// 汇总信息
|
|
905
|
+
summary: {
|
|
906
|
+
id: summary.id,
|
|
907
|
+
actionName: summary.actionName,
|
|
908
|
+
operationTemplateKey: summary.operationTemplateKey,
|
|
909
|
+
description: summary.description,
|
|
910
|
+
descriptionParams: summary.descriptionParams,
|
|
911
|
+
success: summary.success,
|
|
912
|
+
errorMessage: summary.errorMessage,
|
|
913
|
+
duration: summary.duration,
|
|
914
|
+
userId: summary.userId,
|
|
915
|
+
username: summary.username,
|
|
916
|
+
requestId: summary.requestId,
|
|
917
|
+
requestIp: summary.requestIp,
|
|
918
|
+
userAgent: summary.userAgent,
|
|
919
|
+
entityChangesCount: summary.entityChangesCount,
|
|
920
|
+
entityTypes: summary.entityTypes,
|
|
921
|
+
operationStats: summary.operationStats,
|
|
922
|
+
createdAt: summary.createdAt,
|
|
923
|
+
metadata: summary.metadata,
|
|
924
|
+
},
|
|
925
|
+
// 原始实体变更记录
|
|
926
|
+
entityChanges,
|
|
927
|
+
// 详细的变化分析
|
|
928
|
+
detailedChanges,
|
|
929
|
+
// 统计信息
|
|
930
|
+
statistics,
|
|
931
|
+
};
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* 分析字段变化(支持多语言)
|
|
936
|
+
* @param oldValue 旧值
|
|
937
|
+
* @param newValue 新值
|
|
938
|
+
* @param operation 操作类型
|
|
939
|
+
* @param entityClass 实体类(可选,用于获取字段和值的多语言标签)
|
|
940
|
+
* @param language 语言(默认中文)
|
|
941
|
+
*/
|
|
942
|
+
analyzeFieldChanges(oldValue, newValue, operation, entityClass, language = 'zh') {
|
|
943
|
+
const changes = [];
|
|
944
|
+
const allFields = new Set([...Object.keys(oldValue), ...Object.keys(newValue)]);
|
|
945
|
+
for (const field of allFields) {
|
|
946
|
+
const oldVal = oldValue[field];
|
|
947
|
+
const newVal = newValue[field];
|
|
948
|
+
let changeType;
|
|
949
|
+
if (operation === enums_1.AuditOperation.CREATE) {
|
|
950
|
+
// 创建操作,所有字段都是新增
|
|
951
|
+
changeType = 'added';
|
|
952
|
+
}
|
|
953
|
+
else if (operation === enums_1.AuditOperation.DELETE) {
|
|
954
|
+
// 删除操作,所有字段都是移除
|
|
955
|
+
changeType = 'removed';
|
|
956
|
+
}
|
|
957
|
+
else {
|
|
958
|
+
// 更新操作,判断字段变化
|
|
959
|
+
if (oldVal === undefined && newVal !== undefined) {
|
|
960
|
+
changeType = 'added';
|
|
961
|
+
}
|
|
962
|
+
else if (oldVal !== undefined && newVal === undefined) {
|
|
963
|
+
changeType = 'removed';
|
|
964
|
+
}
|
|
965
|
+
else if (JSON.stringify(oldVal) !== JSON.stringify(newVal)) {
|
|
966
|
+
changeType = 'modified';
|
|
967
|
+
}
|
|
968
|
+
else {
|
|
969
|
+
changeType = 'unchanged';
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
// 跳过未变化的字段(对于UPDATE操作)
|
|
973
|
+
if (changeType === 'unchanged' && operation === enums_1.AuditOperation.UPDATE) {
|
|
974
|
+
continue;
|
|
975
|
+
}
|
|
976
|
+
// 获取字段的多语言标签
|
|
977
|
+
let fieldLabel = field;
|
|
978
|
+
if (entityClass) {
|
|
979
|
+
try {
|
|
980
|
+
fieldLabel = (0, entity_audit_decorator_1.getFieldLabel)(entityClass, field, language);
|
|
981
|
+
}
|
|
982
|
+
catch (error) {
|
|
983
|
+
// 忽略错误,使用原字段名
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
// 获取字段值的多语言标签
|
|
987
|
+
let displayOldValue = this.formatDisplayValue(oldVal);
|
|
988
|
+
let displayNewValue = this.formatDisplayValue(newVal);
|
|
989
|
+
if (entityClass) {
|
|
990
|
+
try {
|
|
991
|
+
if (oldVal !== null && oldVal !== undefined) {
|
|
992
|
+
const valueLabel = (0, entity_audit_decorator_1.getFieldValueLabel)(entityClass, field, oldVal, language);
|
|
993
|
+
if (valueLabel !== oldVal) {
|
|
994
|
+
displayOldValue = valueLabel;
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
if (newVal !== null && newVal !== undefined) {
|
|
998
|
+
const valueLabel = (0, entity_audit_decorator_1.getFieldValueLabel)(entityClass, field, newVal, language);
|
|
999
|
+
if (valueLabel !== newVal) {
|
|
1000
|
+
displayNewValue = valueLabel;
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
catch (error) {
|
|
1005
|
+
// 忽略错误,使用默认格式化值
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
changes.push({
|
|
1009
|
+
field,
|
|
1010
|
+
fieldLabel,
|
|
1011
|
+
oldValue: oldVal,
|
|
1012
|
+
newValue: newVal,
|
|
1013
|
+
changeType,
|
|
1014
|
+
displayOldValue,
|
|
1015
|
+
displayNewValue,
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
return changes;
|
|
1019
|
+
}
|
|
1020
|
+
/**
|
|
1021
|
+
* 格式化显示值
|
|
1022
|
+
*/
|
|
1023
|
+
formatDisplayValue(value) {
|
|
1024
|
+
if (value === null || value === undefined) {
|
|
1025
|
+
return '';
|
|
1026
|
+
}
|
|
1027
|
+
if (typeof value === 'object') {
|
|
1028
|
+
return JSON.stringify(value);
|
|
1029
|
+
}
|
|
1030
|
+
return String(value);
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* 获取操作标签
|
|
1034
|
+
*/
|
|
1035
|
+
getOperationLabel(operation) {
|
|
1036
|
+
const labels = {
|
|
1037
|
+
[enums_1.AuditOperation.CREATE]: '创建',
|
|
1038
|
+
[enums_1.AuditOperation.UPDATE]: '更新',
|
|
1039
|
+
[enums_1.AuditOperation.DELETE]: '删除',
|
|
1040
|
+
[enums_1.AuditOperation.RESTORE]: '恢复',
|
|
1041
|
+
};
|
|
1042
|
+
return labels[operation] || operation;
|
|
1043
|
+
}
|
|
1044
|
+
/**
|
|
1045
|
+
* 按字段分组统计
|
|
1046
|
+
*/
|
|
1047
|
+
groupBy(items, field) {
|
|
1048
|
+
return items.reduce((acc, item) => {
|
|
1049
|
+
const key = item[field];
|
|
1050
|
+
acc[key] = (acc[key] || 0) + 1;
|
|
1051
|
+
return acc;
|
|
1052
|
+
}, {});
|
|
1053
|
+
}
|
|
557
1054
|
};
|
|
558
1055
|
exports.EntityAuditService = EntityAuditService;
|
|
559
1056
|
exports.EntityAuditService = EntityAuditService = __decorate([
|
|
560
1057
|
(0, common_1.Injectable)(),
|
|
561
1058
|
__param(0, (0, typeorm_1.InjectRepository)(entities_1.EntityAuditLogEntity)),
|
|
562
1059
|
__param(1, (0, typeorm_1.InjectRepository)(entities_1.EntityTransactionEntity)),
|
|
563
|
-
__param(2, (0,
|
|
564
|
-
__param(
|
|
565
|
-
__param(5, (0, common_1.Inject)('AUDIT_STRATEGY')),
|
|
1060
|
+
__param(2, (0, typeorm_1.InjectRepository)(entities_1.ManualOperationLogEntity)),
|
|
1061
|
+
__param(3, (0, common_1.Inject)('AUDIT_ENTITY_MANAGER')),
|
|
566
1062
|
__param(6, (0, common_1.Optional)()),
|
|
567
|
-
__param(6, (0, common_1.Inject)('
|
|
1063
|
+
__param(6, (0, common_1.Inject)('AUDIT_STRATEGY')),
|
|
568
1064
|
__param(7, (0, common_1.Optional)()),
|
|
569
|
-
__param(7, (0, common_1.Inject)('
|
|
1065
|
+
__param(7, (0, common_1.Inject)('AUDIT_CONFIG')),
|
|
1066
|
+
__param(8, (0, common_1.Optional)()),
|
|
1067
|
+
__param(8, (0, common_1.Inject)('AUDIT_CONNECTION_NAME')),
|
|
1068
|
+
__param(9, (0, common_1.Optional)()),
|
|
1069
|
+
__param(9, (0, typeorm_1.InjectRepository)(entities_1.AuditActionSummaryEntity)),
|
|
570
1070
|
__metadata("design:paramtypes", [typeorm_2.Repository,
|
|
1071
|
+
typeorm_2.Repository,
|
|
571
1072
|
typeorm_2.Repository,
|
|
572
1073
|
typeorm_2.EntityManager,
|
|
573
1074
|
audit_context_service_1.AuditContextService,
|
|
574
|
-
multi_database_service_1.MultiDatabaseService, Object, Object, String])
|
|
1075
|
+
multi_database_service_1.MultiDatabaseService, Object, Object, String, typeorm_2.Repository])
|
|
575
1076
|
], EntityAuditService);
|
|
@@ -3,3 +3,6 @@ export * from './audit-strategy.service';
|
|
|
3
3
|
export * from './entity-audit.service';
|
|
4
4
|
export * from './transaction-audit.service';
|
|
5
5
|
export * from './multi-database.service';
|
|
6
|
+
export * from './operation-description.service';
|
|
7
|
+
export * from './manual-audit-log.service';
|
|
8
|
+
export * from './audit-action.service';
|
package/audit/services/index.js
CHANGED
|
@@ -19,3 +19,6 @@ __exportStar(require("./audit-strategy.service"), exports);
|
|
|
19
19
|
__exportStar(require("./entity-audit.service"), exports);
|
|
20
20
|
__exportStar(require("./transaction-audit.service"), exports);
|
|
21
21
|
__exportStar(require("./multi-database.service"), exports);
|
|
22
|
+
__exportStar(require("./operation-description.service"), exports);
|
|
23
|
+
__exportStar(require("./manual-audit-log.service"), exports);
|
|
24
|
+
__exportStar(require("./audit-action.service"), exports);
|