From d8a7488f46aa9bc9f6be00fa4ec48b9a2842150d Mon Sep 17 00:00:00 2001
From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com>
Date: Thu, 10 Jul 2025 18:32:42 +0700
Subject: [PATCH 1/2] feat: create feature log scheduling activity
---
src/core/strings/constants/table.constants.ts | 1 +
.../1752146975330-data-scheduling-log.ts | 33 ++++
.../data-scheduling/data-scheduling.module.ts | 13 +-
.../data/models/data-scheduling-log.model.ts | 44 +++++
.../domain/entities/data-scheduling.entity.ts | 30 ++++
.../data-scheduling-change-status.handler.ts | 62 +++++++
.../data-scheduling-created.handler.ts | 58 +++++++
.../data-scheduling-deleted.handler.ts | 64 ++++++++
.../data-scheduling-updated.handler.ts | 153 ++++++++++++++++++
9 files changed, 457 insertions(+), 1 deletion(-)
create mode 100644 src/database/migrations/1752146975330-data-scheduling-log.ts
create mode 100644 src/modules/configuration/data-scheduling/data/models/data-scheduling-log.model.ts
create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts
create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts
create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts
create mode 100644 src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts
diff --git a/src/core/strings/constants/table.constants.ts b/src/core/strings/constants/table.constants.ts
index a2a5475..e8990d3 100644
--- a/src/core/strings/constants/table.constants.ts
+++ b/src/core/strings/constants/table.constants.ts
@@ -52,4 +52,5 @@ export enum TABLE_NAME {
DATA_SCHEDULING = 'event_scheduling',
DATA_SCHEDULING_DEFAULT = 'event_scheduling_default',
+ DATA_SCHEDULING_LOG = 'event_scheduling_log',
}
diff --git a/src/database/migrations/1752146975330-data-scheduling-log.ts b/src/database/migrations/1752146975330-data-scheduling-log.ts
new file mode 100644
index 0000000..619f1f0
--- /dev/null
+++ b/src/database/migrations/1752146975330-data-scheduling-log.ts
@@ -0,0 +1,33 @@
+import { MigrationInterface, QueryRunner } from 'typeorm';
+
+export class DataSchedulingLog1752146975330 implements MigrationInterface {
+ name = 'DataSchedulingLog1752146975330';
+
+ public async up(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(
+ `CREATE TYPE "public"."event_scheduling_log_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
+ );
+ await queryRunner.query(
+ `CREATE TYPE "public"."event_scheduling_log_type_enum" AS ENUM('Default Percentage', 'Data Scheduling')`,
+ );
+ await queryRunner.query(
+ `CREATE TYPE "public"."event_scheduling_log_action_enum" AS ENUM('CREATE', 'UPDATE', 'DELETE', 'CHANGE_STATUS')`,
+ );
+ await queryRunner.query(
+ `CREATE TABLE "event_scheduling_log" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "status" "public"."event_scheduling_log_status_enum" NOT NULL DEFAULT 'draft', "type" "public"."event_scheduling_log_type_enum" NOT NULL, "action" "public"."event_scheduling_log_action_enum" NOT NULL, "log_created_at" bigint NOT NULL, "data_id" character varying, "name" character varying, "indexing_key" character varying, "schedule_date_from" date, "schedule_date_to" date, "default_value" integer, "description" text, CONSTRAINT "PK_984247db566636baacab18f593a" PRIMARY KEY ("id"))`,
+ );
+ }
+
+ public async down(queryRunner: QueryRunner): Promise {
+ await queryRunner.query(`DROP TABLE "event_scheduling_log"`);
+ await queryRunner.query(
+ `DROP TYPE "public"."event_scheduling_log_action_enum"`,
+ );
+ await queryRunner.query(
+ `DROP TYPE "public"."event_scheduling_log_type_enum"`,
+ );
+ await queryRunner.query(
+ `DROP TYPE "public"."event_scheduling_log_status_enum"`,
+ );
+ }
+}
diff --git a/src/modules/configuration/data-scheduling/data-scheduling.module.ts b/src/modules/configuration/data-scheduling/data-scheduling.module.ts
index 3912b44..ef409bd 100644
--- a/src/modules/configuration/data-scheduling/data-scheduling.module.ts
+++ b/src/modules/configuration/data-scheduling/data-scheduling.module.ts
@@ -30,15 +30,21 @@ import { DataSchedulingDefaultModel } from './data/models/data-scheduling-defaul
import { DataSchedulingManager } from './domain/usecases/managers/data-scheduling-default.manager';
import { SetupSchedulingGuard } from './infrastructure/guards/setup-scheduling.guard';
+import { DataSchedulingChangeStatusHandler } from './domain/usecases/handlers/data-scheduling-change-status.handler';
+import { DataSchedulingCreatedHandler } from './domain/usecases/handlers/data-scheduling-created.handler';
+import { DataSchedulingDeletedHandler } from './domain/usecases/handlers/data-scheduling-deleted.handler';
+import { DataSchedulingUpdatedHandler } from './domain/usecases/handlers/data-scheduling-updated.handler';
+
import { JwtModule } from '@nestjs/jwt';
import { JWT_EXPIRED } from 'src/core/sessions/constants';
import { JWT_SECRET } from 'src/core/sessions/constants';
+import { DataSchedulingLogModel } from './data/models/data-scheduling-log.model';
@Module({
imports: [
ConfigModule.forRoot(),
TypeOrmModule.forFeature(
- [DataSchedulingModel, DataSchedulingDefaultModel],
+ [DataSchedulingModel, DataSchedulingDefaultModel, DataSchedulingLogModel],
CONNECTION_NAME.DEFAULT,
),
JwtModule.register({
@@ -75,6 +81,11 @@ import { JWT_SECRET } from 'src/core/sessions/constants';
DataSchedulingReadOrchestrator,
DataSchedulingManager,
+
+ DataSchedulingChangeStatusHandler,
+ DataSchedulingCreatedHandler,
+ DataSchedulingDeletedHandler,
+ DataSchedulingUpdatedHandler,
],
})
export class DataSchedulingModule {}
diff --git a/src/modules/configuration/data-scheduling/data/models/data-scheduling-log.model.ts b/src/modules/configuration/data-scheduling/data/models/data-scheduling-log.model.ts
new file mode 100644
index 0000000..4950656
--- /dev/null
+++ b/src/modules/configuration/data-scheduling/data/models/data-scheduling-log.model.ts
@@ -0,0 +1,44 @@
+import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
+import {
+ DataSchedulingLogEntity,
+ SCHEDULING_LOG_ACTION_ENUM,
+ SCHEDULING_LOG_TYPE_ENUM,
+} from '../../domain/entities/data-scheduling.entity';
+import { Column, Entity } from 'typeorm';
+import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model';
+
+@Entity(TABLE_NAME.DATA_SCHEDULING_LOG)
+export class DataSchedulingLogModel
+ extends BaseStatusModel
+ implements DataSchedulingLogEntity
+{
+ @Column({ type: 'enum', enum: SCHEDULING_LOG_TYPE_ENUM, nullable: false })
+ type: SCHEDULING_LOG_TYPE_ENUM;
+
+ @Column({ type: 'enum', enum: SCHEDULING_LOG_ACTION_ENUM, nullable: false })
+ action: SCHEDULING_LOG_ACTION_ENUM;
+
+ @Column({ type: 'bigint', nullable: false })
+ log_created_at: number;
+
+ @Column('varchar', { name: 'data_id', nullable: true })
+ data_id: string;
+
+ @Column('varchar', { name: 'name', nullable: true })
+ name: string;
+
+ @Column('varchar', { name: 'indexing_key', nullable: true })
+ indexing_key: string;
+
+ @Column('date', { name: 'schedule_date_from', nullable: true })
+ schedule_date_from: Date;
+
+ @Column('date', { name: 'schedule_date_to', nullable: true })
+ schedule_date_to: Date;
+
+ @Column('int', { nullable: true })
+ default_value: number;
+
+ @Column('text', { name: 'description', nullable: true })
+ description: string;
+}
diff --git a/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts b/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts
index 9733866..ea2e92b 100644
--- a/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts
+++ b/src/modules/configuration/data-scheduling/domain/entities/data-scheduling.entity.ts
@@ -1,5 +1,6 @@
import { BaseStatusEntity } from 'src/core/modules/domain/entities/base-status.entity';
import { BaseEntity } from 'src/core/modules/domain/entities/base.entity';
+import { STATUS } from 'src/core/strings/constants/base.constants';
export interface DataSchedulingEntity extends BaseStatusEntity {
name: string;
@@ -15,3 +16,32 @@ export interface DataSchedulingDefaultEntity extends BaseEntity {
export interface DataSchedulingActiveEntity {
value: number;
}
+
+export enum SCHEDULING_LOG_TYPE_ENUM {
+ DEFAULT_PERCENTAGE = 'Default Percentage',
+ DATA_SCHEDULING = 'Data Scheduling',
+}
+
+export enum SCHEDULING_LOG_ACTION_ENUM {
+ CREATE = 'CREATE',
+ UPDATE = 'UPDATE',
+ DELETE = 'DELETE',
+ CHANGE_STATUS = 'CHANGE_STATUS',
+}
+
+export interface DataSchedulingLogEntity extends BaseStatusEntity {
+ type: SCHEDULING_LOG_TYPE_ENUM;
+ action: SCHEDULING_LOG_ACTION_ENUM;
+ log_created_at: number;
+
+ data_id?: string;
+
+ name?: string;
+ indexing_key?: number | string;
+ schedule_date_from?: Date;
+ schedule_date_to?: Date;
+
+ default_value?: number;
+
+ description?: string;
+}
diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts
new file mode 100644
index 0000000..d8074e4
--- /dev/null
+++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts
@@ -0,0 +1,62 @@
+import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
+import { DataSchedulingChangeStatusEvent } from '../../entities/event/data-scheduling-change-status.event';
+import { capitalizeEachWord } from 'src/modules/reports/shared/helpers';
+import {
+ DataSchedulingLogEntity,
+ SCHEDULING_LOG_ACTION_ENUM,
+ SCHEDULING_LOG_TYPE_ENUM,
+} from '../../entities/data-scheduling.entity';
+import { InjectRepository } from '@nestjs/typeorm';
+import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model';
+import { Repository } from 'typeorm';
+import { Logger } from '@nestjs/common';
+
+@EventsHandler(DataSchedulingChangeStatusEvent)
+export class DataSchedulingChangeStatusHandler
+ implements IEventHandler
+{
+ private readonly logger = new Logger(DataSchedulingChangeStatusHandler.name);
+
+ constructor(
+ @InjectRepository(DataSchedulingLogModel)
+ private repository: Repository,
+ ) {}
+
+ async handle(event: DataSchedulingChangeStatusEvent) {
+ const oldData = event?.data?.old;
+ const newData = event?.data?.data;
+
+ const oldStatus = capitalizeEachWord(oldData?.status);
+ const newStatus = capitalizeEachWord(newData.status);
+
+ const scheduleName = newData?.name || 'an item';
+ const editorName = newData.editor_name || 'System';
+ const description = `${editorName} changed the status of ${scheduleName} from ${oldStatus} to ${newStatus}.
`;
+
+ const payload: DataSchedulingLogEntity = {
+ type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING,
+ action: SCHEDULING_LOG_ACTION_ENUM.CHANGE_STATUS,
+ log_created_at: new Date().getTime(),
+
+ data_id: newData?.id,
+ name: newData?.name,
+ indexing_key: newData?.indexing_key,
+ schedule_date_from: newData?.schedule_date_from,
+ schedule_date_to: newData?.schedule_date_to,
+
+ status: newData?.status,
+ creator_id: newData?.creator_id,
+ creator_name: newData?.creator_name,
+ editor_id: newData?.editor_id,
+ editor_name: newData?.editor_name,
+ created_at: newData?.created_at,
+ updated_at: newData?.updated_at,
+ description: description,
+ };
+
+ await this.repository.save(payload as any);
+ this.logger.verbose(
+ `[SCHEDULING LOG] Change status data for ID: ${payload.data_id}`,
+ );
+ }
+}
diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts
new file mode 100644
index 0000000..c35b8e2
--- /dev/null
+++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts
@@ -0,0 +1,58 @@
+import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
+import { DataSchedulingCreatedEvent } from '../../entities/event/data-scheduling-created.event';
+import {
+ DataSchedulingLogEntity,
+ SCHEDULING_LOG_ACTION_ENUM,
+ SCHEDULING_LOG_TYPE_ENUM,
+} from '../../entities/data-scheduling.entity';
+import { decryptionTotal } from '../../../infrastructure/helpers';
+import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model';
+import { InjectRepository } from '@nestjs/typeorm';
+import { Repository } from 'typeorm';
+import { Logger } from '@nestjs/common';
+
+@EventsHandler(DataSchedulingCreatedEvent)
+export class DataSchedulingCreatedHandler
+ implements IEventHandler
+{
+ private readonly logger = new Logger(DataSchedulingCreatedHandler.name);
+
+ constructor(
+ @InjectRepository(DataSchedulingLogModel)
+ private repository: Repository,
+ ) {}
+
+ async handle(event: DataSchedulingCreatedEvent) {
+ const data = event?.data?.data;
+ const totalPercentage = decryptionTotal(data?.indexing_key);
+
+ const scheduleName = data?.name || 'a new schedule';
+ const description = `${data.creator_name} created ${scheduleName} schedule from ${data?.schedule_date_from} to ${data.schedule_date_to} with a total percentage of ${totalPercentage}%.
`;
+
+ const payload: DataSchedulingLogEntity = {
+ type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING,
+ action: SCHEDULING_LOG_ACTION_ENUM.CREATE,
+ log_created_at: new Date().getTime(),
+
+ data_id: data?.id,
+ name: data?.name,
+ indexing_key: data?.indexing_key,
+ schedule_date_from: data?.schedule_date_from,
+ schedule_date_to: data?.schedule_date_to,
+
+ status: data?.status,
+ creator_id: data?.creator_id,
+ creator_name: data?.creator_name,
+ editor_id: data?.editor_id,
+ editor_name: data?.editor_name,
+ created_at: data?.created_at,
+ updated_at: data?.updated_at,
+ description: description,
+ };
+
+ await this.repository.save(payload as any);
+ this.logger.verbose(
+ `[SCHEDULING LOG] Create data for ID: ${payload.data_id}`,
+ );
+ }
+}
diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts
new file mode 100644
index 0000000..8ba8e87
--- /dev/null
+++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts
@@ -0,0 +1,64 @@
+import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
+import { DataSchedulingDeletedEvent } from '../../entities/event/data-scheduling-deleted.event';
+import {
+ DataSchedulingLogEntity,
+ SCHEDULING_LOG_ACTION_ENUM,
+ SCHEDULING_LOG_TYPE_ENUM,
+} from '../../entities/data-scheduling.entity';
+import { InjectRepository } from '@nestjs/typeorm';
+import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model';
+import { Repository } from 'typeorm';
+import { Logger } from '@nestjs/common';
+
+@EventsHandler(DataSchedulingDeletedEvent)
+export class DataSchedulingDeletedHandler
+ implements IEventHandler
+{
+ private readonly logger = new Logger(DataSchedulingDeletedHandler.name);
+
+ constructor(
+ @InjectRepository(DataSchedulingLogModel)
+ private repository: Repository,
+ ) {}
+
+ async handle(event: DataSchedulingDeletedEvent) {
+ const deletedData = event?.data?.data;
+ const user = event?.data?.user;
+
+ const deleterName =
+ user?.name ||
+ deletedData?.editor_name ||
+ deletedData?.creator_name ||
+ 'System';
+
+ const scheduleName = deletedData?.name || 'an item';
+
+ const description = `${deleterName} deleted schedule: ${scheduleName}.
`;
+
+ const payload: DataSchedulingLogEntity = {
+ type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING,
+ action: SCHEDULING_LOG_ACTION_ENUM.DELETE,
+ log_created_at: new Date().getTime(),
+
+ data_id: deletedData?.id,
+ name: deletedData?.name,
+ indexing_key: deletedData?.indexing_key,
+ schedule_date_from: deletedData?.schedule_date_from,
+ schedule_date_to: deletedData?.schedule_date_to,
+ status: deletedData?.status,
+
+ creator_id: deletedData?.creator_id,
+ creator_name: deletedData?.creator_name,
+ editor_id: deletedData?.editor_id,
+ editor_name: deletedData?.editor_name,
+ created_at: deletedData?.created_at,
+ updated_at: deletedData?.updated_at,
+ description: description,
+ };
+
+ await this.repository.save(payload as any);
+ this.logger.verbose(
+ `[SCHEDULING LOG] Delete data for ID: ${payload.data_id}`,
+ );
+ }
+}
diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts
new file mode 100644
index 0000000..d170e90
--- /dev/null
+++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts
@@ -0,0 +1,153 @@
+import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
+import { DataSchedulingUpdatedEvent } from '../../entities/event/data-scheduling-updated.event';
+import {
+ DataSchedulingLogEntity,
+ SCHEDULING_LOG_ACTION_ENUM,
+ SCHEDULING_LOG_TYPE_ENUM,
+} from '../../entities/data-scheduling.entity';
+import {
+ decryptionTotal,
+ encryptionTotal,
+} from '../../../infrastructure/helpers';
+import { InjectRepository } from '@nestjs/typeorm';
+import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model';
+import { Repository } from 'typeorm';
+import { Logger } from '@nestjs/common';
+
+@EventsHandler(DataSchedulingUpdatedEvent)
+export class DataSchedulingUpdatedHandler
+ implements IEventHandler
+{
+ private readonly logger = new Logger(DataSchedulingUpdatedHandler.name);
+
+ constructor(
+ @InjectRepository(DataSchedulingLogModel)
+ private repository: Repository,
+ ) {}
+
+ // Map for readable labels
+ private readonly labelMap: { [key: string]: string } = {
+ name: 'Name',
+ indexing_key: 'Total Data',
+ schedule_date_from: 'Start Date',
+ schedule_date_to: 'End Date',
+ };
+
+ // Relevant keys for comparing changes
+ private readonly keysToCompare: string[] = [
+ 'name',
+ 'indexing_key',
+ 'schedule_date_from',
+ 'schedule_date_to',
+ ];
+
+ async handle(event: DataSchedulingUpdatedEvent) {
+ const oldData = event?.data?.old;
+ // Decrypt oldData.indexing_key here before comparison
+ if (oldData?.indexing_key !== undefined && oldData?.indexing_key !== null) {
+ oldData.indexing_key = decryptionTotal(oldData.indexing_key);
+ }
+
+ const newData = event?.data?.data;
+ // Decrypt newData.indexing_key here before comparison
+ if (newData?.indexing_key !== undefined && newData?.indexing_key !== null) {
+ newData.indexing_key = decryptionTotal(newData.indexing_key);
+ }
+
+ const changingData = this.getChangingData(oldData, newData);
+ const description = this.generateDescription(
+ oldData,
+ newData,
+ changingData,
+ );
+
+ const payload: DataSchedulingLogEntity = {
+ type: SCHEDULING_LOG_TYPE_ENUM.DATA_SCHEDULING,
+ action: SCHEDULING_LOG_ACTION_ENUM.UPDATE,
+ log_created_at: new Date().getTime(),
+
+ data_id: newData?.id,
+ name: newData?.name,
+ indexing_key: encryptionTotal(newData?.indexing_key),
+ schedule_date_from: newData?.schedule_date_from,
+ schedule_date_to: newData?.schedule_date_to,
+
+ status: newData?.status,
+ creator_id: newData?.creator_id,
+ creator_name: newData?.creator_name,
+ editor_id: newData?.editor_id,
+ editor_name: newData?.editor_name,
+ created_at: newData?.created_at,
+ updated_at: newData?.updated_at,
+ description: description,
+ };
+
+ await this.repository.save(payload as any);
+ this.logger.verbose(
+ `[SCHEDULING LOG] Update data for ID: ${payload.data_id}`,
+ );
+ }
+
+ /**
+ * Compares old and new data to find changes.
+ * @param oldData Data before the change.
+ * @param newData Data after the change.
+ * @returns An object containing the old and new changed data.
+ */
+ private getChangingData(oldData: any, newData: any): { old: any; new: any } {
+ const changingData: { old: any; new: any } = { old: {}, new: {} };
+
+ this.keysToCompare.forEach((key) => {
+ // Ensure comparisons are made on decrypted values if decryption happens before this
+ if (oldData?.[key] !== newData?.[key]) {
+ changingData.old[key] = oldData?.[key];
+ changingData.new[key] = newData?.[key];
+ }
+ });
+
+ return changingData;
+ }
+
+ /**
+ * Generates an HTML description string based on data changes.
+ * Includes the name from oldData for identification.
+ * @param oldData Old data, used to get the name of the item.
+ * @param newData New data containing editor information.
+ * @param changingData An object containing the changed data.
+ * @returns The HTML string of the change description.
+ */
+ private generateDescription(
+ oldData: any,
+ newData: any,
+ changingData: { old: any; new: any },
+ ): string {
+ const editorName = newData.editor_name || 'System';
+ const itemName = oldData?.name || 'an item';
+
+ let description = `${editorName} has updated schedule for ${itemName}.`;
+
+ if (Object.keys(changingData.old).length > 0) {
+ description += ` Change details:
`;
+ for (const key in changingData.old) {
+ if (Object.prototype.hasOwnProperty.call(changingData.old, key)) {
+ const label = this.labelMap[key] || key;
+ let oldValue = changingData.old[key] || 'empty';
+ let newValue = changingData.new[key] || 'empty';
+
+ // Add '%' suffix if the key is 'indexing_key'
+ if (key === 'indexing_key') {
+ oldValue = `${oldValue}%`;
+ newValue = `${newValue}%`;
+ }
+
+ description += `- ${label} changed from ${oldValue} to ${newValue}.
`;
+ }
+ }
+ description += `
`;
+ } else {
+ description += ` No significant data detail changes.`;
+ }
+
+ return description;
+ }
+}
From 4120e7fc1acfebeb20cfad1984931981d7ed8296 Mon Sep 17 00:00:00 2001
From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com>
Date: Thu, 10 Jul 2025 18:55:53 +0700
Subject: [PATCH 2/2] feat: integration save scheduling data log to database
---
src/app.module.ts | 2 ++
.../data-scheduling/data-scheduling.module.ts | 3 +++
.../services/data-scheduling-log.service.ts | 17 +++++++++++++++++
.../data-scheduling-change-status.handler.ts | 11 +++--------
.../handlers/data-scheduling-created.handler.ts | 11 +++--------
.../handlers/data-scheduling-deleted.handler.ts | 11 +++--------
.../handlers/data-scheduling-updated.handler.ts | 11 +++--------
7 files changed, 34 insertions(+), 32 deletions(-)
create mode 100644 src/modules/configuration/data-scheduling/data/services/data-scheduling-log.service.ts
diff --git a/src/app.module.ts b/src/app.module.ts
index 631989f..0af318e 100644
--- a/src/app.module.ts
+++ b/src/app.module.ts
@@ -110,6 +110,7 @@ import { OtpCheckerGuard } from './core/guards/domain/otp-checker.guard';
import { DataSchedulingModel } from './modules/configuration/data-scheduling/data/models/data-scheduling.model';
import { DataSchedulingModule } from './modules/configuration/data-scheduling/data-scheduling.module';
import { DataSchedulingDefaultModel } from './modules/configuration/data-scheduling/data/models/data-scheduling-default.model';
+import { DataSchedulingLogModel } from './modules/configuration/data-scheduling/data/models/data-scheduling-log.model';
@Module({
imports: [
@@ -183,6 +184,7 @@ import { DataSchedulingDefaultModel } from './modules/configuration/data-schedul
// Data Scheduling
DataSchedulingModel,
DataSchedulingDefaultModel,
+ DataSchedulingLogModel,
],
synchronize: false,
}),
diff --git a/src/modules/configuration/data-scheduling/data-scheduling.module.ts b/src/modules/configuration/data-scheduling/data-scheduling.module.ts
index ef409bd..a47c394 100644
--- a/src/modules/configuration/data-scheduling/data-scheduling.module.ts
+++ b/src/modules/configuration/data-scheduling/data-scheduling.module.ts
@@ -38,6 +38,8 @@ import { DataSchedulingUpdatedHandler } from './domain/usecases/handlers/data-sc
import { JwtModule } from '@nestjs/jwt';
import { JWT_EXPIRED } from 'src/core/sessions/constants';
import { JWT_SECRET } from 'src/core/sessions/constants';
+
+import { DataSchedulingLogService } from './data/services/data-scheduling-log.service';
import { DataSchedulingLogModel } from './data/models/data-scheduling-log.model';
@Module({
@@ -74,6 +76,7 @@ import { DataSchedulingLogModel } from './data/models/data-scheduling-log.model'
BatchConfirmDataSchedulingManager,
BatchInactiveDataSchedulingManager,
+ DataSchedulingLogService,
DataSchedulingDataService,
DataSchedulingReadService,
diff --git a/src/modules/configuration/data-scheduling/data/services/data-scheduling-log.service.ts b/src/modules/configuration/data-scheduling/data/services/data-scheduling-log.service.ts
new file mode 100644
index 0000000..b714c40
--- /dev/null
+++ b/src/modules/configuration/data-scheduling/data/services/data-scheduling-log.service.ts
@@ -0,0 +1,17 @@
+import { Injectable } from '@nestjs/common';
+import { InjectRepository } from '@nestjs/typeorm';
+import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
+import { DataSchedulingLogModel } from '../models/data-scheduling-log.model';
+import { Repository } from 'typeorm';
+
+@Injectable()
+export class DataSchedulingLogService {
+ constructor(
+ @InjectRepository(DataSchedulingLogModel, CONNECTION_NAME.DEFAULT)
+ private repo: Repository,
+ ) {}
+
+ async create(entity: any): Promise {
+ return await this.repo.save(entity);
+ }
+}
diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts
index d8074e4..b7c95dd 100644
--- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts
+++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-change-status.handler.ts
@@ -6,10 +6,8 @@ import {
SCHEDULING_LOG_ACTION_ENUM,
SCHEDULING_LOG_TYPE_ENUM,
} from '../../entities/data-scheduling.entity';
-import { InjectRepository } from '@nestjs/typeorm';
-import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model';
-import { Repository } from 'typeorm';
import { Logger } from '@nestjs/common';
+import { DataSchedulingLogService } from '../../../data/services/data-scheduling-log.service';
@EventsHandler(DataSchedulingChangeStatusEvent)
export class DataSchedulingChangeStatusHandler
@@ -17,10 +15,7 @@ export class DataSchedulingChangeStatusHandler
{
private readonly logger = new Logger(DataSchedulingChangeStatusHandler.name);
- constructor(
- @InjectRepository(DataSchedulingLogModel)
- private repository: Repository,
- ) {}
+ constructor(private service: DataSchedulingLogService) {}
async handle(event: DataSchedulingChangeStatusEvent) {
const oldData = event?.data?.old;
@@ -54,7 +49,7 @@ export class DataSchedulingChangeStatusHandler
description: description,
};
- await this.repository.save(payload as any);
+ await this.service.create(payload as any);
this.logger.verbose(
`[SCHEDULING LOG] Change status data for ID: ${payload.data_id}`,
);
diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts
index c35b8e2..b1c6c1d 100644
--- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts
+++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-created.handler.ts
@@ -6,10 +6,8 @@ import {
SCHEDULING_LOG_TYPE_ENUM,
} from '../../entities/data-scheduling.entity';
import { decryptionTotal } from '../../../infrastructure/helpers';
-import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model';
-import { InjectRepository } from '@nestjs/typeorm';
-import { Repository } from 'typeorm';
import { Logger } from '@nestjs/common';
+import { DataSchedulingLogService } from '../../../data/services/data-scheduling-log.service';
@EventsHandler(DataSchedulingCreatedEvent)
export class DataSchedulingCreatedHandler
@@ -17,10 +15,7 @@ export class DataSchedulingCreatedHandler
{
private readonly logger = new Logger(DataSchedulingCreatedHandler.name);
- constructor(
- @InjectRepository(DataSchedulingLogModel)
- private repository: Repository,
- ) {}
+ constructor(private service: DataSchedulingLogService) {}
async handle(event: DataSchedulingCreatedEvent) {
const data = event?.data?.data;
@@ -50,7 +45,7 @@ export class DataSchedulingCreatedHandler
description: description,
};
- await this.repository.save(payload as any);
+ await this.service.create(payload as any);
this.logger.verbose(
`[SCHEDULING LOG] Create data for ID: ${payload.data_id}`,
);
diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts
index 8ba8e87..d9a13b7 100644
--- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts
+++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-deleted.handler.ts
@@ -5,10 +5,8 @@ import {
SCHEDULING_LOG_ACTION_ENUM,
SCHEDULING_LOG_TYPE_ENUM,
} from '../../entities/data-scheduling.entity';
-import { InjectRepository } from '@nestjs/typeorm';
-import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model';
-import { Repository } from 'typeorm';
import { Logger } from '@nestjs/common';
+import { DataSchedulingLogService } from '../../../data/services/data-scheduling-log.service';
@EventsHandler(DataSchedulingDeletedEvent)
export class DataSchedulingDeletedHandler
@@ -16,10 +14,7 @@ export class DataSchedulingDeletedHandler
{
private readonly logger = new Logger(DataSchedulingDeletedHandler.name);
- constructor(
- @InjectRepository(DataSchedulingLogModel)
- private repository: Repository,
- ) {}
+ constructor(private service: DataSchedulingLogService) {}
async handle(event: DataSchedulingDeletedEvent) {
const deletedData = event?.data?.data;
@@ -56,7 +51,7 @@ export class DataSchedulingDeletedHandler
description: description,
};
- await this.repository.save(payload as any);
+ await this.service.create(payload as any);
this.logger.verbose(
`[SCHEDULING LOG] Delete data for ID: ${payload.data_id}`,
);
diff --git a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts
index d170e90..2d45674 100644
--- a/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts
+++ b/src/modules/configuration/data-scheduling/domain/usecases/handlers/data-scheduling-updated.handler.ts
@@ -9,10 +9,8 @@ import {
decryptionTotal,
encryptionTotal,
} from '../../../infrastructure/helpers';
-import { InjectRepository } from '@nestjs/typeorm';
-import { DataSchedulingLogModel } from '../../../data/models/data-scheduling-log.model';
-import { Repository } from 'typeorm';
import { Logger } from '@nestjs/common';
+import { DataSchedulingLogService } from '../../../data/services/data-scheduling-log.service';
@EventsHandler(DataSchedulingUpdatedEvent)
export class DataSchedulingUpdatedHandler
@@ -20,10 +18,7 @@ export class DataSchedulingUpdatedHandler
{
private readonly logger = new Logger(DataSchedulingUpdatedHandler.name);
- constructor(
- @InjectRepository(DataSchedulingLogModel)
- private repository: Repository,
- ) {}
+ constructor(private service: DataSchedulingLogService) {}
// Map for readable labels
private readonly labelMap: { [key: string]: string } = {
@@ -82,7 +77,7 @@ export class DataSchedulingUpdatedHandler
description: description,
};
- await this.repository.save(payload as any);
+ await this.service.create(payload as any);
this.logger.verbose(
`[SCHEDULING LOG] Update data for ID: ${payload.data_id}`,
);