From ee3ae91957113a7dca0f8cd428fc0c982cf58dde Mon Sep 17 00:00:00 2001 From: Methapon2001 <61303214+Methapon2001@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:32:17 +0700 Subject: [PATCH] feat: service and product with work relation --- src/controllers/service/service-controller.ts | 118 ++++++++++-- src/controllers/work/work-controller.ts | 171 ++++++++++++++++-- 2 files changed, 259 insertions(+), 30 deletions(-) diff --git a/src/controllers/service/service-controller.ts b/src/controllers/service/service-controller.ts index d0a31db..b05120f 100644 --- a/src/controllers/service/service-controller.ts +++ b/src/controllers/service/service-controller.ts @@ -27,19 +27,20 @@ if (!process.env.MINIO_BUCKET) { const MINIO_BUCKET = process.env.MINIO_BUCKET; type ServiceCreate = { - code: string; + code: "MOU" | "mou"; name: string; detail: string; + workId: string[]; }; type ServiceUpdate = { - code: string; name: string; detail: string; + workId: string[]; }; function imageLocation(id: string) { - return `service/img-${id}`; + return `service/${id}/product-image`; } @Route("api/v1/service") @@ -63,6 +64,13 @@ export class ServiceController extends Controller { const [result, total] = await prisma.$transaction([ prisma.service.findMany({ + include: { + workOnService: { + include: { + service: true, + }, + }, + }, orderBy: { createdAt: "asc" }, where, take: pageSize, @@ -87,6 +95,23 @@ export class ServiceController extends Controller { @Get("{serviceId}") async getServiceById(@Path() serviceId: string) { const record = await prisma.service.findFirst({ + include: { + workOnService: { + orderBy: { order: "asc" }, + include: { + work: { + include: { + productOnWork: { + include: { + product: true, + }, + orderBy: { order: "asc" }, + }, + }, + }, + }, + }, + }, where: { id: serviceId }, }); @@ -94,7 +119,7 @@ export class ServiceController extends Controller { throw new HttpError(HttpStatus.NOT_FOUND, "Service cannot be found.", "data_not_found"); return Object.assign(record, { - imageUrl: await minio.presignedGetObject(MINIO_BUCKET, `service/img-${record.id}`, 60 * 60), + imageUrl: await minio.presignedGetObject(MINIO_BUCKET, imageLocation(record.id), 60 * 60), }); } @@ -104,27 +129,96 @@ export class ServiceController extends Controller { @Query() page: number = 1, @Query() pageSize: number = 30, ) { + const where = { + serviceOnWork: { + some: { serviceId }, + }, + } satisfies Prisma.WorkWhereInput; + const [result, total] = await prisma.$transaction([ prisma.work.findMany({ - where: { serviceId }, + include: { + productOnWork: { + include: { + product: true, + }, + }, + }, + where, take: pageSize, skip: (page - 1) * pageSize, }), - prisma.work.count({ where: { serviceId } }), + prisma.work.count({ where }), ]); return { result, page, pageSize, total }; } @Post() async createService(@Request() req: RequestWithUser, @Body() body: ServiceCreate) { - const record = await prisma.service.create({ - data: { - ...body, - createdBy: req.user.name, - updateBy: req.user.name, - }, + const { workId, ...payload } = body; + + const workList = await prisma.work.findMany({ + where: { id: { in: workId } }, }); + if (workList.length !== workList.length) { + throw new HttpError( + HttpStatus.BAD_REQUEST, + "Some product not found.", + "missing_or_invalid_parameter", + ); + } + + const record = await prisma.$transaction( + async (tx) => { + const last = await tx.runningNo.upsert({ + where: { + key: `SERVICE_${body.code.toLocaleUpperCase()}`, + }, + create: { + key: `SERVICE_${body.code.toLocaleUpperCase()}`, + value: 1, + }, + update: { value: { increment: 1 } }, + }); + + return tx.service.create({ + include: { + workOnService: { + orderBy: { order: "asc" }, + include: { + work: { + include: { + productOnWork: { + include: { + product: true, + }, + orderBy: { order: "asc" }, + }, + }, + }, + }, + }, + }, + data: { + ...payload, + code: `${body.code.toLocaleUpperCase()}${last.value.toString().padStart(3, "0")}`, + workOnService: { + createMany: { + data: workId.map((v, i) => ({ + order: i + 1, + workId: v, + })), + }, + }, + createdBy: req.user.name, + updateBy: req.user.name, + }, + }); + }, + { isolationLevel: Prisma.TransactionIsolationLevel.Serializable }, + ); + this.setStatus(HttpStatus.CREATED); return Object.assign(record, { diff --git a/src/controllers/work/work-controller.ts b/src/controllers/work/work-controller.ts index 8df4ddc..8702cf8 100644 --- a/src/controllers/work/work-controller.ts +++ b/src/controllers/work/work-controller.ts @@ -20,15 +20,13 @@ import HttpError from "../../interfaces/http-error"; import HttpStatus from "../../interfaces/http-status"; type WorkCreate = { - order: number; name: string; - serviceId: string; + productId: string[]; }; type WorkUpdate = { - order?: number; name?: string; - serviceId?: string; + productId?: string[]; }; @Route("api/v1/work") @@ -47,8 +45,17 @@ export class WorkController extends Controller { const [result, total] = await prisma.$transaction([ prisma.work.findMany({ + include: { + productOnWork: { + include: { + product: true, + }, + orderBy: { + order: "asc", + }, + }, + }, orderBy: { createdAt: "asc" }, - include: { service: true }, where, take: pageSize, skip: (page - 1) * pageSize, @@ -62,7 +69,6 @@ export class WorkController extends Controller { @Get("{workId}") async getWorkById(@Path() workId: string) { const record = await prisma.work.findFirst({ - include: { service: true }, where: { id: workId }, }); @@ -71,6 +77,7 @@ export class WorkController extends Controller { return record; } + @Get("{workId}/product") async getProductOfWork( @Path() workId: string, @@ -81,9 +88,7 @@ export class WorkController extends Controller { const where = { AND: [ { - workProduct: { - some: { workId }, - }, + workProduct: { some: { workId } }, }, { OR: [{ name: { contains: query } }], @@ -107,28 +112,85 @@ export class WorkController extends Controller { @Post() async createWork(@Request() req: RequestWithUser, @Body() body: WorkCreate) { - const service = await prisma.service.findFirst({ - where: { id: body.serviceId }, + const { productId, ...payload } = body; + + const exist = await prisma.work.findFirst({ + include: { + productOnWork: { + include: { + product: true, + }, + }, + }, + where: { + productOnWork: { + every: { + productId: { in: productId }, + }, + }, + NOT: { + OR: [ + { + productOnWork: { + some: { + productId: { notIn: productId }, + }, + }, + }, + { + productOnWork: { + none: {}, + }, + }, + ], + }, + }, }); - if (!service) { + if (exist) return exist; + + const productList = await prisma.product.findMany({ + where: { id: { in: productId } }, + }); + + if (productList.length !== productId.length) { throw new HttpError( HttpStatus.BAD_REQUEST, - "Service not found.", + "Some product not found.", "missing_or_invalid_parameter", ); } const record = await prisma.work.create({ + include: { + productOnWork: { + include: { + product: true, + }, + orderBy: { + order: "asc", + }, + }, + }, data: { - ...body, + ...payload, + productOnWork: { + createMany: { + data: productId.map((v, i) => ({ + order: i + 1, + productId: v, + createdBy: req.user.name, + updateBy: req.user.name, + })), + }, + }, createdBy: req.user.name, updateBy: req.user.name, }, }); - await prisma.service.updateMany({ - where: { id: service.id }, + await prisma.product.updateMany({ + where: { id: { in: body.productId }, status: Status.CREATED }, data: { status: Status.ACTIVE }, }); @@ -138,18 +200,91 @@ export class WorkController extends Controller { } @Put("{workId}") - async editService( + async editWork( @Request() req: RequestWithUser, @Body() body: WorkUpdate, @Path() workId: string, ) { + const { productId, ...payload } = body; + if (!(await prisma.work.findUnique({ where: { id: workId } }))) { throw new HttpError(HttpStatus.NOT_FOUND, "Work cannot be found.", "data_not_found"); } + const exist = await prisma.work.findFirst({ + include: { + productOnWork: { + include: { + product: true, + }, + }, + }, + where: { + productOnWork: { + every: { + productId: { in: productId }, + }, + }, + NOT: { + OR: [ + { id: workId }, + { + productOnWork: { + some: { + productId: { notIn: productId }, + }, + }, + }, + { + productOnWork: { + none: {}, + }, + }, + ], + }, + }, + }); + + if (exist) return exist; + const record = await prisma.work.update({ - data: { ...body, updateBy: req.user.name }, + include: { + productOnWork: { + include: { + product: true, + }, + orderBy: { + order: "asc", + }, + }, + }, where: { id: workId }, + data: { + ...payload, + productOnWork: productId + ? { + deleteMany: { + productId: { notIn: productId }, + }, + upsert: productId.map((v, i) => ({ + where: { + workId_productId: { + workId, + productId: v, + }, + }, + update: { order: i + 1 }, + create: { + order: i + 1, + productId: v, + createdBy: req.user.name, + updateBy: req.user.name, + }, + })), + } + : undefined, + updateBy: req.user.name, + }, }); return record;