fix: missing logic
parent
5ef7521e9b
commit
dac42b754c
|
@ -0,0 +1,15 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddQueueBucket1730859187883 implements MigrationInterface {
|
||||
name = 'AddQueueBucket1730859187883';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "queue_bucket" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "queue_item_id" character varying NOT NULL, "date" bigint NOT NULL, "regular" integer NOT NULL, "vip" integer NOT NULL, CONSTRAINT "PK_cdd58b0d9e93e4be922da9d8bd6" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE "queue_bucket"`);
|
||||
}
|
||||
}
|
|
@ -2,9 +2,10 @@ 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 { Between, Repository } from 'typeorm';
|
||||
import { BaseReadService } from 'src/core/modules/data/service/base-read.service';
|
||||
import { QueueBucketModel } from '../models/queue-bucket.model';
|
||||
import * as moment from 'moment';
|
||||
|
||||
@Injectable()
|
||||
export class QueueBucketReadService extends BaseReadService<QueueBucketModel> {
|
||||
|
@ -15,11 +16,43 @@ export class QueueBucketReadService extends BaseReadService<QueueBucketModel> {
|
|||
super(repo);
|
||||
}
|
||||
|
||||
getQueue(item_id: string): Promise<QueueBucketModel> {
|
||||
return this.repo.findOne({
|
||||
async getQueue(item_id: string, vip = false): Promise<number> {
|
||||
const start = moment().startOf('day').valueOf();
|
||||
const end = moment().endOf('day').valueOf();
|
||||
|
||||
const queue = await this.repo.findOne({
|
||||
where: {
|
||||
queue_item_id: item_id,
|
||||
date: Between(start, end),
|
||||
},
|
||||
});
|
||||
|
||||
if (!queue) {
|
||||
const regularNumber = vip ? 0 : 1;
|
||||
const vipNumber = vip ? 1 : 0;
|
||||
this.repo.save({
|
||||
queue_item_id: item_id,
|
||||
date: start,
|
||||
regular: regularNumber,
|
||||
vip: vipNumber,
|
||||
});
|
||||
return Promise.resolve(1);
|
||||
} else {
|
||||
const field = vip ? 'vip' : 'regular';
|
||||
const data = await this.repo
|
||||
.createQueryBuilder('bucket')
|
||||
.update(QueueBucketModel)
|
||||
.set({
|
||||
[field]: () => `${field} + 1`,
|
||||
})
|
||||
.where('id = :key', {
|
||||
key: queue.id,
|
||||
})
|
||||
.returning(field)
|
||||
.updateEntity(true)
|
||||
.execute();
|
||||
|
||||
return data.raw[0]?.[field] ?? 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
|||
|
||||
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
|
||||
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
import { DataSource, In, Repository } from 'typeorm';
|
||||
import {
|
||||
QueueItemModel,
|
||||
QueueModel,
|
||||
|
@ -19,6 +19,18 @@ export class QueueDataService extends BaseReadService<QueueModel> {
|
|||
) {
|
||||
super(repo);
|
||||
}
|
||||
|
||||
async queueItems(item_queue_id: string[]): Promise<QueueModel[]> {
|
||||
return this.repo.find({
|
||||
relations: ['item', 'item.item', 'item.item.item_queue'],
|
||||
where: {
|
||||
item: { item: { item_queue: { id: In(item_queue_id) } } },
|
||||
},
|
||||
order: {
|
||||
time: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import * as moment from 'moment';
|
||||
|
||||
export function toTime(timestamp: number): string {
|
||||
const date = moment.unix(timestamp / 1000).add(7, 'hours');
|
||||
|
||||
const hours = date.hours();
|
||||
const minutes = date.minutes();
|
||||
return `${hours < 10 ? '0' : ''}${hours}:${
|
||||
minutes < 10 ? '0' : ''
|
||||
}${minutes}`;
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import { QueueModel } from 'src/modules/queue/data/models/queue.model';
|
||||
import { toTime } from '../../helpers/time.helper';
|
||||
import * as math from 'mathjs';
|
||||
|
||||
export class QueueCondition {
|
||||
private ticketItems = {};
|
||||
constructor(readonly items: QueueModel[]) {
|
||||
items.forEach((item) => {
|
||||
const item_id = item.item.item.item_queue?.id ?? item.item.item.id;
|
||||
const currentItem = this.ticketItems[item_id];
|
||||
this.ticketItems[item_id] = currentItem ? [...currentItem, item] : [item];
|
||||
});
|
||||
}
|
||||
|
||||
condition(item_id: string) {
|
||||
const queues: QueueModel[] = this.ticketItems[item_id] ?? [];
|
||||
const time = queues[0]?.time;
|
||||
const nearest = time ? toTime(time) : 0;
|
||||
return {
|
||||
available: queues.length == 0,
|
||||
average: this.averageTime(queues),
|
||||
nearest: nearest,
|
||||
crowded_level: queues.length,
|
||||
available_time: nearest,
|
||||
};
|
||||
}
|
||||
|
||||
averageTime(queues: QueueModel[]) {
|
||||
if (queues.length == 0) return 0;
|
||||
const calledQueue = queues.filter((q) => q.status === 'called');
|
||||
|
||||
const times = calledQueue.map((queue) => {
|
||||
return queue.call_time - queue.time;
|
||||
});
|
||||
|
||||
return math.sum(times) / times.length;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import {
|
|||
RelationParam,
|
||||
} from 'src/core/modules/domain/entities/base-filter.entity';
|
||||
import { Queue } from '../entities/queue.entity';
|
||||
import * as moment from 'moment';
|
||||
|
||||
@Injectable()
|
||||
export class IndexQueueManager extends BaseIndexManager<Queue> {
|
||||
|
@ -59,6 +60,14 @@ export class IndexQueueManager extends BaseIndexManager<Queue> {
|
|||
setQueryFilter(
|
||||
queryBuilder: SelectQueryBuilder<Queue>,
|
||||
): SelectQueryBuilder<Queue> {
|
||||
const start = moment().startOf('day').valueOf();
|
||||
const end = moment().endOf('day').valueOf();
|
||||
|
||||
queryBuilder.andWhere(`${this.tableName}.time BETWEEN :start AND :end`, {
|
||||
start,
|
||||
end,
|
||||
});
|
||||
|
||||
if (this.filterParam.vip != null) {
|
||||
queryBuilder.andWhere(`${this.tableName}.vip = :vip`, {
|
||||
vip: this.filterParam.vip,
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
import { QueueModel } from 'src/modules/queue/data/models/queue.model';
|
||||
import { CustomerQueueManager } from './customer-queue.manager';
|
||||
import { QueueCondition } from '../formula/queue-condition.formula';
|
||||
|
||||
export class CustomerQueueDetailManager extends CustomerQueueManager {
|
||||
private queues: QueueModel[] = [];
|
||||
currentQueues(queues: QueueModel[]) {
|
||||
this.queues = queues;
|
||||
}
|
||||
get data() {
|
||||
const queueCondition = new QueueCondition(this.queues);
|
||||
return this.tickets.map((ticket) => {
|
||||
return {
|
||||
id: ticket.id,
|
||||
|
@ -16,10 +23,11 @@ export class CustomerQueueDetailManager extends CustomerQueueManager {
|
|||
total_queue: this.totalQueueTickets(ticket),
|
||||
},
|
||||
items: ticket.items.map((item) => {
|
||||
const queueItem = item.item.item_queue ?? item.item;
|
||||
return {
|
||||
id: item.item_id,
|
||||
item_queue_id: item.id,
|
||||
title: item.item.item_queue?.name ?? item.item.name,
|
||||
title: queueItem.name,
|
||||
image_url: item.item.image_url,
|
||||
summary: {
|
||||
total_tickets: item.qty,
|
||||
|
@ -36,13 +44,7 @@ export class CustomerQueueDetailManager extends CustomerQueueManager {
|
|||
status: q.status,
|
||||
};
|
||||
}),
|
||||
queue_condition: {
|
||||
available: true,
|
||||
average: 12,
|
||||
nearest: '13:10',
|
||||
crowded_level: 20,
|
||||
available_time: '15:00',
|
||||
},
|
||||
queue_condition: queueCondition.condition(queueItem.id),
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
import { QueueItemModel } from 'src/modules/queue/data/models/queue.model';
|
||||
import {
|
||||
QueueItemModel,
|
||||
QueueModel,
|
||||
} from 'src/modules/queue/data/models/queue.model';
|
||||
import { CustomerQueueManager } from './customer-queue.manager';
|
||||
import { QueueCondition } from '../formula/queue-condition.formula';
|
||||
|
||||
export class CustomerQueueItemListManager extends CustomerQueueManager {
|
||||
private queues: QueueModel[] = [];
|
||||
currentQueues(queues: QueueModel[]) {
|
||||
this.queues = queues;
|
||||
}
|
||||
get data() {
|
||||
const tickets = this.tickets;
|
||||
const ticketItems = {};
|
||||
|
||||
const queueCondition = new QueueCondition(this.queues);
|
||||
|
||||
tickets.forEach((ticket) => {
|
||||
ticket.items.forEach((item) => {
|
||||
const item_id = item.item.item_queue?.id ?? item.item.id;
|
||||
|
@ -17,26 +27,16 @@ export class CustomerQueueItemListManager extends CustomerQueueManager {
|
|||
return Object.values<QueueItemModel[]>(ticketItems).map((items) => {
|
||||
const item = items[0];
|
||||
const item_qty = items.reduce((acc, item) => acc + item.qty, 0);
|
||||
const queueItem = item.item.item_queue ?? item.item;
|
||||
return {
|
||||
id: item.item_id,
|
||||
queue_item_id: item.id,
|
||||
title: item.item.item_queue?.name ?? item.item.name,
|
||||
title: queueItem.name,
|
||||
image_url: item.item.image_url,
|
||||
qty: item_qty,
|
||||
|
||||
available: true,
|
||||
average: 12,
|
||||
nearest: '13:10',
|
||||
crowded_level: 20,
|
||||
available_time: '15:00',
|
||||
|
||||
queue_condition: {
|
||||
available: true,
|
||||
average: 12,
|
||||
nearest: '13:10',
|
||||
crowded_level: 20,
|
||||
available_time: '15:00',
|
||||
},
|
||||
queue_condition: queueCondition.condition(queueItem.id),
|
||||
...queueCondition.condition(queueItem.id),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import {
|
|||
QueueItemModel,
|
||||
QueueTicketModel,
|
||||
} from '../../../data/models/queue.model';
|
||||
import { toTime } from '../../helpers/time.helper';
|
||||
|
||||
export class CustomerQueueManager {
|
||||
constructor(protected readonly tickets: QueueTicketModel[]) {}
|
||||
|
@ -10,13 +11,7 @@ export class CustomerQueueManager {
|
|||
}
|
||||
|
||||
toTime(timestamp: number): string {
|
||||
// js function to convert timestamp (1729739455000) to time with format HH:mm
|
||||
const date = new Date(timestamp / 1000);
|
||||
const hours = date.getHours() + 7;
|
||||
const minutes = date.getMinutes();
|
||||
return `${hours < 10 ? '0' : ''}${hours}:${
|
||||
minutes < 10 ? '0' : ''
|
||||
}${minutes}`;
|
||||
return toTime(timestamp);
|
||||
}
|
||||
|
||||
totalActivities(ticket: QueueTicketModel): number {
|
||||
|
|
|
@ -7,12 +7,12 @@ import {
|
|||
import { BaseCreateManager } from 'src/core/modules/domain/usecase/managers/base-create.manager';
|
||||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||
import { QueueModel } from '../../data/models/queue.model';
|
||||
import { generateRandom } from 'src/modules/transaction/vip-code/domain/usecases/managers/helpers/generate-random.helper';
|
||||
import { padCode } from 'src/modules/transaction/vip-code/domain/usecases/managers/helpers/generate-random.helper';
|
||||
import { QueueBucketReadService } from '../../data/services/queue-bucket';
|
||||
|
||||
@Injectable()
|
||||
export class RegisterQueueManager extends BaseCreateManager<QueueModel> {
|
||||
constructor(private readonly queueService: QueueBucketReadService) {
|
||||
constructor(private readonly bucketService: QueueBucketReadService) {
|
||||
super();
|
||||
}
|
||||
|
||||
|
@ -22,11 +22,13 @@ export class RegisterQueueManager extends BaseCreateManager<QueueModel> {
|
|||
}
|
||||
|
||||
async beforeProcess(): Promise<void> {
|
||||
const queueNumber = await this.bucketService.getQueue(this.data.item_id);
|
||||
const code = `A${padCode(queueNumber)}`;
|
||||
Object.assign(this.data, {
|
||||
status: STATUS.WAITING,
|
||||
time: new Date().getTime(),
|
||||
vip: false,
|
||||
code: `A${generateRandom(4, true)}`,
|
||||
code,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import { TicketDataService } from '../../data/services/ticket.service';
|
|||
import { QueueOrder } from '../../domain/entities/order.entity';
|
||||
import { QueueTicket } from '../../domain/entities/ticket.entity';
|
||||
import { QueueItem } from '../../domain/entities/queue-item.entity';
|
||||
import * as moment from 'moment';
|
||||
|
||||
@EventsHandler(TransactionChangeStatusEvent, TransactionCreateQueueEvent)
|
||||
export class QueueTransactionHandler
|
||||
|
@ -33,13 +34,16 @@ export class QueueTransactionHandler
|
|||
relations: ['items'],
|
||||
});
|
||||
|
||||
const date = transaction.booking_date ?? transaction.invoice_date;
|
||||
const queue_date = moment(date, 'YYYY-MM-DD').unix();
|
||||
|
||||
const { id, customer_name, customer_phone, invoice_code } = transaction;
|
||||
const current_date = new Date().valueOf();
|
||||
|
||||
const customerOrder = {
|
||||
code: invoice_code,
|
||||
customer: customer_name,
|
||||
phone: customer_phone,
|
||||
date: current_date,
|
||||
date: queue_date,
|
||||
transaction_id: id,
|
||||
};
|
||||
|
||||
|
|
|
@ -22,3 +22,7 @@ export function generateCodeDate() {
|
|||
|
||||
return `${month}${year}`;
|
||||
}
|
||||
|
||||
export function padCode(number: number, pad = 4): string {
|
||||
return String(number).padStart(pad, '0');
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue