浏览代码

Изменение сущности участия

Vadim 3 月之前
父节点
当前提交
e327a2bb4c

+ 46 - 0
src/api/v_0.1.0/client/client-pe-api.ts

@@ -1,5 +1,6 @@
 import {
   CustomFieldWithUserCopyValue,
+  CustomFieldWithValidatorsAndValue,
   CustomFieldWithValue,
   InputFieldValue,
 } from "../types/custom-fields-types.js";
@@ -143,6 +144,51 @@ class ClientPartEntitiesApi {
     }),
   };
 
+  GET_PeForPatch = {
+    req: z.object({
+      peId: z.string().uuid(),
+    }),
+    res: z.object({
+      code: z.enum(["success"]),
+      pe: z.object({
+        peId: z.string().uuid(),
+        peTypeId: z.string().uuid(),
+        peTypeCode: z.string(),
+        peTypeName: z.string(),
+        eventInstId: z.string().uuid(),
+        ownerId: z.string().uuid(),
+        name: z.string(),
+        fields: z.array(
+          CustomFieldWithValidatorsAndValue.extend({
+            peFfId: z.string().uuid(),
+          }),
+        ),
+      }),
+    }),
+  };
+
+  PATCH_PartEntity = {
+    req: {
+      formData: {
+        body: z.object({
+          name: z.string().optional(),
+          fields: z.array(
+            z.object({
+              peFfId: z.string().uuid(),
+              value: InputFieldValue,
+            }),
+          ),
+        }),
+      },
+      params: z.object({
+        peId: z.string().uuid(),
+      }),
+    },
+    res: z.object({
+      code: z.enum(["success"]),
+    }),
+  };
+
   POST_Invite = {
     req: {
       body: z.object({

+ 28 - 0
src/api/v_0.1.0/types/act-types.ts

@@ -1,4 +1,5 @@
 import { z } from "zod";
+import { CustomFieldWithValue } from "./custom-fields-types.js";
 
 class ActTypes {
   ActCategory = z.object({
@@ -38,6 +39,33 @@ class ActTypes {
       }),
     ),
   });
+
+  PeForActivity = z.object({
+    peId: z.string().uuid(),
+    peTypeId: z.string().uuid(),
+    peTypeCode: z.string(),
+    peTypeName: z.string(),
+    eventInstId: z.string().uuid(),
+    ownerId: z.string().uuid(),
+    name: z.string(),
+    members: z.array(
+      z.object({
+        peMemberId: z.string(),
+        userId: z.string().uuid(),
+        email: z.string().email(),
+        fields: z.array(
+          CustomFieldWithValue.extend({
+            userEfId: z.string().uuid(),
+          }),
+        ),
+      }),
+    ),
+    fields: z.array(
+      CustomFieldWithValue.extend({
+        peFfId: z.string().uuid(),
+      }),
+    ),
+  });
 }
 
 export const actTypes = new ActTypes();

+ 97 - 31
src/modules/client/activities/participant-entities/c-pe-controller.ts

@@ -4,7 +4,7 @@ import { DbSchema } from "#db-schema";
 import { sql } from "slonik";
 
 // api
-import { api } from "#api";
+import { api, apiTypes } from "#api";
 
 // other
 import { z } from "zod";
@@ -13,7 +13,6 @@ import { RouterUtils } from "#utils/router-utils.js";
 
 import { Request, Response } from "express";
 import sessionService from "#modules/users/auth/services/session-service.js";
-import { CustomFieldWithValue } from "#api/v_0.1.0/types/custom-fields-types.js";
 import { ApiError } from "#exceptions/api-error.js";
 import { v4, v7 } from "uuid";
 import { cPeService } from "./c-pe-service.js";
@@ -149,6 +148,100 @@ class ClientPeController {
     });
   }
 
