@nest-omni/core 4.1.3-10 → 4.1.3-11
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 +25 -2
- 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 +69 -2
- package/audit/decorators/entity-audit.decorator.js +127 -4
- package/audit/decorators/index.d.ts +1 -0
- package/audit/decorators/index.js +1 -0
- package/audit/entities/entity-audit-log.entity.d.ts +5 -0
- package/audit/entities/entity-audit-log.entity.js +30 -1
- package/audit/entities/entity-transaction.entity.d.ts +7 -0
- package/audit/entities/entity-transaction.entity.js +30 -1
- package/audit/entities/index.d.ts +2 -0
- package/audit/entities/index.js +2 -0
- package/audit/enums/audit.enums.d.ts +30 -5
- package/audit/enums/audit.enums.js +34 -6
- package/audit/index.d.ts +3 -1
- package/audit/index.js +21 -2
- package/audit/interfaces/audit.interfaces.d.ts +140 -2
- package/audit/services/entity-audit.service.d.ts +72 -3
- package/audit/services/entity-audit.service.js +210 -6
- package/audit/services/index.d.ts +2 -0
- package/audit/services/index.js +2 -0
- package/http-client/http-client.module.js +1 -5
- package/package.json +1 -1
- 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/schedule.decorator.js +18 -8
- package/shared/serviceRegistryModule.js +18 -13
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuditOperation, MaskingStrategy, RecordStrategy } from '../enums/audit.enums';
|
|
1
|
+
import { AuditOperation, MaskingStrategy, RecordStrategy, RollbackActionType, ChangeType } from '../enums/audit.enums';
|
|
2
2
|
/**
|
|
3
3
|
* 审计上下文
|
|
4
4
|
*/
|
|
@@ -124,12 +124,38 @@ export interface AuditConfig {
|
|
|
124
124
|
/**
|
|
125
125
|
* 实体级别审计配置
|
|
126
126
|
*/
|
|
127
|
+
/**
|
|
128
|
+
* 实体审计配置
|
|
129
|
+
*
|
|
130
|
+
* @description
|
|
131
|
+
* 支持基础审计配置
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* @EntityAudit({
|
|
136
|
+
* enabled: true,
|
|
137
|
+
* excludeFields: ['password'],
|
|
138
|
+
* maskFields: ['email', 'phone'],
|
|
139
|
+
* templateKey: 'user',
|
|
140
|
+
* })
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
127
143
|
export interface EntityAuditConfig {
|
|
128
144
|
enabled?: boolean;
|
|
129
145
|
strategy?: RecordStrategy;
|
|
130
146
|
includeFields?: string[];
|
|
131
147
|
excludeFields?: string[];
|
|
132
148
|
maskFields?: string[];
|
|
149
|
+
/**
|
|
150
|
+
* 操作模板键前缀
|
|
151
|
+
* @example 'user' -> 生成 'user.create', 'user.update', 'user.delete'
|
|
152
|
+
*/
|
|
153
|
+
templateKey?: string;
|
|
154
|
+
/**
|
|
155
|
+
* @deprecated 使用 maskFields 代替
|
|
156
|
+
* 敏感字段列表(别名,用于兼容旧版本)
|
|
157
|
+
*/
|
|
158
|
+
sensitiveFields?: string[];
|
|
133
159
|
}
|
|
134
160
|
/**
|
|
135
161
|
* 操作级别审计配置
|
|
@@ -142,7 +168,25 @@ export interface OperationAuditConfig {
|
|
|
142
168
|
* 字段显示选项
|
|
143
169
|
*/
|
|
144
170
|
export interface FieldDisplayOptions {
|
|
145
|
-
|
|
171
|
+
/**
|
|
172
|
+
* 字段标签(支持多语言)
|
|
173
|
+
* @example
|
|
174
|
+
* // 单语言
|
|
175
|
+
* label: '姓名'
|
|
176
|
+
*
|
|
177
|
+
* // 多语言
|
|
178
|
+
* label: { zh: '姓名', en: 'Name' }
|
|
179
|
+
*/
|
|
180
|
+
label?: string | Record<string, string>;
|
|
181
|
+
/**
|
|
182
|
+
* 字段值的多语言标签映射
|
|
183
|
+
* @example
|
|
184
|
+
* valueLabels: {
|
|
185
|
+
* active: { zh: '激活', en: 'Active' },
|
|
186
|
+
* inactive: { zh: '未激活', en: 'Inactive' }
|
|
187
|
+
* }
|
|
188
|
+
*/
|
|
189
|
+
valueLabels?: Record<string, Record<string, string>>;
|
|
146
190
|
sensitive?: boolean;
|
|
147
191
|
formatter?: (value: any) => string;
|
|
148
192
|
}
|
|
@@ -159,3 +203,97 @@ export interface ControllerAuditOptions {
|
|
|
159
203
|
export interface MethodAuditOptions {
|
|
160
204
|
enabled?: boolean;
|
|
161
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* 变更详情
|
|
208
|
+
*/
|
|
209
|
+
export interface ChangeDetail {
|
|
210
|
+
fieldName: string;
|
|
211
|
+
fieldLabels?: Record<string, string>;
|
|
212
|
+
oldValue: any;
|
|
213
|
+
newValue: any;
|
|
214
|
+
displayOldValue?: Record<string, string>;
|
|
215
|
+
displayNewValue?: Record<string, string>;
|
|
216
|
+
changeType: ChangeType;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 回滚操作
|
|
220
|
+
*/
|
|
221
|
+
export interface RollbackAction {
|
|
222
|
+
type: RollbackActionType;
|
|
223
|
+
action: string;
|
|
224
|
+
params: Record<string, any>;
|
|
225
|
+
order: number;
|
|
226
|
+
handler?: string;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* 操作模板配置(用于数据库存储)
|
|
230
|
+
*/
|
|
231
|
+
export interface OperationTemplateData {
|
|
232
|
+
key: string;
|
|
233
|
+
entityName: string;
|
|
234
|
+
operation: AuditOperation;
|
|
235
|
+
nameTemplates: Record<string, string>;
|
|
236
|
+
descriptionTemplates: Record<string, string>;
|
|
237
|
+
fieldLabels?: Record<string, Record<string, string>>;
|
|
238
|
+
valueDisplays?: Record<string, Record<string, Record<string, string>>>;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* 手动操作日志数据
|
|
242
|
+
*/
|
|
243
|
+
export interface ManualOperationData {
|
|
244
|
+
transactionId: string;
|
|
245
|
+
operationTemplateKey: string;
|
|
246
|
+
descriptionParams: Record<string, any>;
|
|
247
|
+
userId?: string;
|
|
248
|
+
username?: string;
|
|
249
|
+
requestIp?: string;
|
|
250
|
+
rollbackActions?: RollbackAction[];
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* 手动日志记录选项(支持三种模式)
|
|
254
|
+
*/
|
|
255
|
+
export interface ManualLogOptions {
|
|
256
|
+
templateKey?: string;
|
|
257
|
+
descriptionParams?: Record<string, any>;
|
|
258
|
+
description?: string | Record<string, string>;
|
|
259
|
+
descriptionTemplate?: Record<string, string>;
|
|
260
|
+
userId?: string;
|
|
261
|
+
username?: string;
|
|
262
|
+
requestIp?: string;
|
|
263
|
+
rollbackActions?: RollbackAction[];
|
|
264
|
+
transactionId?: string;
|
|
265
|
+
autoCreateTransaction?: boolean;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* 方法审计选项(支持模板和回滚)
|
|
269
|
+
*/
|
|
270
|
+
export interface AuditOperationOptions {
|
|
271
|
+
templateKey: string;
|
|
272
|
+
descriptionParams?: (args: any[], result: any, context: any) => Record<string, any>;
|
|
273
|
+
rollbackActions?: (args: any[], result: any, context: any) => RollbackAction[];
|
|
274
|
+
autoTransaction?: boolean;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* 增强的实体审计配置(支持多语言)
|
|
278
|
+
*/
|
|
279
|
+
export interface EnhancedEntityAuditConfig extends EntityAuditConfig {
|
|
280
|
+
templateKey?: string;
|
|
281
|
+
fieldLabels?: Record<string, Record<string, string>>;
|
|
282
|
+
valueDisplays?: Record<string, Record<string, Record<string, string>>>;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* 事务描述结果
|
|
286
|
+
*/
|
|
287
|
+
export interface TransactionDescription {
|
|
288
|
+
operationName: string;
|
|
289
|
+
description: string;
|
|
290
|
+
changes: Array<{
|
|
291
|
+
entityName?: string;
|
|
292
|
+
entityId?: string;
|
|
293
|
+
operation?: AuditOperation;
|
|
294
|
+
type?: string;
|
|
295
|
+
description: string;
|
|
296
|
+
details: any;
|
|
297
|
+
createdAt: Date;
|
|
298
|
+
}>;
|
|
299
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Repository, EntityManager } from 'typeorm';
|
|
2
|
-
import { EntityAuditLogEntity, EntityTransactionEntity } from '../entities';
|
|
2
|
+
import { EntityAuditLogEntity, EntityTransactionEntity, ManualOperationLogEntity } from '../entities';
|
|
3
3
|
import { AuditOperation } from '../enums';
|
|
4
|
-
import { AuditConfig, IAuditStrategy, EntityDifference, RestoreOptions, RestoreResult, PreCheckResult } from '../interfaces';
|
|
4
|
+
import { AuditConfig, IAuditStrategy, EntityDifference, RestoreOptions, RestoreResult, PreCheckResult, ManualOperationData, ChangeDetail, RollbackAction } from '../interfaces';
|
|
5
5
|
import { AuditContextService } from './audit-context.service';
|
|
6
6
|
import { MultiDatabaseService } from './multi-database.service';
|
|
7
7
|
import { PageDto } from '../../common/dto';
|
|
@@ -12,13 +12,14 @@ import { AuditLogQueryDto } from '../dto';
|
|
|
12
12
|
export declare class EntityAuditService {
|
|
13
13
|
private readonly auditLogRepository;
|
|
14
14
|
private readonly transactionRepository;
|
|
15
|
+
private readonly manualOperationRepository;
|
|
15
16
|
private readonly entityManager;
|
|
16
17
|
private readonly contextService;
|
|
17
18
|
private readonly multiDbService;
|
|
18
19
|
private readonly auditStrategy;
|
|
19
20
|
private readonly config?;
|
|
20
21
|
private readonly auditConnectionName;
|
|
21
|
-
constructor(auditLogRepository: Repository<EntityAuditLogEntity>, transactionRepository: Repository<EntityTransactionEntity>, entityManager: EntityManager, contextService: AuditContextService, multiDbService: MultiDatabaseService, auditStrategy?: IAuditStrategy, config?: AuditConfig, auditConnectionName?: string);
|
|
22
|
+
constructor(auditLogRepository: Repository<EntityAuditLogEntity>, transactionRepository: Repository<EntityTransactionEntity>, manualOperationRepository: Repository<ManualOperationLogEntity>, entityManager: EntityManager, contextService: AuditContextService, multiDbService: MultiDatabaseService, auditStrategy?: IAuditStrategy, config?: AuditConfig, auditConnectionName?: string);
|
|
22
23
|
/**
|
|
23
24
|
* 记录实体变更
|
|
24
25
|
*/
|
|
@@ -91,4 +92,72 @@ export declare class EntityAuditService {
|
|
|
91
92
|
* 检测冲突
|
|
92
93
|
*/
|
|
93
94
|
private detectConflicts;
|
|
95
|
+
/**
|
|
96
|
+
* 增强的记录实体变更(支持模板和结构化变更详情)
|
|
97
|
+
* @param data 审计数据
|
|
98
|
+
* @returns 审计日志实体
|
|
99
|
+
*/
|
|
100
|
+
logEntityChangeWithTemplate(data: {
|
|
101
|
+
entityType: string;
|
|
102
|
+
entityId: string;
|
|
103
|
+
operation: AuditOperation;
|
|
104
|
+
oldValue?: Record<string, any>;
|
|
105
|
+
newValue?: Record<string, any>;
|
|
106
|
+
changedFields?: string[];
|
|
107
|
+
operationTemplateKey?: string;
|
|
108
|
+
descriptionParams?: Record<string, any>;
|
|
109
|
+
changeDetails?: ChangeDetail[];
|
|
110
|
+
rollbackActions?: RollbackAction[];
|
|
111
|
+
metadata?: Record<string, any>;
|
|
112
|
+
}): Promise<EntityAuditLogEntity | null>;
|
|
113
|
+
/**
|
|
114
|
+
* 记录手动操作
|
|
115
|
+
* @param data 手动操作数据
|
|
116
|
+
* @returns 手动操作日志实体
|
|
117
|
+
*/
|
|
118
|
+
logManualOperation(data: ManualOperationData): Promise<ManualOperationLogEntity>;
|
|
119
|
+
/**
|
|
120
|
+
* 在事务中执行操作
|
|
121
|
+
* @param fn 执行函数
|
|
122
|
+
* @param options 事务选项
|
|
123
|
+
* @returns 执���结果
|
|
124
|
+
*/
|
|
125
|
+
withTransaction<T>(fn: (transactionId: string) => Promise<T>, options?: {
|
|
126
|
+
userId?: string;
|
|
127
|
+
username?: string;
|
|
128
|
+
requestIp?: string;
|
|
129
|
+
operationTemplateKey?: string;
|
|
130
|
+
descriptionParams?: Record<string, any>;
|
|
131
|
+
metadata?: Record<string, any>;
|
|
132
|
+
}): Promise<T>;
|
|
133
|
+
/**
|
|
134
|
+
* 获取当前事务ID
|
|
135
|
+
* @returns 当前事务ID或null
|
|
136
|
+
*/
|
|
137
|
+
getCurrentTransactionId(): Promise<string | null>;
|
|
138
|
+
/**
|
|
139
|
+
* 设置当前事务ID
|
|
140
|
+
* @param transactionId 事务ID
|
|
141
|
+
*/
|
|
142
|
+
setCurrentTransactionId(transactionId: string | null): Promise<void>;
|
|
143
|
+
/**
|
|
144
|
+
* 开始事务
|
|
145
|
+
* @param userId 用户ID
|
|
146
|
+
* @param userName 用户名
|
|
147
|
+
* @param ip IP地址
|
|
148
|
+
* @param operationTemplateKey 操作模板键
|
|
149
|
+
* @param descriptionParams 描述参数
|
|
150
|
+
* @returns 事务ID
|
|
151
|
+
*/
|
|
152
|
+
beginTransaction(userId?: string, userName?: string, ip?: string, operationTemplateKey?: string, descriptionParams?: Record<string, any>): Promise<string>;
|
|
153
|
+
/**
|
|
154
|
+
* 提交事务
|
|
155
|
+
* @param transactionId 事务ID
|
|
156
|
+
*/
|
|
157
|
+
commitTransaction(transactionId: string): Promise<void>;
|
|
158
|
+
/**
|
|
159
|
+
* 回滚事务
|
|
160
|
+
* @param transactionId 事务ID
|
|
161
|
+
*/
|
|
162
|
+
rollbackTransaction(transactionId: string): Promise<void>;
|
|
94
163
|
}
|
|
@@ -36,9 +36,10 @@ const dto_1 = require("../../common/dto");
|
|
|
36
36
|
* 实体审计服务
|
|
37
37
|
*/
|
|
38
38
|
let EntityAuditService = class EntityAuditService {
|
|
39
|
-
constructor(auditLogRepository, transactionRepository, entityManager, contextService, multiDbService, auditStrategy = new audit_strategy_service_1.DefaultAuditStrategy(), config, auditConnectionName) {
|
|
39
|
+
constructor(auditLogRepository, transactionRepository, manualOperationRepository, entityManager, contextService, multiDbService, auditStrategy = new audit_strategy_service_1.DefaultAuditStrategy(), config, auditConnectionName) {
|
|
40
40
|
this.auditLogRepository = auditLogRepository;
|
|
41
41
|
this.transactionRepository = transactionRepository;
|
|
42
|
+
this.manualOperationRepository = manualOperationRepository;
|
|
42
43
|
this.entityManager = entityManager;
|
|
43
44
|
this.contextService = contextService;
|
|
44
45
|
this.multiDbService = multiDbService;
|
|
@@ -554,20 +555,223 @@ let EntityAuditService = class EntityAuditService {
|
|
|
554
555
|
}
|
|
555
556
|
return conflicts;
|
|
556
557
|
}
|
|
558
|
+
// ========== 新增方法:支持模板和手动操作 ==========
|
|
559
|
+
/**
|
|
560
|
+
* 增强的记录实体变更(支持模板和结构化变更详情)
|
|
561
|
+
* @param data 审计数据
|
|
562
|
+
* @returns 审计日志实体
|
|
563
|
+
*/
|
|
564
|
+
logEntityChangeWithTemplate(data) {
|
|
565
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
566
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
567
|
+
// 检查是否应该记录
|
|
568
|
+
if (!this.auditStrategy.shouldRecord(data.entityType, data.operation)) {
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
// 获取记录策略
|
|
572
|
+
const recordStrategy = this.auditStrategy.getRecordStrategy(data.entityType, data.operation);
|
|
573
|
+
if (recordStrategy === enums_1.RecordStrategy.DISABLED) {
|
|
574
|
+
return null;
|
|
575
|
+
}
|
|
576
|
+
// 获取字段过滤器
|
|
577
|
+
const fieldFilter = this.auditStrategy.getFieldFilter(data.entityType);
|
|
578
|
+
// 过滤和脱敏字段
|
|
579
|
+
const filteredOldValue = this.filterAndMaskFields(data.oldValue || {}, fieldFilter);
|
|
580
|
+
const filteredNewValue = this.filterAndMaskFields(data.newValue || {}, fieldFilter);
|
|
581
|
+
// 计算变更字段
|
|
582
|
+
const changedFields = data.changedFields || this.calculateChangedFields(filteredOldValue, filteredNewValue);
|
|
583
|
+
const changedFieldPaths = this.calculateChangedFieldPaths(filteredOldValue, filteredNewValue);
|
|
584
|
+
// 获取上下文信息
|
|
585
|
+
const context = yield this.contextService.getCurrentContext();
|
|
586
|
+
// 如果没有提供操作模板键,使用默认模板
|
|
587
|
+
const operationTemplateKey = data.operationTemplateKey || `${data.entityType}.${data.operation}`;
|
|
588
|
+
// 如果没有提供描述参数,使用实体数据
|
|
589
|
+
const descriptionParams = data.descriptionParams || Object.assign(Object.assign(Object.assign({}, filteredOldValue), filteredNewValue), { changedFields, changedFieldsCount: changedFields.length });
|
|
590
|
+
// 创建审计日志
|
|
591
|
+
const auditLog = this.auditLogRepository.create({
|
|
592
|
+
entityType: data.entityType,
|
|
593
|
+
entityId: data.entityId,
|
|
594
|
+
operation: data.operation,
|
|
595
|
+
oldValue: filteredOldValue,
|
|
596
|
+
newValue: filteredNewValue,
|
|
597
|
+
changedFields,
|
|
598
|
+
changedFieldPaths: changedFieldPaths.join(','),
|
|
599
|
+
userId: context.userId || ((_a = data.metadata) === null || _a === void 0 ? void 0 : _a.userId),
|
|
600
|
+
username: context.username || ((_b = data.metadata) === null || _b === void 0 ? void 0 : _b.username),
|
|
601
|
+
requestId: context.requestId || ((_c = data.metadata) === null || _c === void 0 ? void 0 : _c.requestId),
|
|
602
|
+
requestIp: context.requestIp || ((_d = data.metadata) === null || _d === void 0 ? void 0 : _d.requestIp),
|
|
603
|
+
userAgent: context.userAgent || ((_e = data.metadata) === null || _e === void 0 ? void 0 : _e.userAgent),
|
|
604
|
+
description: this.generateDescription(data.operation, data.entityType, data.entityId, changedFields),
|
|
605
|
+
hashChain: ((_g = (_f = this.config) === null || _f === void 0 ? void 0 : _f.security) === null || _g === void 0 ? void 0 : _g.hashChainEnabled)
|
|
606
|
+
? yield this.generateHashChain(data.entityType, data.entityId, filteredNewValue)
|
|
607
|
+
: undefined,
|
|
608
|
+
// 新增字段
|
|
609
|
+
operationTemplateKey,
|
|
610
|
+
descriptionParams,
|
|
611
|
+
changeDetails: data.changeDetails,
|
|
612
|
+
rollbackActions: data.rollbackActions,
|
|
613
|
+
});
|
|
614
|
+
// 保存变更日志
|
|
615
|
+
const savedLog = yield this.auditLogRepository.save(auditLog);
|
|
616
|
+
return savedLog;
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* 记录手动操作
|
|
621
|
+
* @param data 手动操作数据
|
|
622
|
+
* @returns 手动操作日志实体
|
|
623
|
+
*/
|
|
624
|
+
logManualOperation(data) {
|
|
625
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
626
|
+
// 获取上下文信息
|
|
627
|
+
const context = yield this.contextService.getCurrentContext();
|
|
628
|
+
const manualLog = this.manualOperationRepository.create({
|
|
629
|
+
transactionId: data.transactionId,
|
|
630
|
+
operationTemplateKey: data.operationTemplateKey,
|
|
631
|
+
descriptionParams: data.descriptionParams,
|
|
632
|
+
userId: data.userId || context.userId,
|
|
633
|
+
username: data.username || context.username,
|
|
634
|
+
requestIp: data.requestIp || context.requestIp,
|
|
635
|
+
rollbackActions: data.rollbackActions || [],
|
|
636
|
+
});
|
|
637
|
+
return yield this.manualOperationRepository.save(manualLog);
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* 在事务中执行操作
|
|
642
|
+
* @param fn 执行函数
|
|
643
|
+
* @param options 事务选项
|
|
644
|
+
* @returns 执���结果
|
|
645
|
+
*/
|
|
646
|
+
withTransaction(fn, options) {
|
|
647
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
648
|
+
var _a;
|
|
649
|
+
// 获取上下文信息
|
|
650
|
+
const context = yield this.contextService.getCurrentContext();
|
|
651
|
+
// 创建事务
|
|
652
|
+
const transaction = this.transactionRepository.create({
|
|
653
|
+
description: ((_a = options === null || options === void 0 ? void 0 : options.descriptionParams) === null || _a === void 0 ? void 0 : _a.description) || 'Transaction',
|
|
654
|
+
status: 'PENDING',
|
|
655
|
+
entities: [],
|
|
656
|
+
userId: (options === null || options === void 0 ? void 0 : options.userId) || context.userId,
|
|
657
|
+
username: (options === null || options === void 0 ? void 0 : options.username) || context.username,
|
|
658
|
+
requestIp: (options === null || options === void 0 ? void 0 : options.requestIp) || context.requestIp,
|
|
659
|
+
operationTemplateKey: options === null || options === void 0 ? void 0 : options.operationTemplateKey,
|
|
660
|
+
descriptionParams: options === null || options === void 0 ? void 0 : options.descriptionParams,
|
|
661
|
+
metadata: options === null || options === void 0 ? void 0 : options.metadata,
|
|
662
|
+
});
|
|
663
|
+
const savedTransaction = yield this.transactionRepository.save(transaction);
|
|
664
|
+
const transactionId = savedTransaction.id;
|
|
665
|
+
try {
|
|
666
|
+
// 设置事务ID到上下文
|
|
667
|
+
yield this.contextService.setContext(Object.assign(Object.assign({}, context), { transactionId }));
|
|
668
|
+
// 执行操作
|
|
669
|
+
const result = yield fn(transactionId);
|
|
670
|
+
// 提交事务
|
|
671
|
+
yield this.transactionRepository.update(transactionId, {
|
|
672
|
+
status: 'COMMITTED',
|
|
673
|
+
completedAt: new Date(),
|
|
674
|
+
});
|
|
675
|
+
return result;
|
|
676
|
+
}
|
|
677
|
+
catch (error) {
|
|
678
|
+
// 回滚事务
|
|
679
|
+
yield this.transactionRepository.update(transactionId, {
|
|
680
|
+
status: 'ROLLED_BACK',
|
|
681
|
+
completedAt: new Date(),
|
|
682
|
+
});
|
|
683
|
+
throw error;
|
|
684
|
+
}
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* 获取当前事务ID
|
|
689
|
+
* @returns 当前事务ID或null
|
|
690
|
+
*/
|
|
691
|
+
getCurrentTransactionId() {
|
|
692
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
693
|
+
const context = yield this.contextService.getCurrentContext();
|
|
694
|
+
return context.transactionId || null;
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
/**
|
|
698
|
+
* 设置当前事务ID
|
|
699
|
+
* @param transactionId 事务ID
|
|
700
|
+
*/
|
|
701
|
+
setCurrentTransactionId(transactionId) {
|
|
702
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
703
|
+
const context = yield this.contextService.getCurrentContext();
|
|
704
|
+
yield this.contextService.setContext(Object.assign(Object.assign({}, context), { transactionId }));
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* 开始事务
|
|
709
|
+
* @param userId 用户ID
|
|
710
|
+
* @param userName 用户名
|
|
711
|
+
* @param ip IP地址
|
|
712
|
+
* @param operationTemplateKey 操作模板键
|
|
713
|
+
* @param descriptionParams 描述参数
|
|
714
|
+
* @returns 事务ID
|
|
715
|
+
*/
|
|
716
|
+
beginTransaction(userId, userName, ip, operationTemplateKey, descriptionParams) {
|
|
717
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
718
|
+
// 获取上下文信息
|
|
719
|
+
const context = yield this.contextService.getCurrentContext();
|
|
720
|
+
// 创建事务
|
|
721
|
+
const transaction = this.transactionRepository.create({
|
|
722
|
+
description: (descriptionParams === null || descriptionParams === void 0 ? void 0 : descriptionParams.description) || 'Transaction',
|
|
723
|
+
status: 'pending',
|
|
724
|
+
entities: [],
|
|
725
|
+
userId: userId || context.userId,
|
|
726
|
+
username: userName || context.username,
|
|
727
|
+
requestIp: ip || context.requestIp,
|
|
728
|
+
operationTemplateKey,
|
|
729
|
+
descriptionParams,
|
|
730
|
+
});
|
|
731
|
+
const savedTransaction = yield this.transactionRepository.save(transaction);
|
|
732
|
+
return savedTransaction.id;
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* 提交事务
|
|
737
|
+
* @param transactionId 事务ID
|
|
738
|
+
*/
|
|
739
|
+
commitTransaction(transactionId) {
|
|
740
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
741
|
+
yield this.transactionRepository.update(transactionId, {
|
|
742
|
+
status: 'committed',
|
|
743
|
+
completedAt: new Date(),
|
|
744
|
+
});
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* 回滚事务
|
|
749
|
+
* @param transactionId 事务ID
|
|
750
|
+
*/
|
|
751
|
+
rollbackTransaction(transactionId) {
|
|
752
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
753
|
+
yield this.transactionRepository.update(transactionId, {
|
|
754
|
+
status: 'rolled_back',
|
|
755
|
+
completedAt: new Date(),
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
}
|
|
557
759
|
};
|
|
558
760
|
exports.EntityAuditService = EntityAuditService;
|
|
559
761
|
exports.EntityAuditService = EntityAuditService = __decorate([
|
|
560
762
|
(0, common_1.Injectable)(),
|
|
561
763
|
__param(0, (0, typeorm_1.InjectRepository)(entities_1.EntityAuditLogEntity)),
|
|
562
764
|
__param(1, (0, typeorm_1.InjectRepository)(entities_1.EntityTransactionEntity)),
|
|
563
|
-
__param(2, (0,
|
|
564
|
-
__param(
|
|
565
|
-
__param(5, (0, common_1.Inject)('AUDIT_STRATEGY')),
|
|
765
|
+
__param(2, (0, typeorm_1.InjectRepository)(entities_1.ManualOperationLogEntity)),
|
|
766
|
+
__param(3, (0, common_1.Inject)('AUDIT_ENTITY_MANAGER')),
|
|
566
767
|
__param(6, (0, common_1.Optional)()),
|
|
567
|
-
__param(6, (0, common_1.Inject)('
|
|
768
|
+
__param(6, (0, common_1.Inject)('AUDIT_STRATEGY')),
|
|
568
769
|
__param(7, (0, common_1.Optional)()),
|
|
569
|
-
__param(7, (0, common_1.Inject)('
|
|
770
|
+
__param(7, (0, common_1.Inject)('AUDIT_CONFIG')),
|
|
771
|
+
__param(8, (0, common_1.Optional)()),
|
|
772
|
+
__param(8, (0, common_1.Inject)('AUDIT_CONNECTION_NAME')),
|
|
570
773
|
__metadata("design:paramtypes", [typeorm_2.Repository,
|
|
774
|
+
typeorm_2.Repository,
|
|
571
775
|
typeorm_2.Repository,
|
|
572
776
|
typeorm_2.EntityManager,
|
|
573
777
|
audit_context_service_1.AuditContextService,
|
package/audit/services/index.js
CHANGED
|
@@ -19,3 +19,5 @@ __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);
|
|
@@ -18,7 +18,6 @@ var HttpClientModule_1;
|
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
exports.HttpClientModule = void 0;
|
|
20
20
|
const common_1 = require("@nestjs/common");
|
|
21
|
-
const schedule_1 = require("@nestjs/schedule");
|
|
22
21
|
const config_1 = require("@nestjs/config");
|
|
23
22
|
const typeorm_1 = require("@nestjs/typeorm");
|
|
24
23
|
const cache_service_1 = require("../cache/cache.service");
|
|
@@ -41,7 +40,7 @@ let HttpClientModule = HttpClientModule_1 = class HttpClientModule {
|
|
|
41
40
|
* 动态注册HTTP客户端模块
|
|
42
41
|
*/
|
|
43
42
|
static forRoot(config = {}) {
|
|
44
|
-
var _a, _b
|
|
43
|
+
var _a, _b;
|
|
45
44
|
// 基础服务提供者
|
|
46
45
|
const baseProviders = [
|
|
47
46
|
{
|
|
@@ -87,9 +86,6 @@ let HttpClientModule = HttpClientModule_1 = class HttpClientModule {
|
|
|
87
86
|
if ((_b = (_a = config.logging) === null || _a === void 0 ? void 0 : _a.databaseLogging) === null || _b === void 0 ? void 0 : _b.enabled) {
|
|
88
87
|
dynamicImports.push(typeorm_1.TypeOrmModule.forFeature([entities_1.HttpLogEntity]));
|
|
89
88
|
}
|
|
90
|
-
if ((_c = config.logCleanup) === null || _c === void 0 ? void 0 : _c.enabled) {
|
|
91
|
-
dynamicImports.push(schedule_1.ScheduleModule.forRoot());
|
|
92
|
-
}
|
|
93
89
|
return {
|
|
94
90
|
module: HttpClientModule_1,
|
|
95
91
|
imports: dynamicImports,
|
package/package.json
CHANGED
|
@@ -23,12 +23,12 @@ export declare class LockHeartbeatService implements OnModuleInit, OnModuleDestr
|
|
|
23
23
|
private heartbeatTimer;
|
|
24
24
|
/**
|
|
25
25
|
* Heartbeat interval in milliseconds
|
|
26
|
-
* @default
|
|
26
|
+
* @default 5000 (5 seconds) - reduced for faster failure detection
|
|
27
27
|
*/
|
|
28
28
|
private readonly heartbeatInterval;
|
|
29
29
|
/**
|
|
30
30
|
* Heartbeat TTL in seconds (3x interval for safety)
|
|
31
|
-
* @default
|
|
31
|
+
* @default 15 (15 seconds) - adjusted to match new interval
|
|
32
32
|
*/
|
|
33
33
|
private readonly heartbeatTtl;
|
|
34
34
|
constructor();
|
|
@@ -44,14 +44,14 @@ let LockHeartbeatService = LockHeartbeatService_1 = class LockHeartbeatService {
|
|
|
44
44
|
this.heartbeatTimer = null;
|
|
45
45
|
/**
|
|
46
46
|
* Heartbeat interval in milliseconds
|
|
47
|
-
* @default
|
|
47
|
+
* @default 5000 (5 seconds) - reduced for faster failure detection
|
|
48
48
|
*/
|
|
49
|
-
this.heartbeatInterval =
|
|
49
|
+
this.heartbeatInterval = 5000;
|
|
50
50
|
/**
|
|
51
51
|
* Heartbeat TTL in seconds (3x interval for safety)
|
|
52
|
-
* @default
|
|
52
|
+
* @default 15 (15 seconds) - adjusted to match new interval
|
|
53
53
|
*/
|
|
54
|
-
this.heartbeatTtl =
|
|
54
|
+
this.heartbeatTtl = 15;
|
|
55
55
|
this.instanceId = this.getInstanceId();
|
|
56
56
|
}
|
|
57
57
|
onModuleInit() {
|
|
@@ -69,6 +69,24 @@ export interface LockOptions {
|
|
|
69
69
|
* @default 0 (no automatic extension)
|
|
70
70
|
*/
|
|
71
71
|
autoExtend?: number;
|
|
72
|
+
/**
|
|
73
|
+
* Whether to use exponential backoff for retry attempts
|
|
74
|
+
* If true, retry delay will increase exponentially: retryDelay * 2^(attempt-1)
|
|
75
|
+
* @default false
|
|
76
|
+
*/
|
|
77
|
+
useExponentialBackoff?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Maximum retry delay in milliseconds when using exponential backoff
|
|
80
|
+
* Ensures retry delay doesn't grow indefinitely
|
|
81
|
+
* @default 60000 (60 seconds)
|
|
82
|
+
*/
|
|
83
|
+
maxRetryDelay?: number;
|
|
84
|
+
/**
|
|
85
|
+
* Random jitter to add to retry delay in milliseconds
|
|
86
|
+
* Helps prevent synchronized retries across multiple instances
|
|
87
|
+
* @default 0 (no jitter)
|
|
88
|
+
*/
|
|
89
|
+
retryJitter?: number;
|
|
72
90
|
}
|
|
73
91
|
export interface LockResult {
|
|
74
92
|
/**
|