From c753d8e5a4f49324627625efbb65677538ada483 Mon Sep 17 00:00:00 2001 From: Gerbera3090 Date: Thu, 21 Nov 2024 04:28:32 +0900 Subject: [PATCH 1/5] feat: apiprp001 in interfece --- .../service/organization.public.service.ts | 0 .../project-proposal.module.ts | 6 ++ .../src/feature/proposal/proposal.module.ts | 4 +- .../semester/semester.public.service.ts | 2 +- .../src/api/proposal/endpoint/apiPrp001.ts | 74 +++++++++++++++++++ packages/interface/src/api/proposal/index.ts | 2 + packages/interface/src/common/stringLength.ts | 1 + packages/interface/src/common/type/ids.ts | 3 + 8 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 packages/api/src/feature/organization/service/organization.public.service.ts create mode 100644 packages/api/src/feature/proposal/project-proposal/project-proposal.module.ts create mode 100644 packages/interface/src/api/proposal/endpoint/apiPrp001.ts create mode 100644 packages/interface/src/api/proposal/index.ts create mode 100644 packages/interface/src/common/type/ids.ts diff --git a/packages/api/src/feature/organization/service/organization.public.service.ts b/packages/api/src/feature/organization/service/organization.public.service.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/api/src/feature/proposal/project-proposal/project-proposal.module.ts b/packages/api/src/feature/proposal/project-proposal/project-proposal.module.ts new file mode 100644 index 0000000..06155bf --- /dev/null +++ b/packages/api/src/feature/proposal/project-proposal/project-proposal.module.ts @@ -0,0 +1,6 @@ +import { Module } from "@nestjs/common"; + +@Module({ + imports: [], +}) +export class ProjectProposalModule {} diff --git a/packages/api/src/feature/proposal/proposal.module.ts b/packages/api/src/feature/proposal/proposal.module.ts index 292319a..c7572cd 100644 --- a/packages/api/src/feature/proposal/proposal.module.ts +++ b/packages/api/src/feature/proposal/proposal.module.ts @@ -1,4 +1,6 @@ import { Module } from "@nestjs/common"; -@Module({}) +@Module({ + imports: [], +}) export class ProposalModule {} diff --git a/packages/api/src/feature/semester/semester.public.service.ts b/packages/api/src/feature/semester/semester.public.service.ts index 53568da..d88c0cb 100644 --- a/packages/api/src/feature/semester/semester.public.service.ts +++ b/packages/api/src/feature/semester/semester.public.service.ts @@ -3,7 +3,7 @@ import { SemesterT } from "@sparcs-students/api/drizzle/schema"; import { SemesterRepository } from "./semester.repository"; @Injectable() -export default class SemesterPublicService { +export class SemesterPublicService { constructor(private semesterRepository: SemesterRepository) {} /** diff --git a/packages/interface/src/api/proposal/endpoint/apiPrp001.ts b/packages/interface/src/api/proposal/endpoint/apiPrp001.ts new file mode 100644 index 0000000..c4c3779 --- /dev/null +++ b/packages/interface/src/api/proposal/endpoint/apiPrp001.ts @@ -0,0 +1,74 @@ +import { HttpStatusCode } from "axios"; +import { z } from "zod"; + +import { + zEnumName, + zOrgName, + zUserName, +} from "@sparcs-students/interface/common/stringLength"; +import { zId } from "@sparcs-students/interface/common/type/ids"; + +/** + * @version v0.1 + * @description 쿼리 파라미터로 사업계획서 뷰어 내용을 받아옵니다. + */ + +const url = () => `/student/proposals/project-proposals`; +const method = "GET"; + +const requestParam = z.object({}); + +const requestQuery = z.object({ + semesterId: zId, + organizationId: zId, +}); + +const requestBody = z.object({}); + +const responseBodyMap = { + [HttpStatusCode.Ok]: z.object({ + organizationId: zId, + organizationName: zOrgName, + semesterId: zId, + organizationPresidentId: zId, + organizationPresidentName: zUserName, + submitDate: z.date(), + projects: z + .object({ + projectProposalId: zId, + name: z.coerce.string().max(255), + startTerm: z.date(), + endTerm: z.date(), + acceptedStatus: zEnumName, + }) + .array(), + }), +}; + +const responseErrorMap = {}; + +const apiPrp001 = { + url, + method, + requestParam, + requestQuery, + requestBody, + responseBodyMap, + responseErrorMap, +}; + +type ApiPrp001RequestParam = z.infer; +type ApiPrp001RequestQuery = z.infer; +type ApiPrp001RequestBody = z.infer; +type ApiPrp001ResponseOK = z.infer<(typeof apiPrp001.responseBodyMap)[200]>; + +export default apiPrp001; + +export const ApiPrp001RequestUrl = "/student/proposals/project-proposals"; + +export type { + ApiPrp001RequestParam, + ApiPrp001RequestQuery, + ApiPrp001RequestBody, + ApiPrp001ResponseOK, +}; diff --git a/packages/interface/src/api/proposal/index.ts b/packages/interface/src/api/proposal/index.ts new file mode 100644 index 0000000..e473953 --- /dev/null +++ b/packages/interface/src/api/proposal/index.ts @@ -0,0 +1,2 @@ +export * from "./endpoint/apiPrp001"; +export { default as apiPrp001 } from "./endpoint/apiPrp001"; diff --git a/packages/interface/src/common/stringLength.ts b/packages/interface/src/common/stringLength.ts index 99e4b45..1f6a211 100644 --- a/packages/interface/src/common/stringLength.ts +++ b/packages/interface/src/common/stringLength.ts @@ -4,3 +4,4 @@ export const zOrgName = z.string().max(30); export const zOrgNameEng = z.string().max(100); export const zUserName = z.string().max(255); export const zFileName = z.string().max(256); +export const zEnumName = z.string().max(30); 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 955a7ab926e3ccf516480e80b0c1a6c58612bb4f Mon Sep 17 00:00:00 2001 From: Gerbera3090 Date: Fri, 22 Nov 2024 04:44:59 +0900 Subject: [PATCH 2/5] feat: add prp001 --- packages/api/src/app.module.ts | 4 +- .../api/src/drizzle/schema/enum.schema.ts | 77 ++++++++++----- .../api/src/drizzle/schema/meeting.schema.ts | 4 + .../src/drizzle/schema/organization.schema.ts | 5 + .../api/src/drizzle/schema/proposal.schema.ts | 31 ++++++ .../controller/organization.controller.ts | 2 +- .../organization/organization.module.ts | 9 +- .../repository/organization.repository.ts | 66 ++++++++++++- .../service/organization.public.service.ts | 57 +++++++++++ .../service/organization.service.ts | 3 +- .../project-proposal.controller.ts | 28 ++++++ .../project-proposal.module.ts | 10 +- .../project-proposal.repository.ts | 99 +++++++++++++++++++ .../project-proposal.service.ts | 56 +++++++++++ .../src/feature/proposal/proposal.module.ts | 3 +- .../src/feature/semester/semester.module.ts | 7 +- .../semester/semester.public.service.ts | 1 + 17 files changed, 424 insertions(+), 38 deletions(-) create mode 100644 packages/api/src/feature/proposal/project-proposal/project-proposal.controller.ts create mode 100644 packages/api/src/feature/proposal/project-proposal/project-proposal.repository.ts create mode 100644 packages/api/src/feature/proposal/project-proposal/project-proposal.service.ts diff --git a/packages/api/src/app.module.ts b/packages/api/src/app.module.ts index dd0f27b..1b76dcf 100644 --- a/packages/api/src/app.module.ts +++ b/packages/api/src/app.module.ts @@ -2,9 +2,11 @@ import { Module } from "@nestjs/common"; import { AppController } from "./app.controller"; import { AppService } from "./app.service"; import { DrizzleModule } from "./drizzle/drizzle.module"; +import { OrganizationModule } from "./feature/organization/organization.module"; +import { ProposalModule } from "./feature/proposal/proposal.module"; @Module({ - imports: [DrizzleModule], + imports: [DrizzleModule, OrganizationModule, ProposalModule], controllers: [AppController], providers: [AppService], }) diff --git a/packages/api/src/drizzle/schema/enum.schema.ts b/packages/api/src/drizzle/schema/enum.schema.ts index 565697d..203f39e 100644 --- a/packages/api/src/drizzle/schema/enum.schema.ts +++ b/packages/api/src/drizzle/schema/enum.schema.ts @@ -1,7 +1,7 @@ import { InferSelectModel } from "drizzle-orm"; import { int, varchar, timestamp, mysqlTable } from "drizzle-orm/mysql-core"; -export const OrganizationTypeEnum = mysqlTable("org_typ_e", { +export const OrganizationTypeEnum = mysqlTable("organization_type_enum", { id: int("id").autoincrement().primaryKey().notNull(), name: varchar("name", { length: 30 }).notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), @@ -9,15 +9,18 @@ export const OrganizationTypeEnum = mysqlTable("org_typ_e", { deletedAt: timestamp("deleted_at"), }); -export const OrganizationPresidentTypeEnum = mysqlTable("org_pre_typ_e", { - id: int("id").autoincrement().primaryKey().notNull(), - name: varchar("name", { length: 30 }).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), - deletedAt: timestamp("deleted_at"), -}); +export const OrganizationPresidentTypeEnum = mysqlTable( + "organization_president_type_enum", + { + id: int("id").autoincrement().primaryKey().notNull(), + name: varchar("name", { length: 30 }).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), + deletedAt: timestamp("deleted_at"), + }, +); -export const BudgetDomainEnum = mysqlTable("bud_dom_e", { +export const BudgetDomainEnum = mysqlTable("budget_domain_enum", { id: int("id").autoincrement().primaryKey().notNull(), name: varchar("name", { length: 30 }).notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), @@ -25,23 +28,29 @@ export const BudgetDomainEnum = mysqlTable("bud_dom_e", { deletedAt: timestamp("deleted_at"), }); -export const BudgetDivisionIncomeEnum = mysqlTable("bud_div_inc_e", { - id: int("id").autoincrement().primaryKey().notNull(), - name: varchar("name", { length: 30 }).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), - deletedAt: timestamp("deleted_at"), -}); +export const BudgetDivisionIncomeEnum = mysqlTable( + "budget_division_income_enum", + { + id: int("id").autoincrement().primaryKey().notNull(), + name: varchar("name", { length: 30 }).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), + deletedAt: timestamp("deleted_at"), + }, +); -export const BudgetDivisionExpenseEnum = mysqlTable("bud_div_exp_e", { - id: int("id").autoincrement().primaryKey().notNull(), - name: varchar("name", { length: 30 }).notNull(), - createdAt: timestamp("created_at").defaultNow().notNull(), - updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), - deletedAt: timestamp("deleted_at"), -}); +export const BudgetDivisionExpenseEnum = mysqlTable( + "budget_division_expense_enum", + { + id: int("id").autoincrement().primaryKey().notNull(), + name: varchar("name", { length: 30 }).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), + deletedAt: timestamp("deleted_at"), + }, +); -export const BudgetClassExpenseEnum = mysqlTable("bud_cla_exp_e", { +export const BudgetClassExpenseEnum = mysqlTable("budget_class_expense_enum", { id: int("id").autoincrement().primaryKey().notNull(), name: varchar("name", { length: 30 }).notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), @@ -49,7 +58,7 @@ export const BudgetClassExpenseEnum = mysqlTable("bud_cla_exp_e", { deletedAt: timestamp("deleted_at"), }); -export const TransactionTypeEnum = mysqlTable("tra_typ_e", { +export const TransactionTypeEnum = mysqlTable("transaction_type_enum", { id: int("id").autoincrement().primaryKey().notNull(), name: varchar("name", { length: 30 }).notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), @@ -57,7 +66,7 @@ export const TransactionTypeEnum = mysqlTable("tra_typ_e", { deletedAt: timestamp("deleted_at"), }); -export const ReportFileTypeEnum = mysqlTable("rep_fil_typ_e", { +export const ReportFileTypeEnum = mysqlTable("report_file_type_enum", { id: int("id").autoincrement().primaryKey().notNull(), name: varchar("name", { length: 30 }).notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), @@ -65,7 +74,7 @@ export const ReportFileTypeEnum = mysqlTable("rep_fil_typ_e", { deletedAt: timestamp("deleted_at"), }); -export const AssistantPermissionEnum = mysqlTable("ass_per_e", { +export const AssistantPermissionEnum = mysqlTable("assistant_permission_enum", { id: int("id").autoincrement().primaryKey().notNull(), name: varchar("name", { length: 30 }).notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), @@ -73,9 +82,23 @@ export const AssistantPermissionEnum = mysqlTable("ass_per_e", { deletedAt: timestamp("deleted_at"), }); +export const AgendaAcceptedStatusEnum = mysqlTable( + "agenda_accepted_status_enum", + { + id: int("id").autoincrement().primaryKey().notNull(), + name: varchar("name", { length: 30 }).notNull(), + createdAt: timestamp("created_at").defaultNow().notNull(), + updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), + deletedAt: timestamp("deleted_at"), + }, +); + export type OrganizationTypeEnumT = InferSelectModel< typeof OrganizationTypeEnum >; export type OrganizationPresidentTypeEnumT = InferSelectModel< typeof OrganizationPresidentTypeEnum >; +export type AgendaAcceptedStatusEnumT = InferSelectModel< + typeof AgendaAcceptedStatusEnum +>; diff --git a/packages/api/src/drizzle/schema/meeting.schema.ts b/packages/api/src/drizzle/schema/meeting.schema.ts index 05467d0..0712915 100644 --- a/packages/api/src/drizzle/schema/meeting.schema.ts +++ b/packages/api/src/drizzle/schema/meeting.schema.ts @@ -1,3 +1,4 @@ +import { InferSelectModel } from "drizzle-orm"; import { int, varchar, @@ -39,3 +40,6 @@ export const Agenda = mysqlTable( }), }), ); + +export type MeetingT = InferSelectModel; +export type AgendaT = InferSelectModel; diff --git a/packages/api/src/drizzle/schema/organization.schema.ts b/packages/api/src/drizzle/schema/organization.schema.ts index 3e07eac..a668ae9 100644 --- a/packages/api/src/drizzle/schema/organization.schema.ts +++ b/packages/api/src/drizzle/schema/organization.schema.ts @@ -51,6 +51,7 @@ export const OrganizationPresident = mysqlTable( organizationPresidentTypeEnumId: int( "organization_president_type_enum_id", ).notNull(), + organizationId: int("organization_id").notNull(), phoneNumber: varchar("phone_number", { length: 20 }).notNull(), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), @@ -61,6 +62,10 @@ export const OrganizationPresident = mysqlTable( columns: [table.userId], foreignColumns: [User.id], }), + organizationIdFk: foreignKey({ + columns: [table.organizationId], + foreignColumns: [Organization.id], + }), }), ); diff --git a/packages/api/src/drizzle/schema/proposal.schema.ts b/packages/api/src/drizzle/schema/proposal.schema.ts index d639926..68e15b2 100644 --- a/packages/api/src/drizzle/schema/proposal.schema.ts +++ b/packages/api/src/drizzle/schema/proposal.schema.ts @@ -7,6 +7,7 @@ import { mysqlTable, foreignKey, } from "drizzle-orm/mysql-core"; +import { InferSelectModel } from "drizzle-orm"; import { Organization, Team } from "./organization.schema"; import { Semester } from "./semester.schema"; import { User } from "./user.schema"; @@ -61,6 +62,7 @@ export const ProjectProposalRevision = mysqlTable( target: text("target").notNull(), detail: text("detail").notNull(), note: text("note"), + agendaId: int("agenda_id"), createdAt: timestamp("created_at").defaultNow().notNull(), updatedAt: timestamp("updated_at").defaultNow().onUpdateNow().notNull(), deletedAt: timestamp("deleted_at"), @@ -79,6 +81,11 @@ export const ProjectProposalRevision = mysqlTable( foreignColumns: [ProjectProposal.id], name: "pro_pro_rev_document_id_fk", }), + agendaIdFk: foreignKey({ + columns: [table.agendaId], + foreignColumns: [Agenda.id], + name: "pro_pro_rev_agenda_id_fk", + }), }), ); @@ -299,3 +306,27 @@ export const BudgetProposalExpenseRevision = mysqlTable( }), }), ); + +export type ProjectProposalT = InferSelectModel; +export type ProjectProposalRevisionT = InferSelectModel< + typeof ProjectProposalRevision +>; +export type ProjectProposalTimelineT = InferSelectModel< + typeof ProjectProposalTimeline +>; +export type OperationProposalT = InferSelectModel; +export type OperatingCommitteeProposalT = InferSelectModel< + typeof OperatingCommitteeProposal +>; +export type BudgetProposalIncomeT = InferSelectModel< + typeof BudgetProposalIncome +>; +export type BudgetProposalIncomeRevisionT = InferSelectModel< + typeof BudgetProposalIncomeRevision +>; +export type BudgetProposalExpenseT = InferSelectModel< + typeof BudgetProposalExpense +>; +export type BudgetProposalExpenseRevisionT = InferSelectModel< + typeof BudgetProposalExpenseRevision +>; diff --git a/packages/api/src/feature/organization/controller/organization.controller.ts b/packages/api/src/feature/organization/controller/organization.controller.ts index 3882d8d..d28af44 100644 --- a/packages/api/src/feature/organization/controller/organization.controller.ts +++ b/packages/api/src/feature/organization/controller/organization.controller.ts @@ -10,7 +10,7 @@ import { import { OrganizationService } from "../service/organization.service"; -@Controller("organization") +@Controller() export class OrganizationController { constructor(private readonly organizationService: OrganizationService) {} diff --git a/packages/api/src/feature/organization/organization.module.ts b/packages/api/src/feature/organization/organization.module.ts index 03a95c7..4cccd4b 100644 --- a/packages/api/src/feature/organization/organization.module.ts +++ b/packages/api/src/feature/organization/organization.module.ts @@ -4,10 +4,17 @@ import { DrizzleModule } from "src/drizzle/drizzle.module"; import { SemesterModule } from "src/feature/semester/semester.module"; import { OrganizationService } from "./service/organization.service"; import { OrganizationController } from "./controller/organization.controller"; +import { OrganizationPublicService } from "./service/organization.public.service"; +import { OrganizationRepository } from "./repository/organization.repository"; @Module({ imports: [DrizzleModule, SemesterModule], controllers: [OrganizationController], - providers: [OrganizationService], + providers: [ + OrganizationService, + OrganizationRepository, + OrganizationPublicService, + ], + exports: [OrganizationPublicService], }) export class OrganizationModule {} diff --git a/packages/api/src/feature/organization/repository/organization.repository.ts b/packages/api/src/feature/organization/repository/organization.repository.ts index 3b0d8c0..f7ef4a3 100644 --- a/packages/api/src/feature/organization/repository/organization.repository.ts +++ b/packages/api/src/feature/organization/repository/organization.repository.ts @@ -1,5 +1,5 @@ import { Injectable, Inject } from "@nestjs/common"; -import { and, or, lte, gte, eq } from "drizzle-orm"; +import { and, or, lte, gte, eq, isNull } from "drizzle-orm"; import { MySql2Database } from "drizzle-orm/mysql2"; import { DrizzleAsyncProvider } from "src/drizzle/drizzle.provider"; @@ -8,14 +8,71 @@ import { OrganizationTypeEnum, OrganizationT, OrganizationTypeEnumT, + User, + UserStudent, + OrganizationPresident, + OrganizationPresidentT, + UserT, + UserStudentT, } from "src/drizzle/schema"; +export type OrganizationWithPresidentT = { + organization: OrganizationT; + organizationType: OrganizationTypeEnumT; + president: OrganizationPresidentT; + user: UserT; + userStudent: UserStudentT; +}; + @Injectable() export class OrganizationRepository { constructor( @Inject(DrizzleAsyncProvider) private readonly db: MySql2Database, ) {} + async getOrganizationById(id: number): Promise { + return this.db.select().from(Organization).where(eq(Organization.id, id)); + } + + async getOrganizationWithPresidentById( + organizationId: number, + date: Date, + ): Promise { + const res = await this.db + .select() + .from(Organization) + .innerJoin( + OrganizationTypeEnum, + eq(Organization.organizationTypeEnumId, OrganizationTypeEnum.id), + ) + .innerJoin( + OrganizationPresident, + eq(Organization.id, OrganizationPresident.organizationId), + ) + .innerJoin(User, eq(OrganizationPresident.userId, User.id)) + .innerJoin(UserStudent, eq(UserStudent.userId, User.id)) + .where( + and( + eq(Organization.id, organizationId), + and( + lte(OrganizationPresident.startTerm, date), + or( + gte(OrganizationPresident.endTerm, date), + isNull(OrganizationPresident.endTerm), + ), + ), + eq(OrganizationPresident.organizationPresidentTypeEnumId, 1), + ), + ); + return res.map(row => ({ + organization: row.organization, + organizationType: row.organization_type_enum, + president: row.organization_president, + user: row.user, + userStudent: row.user_student, + })); + } + async getOrganizationsByTerms( startTerm: Date, endTerm: Date, @@ -40,11 +97,14 @@ export class OrganizationRepository { ), and( lte(Organization.startTerm, endTerm), - eq(Organization.endTerm, null), + isNull(Organization.endTerm), ), ), ); - return res.map(row => ({ ...row, organizationTypeEnum: row.org_typ_e })); + return res.map(row => ({ + ...row, + organizationTypeEnum: row.organization_type_enum, + })); } } diff --git a/packages/api/src/feature/organization/service/organization.public.service.ts b/packages/api/src/feature/organization/service/organization.public.service.ts index e69de29..17ebdaa 100644 --- a/packages/api/src/feature/organization/service/organization.public.service.ts +++ b/packages/api/src/feature/organization/service/organization.public.service.ts @@ -0,0 +1,57 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; + +import { SemesterPublicService } from "src/feature/semester/semester.public.service"; + +import { + OrganizationRepository, + OrganizationWithPresidentT, +} from "../repository/organization.repository"; + +@Injectable() +export class OrganizationPublicService { + constructor( + private readonly organizationRepository: OrganizationRepository, + private readonly semesterPublicService: SemesterPublicService, + ) {} + + /** + * @param organizationId, date + * @returns OrganizationWithPresidentT + * @description 해당 시간의 해당 기관의 president 정보와 함께 반환합니다. + */ + async getOrganizationWithPresidentByOrganizationIdAndDate( + organizationId: number, + date: Date, + ): Promise { + const res = + await this.organizationRepository.getOrganizationWithPresidentById( + organizationId, + date, + ); + if (!res) { + throw new NotFoundException( + `Organization with ID ${organizationId} not found.`, + ); + } + return res[0]; + } + + /** + * @param organizationId, semesterId + * @returns OrganizationWithPresidentT + * @description 해당 학기 마지막 날의 해당 기관의 president 정보와 함께 반환합니다. 즉, 학기로 기간을 얻을 수 있습니다. + */ + async getOrganizationWithPresidentByOrganizationIdAndSemesterId( + organizationId: number, + semesterId: number, + ): Promise { + const { endTerm } = + await this.semesterPublicService.getSemesterById(semesterId); + + const res = await this.getOrganizationWithPresidentByOrganizationIdAndDate( + organizationId, + endTerm, + ); + 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 2651040..99dc78c 100644 --- a/packages/api/src/feature/organization/service/organization.service.ts +++ b/packages/api/src/feature/organization/service/organization.service.ts @@ -5,7 +5,7 @@ import { ApiOrg001ResponseOK, } from "@sparcs-students/interface/api/organization/index"; -import SemesterPublicService from "src/feature/semester/semester.public.service"; +import { SemesterPublicService } from "src/feature/semester/semester.public.service"; import { OrganizationRepository } from "../repository/organization.repository"; @@ -26,7 +26,6 @@ export class OrganizationService { startTerm, endTerm, ); - // 변환 작업: OriginalResponse -> ApiOrg001ResponseOK const organizationTypesMap = organizations.reduce((acc, curr) => { const { organization, organizationTypeEnum } = curr; diff --git a/packages/api/src/feature/proposal/project-proposal/project-proposal.controller.ts b/packages/api/src/feature/proposal/project-proposal/project-proposal.controller.ts new file mode 100644 index 0000000..0d02af4 --- /dev/null +++ b/packages/api/src/feature/proposal/project-proposal/project-proposal.controller.ts @@ -0,0 +1,28 @@ +import { Controller, Get, Query, UsePipes } from "@nestjs/common"; + +import { ZodPipe } from "@sparcs-students/api/common/pipes/zod-pipe"; +import { + ApiPrp001ResponseOK, + ApiPrp001RequestUrl, + apiPrp001, + ApiPrp001RequestQuery, +} from "@sparcs-students/interface/api/proposal/index"; + +import { ProjectProposalService } from "./project-proposal.service"; + +@Controller() +export class ProjectProposalController { + constructor( + private readonly projectProposalService: ProjectProposalService, + ) {} + + @Get(ApiPrp001RequestUrl) + @UsePipes(new ZodPipe(apiPrp001)) + async getPrpanizationsBySemesterId( + @Query() query: ApiPrp001RequestQuery, + ): Promise { + return this.projectProposalService.getProjectProposalsForStudentsBySemesterId( + query, + ); + } +} diff --git a/packages/api/src/feature/proposal/project-proposal/project-proposal.module.ts b/packages/api/src/feature/proposal/project-proposal/project-proposal.module.ts index 06155bf..b2dcfcb 100644 --- a/packages/api/src/feature/proposal/project-proposal/project-proposal.module.ts +++ b/packages/api/src/feature/proposal/project-proposal/project-proposal.module.ts @@ -1,6 +1,14 @@ import { Module } from "@nestjs/common"; +import { DrizzleModule } from "src/drizzle/drizzle.module"; +import { OrganizationModule } from "src/feature/organization/organization.module"; +import { ProjectProposalRepository } from "./project-proposal.repository"; +import { ProjectProposalService } from "./project-proposal.service"; +import { ProjectProposalController } from "./project-proposal.controller"; + @Module({ - imports: [], + imports: [OrganizationModule, DrizzleModule], + providers: [ProjectProposalRepository, ProjectProposalService], + controllers: [ProjectProposalController], }) export class ProjectProposalModule {} diff --git a/packages/api/src/feature/proposal/project-proposal/project-proposal.repository.ts b/packages/api/src/feature/proposal/project-proposal/project-proposal.repository.ts new file mode 100644 index 0000000..41afe69 --- /dev/null +++ b/packages/api/src/feature/proposal/project-proposal/project-proposal.repository.ts @@ -0,0 +1,99 @@ +import { Injectable, Inject } from "@nestjs/common"; + +import { + Agenda, + AgendaAcceptedStatusEnum, + ProjectProposal, + ProjectProposalRevision, +} from "@sparcs-students/api/drizzle/schema"; +import { ApiPrp001ResponseOK } from "@sparcs-students/interface/api/proposal/index"; + +import { or, isNull, isNotNull, and, eq, desc } from "drizzle-orm"; + +import { MySql2Database } from "drizzle-orm/mysql2"; +import { DrizzleAsyncProvider } from "src/drizzle/drizzle.provider"; + +@Injectable() +export class ProjectProposalRepository { + constructor( + @Inject(DrizzleAsyncProvider) private readonly db: MySql2Database, + ) {} + + /** + * @param organizationId, semester id + * @returns 해당 동아리와 학기에 해당하는 ProjectProposals list 객체를 리턴합니다. + * @description 이미 사업계획서를 모두 심사한 후를 기준으로 하는 상태로, 일반 학생들도 볼 수 있는 상태입니다. + */ + async getProjectProposalsForStudentsByOrganizationIdAndSemesterId( + organizationId: number, + semesterId: number, + ): Promise { + const res = await this.db + .select() + .from(ProjectProposal) + .innerJoin( + ProjectProposalRevision, + eq(ProjectProposal.revisionId, ProjectProposalRevision.id), + ) + .leftJoin(Agenda, eq(ProjectProposalRevision.agendaId, Agenda.id)) + .leftJoin( + AgendaAcceptedStatusEnum, + or( + and( + isNotNull(ProjectProposalRevision.agendaId), + eq(Agenda.accepted, true), + eq(AgendaAcceptedStatusEnum.id, 1), // 조건에 따라 `id = 1` + ), + and( + or( + isNull(ProjectProposalRevision.agendaId), + eq(Agenda.accepted, false), + ), + eq(AgendaAcceptedStatusEnum.id, 2), // 조건에 따라 `id = 2` + ), + ), + ) + .where( + and( + eq(ProjectProposal.organizationId, organizationId), + eq(ProjectProposal.semesterId, semesterId), + ), + ); + return res.map(row => ({ + name: row.project_proposal_revision.name, + projectProposalId: row.project_proposal.id, + startTerm: row.project_proposal_revision.startTerm, + endTerm: row.project_proposal_revision.endTerm, + acceptedStatus: row.agenda_accepted_status_enum.name, + })); + } + + /** + * @param organizationId, semesterId + * @returns 해당 기구 해당 학기에 해당하는 ProjectProposal의 제출연월일을 리턴합니다. + * @description 가장 최근의 제출일을 리턴합니다. + */ + async getProjectProposalSubmitDate( + organizationId: number, + semesterId: number, + ): Promise { + const res = await this.db + .select() + .from(ProjectProposal) + .innerJoin( + ProjectProposalRevision, + eq(ProjectProposal.revisionId, ProjectProposalRevision.id), + ) + .innerJoin(Agenda, eq(ProjectProposalRevision.agendaId, Agenda.id)) + .where( + and( + eq(ProjectProposal.semesterId, semesterId), + eq(ProjectProposal.organizationId, organizationId), + ), + ) + .orderBy(desc(Agenda.submittedAt)) + .limit(1); + + return res.map(row => row.agenda.submittedAt); + } +} diff --git a/packages/api/src/feature/proposal/project-proposal/project-proposal.service.ts b/packages/api/src/feature/proposal/project-proposal/project-proposal.service.ts new file mode 100644 index 0000000..6fba559 --- /dev/null +++ b/packages/api/src/feature/proposal/project-proposal/project-proposal.service.ts @@ -0,0 +1,56 @@ +import { Injectable, NotFoundException } from "@nestjs/common"; + +import { + ApiPrp001RequestQuery, + ApiPrp001ResponseOK, +} from "@sparcs-students/interface/api/proposal/index"; +import { OrganizationPublicService } from "src/feature/organization/service/organization.public.service"; +import { ProjectProposalRepository } from "./project-proposal.repository"; + +@Injectable() +export class ProjectProposalService { + constructor( + private readonly projectProposalRepository: ProjectProposalRepository, + private readonly organizationPublicService: OrganizationPublicService, + ) {} + + async getProjectProposalsForStudentsBySemesterId( + param: ApiPrp001RequestQuery, + ): Promise { + const organizationInfo = + await this.organizationPublicService.getOrganizationWithPresidentByOrganizationIdAndSemesterId( + param.organizationId, + param.semesterId, + ); + const projectProposals = + await this.projectProposalRepository.getProjectProposalsForStudentsByOrganizationIdAndSemesterId( + param.organizationId, + param.semesterId, + ); + if (projectProposals.length === 0) { + throw new NotFoundException( + `ProjectProposals with Organization ID ${param.organizationId} and semesterId ${param.semesterId} not found`, + ); + } + + const submitDate = + await this.projectProposalRepository.getProjectProposalSubmitDate( + param.organizationId, + param.semesterId, + ); + if (submitDate.length === 0) { + throw new NotFoundException( + `ProjectProposal submitdate with Organization ID ${param.organizationId} and semesterId ${param.semesterId} not found`, + ); + } + return { + semesterId: param.semesterId, + organizationId: param.organizationId, + organizationName: organizationInfo.organization.name, + organizationPresidentId: organizationInfo.user.id, + organizationPresidentName: organizationInfo.user.name, + submitDate: submitDate[0], + projects: projectProposals, + }; + } +} diff --git a/packages/api/src/feature/proposal/proposal.module.ts b/packages/api/src/feature/proposal/proposal.module.ts index c7572cd..3a0bd2e 100644 --- a/packages/api/src/feature/proposal/proposal.module.ts +++ b/packages/api/src/feature/proposal/proposal.module.ts @@ -1,6 +1,7 @@ import { Module } from "@nestjs/common"; +import { ProjectProposalModule } from "./project-proposal/project-proposal.module"; @Module({ - imports: [], + imports: [ProjectProposalModule], }) export class ProposalModule {} diff --git a/packages/api/src/feature/semester/semester.module.ts b/packages/api/src/feature/semester/semester.module.ts index 74ab120..0854c3e 100644 --- a/packages/api/src/feature/semester/semester.module.ts +++ b/packages/api/src/feature/semester/semester.module.ts @@ -1,6 +1,11 @@ import { Module } from "@nestjs/common"; import { DrizzleModule } from "@sparcs-students/api/drizzle/drizzle.module"; import { SemesterRepository } from "./semester.repository"; +import { SemesterPublicService } from "./semester.public.service"; -@Module({ exports: [SemesterRepository], imports: [DrizzleModule] }) +@Module({ + providers: [SemesterRepository, SemesterPublicService], + exports: [SemesterPublicService], + imports: [DrizzleModule], +}) export class SemesterModule {} diff --git a/packages/api/src/feature/semester/semester.public.service.ts b/packages/api/src/feature/semester/semester.public.service.ts index d88c0cb..eff1eff 100644 --- a/packages/api/src/feature/semester/semester.public.service.ts +++ b/packages/api/src/feature/semester/semester.public.service.ts @@ -16,6 +16,7 @@ export class SemesterPublicService { if (semesters.length === 0) { throw new NotFoundException(`Semester with ID ${id} not found.`); } + return semesters[0]; } } From e2134ad8a669910f024d59fb084434f8833b54b0 Mon Sep 17 00:00:00 2001 From: Gerbera3090 Date: Mon, 25 Nov 2024 01:26:22 +0900 Subject: [PATCH 3/5] fix: native enums and enum response policy --- .../service/organization.public.service.ts | 2 +- .../project-proposal.repository.ts | 40 ++-- .../project-proposal.service.ts | 13 +- .../src/api/proposal/endpoint/apiPrp001.ts | 6 +- .../interface/src/common/enum/budget.enum.ts | 212 ++++++++++++++++++ packages/interface/src/common/enum/index.ts | 3 + .../interface/src/common/enum/meeting.enum.ts | 57 +++++ .../src/common/enum/organization.enum.ts | 63 ++++++ packages/interface/src/common/stringLength.ts | 1 - 9 files changed, 366 insertions(+), 31 deletions(-) create mode 100644 packages/interface/src/common/enum/budget.enum.ts create mode 100644 packages/interface/src/common/enum/index.ts create mode 100644 packages/interface/src/common/enum/meeting.enum.ts create mode 100644 packages/interface/src/common/enum/organization.enum.ts diff --git a/packages/api/src/feature/organization/service/organization.public.service.ts b/packages/api/src/feature/organization/service/organization.public.service.ts index 17ebdaa..2b19d2a 100644 --- a/packages/api/src/feature/organization/service/organization.public.service.ts +++ b/packages/api/src/feature/organization/service/organization.public.service.ts @@ -39,7 +39,7 @@ export class OrganizationPublicService { /** * @param organizationId, semesterId * @returns OrganizationWithPresidentT - * @description 해당 학기 마지막 날의 해당 기관의 president 정보와 함께 반환합니다. 즉, 학기로 기간을 얻을 수 있습니다. + * @description 해당 학기 마지막 날의 해당 기관의 president 정보와 함께 반환합니다. 즉, 기간을 정확히 명시하기 보단 학기 id를 통해 찾을 수 있도록 합니다. */ async getOrganizationWithPresidentByOrganizationIdAndSemesterId( organizationId: number, diff --git a/packages/api/src/feature/proposal/project-proposal/project-proposal.repository.ts b/packages/api/src/feature/proposal/project-proposal/project-proposal.repository.ts index 41afe69..191b760 100644 --- a/packages/api/src/feature/proposal/project-proposal/project-proposal.repository.ts +++ b/packages/api/src/feature/proposal/project-proposal/project-proposal.repository.ts @@ -2,13 +2,12 @@ import { Injectable, Inject } from "@nestjs/common"; import { Agenda, - AgendaAcceptedStatusEnum, ProjectProposal, ProjectProposalRevision, } from "@sparcs-students/api/drizzle/schema"; import { ApiPrp001ResponseOK } from "@sparcs-students/interface/api/proposal/index"; -import { or, isNull, isNotNull, and, eq, desc } from "drizzle-orm"; +import { and, eq, desc } from "drizzle-orm"; import { MySql2Database } from "drizzle-orm/mysql2"; import { DrizzleAsyncProvider } from "src/drizzle/drizzle.provider"; @@ -29,42 +28,33 @@ export class ProjectProposalRepository { semesterId: number, ): Promise { const res = await this.db - .select() + .select({ + projectName: ProjectProposalRevision.name, + proposalId: ProjectProposal.id, + startTerm: ProjectProposalRevision.startTerm, + endTerm: ProjectProposalRevision.endTerm, + agendaAccepted: Agenda.accepted, + agendaExists: ProjectProposalRevision.agendaId, + }) .from(ProjectProposal) .innerJoin( ProjectProposalRevision, eq(ProjectProposal.revisionId, ProjectProposalRevision.id), ) .leftJoin(Agenda, eq(ProjectProposalRevision.agendaId, Agenda.id)) - .leftJoin( - AgendaAcceptedStatusEnum, - or( - and( - isNotNull(ProjectProposalRevision.agendaId), - eq(Agenda.accepted, true), - eq(AgendaAcceptedStatusEnum.id, 1), // 조건에 따라 `id = 1` - ), - and( - or( - isNull(ProjectProposalRevision.agendaId), - eq(Agenda.accepted, false), - ), - eq(AgendaAcceptedStatusEnum.id, 2), // 조건에 따라 `id = 2` - ), - ), - ) .where( and( eq(ProjectProposal.organizationId, organizationId), eq(ProjectProposal.semesterId, semesterId), ), ); + return res.map(row => ({ - name: row.project_proposal_revision.name, - projectProposalId: row.project_proposal.id, - startTerm: row.project_proposal_revision.startTerm, - endTerm: row.project_proposal_revision.endTerm, - acceptedStatus: row.agenda_accepted_status_enum.name, + name: row.projectName, + projectProposalId: row.proposalId, + startTerm: row.startTerm, + endTerm: row.endTerm, + acceptedStatus: row.agendaExists && row.agendaAccepted ? 1 : 2, })); } diff --git a/packages/api/src/feature/proposal/project-proposal/project-proposal.service.ts b/packages/api/src/feature/proposal/project-proposal/project-proposal.service.ts index 6fba559..baa7252 100644 --- a/packages/api/src/feature/proposal/project-proposal/project-proposal.service.ts +++ b/packages/api/src/feature/proposal/project-proposal/project-proposal.service.ts @@ -1,4 +1,9 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; +import { + HttpException, + HttpStatus, + Injectable, + NotFoundException, +} from "@nestjs/common"; import { ApiPrp001RequestQuery, @@ -42,7 +47,13 @@ export class ProjectProposalService { throw new NotFoundException( `ProjectProposal submitdate with Organization ID ${param.organizationId} and semesterId ${param.semesterId} not found`, ); + } else if (submitDate.length > 1) { + throw new HttpException( + "unreachable: multiple submitDate", + HttpStatus.INTERNAL_SERVER_ERROR, + ); } + return { semesterId: param.semesterId, organizationId: param.organizationId, diff --git a/packages/interface/src/api/proposal/endpoint/apiPrp001.ts b/packages/interface/src/api/proposal/endpoint/apiPrp001.ts index c4c3779..0d0b0af 100644 --- a/packages/interface/src/api/proposal/endpoint/apiPrp001.ts +++ b/packages/interface/src/api/proposal/endpoint/apiPrp001.ts @@ -2,15 +2,15 @@ import { HttpStatusCode } from "axios"; import { z } from "zod"; import { - zEnumName, zOrgName, zUserName, } from "@sparcs-students/interface/common/stringLength"; import { zId } from "@sparcs-students/interface/common/type/ids"; +import { AgendaAcceptedStatusE } from "@sparcs-students/interface/common/enum"; /** * @version v0.1 - * @description 쿼리 파라미터로 사업계획서 뷰어 내용을 받아옵니다. + * @description semesterId랑 organizationId로 사업계획서 뷰어 내용을 받아옵니다. */ const url = () => `/student/proposals/project-proposals`; @@ -39,7 +39,7 @@ const responseBodyMap = { name: z.coerce.string().max(255), startTerm: z.date(), endTerm: z.date(), - acceptedStatus: zEnumName, + acceptedStatus: z.nativeEnum(AgendaAcceptedStatusE), }) .array(), }), diff --git a/packages/interface/src/common/enum/budget.enum.ts b/packages/interface/src/common/enum/budget.enum.ts new file mode 100644 index 0000000..a2d0cfa --- /dev/null +++ b/packages/interface/src/common/enum/budget.enum.ts @@ -0,0 +1,212 @@ +// 예산 영역 E +export enum BudgetDomainE { + Student = 1, // 학생회비 + School, // 본회계 + Autonomous, // 자치 +} + +// 예산 구분(수입) E +export enum BudgetDivisionIncomeE { + Substratum = 1, // 기층기구회계 + Central, // 중앙회계 + Incentive, // 격려기금 + Interest, // 예금이자 + School, // 학교지원금 + Carryover, // 이월금 + Department, // 과비 + Organizational, // 단비 + External, // 외부지원금 + Extra, // 기타수익금 + CAC, // 문화자치기금 +} + +// 예산 구분(지출) E +export enum BudgetDivisionExpenseE { + Operating = 1, // 운영비 + Regular, // 정기사업비 + New, // 비정기사업비 +} + +// 예산 클래스 E +export enum BudgetClassExpenseE { + Product = 1, // 상품비 + Gift, // 증정비 + Supply, // 물품비 + Print, // 인쇄비 + Transport, // 출장비 + Meeting, // 회의비 + Labor, // 인건비 + Welfare, // 복리후생비 + Install, // 설치비 + Delivery, // 운반비 + Repair, // 수선비 + Telecom, // 통신비 + Insurance, // 보험료 + Extra, // 기타비 + Backup, // 예비비 + Incentive, // 격려금 +} + +// 거래 유형 E +export enum TransactionTypeE { + OfficialCard = 1, // 공금카드 + Bank, // 계좌이체 + Cash, // 현금거래 + PrivateCard, // 개인카드 + Personal, // 사비집행 +} + +// 보고서 파일 유형 E +export enum ReportFileTypeE { + Receipt = 1, // 영수증 + CardPayment, // 카드매출전표 + Withdrawal, // 출금명세서 + Cash, // 현금영수증 + BankTransfer, // 개인/법인통장 이체명세서 +} + +// BudgetDomainE +export const getDisplayNameBudgetDomainE = ( + type: BudgetDomainE | undefined, +) => { + switch (type) { + case BudgetDomainE.Student: + return "학생회비"; + case BudgetDomainE.School: + return "본회계"; + case BudgetDomainE.Autonomous: + return "자치"; + default: + return ""; + } +}; + +// BudgetDivisionIncomeE +export const getDisplayNameBudgetDivisionIncomeE = ( + type: BudgetDivisionIncomeE | undefined, +) => { + switch (type) { + case BudgetDivisionIncomeE.Substratum: + return "기층기구회계"; + case BudgetDivisionIncomeE.Central: + return "중앙회계"; + case BudgetDivisionIncomeE.Incentive: + return "격려기금"; + case BudgetDivisionIncomeE.Interest: + return "예금이자"; + case BudgetDivisionIncomeE.School: + return "학교지원금"; + case BudgetDivisionIncomeE.Carryover: + return "이월금"; + case BudgetDivisionIncomeE.Department: + return "과비"; + case BudgetDivisionIncomeE.Organizational: + return "단비"; + case BudgetDivisionIncomeE.External: + return "외부지원금"; + case BudgetDivisionIncomeE.Extra: + return "기타수익금"; + case BudgetDivisionIncomeE.CAC: + return "문화자치기금"; + default: + return ""; + } +}; + +// BudgetDivisionExpenseE +export const getDisplayNameBudgetDivisionExpenseE = ( + type: BudgetDivisionExpenseE | undefined, +) => { + switch (type) { + case BudgetDivisionExpenseE.Operating: + return "운영비"; + case BudgetDivisionExpenseE.Regular: + return "정기사업비"; + case BudgetDivisionExpenseE.New: + return "비정기사업비"; + default: + return ""; + } +}; + +// BudgetClassExpenseE +export const getDisplayNameBudgetClassExpenseE = ( + type: BudgetClassExpenseE | undefined, +) => { + switch (type) { + case BudgetClassExpenseE.Product: + return "상품비"; + case BudgetClassExpenseE.Gift: + return "증정비"; + case BudgetClassExpenseE.Supply: + return "물품비"; + case BudgetClassExpenseE.Print: + return "인쇄비"; + case BudgetClassExpenseE.Transport: + return "출장비"; + case BudgetClassExpenseE.Meeting: + return "회의비"; + case BudgetClassExpenseE.Labor: + return "인건비"; + case BudgetClassExpenseE.Welfare: + return "복리후생비"; + case BudgetClassExpenseE.Install: + return "설치비"; + case BudgetClassExpenseE.Delivery: + return "운반비"; + case BudgetClassExpenseE.Repair: + return "수선비"; + case BudgetClassExpenseE.Telecom: + return "통신비"; + case BudgetClassExpenseE.Insurance: + return "보험료"; + case BudgetClassExpenseE.Extra: + return "기타비"; + case BudgetClassExpenseE.Backup: + return "예비비"; + case BudgetClassExpenseE.Incentive: + return "격려금"; + default: + return ""; + } +}; + +// TransactionTypeE +export const getDisplayNameTransactionTypeE = ( + type: TransactionTypeE | undefined, +) => { + switch (type) { + case TransactionTypeE.OfficialCard: + return "공금카드"; + case TransactionTypeE.Bank: + return "계좌이체"; + case TransactionTypeE.Cash: + return "현금거래"; + case TransactionTypeE.PrivateCard: + return "개인카드"; + case TransactionTypeE.Personal: + return "사비집행"; + default: + return ""; + } +}; + +// ReportFileTypeE +export const getDisplayNameReportFileTypeE = ( + type: ReportFileTypeE | undefined, +) => { + switch (type) { + case ReportFileTypeE.Receipt: + return "영수증"; + case ReportFileTypeE.CardPayment: + return "카드매출전표"; + case ReportFileTypeE.Withdrawal: + return "출금명세서"; + case ReportFileTypeE.Cash: + return "현금영수증"; + case ReportFileTypeE.BankTransfer: + return "개인/법인통장 이체명세서"; + default: + return ""; + } +}; diff --git a/packages/interface/src/common/enum/index.ts b/packages/interface/src/common/enum/index.ts new file mode 100644 index 0000000..141c21e --- /dev/null +++ b/packages/interface/src/common/enum/index.ts @@ -0,0 +1,3 @@ +export * from "./budget.enum"; +export * from "./meeting.enum"; +export * from "./organization.enum"; diff --git a/packages/interface/src/common/enum/meeting.enum.ts b/packages/interface/src/common/enum/meeting.enum.ts new file mode 100644 index 0000000..4646fe5 --- /dev/null +++ b/packages/interface/src/common/enum/meeting.enum.ts @@ -0,0 +1,57 @@ +// 권한 E +export enum AssistantPermissionE { + President = 1, // 총학회장 + COC, // 중운위원 + GSRC, // 전학대의원 + UA, // 총학중집 + BAI, // 감사원 +} + +// 안건 상태 E +export enum AgendaAcceptedStatusE { + Approve = 1, // 승인 + Reject, // 반려 + Revision, // 수정요청 + Progress, // 검토중 + Post, // 사후승인 +} + +// AssistantPermissionE +export const getDisplayNameAssistantPermissionE = ( + type: AssistantPermissionE | undefined, +) => { + switch (type) { + case AssistantPermissionE.President: + return "총학회장"; + case AssistantPermissionE.COC: + return "중운위원"; + case AssistantPermissionE.GSRC: + return "전학대의원"; + case AssistantPermissionE.UA: + return "총학중집"; + case AssistantPermissionE.BAI: + return "감사원"; + default: + return ""; + } +}; + +// AgendaAcceptedStatusE +export const getDisplayNameAgendaAcceptedStatusE = ( + type: AgendaAcceptedStatusE | undefined, +) => { + switch (type) { + case AgendaAcceptedStatusE.Approve: + return "승인"; + case AgendaAcceptedStatusE.Reject: + return "반려"; + case AgendaAcceptedStatusE.Revision: + return "수정요청"; + case AgendaAcceptedStatusE.Progress: + return "검토중"; + case AgendaAcceptedStatusE.Post: + return "사후승인"; + default: + return ""; + } +}; 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/stringLength.ts b/packages/interface/src/common/stringLength.ts index 1f6a211..99e4b45 100644 --- a/packages/interface/src/common/stringLength.ts +++ b/packages/interface/src/common/stringLength.ts @@ -4,4 +4,3 @@ export const zOrgName = z.string().max(30); export const zOrgNameEng = z.string().max(100); export const zUserName = z.string().max(255); export const zFileName = z.string().max(256); -export const zEnumName = z.string().max(30); From d18ae69ef0e40c04db9c68d77d1d2b9e949021f2 Mon Sep 17 00:00:00 2001 From: Gerbera3090 Date: Mon, 25 Nov 2024 01:40:46 +0900 Subject: [PATCH 4/5] fix: minor errors for merge --- .../service/organization.public.service.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/api/src/feature/organization/service/organization.public.service.ts b/packages/api/src/feature/organization/service/organization.public.service.ts index 46b3832..48640e0 100644 --- a/packages/api/src/feature/organization/service/organization.public.service.ts +++ b/packages/api/src/feature/organization/service/organization.public.service.ts @@ -1,4 +1,10 @@ -import { Injectable, NotFoundException } from "@nestjs/common"; +import { + HttpException, + HttpStatus, + Injectable, + NotFoundException, +} from "@nestjs/common"; +import { OrganizationT } from "@sparcs-students/api/drizzle/schema"; import { SemesterPublicService } from "src/feature/semester/semester.public.service"; @@ -14,7 +20,7 @@ export class OrganizationPublicService { private readonly semesterPublicService: SemesterPublicService, ) {} - /** + /** * @param id organization id * @returns OrganizationT id에 해당하는 OrganizationT 객체를 리턴합니다. * @description 해당 id의 organization이 없으면 404 exception을 throw 합니다. @@ -27,7 +33,7 @@ export class OrganizationPublicService { } return organizations[0]; } - + /** * @param id organizationId, date * @returns OrganizationT id에 해당하는 OrganizationT 객체를 리턴합니다. @@ -52,7 +58,7 @@ export class OrganizationPublicService { } return organizations[0]; } - + /** * @param id organizationId, semesterId * @returns OrganizationT id에 해당하는 semester의 가장 후임인 OrganizationWithPresidentT 객체를 리턴합니다. @@ -66,7 +72,7 @@ export class OrganizationPublicService { const { endTerm } = await this.semesterPublicService.getSemesterById(semesterId); - const res = await this.getOrganizationWithPresidentByOrganizationIdAndDate( + const res = await this.getOrganizationWithPresidentByIdAndDate( organizationId, endTerm, ); From 592a6f9f28e409d647117eca1833f1535f47b368 Mon Sep 17 00:00:00 2001 From: Gerbera3090 Date: Mon, 25 Nov 2024 15:25:14 +0900 Subject: [PATCH 5/5] fix: native enum usage for proprp repository --- .../project-proposal/project-proposal.repository.ts | 6 +++++- packages/interface/src/common/enum/meeting.enum.ts | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/api/src/feature/proposal/project-proposal/project-proposal.repository.ts b/packages/api/src/feature/proposal/project-proposal/project-proposal.repository.ts index 191b760..9afebc2 100644 --- a/packages/api/src/feature/proposal/project-proposal/project-proposal.repository.ts +++ b/packages/api/src/feature/proposal/project-proposal/project-proposal.repository.ts @@ -6,6 +6,7 @@ import { ProjectProposalRevision, } from "@sparcs-students/api/drizzle/schema"; import { ApiPrp001ResponseOK } from "@sparcs-students/interface/api/proposal/index"; +import { AgendaAcceptedStatusE } from "@sparcs-students/interface/common/enum/meeting.enum"; import { and, eq, desc } from "drizzle-orm"; @@ -54,7 +55,10 @@ export class ProjectProposalRepository { projectProposalId: row.proposalId, startTerm: row.startTerm, endTerm: row.endTerm, - acceptedStatus: row.agendaExists && row.agendaAccepted ? 1 : 2, + acceptedStatus: + row.agendaExists && row.agendaAccepted + ? AgendaAcceptedStatusE.Accepted + : AgendaAcceptedStatusE.Reject, })); } diff --git a/packages/interface/src/common/enum/meeting.enum.ts b/packages/interface/src/common/enum/meeting.enum.ts index 4646fe5..dfaf44c 100644 --- a/packages/interface/src/common/enum/meeting.enum.ts +++ b/packages/interface/src/common/enum/meeting.enum.ts @@ -9,7 +9,7 @@ export enum AssistantPermissionE { // 안건 상태 E export enum AgendaAcceptedStatusE { - Approve = 1, // 승인 + Accepted = 1, // 승인 Reject, // 반려 Revision, // 수정요청 Progress, // 검토중 @@ -41,7 +41,7 @@ export const getDisplayNameAgendaAcceptedStatusE = ( type: AgendaAcceptedStatusE | undefined, ) => { switch (type) { - case AgendaAcceptedStatusE.Approve: + case AgendaAcceptedStatusE.Accepted: return "승인"; case AgendaAcceptedStatusE.Reject: return "반려";