From 2d0ccf67f2a7bccc0568162f18f7e943c248647a Mon Sep 17 00:00:00 2001 From: shancheas Date: Tue, 22 Oct 2024 11:47:22 +0700 Subject: [PATCH] WIP: module Queue --- .../strings/constants/module.constants.ts | 3 + src/core/strings/constants/table.constants.ts | 5 ++ .../migrations/1729151429165-queue-table.ts | 43 +++++++++ .../1729248576381-video-url-to-json.ts | 18 ++++ .../migrations/1729570177597-item-queues.ts | 33 +++++++ .../item-related/item-queue/constants.ts | 7 ++ .../infrastructure/item-read.controller.ts | 28 +++--- src/modules/queue/data/models/queue.model.ts | 89 +++++++++++++++++++ .../queue/data/services/ticket.service.ts | 25 ++++++ .../queue/domain/entities/order.entity.ts | 10 +++ .../domain/entities/queue-item.entity.ts | 10 +++ .../queue/domain/entities/ticket.entity.ts | 13 +++ .../queue/domain/queue.orchestrator.ts | 6 ++ src/modules/queue/index.ts | 0 .../controllers/queue.controller.ts | 24 +++++ src/modules/queue/queue.module.ts | 46 ++++++++++ 16 files changed, 346 insertions(+), 14 deletions(-) create mode 100644 src/database/migrations/1729151429165-queue-table.ts create mode 100644 src/database/migrations/1729248576381-video-url-to-json.ts create mode 100644 src/database/migrations/1729570177597-item-queues.ts create mode 100644 src/modules/item-related/item-queue/constants.ts create mode 100644 src/modules/queue/data/models/queue.model.ts create mode 100644 src/modules/queue/data/services/ticket.service.ts create mode 100644 src/modules/queue/domain/entities/order.entity.ts create mode 100644 src/modules/queue/domain/entities/queue-item.entity.ts create mode 100644 src/modules/queue/domain/entities/ticket.entity.ts create mode 100644 src/modules/queue/domain/queue.orchestrator.ts create mode 100644 src/modules/queue/index.ts create mode 100644 src/modules/queue/infrastructure/controllers/queue.controller.ts create mode 100644 src/modules/queue/queue.module.ts diff --git a/src/core/strings/constants/module.constants.ts b/src/core/strings/constants/module.constants.ts index f78ebca..b05838e 100644 --- a/src/core/strings/constants/module.constants.ts +++ b/src/core/strings/constants/module.constants.ts @@ -4,6 +4,7 @@ export enum MODULE_NAME { GATE = 'gates', ITEM = 'items', ITEM_CATEGORY = 'item-categories', + ITEM_QUEUE = 'item-queues', ITEM_RATE = 'item-rates', NEWS = 'news', PAYMENT_METHOD = 'payment-methods', @@ -25,4 +26,6 @@ export enum MODULE_NAME { REPORT_BOOKMARK = 'report-bookmark', REPORT_EXPORT = 'report-export', REPORT_SUMMARY = 'report-summary', + + QUEUE = 'queue', } diff --git a/src/core/strings/constants/table.constants.ts b/src/core/strings/constants/table.constants.ts index 990c4cb..13eb804 100644 --- a/src/core/strings/constants/table.constants.ts +++ b/src/core/strings/constants/table.constants.ts @@ -4,6 +4,7 @@ export enum TABLE_NAME { FAQ = 'faqs', ITEM = 'items', ITEM_CATEGORY = 'item_categories', + ITEM_QUEUE = 'item_queues', ITEM_RATE = 'item_rates', GATE = 'gates', LOG = 'logs', @@ -35,4 +36,8 @@ export enum TABLE_NAME { REPORT_BOOKMARK = 'report_bookmark', EXPORT_REPORT_HISTORY = 'export_report_history', + + QUEUE_ORDER = 'queue_orders', + QUEUE_TICKET = 'queue_tickets', + QUEUE_ITEM = 'queue_items', } diff --git a/src/database/migrations/1729151429165-queue-table.ts b/src/database/migrations/1729151429165-queue-table.ts new file mode 100644 index 0000000..27f1dd7 --- /dev/null +++ b/src/database/migrations/1729151429165-queue-table.ts @@ -0,0 +1,43 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class QueueTable1729151429165 implements MigrationInterface { + name = 'QueueTable1729151429165'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "queue_orders" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "code" character varying NOT NULL, "customer" character varying, "phone" character varying, "date" bigint NOT NULL, CONSTRAINT "PK_b139e4cc9ca3e709c152f820d2e" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TABLE "queue_tickets" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "code" character varying NOT NULL, "customer" character varying, "phone" character varying, "date" bigint NOT NULL, "order_id" uuid, CONSTRAINT "PK_1b903aa90bcc04136caa6540c55" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `CREATE TABLE "queue_items" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "qty" integer NOT NULL, "ticket_id" uuid, "item_id" uuid, CONSTRAINT "PK_2245e11ac3517494bacfe932773" PRIMARY KEY ("id"))`, + ); + + await queryRunner.query( + `ALTER TABLE "queue_tickets" ADD CONSTRAINT "FK_0e9823b8b7ca9523b3be73878e5" FOREIGN KEY ("order_id") REFERENCES "queue_orders"("id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + await queryRunner.query( + `ALTER TABLE "queue_items" ADD CONSTRAINT "FK_25352739034765f6917757df74b" FOREIGN KEY ("ticket_id") REFERENCES "queue_tickets"("id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + await queryRunner.query( + `ALTER TABLE "queue_items" ADD CONSTRAINT "FK_ab15c053aeb4f739ebf533b61cd" FOREIGN KEY ("item_id") REFERENCES "items"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "queue_items" DROP CONSTRAINT "FK_ab15c053aeb4f739ebf533b61cd"`, + ); + await queryRunner.query( + `ALTER TABLE "queue_items" DROP CONSTRAINT "FK_25352739034765f6917757df74b"`, + ); + await queryRunner.query( + `ALTER TABLE "queue_tickets" DROP CONSTRAINT "FK_0e9823b8b7ca9523b3be73878e5"`, + ); + + await queryRunner.query(`DROP TABLE "queue_items"`); + await queryRunner.query(`DROP TABLE "queue_tickets"`); + await queryRunner.query(`DROP TABLE "queue_orders"`); + } +} diff --git a/src/database/migrations/1729248576381-video-url-to-json.ts b/src/database/migrations/1729248576381-video-url-to-json.ts new file mode 100644 index 0000000..4761e3c --- /dev/null +++ b/src/database/migrations/1729248576381-video-url-to-json.ts @@ -0,0 +1,18 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class VideoUrlToJson1729248576381 implements MigrationInterface { + name = 'VideoUrlToJson1729248576381'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "items" DROP COLUMN "video_url"`); + await queryRunner.query(`ALTER TABLE "items" ADD "video_url" json`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "items" DROP COLUMN "video_url"`); + await queryRunner.query( + `ALTER TABLE "items" ADD "video_url" character varying`, + ); + await queryRunner.query(`DROP TABLE "item_queues"`); + } +} diff --git a/src/database/migrations/1729570177597-item-queues.ts b/src/database/migrations/1729570177597-item-queues.ts new file mode 100644 index 0000000..96acb18 --- /dev/null +++ b/src/database/migrations/1729570177597-item-queues.ts @@ -0,0 +1,33 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class ItemQueues1729570177597 implements MigrationInterface { + name = 'ItemQueues1729570177597'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "public"."item_queues_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"."item_queues_item_type_enum" AS ENUM('tiket masuk', 'wahana', 'bundling', 'free gift', 'other')`, + ); + await queryRunner.query( + `CREATE TABLE "item_queues" ("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"."item_queues_status_enum" NOT NULL DEFAULT 'draft', "name" character varying NOT NULL, "item_type" "public"."item_queues_item_type_enum" NOT NULL DEFAULT 'tiket masuk', CONSTRAINT "PK_e19adb0b99d995e8f10c189985f" PRIMARY KEY ("id"))`, + ); + await queryRunner.query(`ALTER TABLE "items" ADD "item_queue_id" uuid`); + + await queryRunner.query( + `ALTER TABLE "items" ADD CONSTRAINT "FK_2cbbeb03e176addcf60d65f7c9c" FOREIGN KEY ("item_queue_id") REFERENCES "item_queues"("id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "items" DROP CONSTRAINT "FK_2cbbeb03e176addcf60d65f7c9c"`, + ); + + await queryRunner.query(`ALTER TABLE "items" DROP COLUMN "item_queue_id"`); + await queryRunner.query(`DROP TABLE "item_queues"`); + await queryRunner.query(`DROP TYPE "public"."item_queues_item_type_enum"`); + await queryRunner.query(`DROP TYPE "public"."item_queues_status_enum"`); + } +} diff --git a/src/modules/item-related/item-queue/constants.ts b/src/modules/item-related/item-queue/constants.ts new file mode 100644 index 0000000..9b62bb1 --- /dev/null +++ b/src/modules/item-related/item-queue/constants.ts @@ -0,0 +1,7 @@ +export enum ItemType { + TIKET_MASUK = 'tiket masuk', + WAHANA = 'wahana', + BUNDLING = 'bundling', + FREE_GIFT = 'free gift', + OTHER = 'other', +} diff --git a/src/modules/item-related/item/infrastructure/item-read.controller.ts b/src/modules/item-related/item/infrastructure/item-read.controller.ts index 91c7ee5..ae27dfb 100644 --- a/src/modules/item-related/item/infrastructure/item-read.controller.ts +++ b/src/modules/item-related/item/infrastructure/item-read.controller.ts @@ -41,18 +41,18 @@ export class ItemReadController { } } -@ApiTags(`Item Queue - Read`) -@Controller(`v1/item-queue`) -@Public(true) -export class ItemReadQueueController { - constructor(private orchestrator: ItemReadOrchestrator) {} +// @ApiTags(`Item Queue - Read`) +// @Controller(`v1/item-queue`) +// @Public(true) +// export class ItemReadQueueController { +// constructor(private orchestrator: ItemReadOrchestrator) {} - @Get() - @Pagination() - @ExcludePrivilege() - async indexQueue( - @Query() params: FilterItemDto, - ): Promise> { - return await this.orchestrator.indexQueue(params); - } -} +// @Get() +// @Pagination() +// @ExcludePrivilege() +// async indexQueue( +// @Query() params: FilterItemDto, +// ): Promise> { +// return await this.orchestrator.indexQueue(params); +// } +// } diff --git a/src/modules/queue/data/models/queue.model.ts b/src/modules/queue/data/models/queue.model.ts new file mode 100644 index 0000000..5e65c73 --- /dev/null +++ b/src/modules/queue/data/models/queue.model.ts @@ -0,0 +1,89 @@ +import { BaseCoreModel } from 'src/core/modules/data/model/base-core.model'; +import { QueueOrder } from '../../domain/entities/order.entity'; +import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { QueueTicket } from '../../domain/entities/ticket.entity'; +import { QueueItem } from '../../domain/entities/queue-item.entity'; +import { ItemEntity } from 'src/modules/item-related/item/domain/entities/item.entity'; + +import { ItemModel } from 'src/modules/item-related/item/data/models/item.model'; + +@Entity(TABLE_NAME.QUEUE_ORDER) +export class QueueOrderModel + extends BaseCoreModel + implements QueueOrder +{ + @Column('varchar') + code: string; + + @Column('varchar', { nullable: true }) + customer: string; + + @Column('varchar', { nullable: true }) + phone: string; + + @OneToMany(() => QueueTicketModel, (model) => model.order, { + cascade: true, + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + tickets: QueueTicket[]; + + @Column({ type: 'bigint' }) + date: number; +} + +@Entity(TABLE_NAME.QUEUE_TICKET) +export class QueueTicketModel + extends BaseCoreModel + implements QueueTicket +{ + @Column('varchar') + code: string; + + @Column('varchar', { nullable: true }) + customer: string; + + @Column('varchar', { nullable: true }) + phone: string; + + @ManyToOne(() => QueueOrderModel, (model) => model.tickets, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + @JoinColumn({ name: 'order_id' }) + order: QueueOrder; + + @Column({ type: 'bigint' }) + date: number; + + @OneToMany(() => QueueItemModel, (model) => model.ticket, { + cascade: true, + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + items: QueueItem[]; +} + +@Entity(TABLE_NAME.QUEUE_ITEM) +export class QueueItemModel + extends BaseCoreModel + implements QueueItem +{ + @ManyToOne(() => QueueTicketModel, (model) => model.items, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + @JoinColumn({ name: 'ticket_id' }) + ticket: QueueTicket; + + @Column('varchar') + item_id: string; + + @ManyToOne(() => ItemModel) + @JoinColumn({ name: 'item_id' }) + item: ItemEntity; + + @Column('int') + qty: number; +} diff --git a/src/modules/queue/data/services/ticket.service.ts b/src/modules/queue/data/services/ticket.service.ts new file mode 100644 index 0000000..cd408d0 --- /dev/null +++ b/src/modules/queue/data/services/ticket.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@nestjs/common'; +import { BaseDataService } from 'src/core/modules/data/service/base-data.service'; +import { InjectRepository } from '@nestjs/typeorm'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { Repository } from 'typeorm'; +import { QueueTicket } from '../../domain/entities/ticket.entity'; +import { QueueOrderModel, QueueTicketModel } from '../models/queue.model'; +import { QueueOrder } from '../../domain/entities/order.entity'; + +@Injectable() +export class TicketDataService extends BaseDataService { + constructor( + @InjectRepository(QueueTicketModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + + @InjectRepository(QueueOrderModel, CONNECTION_NAME.DEFAULT) + private order: Repository, + ) { + super(repo); + } + + async createQueueOrder(order: QueueOrder): Promise { + return await this.order.save(order); + } +} diff --git a/src/modules/queue/domain/entities/order.entity.ts b/src/modules/queue/domain/entities/order.entity.ts new file mode 100644 index 0000000..0ce59f2 --- /dev/null +++ b/src/modules/queue/domain/entities/order.entity.ts @@ -0,0 +1,10 @@ +import { BaseCoreEntity } from 'src/core/modules/domain/entities/base-core.entity'; +import { QueueTicket } from './ticket.entity'; + +export interface QueueOrder extends BaseCoreEntity { + code: string; + customer: string; + phone: string; + date: number; + tickets: QueueTicket[]; +} diff --git a/src/modules/queue/domain/entities/queue-item.entity.ts b/src/modules/queue/domain/entities/queue-item.entity.ts new file mode 100644 index 0000000..f506ac4 --- /dev/null +++ b/src/modules/queue/domain/entities/queue-item.entity.ts @@ -0,0 +1,10 @@ +import { BaseCoreEntity } from 'src/core/modules/domain/entities/base-core.entity'; +import { ItemEntity } from 'src/modules/item-related/item/domain/entities/item.entity'; +import { QueueTicket } from './ticket.entity'; + +export interface QueueItem extends BaseCoreEntity { + ticket?: QueueTicket; + item?: ItemEntity; + item_id: string; + qty: number; +} diff --git a/src/modules/queue/domain/entities/ticket.entity.ts b/src/modules/queue/domain/entities/ticket.entity.ts new file mode 100644 index 0000000..915990c --- /dev/null +++ b/src/modules/queue/domain/entities/ticket.entity.ts @@ -0,0 +1,13 @@ +import { BaseCoreEntity } from 'src/core/modules/domain/entities/base-core.entity'; +import { QueueOrder } from './order.entity'; +import { QueueItem } from './queue-item.entity'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface QueueTicket extends BaseCoreEntity { + code: string; + customer: string; + phone: string; + date: number; + order?: QueueOrder; + items: QueueItem[]; +} diff --git a/src/modules/queue/domain/queue.orchestrator.ts b/src/modules/queue/domain/queue.orchestrator.ts new file mode 100644 index 0000000..1126b28 --- /dev/null +++ b/src/modules/queue/domain/queue.orchestrator.ts @@ -0,0 +1,6 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class QueueOrchestrator { + // constructor() {} +} diff --git a/src/modules/queue/index.ts b/src/modules/queue/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/queue/infrastructure/controllers/queue.controller.ts b/src/modules/queue/infrastructure/controllers/queue.controller.ts new file mode 100644 index 0000000..d5ab232 --- /dev/null +++ b/src/modules/queue/infrastructure/controllers/queue.controller.ts @@ -0,0 +1,24 @@ +import { + Body, + Controller, + Delete, + Param, + Patch, + Post, + Put, + Res, +} from '@nestjs/common'; + +import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; +import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; + +import { Public } from 'src/core/guards'; +import { QueueOrchestrator } from '../../domain/queue.orchestrator'; + +@ApiTags(`Queue`) +@Controller(`v1/${MODULE_NAME.QUEUE}`) +@Public(false) +@ApiBearerAuth('JWT') +export class QueueController { + constructor(private orchestrator: QueueOrchestrator) {} +} diff --git a/src/modules/queue/queue.module.ts b/src/modules/queue/queue.module.ts new file mode 100644 index 0000000..3485f20 --- /dev/null +++ b/src/modules/queue/queue.module.ts @@ -0,0 +1,46 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; + +import { CqrsModule } from '@nestjs/cqrs'; + +import { ItemModel } from 'src/modules/item-related/item/data/models/item.model'; +import { + QueueItemModel, + QueueOrderModel, + QueueTicketModel, +} from './data/models/queue.model'; +import { QueueController } from './infrastructure/controllers/queue.controller'; +import { QueueOrchestrator } from './domain/queue.orchestrator'; +import { QueueTransactionHandler } from './infrastructure/handlers/transaction.handler'; +import { TransactionDataService } from '../transaction/transaction/data/services/transaction-data.service'; +import { TransactionModel } from '../transaction/transaction/data/models/transaction.model'; +import { TicketDataService } from './data/services/ticket.service'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + TypeOrmModule.forFeature( + [ + QueueOrderModel, + QueueTicketModel, + QueueItemModel, + ItemModel, + TransactionModel, + ], + CONNECTION_NAME.DEFAULT, + ), + CqrsModule, + ], + controllers: [QueueController], + providers: [ + QueueTransactionHandler, + + QueueOrchestrator, + + TransactionDataService, + TicketDataService, + ], +}) +export class QueueModule {}