From 9e767210755fd2de86d82140c5117d0cf238493f Mon Sep 17 00:00:00 2001 From: Gerbera3090 Date: Mon, 25 Nov 2024 04:06:05 +0900 Subject: [PATCH 1/2] feat: apiOrg002 interface added --- .../api/organization/endpoint/apiOrg002.ts | 64 +++++++++++++++++++ packages/interface/src/common/enum/index.ts | 1 + .../src/common/enum/organization.enum.ts | 63 ++++++++++++++++++ packages/interface/src/common/type/ids.ts | 3 + 4 files changed, 131 insertions(+) create mode 100644 packages/interface/src/api/organization/endpoint/apiOrg002.ts create mode 100644 packages/interface/src/common/enum/index.ts create mode 100644 packages/interface/src/common/enum/organization.enum.ts create mode 100644 packages/interface/src/common/type/ids.ts diff --git a/packages/interface/src/api/organization/endpoint/apiOrg002.ts b/packages/interface/src/api/organization/endpoint/apiOrg002.ts new file mode 100644 index 0000000..e96795a --- /dev/null +++ b/packages/interface/src/api/organization/endpoint/apiOrg002.ts @@ -0,0 +1,64 @@ +import { HttpStatusCode } from "axios"; +import { z } from "zod"; + +import { + zOrgName, + zOrgNameEng, +} from "@sparcs-students/interface/common/stringLength"; +import { OrganizationTypeE } from "@sparcs-students/interface/common/enum"; +import { zId } from "@sparcs-students/interface/common/type/ids"; + +/** + * @version v0.1 + * @description 총학생회장 권한으로 새로운 단체를 생성합니다. + */ + +const url = () => `/uapresident/organizations/organization`; +const method = "POST"; + +const requestParam = z.object({}); + +const requestQuery = z.object({}); + +const requestBody = z.object({ + name: zOrgName, + nameEng: zOrgNameEng, + organizationTypeId: z.nativeEnum(OrganizationTypeE), + foundingYear: z.coerce.number().int(), + startTerm: z.coerce.date(), + endTerm: z.coerce.date().optional(), +}); + +const responseBodyMap = { + [HttpStatusCode.Ok]: z.object({ + organizationId: zId, + }), +}; + +const responseErrorMap = {}; + +const apiOrg002 = { + url, + method, + requestParam, + requestQuery, + requestBody, + responseBodyMap, + responseErrorMap, +}; + +type ApiOrg002RequestParam = z.infer; +type ApiOrg002RequestQuery = z.infer; +type ApiOrg002RequestBody = z.infer; +type ApiOrg002ResponseOK = z.infer<(typeof apiOrg002.responseBodyMap)[200]>; + +export default apiOrg002; + +export const ApiOrg002RequestUrl = "/uapresident/organizations/organization"; + +export type { + ApiOrg002RequestParam, + ApiOrg002RequestQuery, + ApiOrg002RequestBody, + ApiOrg002ResponseOK, +}; diff --git a/packages/interface/src/common/enum/index.ts b/packages/interface/src/common/enum/index.ts new file mode 100644 index 0000000..02980fa --- /dev/null +++ b/packages/interface/src/common/enum/index.ts @@ -0,0 +1 @@ +export * from "./organization.enum"; diff --git a/packages/interface/src/common/enum/organization.enum.ts b/packages/interface/src/common/enum/organization.enum.ts new file mode 100644 index 0000000..515e049 --- /dev/null +++ b/packages/interface/src/common/enum/organization.enum.ts @@ -0,0 +1,63 @@ +// 조직 유형 E +export enum OrganizationTypeE { + Autonomous = 1, // 자치기구 + Standing, // 상설위원회 + Specialized, // 전문기구 + Special, // 특별기구 + Emergency, // 비상대책위원회 + UA, // 학부 총학생회 + StudentCouncil, // 학과학생회 + Preparatory, // 준비위원회 + Affairs, // 특임위원회 + Inspect, // 조사위원회 +} + +// 조직 대표 유형 E +export enum OrganizationPresidentTypeE { + Chief = 1, // 정 + Vice, // 부 +} + +// OrganizationTypeE +export const getDisplayNameOrganizationTypeE = ( + type: OrganizationTypeE | undefined, +) => { + switch (type) { + case OrganizationTypeE.Autonomous: + return "자치기구"; + case OrganizationTypeE.Standing: + return "상설위원회"; + case OrganizationTypeE.Specialized: + return "전문기구"; + case OrganizationTypeE.Special: + return "특별기구"; + case OrganizationTypeE.Emergency: + return "비상대책위원회"; + case OrganizationTypeE.UA: + return "학부 총학생회"; + case OrganizationTypeE.StudentCouncil: + return "학과학생회"; + case OrganizationTypeE.Preparatory: + return "준비위원회"; + case OrganizationTypeE.Affairs: + return "특임위원회"; + case OrganizationTypeE.Inspect: + return "조사위원회"; + default: + return ""; + } +}; + +// OrganizationPresidentTypeE +export const getDisplayNameOrganizationPresidentTypeE = ( + type: OrganizationPresidentTypeE | undefined, +) => { + switch (type) { + case OrganizationPresidentTypeE.Chief: + return "정"; + case OrganizationPresidentTypeE.Vice: + return "부"; + default: + return ""; + } +}; diff --git a/packages/interface/src/common/type/ids.ts b/packages/interface/src/common/type/ids.ts new file mode 100644 index 0000000..a6a0a06 --- /dev/null +++ b/packages/interface/src/common/type/ids.ts @@ -0,0 +1,3 @@ +import z from "zod"; + +export const zId = z.coerce.number().int().min(1); From edf6e1e90a7aa88b748e34b8094e3101d3258ed7 Mon Sep 17 00:00:00 2001 From: Gerbera3090 Date: Mon, 25 Nov 2024 04:34:54 +0900 Subject: [PATCH 2/2] feat: post for organiazation that apiOrg002 --- .../controller/organization.controller.ts | 15 ++++++- .../repository/organization.repository.ts | 40 +++++++++++++++++++ .../service/organization.service.ts | 27 ++++++++++++- .../interface/src/api/organization/index.ts | 3 ++ 4 files changed, 83 insertions(+), 2 deletions(-) diff --git a/packages/api/src/feature/organization/controller/organization.controller.ts b/packages/api/src/feature/organization/controller/organization.controller.ts index d28af44..c403d4f 100644 --- a/packages/api/src/feature/organization/controller/organization.controller.ts +++ b/packages/api/src/feature/organization/controller/organization.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Param, UsePipes } from "@nestjs/common"; +import { Body, Controller, Get, Param, Post, UsePipes } from "@nestjs/common"; import { ZodPipe } from "@sparcs-students/api/common/pipes/zod-pipe"; import { @@ -6,6 +6,10 @@ import { ApiOrg001RequestUrl, apiOrg001, ApiOrg001RequestParam, + ApiOrg002RequestUrl, + apiOrg002, + ApiOrg002RequestBody, + ApiOrg002ResponseOK, } from "@sparcs-students/interface/api/organization/index"; import { OrganizationService } from "../service/organization.service"; @@ -21,4 +25,13 @@ export class OrganizationController { ): Promise { return this.organizationService.getOrganizationsBySemesterId(param); } + + @Post(ApiOrg002RequestUrl) + @UsePipes(new ZodPipe(apiOrg002)) + async postOrganization( + @Body() body: ApiOrg002RequestBody, + ): Promise { + const res = await this.organizationService.postOrganization(body); + return res; + } } diff --git a/packages/api/src/feature/organization/repository/organization.repository.ts b/packages/api/src/feature/organization/repository/organization.repository.ts index 410a62e..14c67cf 100644 --- a/packages/api/src/feature/organization/repository/organization.repository.ts +++ b/packages/api/src/feature/organization/repository/organization.repository.ts @@ -1,4 +1,5 @@ import { Injectable, Inject } from "@nestjs/common"; +import { ApiOrg002RequestBody } from "@sparcs-students/interface/api/organization/index"; import { and, or, lte, gte, eq, isNull } from "drizzle-orm"; import { MySql2Database } from "drizzle-orm/mysql2"; import { DrizzleAsyncProvider } from "src/drizzle/drizzle.provider"; @@ -106,4 +107,43 @@ export class OrganizationRepository { organizationTypeEnum: row.organization_type_enum, })); } + + async ckOrganizationBeforeCreate( + body: ApiOrg002RequestBody, + ): Promise { + const select = await this.db + .select() + .from(Organization) + .where( + and( + eq(Organization.name, body.name), + eq(Organization.nameEng, body.nameEng), + eq(Organization.organizationTypeEnumId, body.organizationTypeId), + eq(Organization.foundingYear, body.foundingYear), + eq(Organization.startTerm, body.startTerm), + ), + ) + .limit(1); + if (select.length === 0) { + return 0; + } + return select[0].id; + } + + async createOrganization(body: ApiOrg002RequestBody): Promise { + await this.db + .insert(Organization) + .values({ + name: body.name, + nameEng: body.nameEng, + organizationTypeEnumId: body.organizationTypeId, + foundingYear: body.foundingYear, + startTerm: body.startTerm, + endTerm: body.endTerm ? body.endTerm : null, + }) + .execute(); + + const res = await this.ckOrganizationBeforeCreate(body); + return res; + } } diff --git a/packages/api/src/feature/organization/service/organization.service.ts b/packages/api/src/feature/organization/service/organization.service.ts index dcc6b05..3ba4397 100644 --- a/packages/api/src/feature/organization/service/organization.service.ts +++ b/packages/api/src/feature/organization/service/organization.service.ts @@ -1,8 +1,10 @@ -import { Injectable } from "@nestjs/common"; +import { HttpException, HttpStatus, Injectable } from "@nestjs/common"; import { ApiOrg001RequestParam, ApiOrg001ResponseOK, + ApiOrg002RequestBody, + ApiOrg002ResponseOK, } from "@sparcs-students/interface/api/organization/index"; import { SemesterPublicService } from "src/feature/semester/semester.public.service"; @@ -56,4 +58,27 @@ export class OrganizationService { return { organizationTypes }; } + + async postOrganization( + body: ApiOrg002RequestBody, + ): Promise { + const ck = + await this.organizationRepository.ckOrganizationBeforeCreate(body); + if (ck > 0) { + throw new HttpException( + "Organization already exists", + HttpStatus.BAD_REQUEST, + ); + } + const organizationId = + await this.organizationRepository.createOrganization(body); + if (organizationId < 1) { + throw new HttpException( + "Failed to create organization", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + + return { organizationId }; + } } diff --git a/packages/interface/src/api/organization/index.ts b/packages/interface/src/api/organization/index.ts index 0201a28..2bbb282 100644 --- a/packages/interface/src/api/organization/index.ts +++ b/packages/interface/src/api/organization/index.ts @@ -1,2 +1,5 @@ export * from "./endpoint/apiOrg001"; export { default as apiOrg001 } from "./endpoint/apiOrg001"; // default export 추가 + +export * from "./endpoint/apiOrg002"; +export { default as apiOrg002 } from "./endpoint/apiOrg002"; // default export 추가