fix: missing logic
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details

pull/115/head 20.2.10-alpha.1
shancheas 2024-11-06 11:06:14 +07:00
parent 5ef7521e9b
commit dac42b754c
12 changed files with 164 additions and 39 deletions

View File

@ -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"`);
}
}

View File

@ -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;
}
}
}

View File

@ -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()

View File

@ -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}`;
}

View File

@ -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;
}
}

View File

@ -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,

View File

@ -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),
};
}),
};

View File

@ -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),
};
});
}

View File

@ -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 {

View File

@ -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;
}

View File

@ -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,
};

View File

@ -22,3 +22,7 @@ export function generateCodeDate() {
return `${month}${year}`;
}
export function padCode(number: number, pad = 4): string {
return String(number).padStart(pad, '0');
}