jws-backend/src/controllers/04-flow-template-controller.ts

337 lines
9.3 KiB
TypeScript
Raw Normal View History

2024-10-24 15:56:03 +07:00
import {
Body,
Controller,
Delete,
Get,
Path,
Post,
Put,
Query,
Request,
Route,
Security,
Tags,
} from "tsoa";
2024-10-07 11:58:43 +07:00
import { RequestWithUser } from "../interfaces/user";
import prisma from "../db";
2024-10-25 16:45:52 +07:00
import { Prisma, Status } from "@prisma/client";
2024-10-24 15:56:03 +07:00
import {
branchRelationPermInclude,
createPermCheck,
createPermCondition,
} from "../services/permission";
import HttpError from "../interfaces/http-error";
import HttpStatus from "../interfaces/http-status";
import { notFoundError } from "../utils/error";
2024-10-25 17:10:45 +07:00
import { filterStatus } from "../services/prisma";
2025-04-17 13:41:22 +07:00
import { queryOrNot, whereDateQuery } from "../utils/relation";
2024-10-07 11:58:43 +07:00
type WorkflowPayload = {
name: string;
step: {
id?: string;
2024-10-07 11:58:43 +07:00
name: string;
detail?: string | null;
type?: string | null;
value?: string[] | null;
attributes?: { [key: string]: any };
2024-10-07 11:58:43 +07:00
responsiblePersonId?: string[];
2024-11-07 14:00:05 +07:00
responsibleInstitution?: string[];
2025-04-24 11:40:02 +07:00
responsibleGroup?: string[];
messengerByArea?: boolean;
2024-10-07 11:58:43 +07:00
}[];
2024-10-24 15:56:03 +07:00
registeredBranchId?: string;
2024-10-25 16:45:52 +07:00
status?: Status;
2024-10-07 11:58:43 +07:00
};
2024-10-24 15:56:03 +07:00
const permissionCondCompany = createPermCondition((_) => true);
const permissionCheckCompany = createPermCheck((_) => true);
2024-10-07 11:58:43 +07:00
@Route("api/v1/workflow-template")
@Tags("Workflow")
2024-10-24 15:56:03 +07:00
@Security("keycloak")
2024-10-07 11:58:43 +07:00
export class FlowTemplateController extends Controller {
@Get()
async getFlowTemplate(
2024-10-24 15:56:03 +07:00
@Request() req: RequestWithUser,
2024-10-07 11:58:43 +07:00
@Query() page: number = 1,
@Query() pageSize: number = 30,
2024-10-25 17:10:45 +07:00
@Query() status?: Status,
2024-10-24 15:56:03 +07:00
@Query() query = "",
@Query() activeOnly?: boolean,
2025-04-17 13:41:22 +07:00
@Query() startDate?: Date,
@Query() endDate?: Date,
2024-10-07 11:58:43 +07:00
) {
2024-10-24 15:56:03 +07:00
const where = {
OR: queryOrNot(query, [
2025-04-09 11:54:52 +07:00
{ name: { contains: query, mode: "insensitive" } },
2024-10-24 15:56:03 +07:00
{
step: {
2025-04-09 11:54:52 +07:00
some: { name: { contains: query, mode: "insensitive" } },
2024-10-24 15:56:03 +07:00
},
},
]),
AND: {
...filterStatus(activeOnly ? Status.ACTIVE : status),
registeredBranch: {
OR: permissionCondCompany(req.user, { activeOnly: true }),
},
2024-10-24 15:56:03 +07:00
},
2025-04-17 13:41:22 +07:00
...whereDateQuery(startDate, endDate),
2024-10-24 15:56:03 +07:00
} satisfies Prisma.WorkflowTemplateWhereInput;
const [result, total] = await prisma.$transaction([
prisma.workflowTemplate.findMany({
2024-10-24 15:56:03 +07:00
where,
include: {
step: {
include: {
value: true,
responsiblePerson: {
include: { user: true },
},
2024-11-07 14:00:05 +07:00
responsibleInstitution: true,
2025-04-24 11:40:02 +07:00
responsibleGroup: true,
2024-10-24 13:25:23 +07:00
},
2024-11-12 13:12:03 +07:00
orderBy: { order: "asc" },
2024-10-24 13:25:23 +07:00
},
2024-10-07 11:58:43 +07:00
},
2024-10-25 16:45:52 +07:00
orderBy: [{ statusOrder: "asc" }, { createdAt: "asc" }],
take: pageSize,
skip: (page - 1) * pageSize,
}),
2024-10-24 15:56:03 +07:00
prisma.workflowTemplate.count({ where }),
]);
2024-11-07 14:00:05 +07:00
return {
result: result.map((r) => ({
...r,
step: r.step.map((v) => ({
...v,
responsibleInstitution: v.responsibleInstitution.map((institution) => institution.group),
2025-04-24 11:40:02 +07:00
responsibleGroup: v.responsibleGroup.map((group) => group.group),
2024-11-07 14:00:05 +07:00
})),
})),
page,
pageSize,
total,
};
2024-10-07 11:58:43 +07:00
}
@Get("{templateId}")
async getFlowTemplateById(@Request() _req: RequestWithUser, @Path() templateId: string) {
2024-11-07 14:00:05 +07:00
const record = await prisma.workflowTemplate.findFirst({
2024-10-07 11:58:43 +07:00
include: {
step: {
2024-12-26 10:18:22 +07:00
orderBy: { order: "asc" },
2024-10-24 13:25:23 +07:00
include: {
value: true,
responsiblePerson: {
include: { user: true },
},
2024-11-07 14:00:05 +07:00
responsibleInstitution: true,
2025-04-24 11:40:02 +07:00
responsibleGroup: true,
2024-10-24 13:25:23 +07:00
},
2024-10-07 11:58:43 +07:00
},
},
where: { id: templateId },
orderBy: { createdAt: "asc" },
});
2024-11-07 14:00:05 +07:00
if (!record) throw notFoundError("FlowTemplate");
return {
...record,
step: record.step.map((v) => ({
...v,
responsibleInstitution: v.responsibleInstitution.map((institution) => institution.group),
2025-04-24 11:40:02 +07:00
responsibleGroup: v.responsibleGroup.map((group) => group.group),
2024-11-07 14:00:05 +07:00
})),
};
2024-10-07 11:58:43 +07:00
}
@Post()
2024-10-24 15:56:03 +07:00
async createFlowTemplate(@Request() req: RequestWithUser, @Body() body: WorkflowPayload) {
2024-11-05 08:31:57 +07:00
const where = {
OR: [
{ name: { contains: body.name } },
{
step: {
some: { name: { contains: body.name } },
},
},
],
AND: {
registeredBranch: {
OR: permissionCondCompany(req.user),
},
},
} satisfies Prisma.WorkflowTemplateWhereInput;
const exists = await prisma.workflowTemplate.findFirst({ where });
if (exists) {
throw new HttpError(
HttpStatus.BAD_REQUEST,
"Workflow template with this name already exists",
"sameNameExists",
);
}
2024-10-24 15:56:03 +07:00
const userAffiliatedBranch = await prisma.branch.findFirst({
include: branchRelationPermInclude(req.user),
where: body.registeredBranchId
? { id: body.registeredBranchId }
: {
user: { some: { userId: req.user.sub } },
},
});
if (!userAffiliatedBranch) {
throw new HttpError(
HttpStatus.BAD_REQUEST,
2024-10-29 16:01:13 +07:00
"You must be affilated with at least one branch or specify branch to be registered (System permission required).",
2024-10-24 15:56:03 +07:00
"reqMinAffilatedBranch",
);
}
await permissionCheckCompany(req.user, userAffiliatedBranch);
2024-10-07 11:58:43 +07:00
return await prisma.workflowTemplate.create({
data: {
...body,
2024-10-24 15:56:03 +07:00
registeredBranchId: userAffiliatedBranch.id,
2024-10-07 11:58:43 +07:00
step: {
create: body.step.map((v, i) => ({
type: v.type,
value: {
create: v.value?.map((val) => ({
value: val,
})),
},
name: v.name,
2024-11-07 15:01:30 +07:00
detail: v.detail,
2024-10-07 11:58:43 +07:00
order: i + 1,
2024-11-11 17:40:10 +07:00
attributes: v.attributes,
messengerByArea: v.messengerByArea,
2024-10-07 11:58:43 +07:00
responsiblePerson: {
create: v.responsiblePersonId?.map((id) => ({
userId: id,
})),
},
2024-11-07 14:00:05 +07:00
responsibleInstitution: {
create: v.responsibleInstitution?.map((group) => ({ group })),
},
2025-04-24 11:40:02 +07:00
responsibleGroup: {
create: v.responsibleGroup?.map((group) => ({ group })),
},
2024-10-07 11:58:43 +07:00
})),
},
},
});
}
@Put("{templateId}")
async updateFlowTemplate(
2024-10-24 15:56:03 +07:00
@Request() req: RequestWithUser,
2024-10-07 11:58:43 +07:00
@Path() templateId: string,
@Body() body: WorkflowPayload,
) {
2024-10-24 15:56:03 +07:00
const record = await prisma.workflowTemplate.findUnique({
where: { id: templateId },
include: {
registeredBranch: {
include: branchRelationPermInclude(req.user),
},
},
});
if (!record) throw notFoundError("Workflow");
await permissionCheckCompany(req.user, record.registeredBranch);
2024-10-07 11:58:43 +07:00
return await prisma.workflowTemplate.update({
where: { id: templateId },
data: {
...body,
2024-10-25 16:45:52 +07:00
statusOrder: +(body.status === "INACTIVE"),
2024-10-07 11:58:43 +07:00
step: {
deleteMany: {
id: { notIn: body.step.flatMap((v) => v.id || []) },
},
upsert: body.step.map((v, i) => ({
where: { id: v.id || "" },
create: {
type: v.type,
name: v.name,
2024-11-07 15:01:30 +07:00
detail: v.detail,
order: i + 1,
2024-11-11 17:40:10 +07:00
attributes: v.attributes,
value: {
create: v.value?.map((val) => ({ value: val })),
},
messengerByArea: v.messengerByArea,
responsiblePerson: {
2024-10-28 16:18:01 +07:00
createMany: {
data: v.responsiblePersonId?.map((id) => ({ userId: id })) || [],
skipDuplicates: true,
},
},
id: undefined,
2024-10-07 11:58:43 +07:00
},
update: {
type: v.type,
name: v.name,
2024-11-07 15:01:30 +07:00
detail: v.detail,
order: i + 1,
2024-11-11 17:40:10 +07:00
attributes: v.attributes,
value: {
2024-10-28 16:18:01 +07:00
deleteMany: {},
create: v.value?.map((val) => ({ value: val })),
},
messengerByArea: v.messengerByArea,
2024-12-25 16:51:52 +07:00
responsiblePerson: v.responsiblePersonId
? {
deleteMany: {
userId: { notIn: v.responsiblePersonId },
},
createMany: {
data: v.responsiblePersonId?.map((id) => ({ userId: id })) || [],
skipDuplicates: true,
},
}
: undefined,
2024-11-07 14:00:05 +07:00
responsibleInstitution: {
deleteMany: {},
create: v.responsibleInstitution?.map((group) => ({ group })),
},
2025-04-24 11:40:02 +07:00
responsibleGroup: {
deleteMany: {},
create: v.responsibleGroup?.map((group) => ({ group })),
},
2024-10-07 11:58:43 +07:00
},
})),
},
},
});
}
@Delete("{templateId}")
2024-10-24 15:56:03 +07:00
async deleteFlowTemplateById(@Request() req: RequestWithUser, @Path() templateId: string) {
const record = await prisma.workflowTemplate.findUnique({
where: { id: templateId },
include: {
registeredBranch: {
include: branchRelationPermInclude(req.user),
},
},
});
if (!record) throw notFoundError("Workflow");
await permissionCheckCompany(req.user, record.registeredBranch);
2024-10-07 11:58:43 +07:00
return await prisma.workflowTemplate.delete({
where: { id: templateId },
});
}
}