From 5b507a1c3cce450b9d4de6f74fa1029d94e8610e Mon Sep 17 00:00:00 2001 From: shancheas Date: Fri, 25 Oct 2024 14:49:56 +0700 Subject: [PATCH] feat: add queue admin list --- src/app.module.ts | 2 + src/core/strings/constants/table.constants.ts | 1 + .../1729756969674-add-queue-table.ts | 22 ++++++ .../1729838994129-add-queue-base-model.ts | 39 +++++++++++ .../data/services/item-queue-read.service.ts | 4 ++ .../usecases/item-queue-read.orchestrator.ts | 5 ++ .../item-queue-read.controller.ts | 13 ++++ src/modules/queue/data/models/queue.model.ts | 40 ++++++++++- .../queue/data/services/queue.service.ts | 17 +++++ .../queue/domain/entities/filter.entity.ts | 4 ++ .../queue/domain/entities/queue.entity.ts | 12 ++++ .../queue/domain/queue-admin.orchestrator.ts | 21 ++++++ .../domain/usecases/index-queue.manager.ts | 69 +++++++++++++++++++ .../controllers/dto/queue.filter.ts | 24 +++++++ .../controllers/queue-admin.controller.ts | 23 +++++++ src/modules/queue/queue.module.ts | 12 +++- 16 files changed, 306 insertions(+), 2 deletions(-) create mode 100644 src/database/migrations/1729756969674-add-queue-table.ts create mode 100644 src/database/migrations/1729838994129-add-queue-base-model.ts create mode 100644 src/modules/queue/data/services/queue.service.ts create mode 100644 src/modules/queue/domain/entities/filter.entity.ts create mode 100644 src/modules/queue/domain/entities/queue.entity.ts create mode 100644 src/modules/queue/domain/queue-admin.orchestrator.ts create mode 100644 src/modules/queue/domain/usecases/index-queue.manager.ts create mode 100644 src/modules/queue/infrastructure/controllers/dto/queue.filter.ts create mode 100644 src/modules/queue/infrastructure/controllers/queue-admin.controller.ts diff --git a/src/app.module.ts b/src/app.module.ts index 36efd42..e8696ac 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -88,6 +88,7 @@ import { QueueOrderModel, QueueTicketModel, QueueItemModel, + QueueModel, } from './modules/queue/data/models/queue.model'; import { ItemQueueModule } from './modules/item-related/item-queue/item-queue.module'; import { ItemQueueModel } from './modules/item-related/item-queue/data/models/item-queue.model'; @@ -149,6 +150,7 @@ import { ItemQueueModel } from './modules/item-related/item-queue/data/models/it QueueOrderModel, QueueTicketModel, QueueItemModel, + QueueModel, ], synchronize: false, }), diff --git a/src/core/strings/constants/table.constants.ts b/src/core/strings/constants/table.constants.ts index 13eb804..1e5129e 100644 --- a/src/core/strings/constants/table.constants.ts +++ b/src/core/strings/constants/table.constants.ts @@ -37,6 +37,7 @@ export enum TABLE_NAME { REPORT_BOOKMARK = 'report_bookmark', EXPORT_REPORT_HISTORY = 'export_report_history', + QUEUE = 'queues', QUEUE_ORDER = 'queue_orders', QUEUE_TICKET = 'queue_tickets', QUEUE_ITEM = 'queue_items', diff --git a/src/database/migrations/1729756969674-add-queue-table.ts b/src/database/migrations/1729756969674-add-queue-table.ts new file mode 100644 index 0000000..6b25c0c --- /dev/null +++ b/src/database/migrations/1729756969674-add-queue-table.ts @@ -0,0 +1,22 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddQueueTable1729756969674 implements MigrationInterface { + name = 'AddQueueTable1729756969674'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "queues" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "code" character varying NOT NULL, "status" character varying NOT NULL, "time" bigint NOT NULL, "call_time" bigint NOT NULL, "vip" boolean NOT NULL, "item_id" uuid NOT NULL, "qty" integer NOT NULL, CONSTRAINT "PK_d966f9eb39a9396658387071bb3" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `ALTER TABLE "queues" ADD CONSTRAINT "FK_435954e9a0d9967f17e043d54b4" FOREIGN KEY ("item_id") REFERENCES "queue_items"("id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "queues" DROP CONSTRAINT "FK_435954e9a0d9967f17e043d54b4"`, + ); + + await queryRunner.query(`DROP TABLE "queues"`); + } +} diff --git a/src/database/migrations/1729838994129-add-queue-base-model.ts b/src/database/migrations/1729838994129-add-queue-base-model.ts new file mode 100644 index 0000000..5a85010 --- /dev/null +++ b/src/database/migrations/1729838994129-add-queue-base-model.ts @@ -0,0 +1,39 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddQueueBaseModel1729838994129 implements MigrationInterface { + name = 'AddQueueBaseModel1729838994129'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "queues" ADD "creator_id" character varying(36)`, + ); + await queryRunner.query( + `ALTER TABLE "queues" ADD "creator_name" character varying(125)`, + ); + await queryRunner.query( + `ALTER TABLE "queues" ADD "editor_id" character varying(36)`, + ); + await queryRunner.query( + `ALTER TABLE "queues" ADD "editor_name" character varying(125)`, + ); + await queryRunner.query( + `ALTER TABLE "queues" ADD "created_at" bigint NOT NULL`, + ); + await queryRunner.query( + `ALTER TABLE "queues" ADD "updated_at" bigint NOT NULL`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "queues" ALTER COLUMN "call_time" DROP NOT NULL`, + ); + + await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "updated_at"`); + await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "created_at"`); + await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "editor_name"`); + await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "editor_id"`); + await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "creator_name"`); + await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "creator_id"`); + } +} diff --git a/src/modules/item-related/item-queue/data/services/item-queue-read.service.ts b/src/modules/item-related/item-queue/data/services/item-queue-read.service.ts index 64414f6..40d3ab9 100644 --- a/src/modules/item-related/item-queue/data/services/item-queue-read.service.ts +++ b/src/modules/item-related/item-queue/data/services/item-queue-read.service.ts @@ -14,4 +14,8 @@ export class ItemQueueReadService extends BaseReadService { ) { super(repo); } + + async list(): Promise { + return this.repo.find(); + } } diff --git a/src/modules/item-related/item-queue/domain/usecases/item-queue-read.orchestrator.ts b/src/modules/item-related/item-queue/domain/usecases/item-queue-read.orchestrator.ts index 281d389..4c533fc 100644 --- a/src/modules/item-related/item-queue/domain/usecases/item-queue-read.orchestrator.ts +++ b/src/modules/item-related/item-queue/domain/usecases/item-queue-read.orchestrator.ts @@ -24,6 +24,11 @@ export class ItemQueueReadOrchestrator extends BaseReadOrchestrator { + const items = await this.serviceData.list(); + return items; + } + async detail(dataId: string): Promise { this.detailManager.setData(dataId); this.detailManager.setService(this.serviceData, TABLE_NAME.ITEM_QUEUE); diff --git a/src/modules/item-related/item-queue/infrastructure/item-queue-read.controller.ts b/src/modules/item-related/item-queue/infrastructure/item-queue-read.controller.ts index 54490f7..8932974 100644 --- a/src/modules/item-related/item-queue/infrastructure/item-queue-read.controller.ts +++ b/src/modules/item-related/item-queue/infrastructure/item-queue-read.controller.ts @@ -23,6 +23,19 @@ export class ItemQueueReadController { return await this.orchestrator.index(params); } + @Get('list') + @Public(true) + async list() { + const list = await this.orchestrator.list(); + return list.map(({ id, name, item_type }) => { + return { + id, + name, + item_type, + }; + }); + } + @Get(':id') async detail(@Param('id') id: string): Promise { return await this.orchestrator.detail(id); diff --git a/src/modules/queue/data/models/queue.model.ts b/src/modules/queue/data/models/queue.model.ts index c5377f4..dcdd18c 100644 --- a/src/modules/queue/data/models/queue.model.ts +++ b/src/modules/queue/data/models/queue.model.ts @@ -4,9 +4,10 @@ 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'; +import { Queue } from '../../domain/entities/queue.entity'; +import { BaseModel } from 'src/core/modules/data/model/base.model'; @Entity(TABLE_NAME.QUEUE_ORDER) export class QueueOrderModel @@ -83,6 +84,12 @@ export class QueueItemModel @JoinColumn({ name: 'ticket_id' }) ticket: QueueTicket; + @OneToMany(() => QueueModel, (model) => model.item, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + queue: QueueModel[]; + @Column('varchar') item_id: string; @@ -93,3 +100,34 @@ export class QueueItemModel @Column('int') qty: number; } + +@Entity(TABLE_NAME.QUEUE) +export class QueueModel extends BaseModel implements Queue { + @Column('varchar') + code: string; + + @Column('varchar') + status: string; + + @Column({ type: 'bigint' }) + time: number; + + @Column({ type: 'bigint' }) + call_time: number; + + @Column({ type: 'boolean' }) + vip: boolean; + + @ManyToOne(() => QueueItemModel, (model) => model.queue, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + @JoinColumn({ name: 'item_id' }) + item: QueueItemModel; + + @Column('varchar') + item_id: string; + + @Column('int') + qty: number; +} diff --git a/src/modules/queue/data/services/queue.service.ts b/src/modules/queue/data/services/queue.service.ts new file mode 100644 index 0000000..18ed187 --- /dev/null +++ b/src/modules/queue/data/services/queue.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 { Repository } from 'typeorm'; +import { QueueModel } from '../models/queue.model'; +import { BaseReadService } from 'src/core/modules/data/service/base-read.service'; + +@Injectable() +export class QueueDataService extends BaseReadService { + constructor( + @InjectRepository(QueueModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) { + super(repo); + } +} diff --git a/src/modules/queue/domain/entities/filter.entity.ts b/src/modules/queue/domain/entities/filter.entity.ts new file mode 100644 index 0000000..3bd24f5 --- /dev/null +++ b/src/modules/queue/domain/entities/filter.entity.ts @@ -0,0 +1,4 @@ +export interface FilterQueueEntity { + vip: boolean; + status: string[]; +} diff --git a/src/modules/queue/domain/entities/queue.entity.ts b/src/modules/queue/domain/entities/queue.entity.ts new file mode 100644 index 0000000..fea1945 --- /dev/null +++ b/src/modules/queue/domain/entities/queue.entity.ts @@ -0,0 +1,12 @@ +import { BaseCoreEntity } from 'src/core/modules/domain/entities/base-core.entity'; +import { QueueItem } from './queue-item.entity'; + +export interface Queue extends BaseCoreEntity { + code: string; + qty: number; + status: string; + time: number; + vip: boolean; + call_time: number; + item: QueueItem; +} diff --git a/src/modules/queue/domain/queue-admin.orchestrator.ts b/src/modules/queue/domain/queue-admin.orchestrator.ts new file mode 100644 index 0000000..ef9664a --- /dev/null +++ b/src/modules/queue/domain/queue-admin.orchestrator.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; +import { QueueDataService } from '../data/services/queue.service'; +import { IndexQueueManager } from './usecases/index-queue.manager'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { Queue } from './entities/queue.entity'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; + +@Injectable() +export class QueueAdminOrchestrator { + constructor( + private readonly dataService: QueueDataService, + private indexManager: IndexQueueManager, + ) {} + + async index(params): Promise> { + this.indexManager.setFilterParam(params); + this.indexManager.setService(this.dataService, TABLE_NAME.QUEUE); + await this.indexManager.execute(); + return this.indexManager.getResult(); + } +} diff --git a/src/modules/queue/domain/usecases/index-queue.manager.ts b/src/modules/queue/domain/usecases/index-queue.manager.ts new file mode 100644 index 0000000..f94d39f --- /dev/null +++ b/src/modules/queue/domain/usecases/index-queue.manager.ts @@ -0,0 +1,69 @@ +import { Injectable } from '@nestjs/common'; +import { BaseIndexManager } from 'src/core/modules/domain/usecase/managers/base-index.manager'; +import { SelectQueryBuilder } from 'typeorm'; +import { + Param, + RelationParam, +} from 'src/core/modules/domain/entities/base-filter.entity'; +import { Queue } from '../entities/queue.entity'; + +@Injectable() +export class IndexQueueManager extends BaseIndexManager { + async prepareData(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get relations(): RelationParam { + return { + joinRelations: [], + selectRelations: ['item', 'item.ticket'], + countRelations: [], + }; + } + + get selects(): string[] { + return [ + `${this.tableName}.id`, + `${this.tableName}.code`, + `${this.tableName}.status`, + `${this.tableName}.time`, + `${this.tableName}.call_time`, + `${this.tableName}.vip`, + `${this.tableName}.item`, + `${this.tableName}.qty`, + `${this.tableName}.created_at`, + + `item.id`, + `ticket.customer`, + `ticket.phone`, + ]; + } + + get specificFilter(): Param[] { + return [ + { + cols: `${this.tableName}.status`, + data: this.filterParam.status, + }, + ]; + } + + setQueryFilter( + queryBuilder: SelectQueryBuilder, + ): SelectQueryBuilder { + if (this.filterParam.vip != null) { + queryBuilder.andWhere(`${this.tableName}.vip = :vip`, { + vip: this.filterParam.vip, + }); + } + return queryBuilder; + } +} diff --git a/src/modules/queue/infrastructure/controllers/dto/queue.filter.ts b/src/modules/queue/infrastructure/controllers/dto/queue.filter.ts new file mode 100644 index 0000000..c48bb27 --- /dev/null +++ b/src/modules/queue/infrastructure/controllers/dto/queue.filter.ts @@ -0,0 +1,24 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsBoolean, ValidateIf } from 'class-validator'; +import { Transform } from 'class-transformer'; +import { FilterQueueEntity } from 'src/modules/queue/domain/entities/filter.entity'; + +export class QueueDto implements FilterQueueEntity { + @ApiProperty({ + isArray: true, + required: false, + }) + // @IsString() + @ValidateIf((body) => body.status) + @Transform(({ value }) => { + if (!value) return []; + return Array.isArray(value) ? value : [value]; + }) + status: string[]; + + @ApiProperty({ type: Boolean, required: false }) + @Transform((body) => body.value == 'true') + @IsBoolean() + @ValidateIf((body) => body.vip) + vip: boolean; +} diff --git a/src/modules/queue/infrastructure/controllers/queue-admin.controller.ts b/src/modules/queue/infrastructure/controllers/queue-admin.controller.ts new file mode 100644 index 0000000..2694854 --- /dev/null +++ b/src/modules/queue/infrastructure/controllers/queue-admin.controller.ts @@ -0,0 +1,23 @@ +import { Controller, Get, Query } 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 { QueueAdminOrchestrator } from '../../domain/queue-admin.orchestrator'; +import { PaginationResponse } from 'src/core/response/domain/ok-response.interface'; +import { Queue } from '../../domain/entities/queue.entity'; +import { QueueDto } from './dto/queue.filter'; + +@ApiTags(`Queue Admin`) +@Controller(`v1/${MODULE_NAME.QUEUE}-admin`) +@Public(true) +@ApiBearerAuth('JWT') +export class QueueAdminController { + constructor(private orchestrator: QueueAdminOrchestrator) {} + + @Get('queues') + async index(@Query() params: QueueDto): Promise> { + return await this.orchestrator.index(params); + } +} diff --git a/src/modules/queue/queue.module.ts b/src/modules/queue/queue.module.ts index 3485f20..0f51e9e 100644 --- a/src/modules/queue/queue.module.ts +++ b/src/modules/queue/queue.module.ts @@ -8,6 +8,7 @@ import { CqrsModule } from '@nestjs/cqrs'; import { ItemModel } from 'src/modules/item-related/item/data/models/item.model'; import { QueueItemModel, + QueueModel, QueueOrderModel, QueueTicketModel, } from './data/models/queue.model'; @@ -17,6 +18,10 @@ import { QueueTransactionHandler } from './infrastructure/handlers/transaction.h 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'; +import { QueueDataService } from './data/services/queue.service'; +import { QueueAdminController } from './infrastructure/controllers/queue-admin.controller'; +import { QueueAdminOrchestrator } from './domain/queue-admin.orchestrator'; +import { IndexQueueManager } from './domain/usecases/index-queue.manager'; @Module({ imports: [ @@ -26,6 +31,7 @@ import { TicketDataService } from './data/services/ticket.service'; QueueOrderModel, QueueTicketModel, QueueItemModel, + QueueModel, ItemModel, TransactionModel, ], @@ -33,14 +39,18 @@ import { TicketDataService } from './data/services/ticket.service'; ), CqrsModule, ], - controllers: [QueueController], + controllers: [QueueController, QueueAdminController], providers: [ QueueTransactionHandler, QueueOrchestrator, + QueueAdminOrchestrator, TransactionDataService, TicketDataService, + QueueDataService, + + IndexQueueManager, ], }) export class QueueModule {}