+  async getPeForPatch(req: Request, res: Response) {
+    const { peId } = api.client.pe.GET_PeForPatch.req.parse(req.params);
+
+    const user = sessionService.getUserFromReq(req);
+
+    const pe = await cPeService.getPeWithValidatorsAndValues(peId);
+    if (!pe)
+      throw ApiError.BadRequest("peNotFound", "Сущность участия не найдена");
+
+    if (pe.ownerId !== user.userId) throw ApiError.ForbiddenError();
+
+    RouterUtils.validAndSendResponse(api.client.pe.GET_PeForPatch.res, res, {
+      code: "success",
+      pe: pe,
+    });
+  }
+
+  async patchPe(req: Request, res: Response) {
+    const { peId } = api.client.pe.PATCH_PartEntity.req.params.parse(
+      req.params,
+    );
+    const { name, fields } =
+      api.client.pe.PATCH_PartEntity.req.formData.body.parse(
+        JSON.parse(req.body.body),
+      );
+
+    const files = req.files;
+    const user = sessionService.getUserFromReq(req);
+
+    const peType = await cPeService.getPeWithValidatorsAndValues(peId);
+    if (!peType)
+      throw ApiError.BadRequest(
+        "peTypeNotFound",
+        "Тип сущности участия не найден",
+      );
+
+    // проверка доступа
+    if (peType.ownerId !== user.userId) throw ApiError.ForbiddenError();
+
+    const refFields = peType.fields
+      .map((f) => ({
+        ...f,
+        idKey: "peFfId",
+      }))
+      // только изменяемые
+      .filter((f) => fields.some((ff) => ff.peFfId === f.peFfId));
+
+    // валидация
+    const validationResult =
+      await cCustomFieldsValidateService.processAndValidateFields({
+        inputFields: fields,
+        referenceFields: refFields,
+        files,
+        idKey: "peFfId",
+        addOldValue: true,
+      });
+
+    if (!validationResult.isValid)
+      throw ApiError.BadRequest(
+        "fieldsValidationFailed",
+        JSON.stringify(validationResult.messages),
+      );
+
+    const validatedFields = validationResult.checkedfields;
+
+    //
+    //
+    // вставляем в базу и сохраняем файлы
+    await updPool.transaction(async (tr) => {
+      if (name) {
+        await tr.query(sql.unsafe`
+        update act.part_entities
+        set
+          name = ${name}
+        where
+          pe_id = ${peId}
+      `);
+      }
+
+      await cCustomFieldsValidateService.saveCustomFieldValuesInTransaction({
+        tr,
+        parentId: peId,
+        action: "peCreate",
+        inputFields: validatedFields,
+        files,
+        isDeleteBefore: true,
+      });
+    });
+
+    RouterUtils.validAndSendResponse(api.client.pe.PATCH_PartEntity.res, res, {
+      code: "success",
+    });
+  }
+
   async getMyPes(req: Request, res: Response) {
     const user = sessionService.getUserFromReq(req);
     const event = await sessionService.getCurrentEventFromReq(req);
@@ -272,7 +365,7 @@ class ClientPeController {
 
     if (!isMember) throw ApiError.ForbiddenError();
 
-    const pe = await cPeService.getSimplePe(peId);
+    const pe = await cPeService.getPeForMember(peId);
     if (!pe)
       throw ApiError.BadRequest("peNotFound", "Сущность участия не найдена");
 
@@ -417,34 +510,7 @@ class ClientPeController {
 
     const event = await sessionService.getCurrentEventFromReq(req);
 
-    const pes = await selPool.any(sql.type(
-      z.object({
-        peId: DbSchema.act.partEntities.peId,
-        peTypeId: DbSchema.act.partEntities.peTypeId,
-        peTypeCode: DbSchema.act.peTypes.code,
-        peTypeName: DbSchema.act.peTypes.name,
-        eventInstId: DbSchema.act.partEntities.eventInstId,
-        ownerId: DbSchema.act.partEntities.ownerId,
-        name: DbSchema.act.partEntities.name,
-        members: z.array(
-          z.object({
-            peMemberId: DbSchema.act.peMembers.peMemberId,
-            userId: DbSchema.usr.users.userId,
-            email: DbSchema.usr.users.email,
-            fields: z.array(
-              CustomFieldWithValue.extend({
-                userEfId: z.string().uuid(),
-              }),
-            ),
-          }),
-        ),
-        fields: z.array(
-          CustomFieldWithValue.extend({
-            peFfId: z.string().uuid(),
-          }),
-        ),
-      }),
-    )`
+    const pes = await selPool.any(sql.type(apiTypes.activities.PeForActivity)`
       select
         pe.pe_id "peId",
         pe.event_inst_id "eventInstId",

+ 10 - 1
src/modules/client/activities/participant-entities/c-pe-router.ts

@@ -31,7 +31,16 @@ router.get(
 
 router.get("/:peId", RouterUtils.asyncHandler(clientPeController.getPe));
 
-// router.get("/:peId/members", RouterUtils.asyncHandler(clientPeController.getMembers));
+router.patch(
+  "/:peId",
+  upload.any(),
+  RouterUtils.asyncHandler(clientPeController.patchPe),
+);
+
+router.get(
+  "/peForPatch/:peId",
+  RouterUtils.asyncHandler(clientPeController.getPeForPatch),
+);
 
 router.get(
   "/:peId/invites",

+ 64 - 1
src/modules/client/activities/participant-entities/c-pe-service.ts

@@ -1,6 +1,7 @@
 import {
   CustomFieldWithUserCopyValue,
   CustomFieldWithValidators,
+  CustomFieldWithValidatorsAndValue,
   CustomFieldWithValue,
 } from "#api/v_0.1.0/types/custom-fields-types.js";
 import { DbSchema } from "#db/db-schema.js";
@@ -54,7 +55,69 @@ class CPeService {
     `);
   }
 
-  async getSimplePe(peId: string) {
+  async getPeWithValidatorsAndValues(peId: string) {
+    return await selPool.maybeOne(sql.type(
+      z.object({
+        peId: z.string().uuid(),
+        peTypeId: z.string().uuid(),
+        peTypeCode: z.string(),
+        peTypeName: z.string(),
+        eventInstId: z.string().uuid(),
+        ownerId: z.string().uuid(),
+        name: z.string(),
+        fields: z.array(
+          CustomFieldWithValidatorsAndValue.extend({
+            peFfId: z.string().uuid(),
+          }),
+        ),
+      }),
+    )`
+      select
+        pe.pe_id "peId",
+        pt.pe_type_id "peTypeId",
+        pt.code "peTypeCode",
+        pt.name "peTypeName",
+        pt.event_inst_id "eventInstId",
+        pe.owner_id "ownerId",
+        pe.name,
+        coalesce(f.fields, '[]'::jsonb) as fields
+      from
+        act.part_entities pe
+      left join act.pe_types pt on
+        pt.pe_type_id = pe.pe_type_id
+      left join lateral (
+        select
+          jsonb_agg(jsonb_build_object(
+            'fieldDefinitionId', cfd.field_definition_id, 
+            'peFfId', pff.pe_ff_id, 
+            'isCopyUserValue', pff.is_copy_user_value, 
+            'code', cfd.code, 'value', pfv.value, 
+            'fieldTypeCode', ft.code, 
+            'title', coalesce(pff.field_title_override, cfd.title), 
+            'mask', cfd.mask, 
+            'options', cfd.options,
+            'validators', cfwv.validators
+          )) as fields
+        from
+          act.pe_form_fields pff
+        left join cf.custom_field_definitions cfd on
+          pff.field_definition_id = cfd.field_definition_id
+        left join cf.field_types ft on
+          cfd.field_type_id = ft.field_type_id
+        left join act.pe_field_values pfv on
+          pff.pe_ff_id = pfv.pe_ff_id
+          and pe.pe_id = pfv.pe_id
+        left join cf.custom_fields_with_validators cfwv on
+          cfwv.field_definition_id = pff.field_definition_id
+        where
+          pff.pe_type_id = pe.pe_type_id) f on
+        true
+      where
+        pe.pe_id = ${peId}
+    `);
+  }
+
+  async getPeForMember(peId: string) {
     return await selPool.maybeOne(sql.type(
       z.object({
         peMemberId: DbSchema.act.peMembers.peMemberId,