From fb628d68cea909d99a61767e4afd559bddb41203 Mon Sep 17 00:00:00 2001 From: Gerbera3090 Date: Fri, 29 Nov 2024 00:24:14 +0900 Subject: [PATCH 1/5] feat: interface implement from org7 to 9 --- .../team/controller/team.controller.ts | 0 .../team/repository/team.repository.ts | 0 .../organization/team/service/team.service.ts | 0 .../feature/organization/team/team.module.ts | 11 ++++ .../api/organization/endpoint/apiOrg007.ts | 59 ++++++++++++++++++ .../api/organization/endpoint/apiOrg008.ts | 60 ++++++++++++++++++ .../api/organization/endpoint/apiOrg009.ts | 61 +++++++++++++++++++ 7 files changed, 191 insertions(+) create mode 100644 packages/api/src/feature/organization/team/controller/team.controller.ts create mode 100644 packages/api/src/feature/organization/team/repository/team.repository.ts create mode 100644 packages/api/src/feature/organization/team/service/team.service.ts create mode 100644 packages/api/src/feature/organization/team/team.module.ts create mode 100644 packages/interface/src/api/organization/endpoint/apiOrg007.ts create mode 100644 packages/interface/src/api/organization/endpoint/apiOrg008.ts create mode 100644 packages/interface/src/api/organization/endpoint/apiOrg009.ts diff --git a/packages/api/src/feature/organization/team/controller/team.controller.ts b/packages/api/src/feature/organization/team/controller/team.controller.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/api/src/feature/organization/team/repository/team.repository.ts b/packages/api/src/feature/organization/team/repository/team.repository.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/api/src/feature/organization/team/service/team.service.ts b/packages/api/src/feature/organization/team/service/team.service.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/api/src/feature/organization/team/team.module.ts b/packages/api/src/feature/organization/team/team.module.ts new file mode 100644 index 0000000..1e047be --- /dev/null +++ b/packages/api/src/feature/organization/team/team.module.ts @@ -0,0 +1,11 @@ +import { Module } from "@nestjs/common"; + +import { DrizzleModule } from "src/drizzle/drizzle.module"; + +@Module({ + imports: [DrizzleModule], + controllers: [], + providers: [], + exports: [], +}) +export class TeamModule {} diff --git a/packages/interface/src/api/organization/endpoint/apiOrg007.ts b/packages/interface/src/api/organization/endpoint/apiOrg007.ts new file mode 100644 index 0000000..059ddd9 --- /dev/null +++ b/packages/interface/src/api/organization/endpoint/apiOrg007.ts @@ -0,0 +1,59 @@ +import { HttpStatusCode } from "axios"; +import { z } from "zod"; + +import { zOrgName } from "@sparcs-students/interface/common/stringLength"; +import { zId } from "@sparcs-students/interface/common/type/ids"; + +/** + * @version v0.1 + * @description 기구장단 권한으로 새로운 팀을 생성합니다. + */ + +const url = () => `/president/organizations/teams/team`; +const method = "POST"; +export const ApiOrg007RequestUrl = "/president/organizations/teams/team"; + +const requestParam = z.object({}); + +const requestQuery = z.object({}); + +const requestBody = z.object({ + organizationId: zId, + semesterId: zId, + name: zOrgName, + detail: z.coerce.string(), +}); + +const responseBodyMap = { + [HttpStatusCode.Created]: z.object({ + teamId: zId, + }), +}; + +const responseErrorMap = {}; + +const apiOrg007 = { + url, + method, + requestParam, + requestQuery, + requestBody, + responseBodyMap, + responseErrorMap, +}; + +type ApiOrg007RequestParam = z.infer; +type ApiOrg007RequestQuery = z.infer; +type ApiOrg007RequestBody = z.infer; +type ApiOrg007ResponseCreated = z.infer< + (typeof apiOrg007.responseBodyMap)[201] +>; + +export default apiOrg007; + +export type { + ApiOrg007RequestParam, + ApiOrg007RequestQuery, + ApiOrg007RequestBody, + ApiOrg007ResponseCreated, +}; diff --git a/packages/interface/src/api/organization/endpoint/apiOrg008.ts b/packages/interface/src/api/organization/endpoint/apiOrg008.ts new file mode 100644 index 0000000..1b3d508 --- /dev/null +++ b/packages/interface/src/api/organization/endpoint/apiOrg008.ts @@ -0,0 +1,60 @@ +import { HttpStatusCode } from "axios"; +import { z } from "zod"; + +import { zId } from "@sparcs-students/interface/common/type/ids"; + +/** + * @version v0.1 + * @description 기구장단 권한으로 팀에 멤버를 생성합니다. + * 만약 팀이 없거나 유저가 없는 경우 404 에러를 반환합니다. + * 만약 팀에 이미 유저가 있는 경우 400 에러를 반환합니다. + */ + +const url = () => `/president/organizations/teams/member`; +const method = "POST"; +export const ApiOrg008RequestUrl = "/president/organizations/teams/member"; + +const requestParam = z.object({}); + +const requestQuery = z.object({}); + +const requestBody = z.object({ + teamId: zId, + userId: zId, + startTerm: z.coerce.date(), + endTerm: z.coerce.date().optional(), +}); + +const responseBodyMap = { + [HttpStatusCode.Created]: z.object({ + teamMemberId: zId, + }), +}; + +const responseErrorMap = {}; + +const apiOrg008 = { + url, + method, + requestParam, + requestQuery, + requestBody, + responseBodyMap, + responseErrorMap, +}; + +type ApiOrg008RequestParam = z.infer; +type ApiOrg008RequestQuery = z.infer; +type ApiOrg008RequestBody = z.infer; +type ApiOrg008ResponseCreated = z.infer< + (typeof apiOrg008.responseBodyMap)[201] +>; + +export default apiOrg008; + +export type { + ApiOrg008RequestParam, + ApiOrg008RequestQuery, + ApiOrg008RequestBody, + ApiOrg008ResponseCreated, +}; diff --git a/packages/interface/src/api/organization/endpoint/apiOrg009.ts b/packages/interface/src/api/organization/endpoint/apiOrg009.ts new file mode 100644 index 0000000..66667a1 --- /dev/null +++ b/packages/interface/src/api/organization/endpoint/apiOrg009.ts @@ -0,0 +1,61 @@ +import { HttpStatusCode } from "axios"; +import { z } from "zod"; + +import { zId } from "@sparcs-students/interface/common/type/ids"; + +/** + * @version v0.1 + * @description 기구장단 권한으로 팀에 리더를 생성합니다. + * 만약 팀이 없거나 유저가 없거나 유저가 팀멤버가 아닌 경우 404 에러를 반환합니다. + * 만약 이미 팀에 리더가 있는 경우 해당 리더를 은퇴 처리 후 새로운 리더를 임명합니다. + */ + +const url = () => `/president/organizations/teams/leader`; +const method = "POST"; +export const ApiOrg009RequestUrl = "/president/organizations/teams/leader"; + +const requestParam = z.object({}); + +const requestQuery = z.object({}); + +const requestBody = z.object({ + teamId: zId, + userId: zId, + role: z.string().max(30), + startTerm: z.coerce.date(), + endTerm: z.coerce.date().optional(), +}); + +const responseBodyMap = { + [HttpStatusCode.Created]: z.object({ + teamLeaderId: zId, + }), +}; + +const responseErrorMap = {}; + +const apiOrg009 = { + url, + method, + requestParam, + requestQuery, + requestBody, + responseBodyMap, + responseErrorMap, +}; + +type ApiOrg009RequestParam = z.infer; +type ApiOrg009RequestQuery = z.infer; +type ApiOrg009RequestBody = z.infer; +type ApiOrg009ResponseCreated = z.infer< + (typeof apiOrg009.responseBodyMap)[201] +>; + +export default apiOrg009; + +export type { + ApiOrg009RequestParam, + ApiOrg009RequestQuery, + ApiOrg009RequestBody, + ApiOrg009ResponseCreated, +}; From 716e12bfb065ede8c9fe50d19f3622abd7801e7c Mon Sep 17 00:00:00 2001 From: Gerbera3090 Date: Fri, 29 Nov 2024 01:22:22 +0900 Subject: [PATCH 2/5] fix: api index add and example module --- .../_example/controller/example.controller.ts | 8 ++++++++ .../api/src/feature/_example/example.module.ts | 14 ++++++++++++++ .../_example/repository/example.repository.ts | 11 +++++++++++ .../feature/_example/service/example.service.ts | 7 +++++++ .../team/repository/team.repository.ts | 11 +++++++++++ packages/interface/src/api/organization/index.ts | 9 +++++++++ 6 files changed, 60 insertions(+) create mode 100644 packages/api/src/feature/_example/controller/example.controller.ts create mode 100644 packages/api/src/feature/_example/example.module.ts create mode 100644 packages/api/src/feature/_example/repository/example.repository.ts create mode 100644 packages/api/src/feature/_example/service/example.service.ts diff --git a/packages/api/src/feature/_example/controller/example.controller.ts b/packages/api/src/feature/_example/controller/example.controller.ts new file mode 100644 index 0000000..2529d17 --- /dev/null +++ b/packages/api/src/feature/_example/controller/example.controller.ts @@ -0,0 +1,8 @@ +import { Controller } from "@nestjs/common"; + +import { ExampleService } from "../service/example.service"; + +@Controller() +export class ExampleController { + constructor(private readonly exampleService: ExampleService) {} +} diff --git a/packages/api/src/feature/_example/example.module.ts b/packages/api/src/feature/_example/example.module.ts new file mode 100644 index 0000000..822d869 --- /dev/null +++ b/packages/api/src/feature/_example/example.module.ts @@ -0,0 +1,14 @@ +import { Module } from "@nestjs/common"; + +import { DrizzleModule } from "src/drizzle/drizzle.module"; +import { ExampleService } from "./service/example.service"; +import { ExampleController } from "./controller/example.controller"; +import { ExampleRepository } from "./repository/example.repository"; + +@Module({ + imports: [DrizzleModule], + controllers: [ExampleController], + providers: [ExampleService, ExampleRepository], + exports: [], +}) +export class ExampleModule {} diff --git a/packages/api/src/feature/_example/repository/example.repository.ts b/packages/api/src/feature/_example/repository/example.repository.ts new file mode 100644 index 0000000..b868d7b --- /dev/null +++ b/packages/api/src/feature/_example/repository/example.repository.ts @@ -0,0 +1,11 @@ +import { Injectable, Inject } from "@nestjs/common"; + +import { MySql2Database } from "drizzle-orm/mysql2"; +import { DrizzleAsyncProvider } from "src/drizzle/drizzle.provider"; + +@Injectable() +export class ExampleRepository { + constructor( + @Inject(DrizzleAsyncProvider) private readonly db: MySql2Database, + ) {} +} diff --git a/packages/api/src/feature/_example/service/example.service.ts b/packages/api/src/feature/_example/service/example.service.ts new file mode 100644 index 0000000..73a052c --- /dev/null +++ b/packages/api/src/feature/_example/service/example.service.ts @@ -0,0 +1,7 @@ +import { Injectable } from "@nestjs/common"; +import { ExampleRepository } from "../repository/example.repository"; + +@Injectable() +export class ExampleService { + constructor(private readonly exampleRepository: ExampleRepository) {} +} diff --git a/packages/api/src/feature/organization/team/repository/team.repository.ts b/packages/api/src/feature/organization/team/repository/team.repository.ts index e69de29..198eb27 100644 --- a/packages/api/src/feature/organization/team/repository/team.repository.ts +++ b/packages/api/src/feature/organization/team/repository/team.repository.ts @@ -0,0 +1,11 @@ +import { Injectable, Inject } from "@nestjs/common"; + +import { MySql2Database } from "drizzle-orm/mysql2"; +import { DrizzleAsyncProvider } from "src/drizzle/drizzle.provider"; + +@Injectable() +export class TeamRepository { + constructor( + @Inject(DrizzleAsyncProvider) private readonly db: MySql2Database, + ) {} +} diff --git a/packages/interface/src/api/organization/index.ts b/packages/interface/src/api/organization/index.ts index 2e8ac23..925a357 100644 --- a/packages/interface/src/api/organization/index.ts +++ b/packages/interface/src/api/organization/index.ts @@ -15,3 +15,12 @@ export { default as apiOrg005 } from "./endpoint/apiOrg005"; // default export export * from "./endpoint/apiOrg006"; export { default as apiOrg006 } from "./endpoint/apiOrg006"; // default export 추가 + +export * from "./endpoint/apiOrg007"; +export { default as apiOrg007 } from "./endpoint/apiOrg007"; // default export 추가 + +export * from "./endpoint/apiOrg008"; +export { default as apiOrg008 } from "./endpoint/apiOrg008"; // default export 추가 + +export * from "./endpoint/apiOrg009"; +export { default as apiOrg009 } from "./endpoint/apiOrg009"; // default export 추가 From ed98af3ad4a57b4412eadac1069d90bbff7b890b Mon Sep 17 00:00:00 2001 From: Gerbera3090 Date: Fri, 29 Nov 2024 02:18:19 +0900 Subject: [PATCH 3/5] feat: apiOrg007 for post new team implemented --- .../organization/organization.module.ts | 3 +- .../team/controller/team.controller.ts | 23 +++++++++++++ .../team/repository/team.repository.ts | 28 ++++++++++++++++ .../organization/team/service/team.service.ts | 32 +++++++++++++++++++ .../feature/organization/team/team.module.ts | 10 ++++-- 5 files changed, 92 insertions(+), 4 deletions(-) diff --git a/packages/api/src/feature/organization/organization.module.ts b/packages/api/src/feature/organization/organization.module.ts index be16f92..839de5f 100644 --- a/packages/api/src/feature/organization/organization.module.ts +++ b/packages/api/src/feature/organization/organization.module.ts @@ -7,9 +7,10 @@ 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"; +import { TeamModule } from "./team/team.module"; @Module({ - imports: [DrizzleModule, SemesterModule, UserModule], + imports: [DrizzleModule, SemesterModule, UserModule, TeamModule], controllers: [OrganizationController], providers: [ OrganizationService, diff --git a/packages/api/src/feature/organization/team/controller/team.controller.ts b/packages/api/src/feature/organization/team/controller/team.controller.ts index e69de29..90134f9 100644 --- a/packages/api/src/feature/organization/team/controller/team.controller.ts +++ b/packages/api/src/feature/organization/team/controller/team.controller.ts @@ -0,0 +1,23 @@ +import { Body, Controller, Post, UsePipes } from "@nestjs/common"; +import { + ApiOrg007RequestBody, + ApiOrg007RequestUrl, + ApiOrg007ResponseCreated, + apiOrg007, +} from "@sparcs-students/interface/api/organization/index"; +import { ZodPipe } from "@sparcs-students/api/common/pipes/zod-pipe"; + +import { TeamService } from "../service/team.service"; + +@Controller() +export class TeamController { + constructor(private readonly teamService: TeamService) {} + + @Post(ApiOrg007RequestUrl) + @UsePipes(new ZodPipe(apiOrg007)) + async postTeam( + @Body() body: ApiOrg007RequestBody, + ): Promise { + return this.teamService.postTeam(body); + } +} diff --git a/packages/api/src/feature/organization/team/repository/team.repository.ts b/packages/api/src/feature/organization/team/repository/team.repository.ts index 198eb27..e427fa0 100644 --- a/packages/api/src/feature/organization/team/repository/team.repository.ts +++ b/packages/api/src/feature/organization/team/repository/team.repository.ts @@ -1,5 +1,8 @@ import { Injectable, Inject } from "@nestjs/common"; +import { Team } from "@sparcs-students/api/drizzle/schema"; +import { ApiOrg007RequestBody } from "@sparcs-students/interface/api/organization/index"; +import { and, eq, isNull } from "drizzle-orm"; import { MySql2Database } from "drizzle-orm/mysql2"; import { DrizzleAsyncProvider } from "src/drizzle/drizzle.provider"; @@ -8,4 +11,29 @@ export class TeamRepository { constructor( @Inject(DrizzleAsyncProvider) private readonly db: MySql2Database, ) {} + + async ckTeamBeforeCreate(body: ApiOrg007RequestBody): Promise { + const res = await this.db + .select() + .from(Team) + .where( + and( + eq(Team.name, body.name), + eq(Team.semesterId, body.semesterId), + eq(Team.organizationId, body.organizationId), + isNull(Team.deletedAt), + ), + ) + .execute(); + if (res.length === 0) { + return 0; + } + return res[0].id; + } + + async insertTeam(body: ApiOrg007RequestBody): Promise { + await this.db.insert(Team).values(body).execute(); + const res = await this.ckTeamBeforeCreate(body); + return res; + } } diff --git a/packages/api/src/feature/organization/team/service/team.service.ts b/packages/api/src/feature/organization/team/service/team.service.ts index e69de29..f4d8a63 100644 --- a/packages/api/src/feature/organization/team/service/team.service.ts +++ b/packages/api/src/feature/organization/team/service/team.service.ts @@ -0,0 +1,32 @@ +import { HttpException, HttpStatus, Injectable } from "@nestjs/common"; +import { + ApiOrg007RequestBody, + ApiOrg007ResponseCreated, +} from "@sparcs-students/interface/api/organization/index"; +import { UserPublicService } from "@sparcs-students/api/feature/user/service/user.public.service"; +import { TeamRepository } from "../repository/team.repository"; + +@Injectable() +export class TeamService { + constructor( + private readonly teamRepository: TeamRepository, + private readonly userPublicService: UserPublicService, + ) {} + + async postTeam( + body: ApiOrg007RequestBody, + ): Promise { + const ck = await this.teamRepository.ckTeamBeforeCreate(body); + if (ck !== 0) { + throw new HttpException("Team already exists", HttpStatus.BAD_REQUEST); + } + const teamId = await this.teamRepository.insertTeam(body); + if (teamId === 0) { + throw new HttpException( + "Team creation failed", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + return { teamId }; + } +} diff --git a/packages/api/src/feature/organization/team/team.module.ts b/packages/api/src/feature/organization/team/team.module.ts index 1e047be..1f296da 100644 --- a/packages/api/src/feature/organization/team/team.module.ts +++ b/packages/api/src/feature/organization/team/team.module.ts @@ -1,11 +1,15 @@ import { Module } from "@nestjs/common"; import { DrizzleModule } from "src/drizzle/drizzle.module"; +import { UserModule } from "src/feature/user/user.module"; +import { TeamService } from "./service/team.service"; +import { TeamController } from "./controller/team.controller"; +import { TeamRepository } from "./repository/team.repository"; @Module({ - imports: [DrizzleModule], - controllers: [], - providers: [], + imports: [DrizzleModule, UserModule], + controllers: [TeamController], + providers: [TeamService, TeamRepository], exports: [], }) export class TeamModule {} From 50a051ce82475141409fe9a66885b7615ab53b5a Mon Sep 17 00:00:00 2001 From: Gerbera3090 Date: Fri, 29 Nov 2024 18:43:18 +0900 Subject: [PATCH 4/5] feat: impl post teamMember with circular ref and new select method --- .../organization/organization.module.ts | 11 +++- .../repository/organization.repository.ts | 45 ++++++++++++- .../service/organization.public.service.ts | 52 ++++++++++++++- .../team/controller/team.controller.ts | 12 ++++ .../team/repository/team.repository.ts | 43 ++++++++++++- .../organization/team/service/team.service.ts | 64 ++++++++++++++++++- .../feature/organization/team/team.module.ts | 13 +++- .../api/organization/endpoint/apiOrg008.ts | 2 - .../api/organization/endpoint/apiOrg009.ts | 4 +- 9 files changed, 232 insertions(+), 14 deletions(-) diff --git a/packages/api/src/feature/organization/organization.module.ts b/packages/api/src/feature/organization/organization.module.ts index 839de5f..c6c2550 100644 --- a/packages/api/src/feature/organization/organization.module.ts +++ b/packages/api/src/feature/organization/organization.module.ts @@ -1,4 +1,4 @@ -import { Module } from "@nestjs/common"; +import { Module, forwardRef } from "@nestjs/common"; import { DrizzleModule } from "src/drizzle/drizzle.module"; import { SemesterModule } from "src/feature/semester/semester.module"; @@ -7,10 +7,17 @@ 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"; +/* eslint-disable import/no-cycle */ import { TeamModule } from "./team/team.module"; +/* eslint-disable import/no-cycle */ @Module({ - imports: [DrizzleModule, SemesterModule, UserModule, TeamModule], + imports: [ + DrizzleModule, + SemesterModule, + UserModule, + forwardRef(() => TeamModule), + ], controllers: [OrganizationController], providers: [ OrganizationService, diff --git a/packages/api/src/feature/organization/repository/organization.repository.ts b/packages/api/src/feature/organization/repository/organization.repository.ts index 28e9d11..80e59e3 100644 --- a/packages/api/src/feature/organization/repository/organization.repository.ts +++ b/packages/api/src/feature/organization/repository/organization.repository.ts @@ -1,5 +1,4 @@ import { Injectable, Inject } from "@nestjs/common"; -import logger from "@sparcs-students/api/common/util/logger"; import { ApiOrg002RequestBody, @@ -274,7 +273,6 @@ export class OrganizationRepository { ) .orderBy(desc(OrganizationMember.createdAt)) .limit(1); - logger.info(res); if (res.length === 0) { return 0; } @@ -347,4 +345,47 @@ export class OrganizationRepository { const res = await this.ckOrganizationManagerBeforeCreate(body); return res; } + + async selectOrganizationMember(target: Partial) { + const { userId, startTerm, endTerm, organizationId } = target; + let query = this.db.select().from(OrganizationMember).$dynamic(); + + const whereConditions = []; + + if (userId) { + whereConditions.push(eq(OrganizationMember.userId, userId)); + } + + if (startTerm) { + whereConditions.push( + or( + gte(OrganizationMember.endTerm, startTerm), + isNull(OrganizationMember.endTerm), + ), + ); + } + + if (endTerm) { + whereConditions.push(lte(OrganizationMember.startTerm, endTerm)); + } + + if (organizationId) { + whereConditions.push( + eq(OrganizationMember.organizationId, organizationId), + ); + } + + // 삭제된 항목 제외 + whereConditions.push(isNull(OrganizationMember.deletedAt)); + + // 조건이 하나라도 있으면 AND로 묶어서 처리 + if (whereConditions.length > 0) { + query = query.where(and(...whereConditions)); + } + + // 쿼리 실행 + const res = await query.execute(); + + return res; + } } 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 448f00e..6efc21b 100644 --- a/packages/api/src/feature/organization/service/organization.public.service.ts +++ b/packages/api/src/feature/organization/service/organization.public.service.ts @@ -4,7 +4,7 @@ import { Injectable, NotFoundException, } from "@nestjs/common"; -import { OrganizationT, TeamT } from "src/drizzle/schema"; +import { OrganizationMemberT, OrganizationT, TeamT } from "src/drizzle/schema"; import { SemesterPublicService } from "src/feature/semester/semester.public.service"; import { @@ -95,4 +95,54 @@ export class OrganizationPublicService { } return res[0]; } + + /** + * @param userId, organizationId, startTerm, endTerm + * @returns TeamMemeberT 해당 학기 해당 단체에 해당하는 TeamMemberT 객체를 리턴합니다. + * @description 해당 시기에 해당하는 OrganizationMember가 없으면 404 exception을 throw 합니다. + */ + async getOrganizationMemberByUserAndOrgAndDate( + userId: number, + organizationId: number, + startTerm: Date, + endTerm: Date, + ): Promise { + const res = await this.organizationRepository.selectOrganizationMember({ + userId, + startTerm, + endTerm, + organizationId, + }); + if (res.length === 0) { + throw new NotFoundException( + `OrganizationMember with userId ${userId} not found.`, + ); + } else if (res.length > 1) { + throw new HttpException( + `Unreachable: OrganizationMember with userId ${userId} has multiple records.`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + return res[0]; + } + + /** + * @param userId, organizationId, semesterId + * @returns TeamMemeberT 해당 학기 해당 단체에 해당하는 TeamMemberT 객체를 리턴합니다. + * @description 해당 시기에 해당하는 OrganizationMember가 없으면 404 exception을 throw 합니다. + */ + async getOrganizationMemberByUserAndOrgAndSemester( + userId: number, + semesterId: number, + organizationId: number, + ): Promise { + const { startTerm, endTerm } = + await this.semesterPublicService.getSemesterById(semesterId); + return this.getOrganizationMemberByUserAndOrgAndDate( + userId, + organizationId, + startTerm, + endTerm, + ); + } } diff --git a/packages/api/src/feature/organization/team/controller/team.controller.ts b/packages/api/src/feature/organization/team/controller/team.controller.ts index 90134f9..982ae09 100644 --- a/packages/api/src/feature/organization/team/controller/team.controller.ts +++ b/packages/api/src/feature/organization/team/controller/team.controller.ts @@ -3,7 +3,11 @@ import { ApiOrg007RequestBody, ApiOrg007RequestUrl, ApiOrg007ResponseCreated, + ApiOrg008RequestBody, + ApiOrg008RequestUrl, + ApiOrg008ResponseCreated, apiOrg007, + apiOrg008, } from "@sparcs-students/interface/api/organization/index"; import { ZodPipe } from "@sparcs-students/api/common/pipes/zod-pipe"; @@ -20,4 +24,12 @@ export class TeamController { ): Promise { return this.teamService.postTeam(body); } + + @Post(ApiOrg008RequestUrl) + @UsePipes(new ZodPipe(apiOrg008)) + async postTeamMember( + @Body() body: ApiOrg008RequestBody, + ): Promise { + return this.teamService.postTeamMember(body); + } } diff --git a/packages/api/src/feature/organization/team/repository/team.repository.ts b/packages/api/src/feature/organization/team/repository/team.repository.ts index e427fa0..65d5564 100644 --- a/packages/api/src/feature/organization/team/repository/team.repository.ts +++ b/packages/api/src/feature/organization/team/repository/team.repository.ts @@ -1,5 +1,5 @@ import { Injectable, Inject } from "@nestjs/common"; -import { Team } from "@sparcs-students/api/drizzle/schema"; +import { Team, TeamMember, TeamT } from "@sparcs-students/api/drizzle/schema"; import { ApiOrg007RequestBody } from "@sparcs-students/interface/api/organization/index"; import { and, eq, isNull } from "drizzle-orm"; @@ -36,4 +36,45 @@ export class TeamRepository { const res = await this.ckTeamBeforeCreate(body); return res; } + + async ckTeamMemberBeforeCreate(userId, teamId): Promise { + const res = await this.db + .select() + .from(TeamMember) + .where( + and( + eq(TeamMember.teamId, teamId), + eq(TeamMember.userId, userId), + isNull(TeamMember.deletedAt), + ), + ) + .execute(); + if (res.length === 0) { + return 0; + } + return res[0].id; + } + + async insertTeamMember( + userId: number, + teamId: number, + startTerm: Date, + endTerm: Date, + ): Promise { + await this.db + .insert(TeamMember) + .values({ userId, teamId, startTerm, endTerm }) + .execute(); + const res = await this.ckTeamMemberBeforeCreate(userId, teamId); + return res; + } + + async selectTeamById(teamId: number): Promise { + const res = await this.db + .select() + .from(Team) + .where(and(eq(Team.id, teamId), isNull(Team.deletedAt))) + .execute(); + return res; + } } diff --git a/packages/api/src/feature/organization/team/service/team.service.ts b/packages/api/src/feature/organization/team/service/team.service.ts index f4d8a63..24003c3 100644 --- a/packages/api/src/feature/organization/team/service/team.service.ts +++ b/packages/api/src/feature/organization/team/service/team.service.ts @@ -1,9 +1,18 @@ -import { HttpException, HttpStatus, Injectable } from "@nestjs/common"; +import { + HttpException, + HttpStatus, + Injectable, + NotFoundException, +} from "@nestjs/common"; import { ApiOrg007RequestBody, ApiOrg007ResponseCreated, + ApiOrg008RequestBody, + ApiOrg008ResponseCreated, } from "@sparcs-students/interface/api/organization/index"; import { UserPublicService } from "@sparcs-students/api/feature/user/service/user.public.service"; +import { SemesterPublicService } from "@sparcs-students/api/feature/semester/semester.public.service"; +import { OrganizationPublicService } from "@sparcs-students/api/feature/organization/service/organization.public.service"; import { TeamRepository } from "../repository/team.repository"; @Injectable() @@ -11,6 +20,8 @@ export class TeamService { constructor( private readonly teamRepository: TeamRepository, private readonly userPublicService: UserPublicService, + private readonly organizationPublicService: OrganizationPublicService, + private readonly semesterPublicService: SemesterPublicService, ) {} async postTeam( @@ -29,4 +40,55 @@ export class TeamService { } return { teamId }; } + + async postTeamMember( + body: ApiOrg008RequestBody, + ): Promise { + // userId와 teamId의 유효성 체크 + await this.userPublicService.getUserById(body.userId); + const ckTeam = await this.teamRepository.selectTeamById(body.teamId); + if (ckTeam.length === 0) { + throw new NotFoundException("Team not found"); + } else if (ckTeam.length > 1) { + throw new HttpException( + "Unreachable: Team has multiple records", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + const { startTerm, endTerm } = + await this.semesterPublicService.getSemesterById(ckTeam[0].semesterId); + + // team member OrganizationMember인지 체크 + await this.organizationPublicService.getOrganizationMemberByUserAndOrgAndSemester( + body.userId, + ckTeam[0].organizationId, + ckTeam[0].semesterId, + ); + + // team member 중복 체크 + const ckMember = await this.teamRepository.ckTeamMemberBeforeCreate( + body.userId, + body.teamId, + ); + if (ckMember !== 0) { + throw new HttpException( + "Team member already exists", + HttpStatus.BAD_REQUEST, + ); + } + // team member 생성 + const teamMemberId = await this.teamRepository.insertTeamMember( + body.userId, + body.teamId, + startTerm, + endTerm, + ); + if (teamMemberId === 0) { + throw new HttpException( + "Team member creation failed", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + return { teamMemberId }; + } } diff --git a/packages/api/src/feature/organization/team/team.module.ts b/packages/api/src/feature/organization/team/team.module.ts index 1f296da..5941999 100644 --- a/packages/api/src/feature/organization/team/team.module.ts +++ b/packages/api/src/feature/organization/team/team.module.ts @@ -1,13 +1,22 @@ -import { Module } from "@nestjs/common"; +import { Module, forwardRef } from "@nestjs/common"; import { DrizzleModule } from "src/drizzle/drizzle.module"; import { UserModule } from "src/feature/user/user.module"; +import { SemesterModule } from "src/feature/semester/semester.module"; import { TeamService } from "./service/team.service"; import { TeamController } from "./controller/team.controller"; import { TeamRepository } from "./repository/team.repository"; +/* eslint-disable import/no-cycle */ +import { OrganizationModule } from "../organization.module"; +/* eslint-disable import/no-cycle */ @Module({ - imports: [DrizzleModule, UserModule], + imports: [ + DrizzleModule, + UserModule, + SemesterModule, + forwardRef(() => OrganizationModule), + ], controllers: [TeamController], providers: [TeamService, TeamRepository], exports: [], diff --git a/packages/interface/src/api/organization/endpoint/apiOrg008.ts b/packages/interface/src/api/organization/endpoint/apiOrg008.ts index 1b3d508..ef8f6fd 100644 --- a/packages/interface/src/api/organization/endpoint/apiOrg008.ts +++ b/packages/interface/src/api/organization/endpoint/apiOrg008.ts @@ -21,8 +21,6 @@ const requestQuery = z.object({}); const requestBody = z.object({ teamId: zId, userId: zId, - startTerm: z.coerce.date(), - endTerm: z.coerce.date().optional(), }); const responseBodyMap = { diff --git a/packages/interface/src/api/organization/endpoint/apiOrg009.ts b/packages/interface/src/api/organization/endpoint/apiOrg009.ts index 66667a1..a8e1775 100644 --- a/packages/interface/src/api/organization/endpoint/apiOrg009.ts +++ b/packages/interface/src/api/organization/endpoint/apiOrg009.ts @@ -7,7 +7,7 @@ import { zId } from "@sparcs-students/interface/common/type/ids"; * @version v0.1 * @description 기구장단 권한으로 팀에 리더를 생성합니다. * 만약 팀이 없거나 유저가 없거나 유저가 팀멤버가 아닌 경우 404 에러를 반환합니다. - * 만약 이미 팀에 리더가 있는 경우 해당 리더를 은퇴 처리 후 새로운 리더를 임명합니다. + * 만약 이미 팀에 리더가 있는 경우 에러가 발생합니다. */ const url = () => `/president/organizations/teams/leader`; @@ -22,8 +22,6 @@ const requestBody = z.object({ teamId: zId, userId: zId, role: z.string().max(30), - startTerm: z.coerce.date(), - endTerm: z.coerce.date().optional(), }); const responseBodyMap = { From b8b2c4c305f6201fded1386b3cba4c289ed67213 Mon Sep 17 00:00:00 2001 From: Gerbera3090 Date: Fri, 29 Nov 2024 20:18:25 +0900 Subject: [PATCH 5/5] feat: implement apiOrg009 for post team leaders and fix teamPublicService --- .../repository/organization.repository.ts | 13 +- .../service/organization.public.service.ts | 20 +-- .../team/controller/team.controller.ts | 12 ++ .../team/repository/team.repository.ts | 164 +++++++++++++++++- .../team/service/team.public.service.ts | 31 ++++ .../organization/team/service/team.service.ts | 63 ++++++- .../feature/organization/team/team.module.ts | 5 +- .../project-proposal.module.ts | 3 +- .../service/project-proposal.service.ts | 4 +- 9 files changed, 276 insertions(+), 39 deletions(-) create mode 100644 packages/api/src/feature/organization/team/service/team.public.service.ts diff --git a/packages/api/src/feature/organization/repository/organization.repository.ts b/packages/api/src/feature/organization/repository/organization.repository.ts index 80e59e3..2e8beff 100644 --- a/packages/api/src/feature/organization/repository/organization.repository.ts +++ b/packages/api/src/feature/organization/repository/organization.repository.ts @@ -22,8 +22,6 @@ import { OrganizationPresidentT, UserT, UserStudentT, - TeamT, - Team, OrganizationMember, OrganizationManager, OrganizationMemberT, @@ -121,11 +119,6 @@ export class OrganizationRepository { })); } - async getTeamById(id: number): Promise { - const res = await this.db.select().from(Team).where(eq(Team.id, id)); - return res; - } - async ckOrganizationBeforeCreate( body: ApiOrg002RequestBody, ): Promise { @@ -347,11 +340,15 @@ export class OrganizationRepository { } async selectOrganizationMember(target: Partial) { - const { userId, startTerm, endTerm, organizationId } = target; + const { id, userId, startTerm, endTerm, organizationId } = target; let query = this.db.select().from(OrganizationMember).$dynamic(); const whereConditions = []; + if (id) { + whereConditions.push(eq(OrganizationMember.id, id)); + } + if (userId) { whereConditions.push(eq(OrganizationMember.userId, userId)); } 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 6efc21b..eb4a640 100644 --- a/packages/api/src/feature/organization/service/organization.public.service.ts +++ b/packages/api/src/feature/organization/service/organization.public.service.ts @@ -4,7 +4,7 @@ import { Injectable, NotFoundException, } from "@nestjs/common"; -import { OrganizationMemberT, OrganizationT, TeamT } from "src/drizzle/schema"; +import { OrganizationMemberT, OrganizationT } from "src/drizzle/schema"; import { SemesterPublicService } from "src/feature/semester/semester.public.service"; import { @@ -78,24 +78,6 @@ export class OrganizationPublicService { return res; } - /** - * @param teamId - * @returns TeamT id에 해당하는 TeamT 객체를 리턴합니다. - * @description 해당 id의 Team이 없으면 404 exception을 throw 합니다. - */ - async getTeamById(teamId: number): Promise { - const res = await this.organizationRepository.getTeamById(teamId); - if (res.length === 0) { - throw new NotFoundException(`Team with ID ${teamId} not found.`); - } else if (res.length > 1) { - throw new HttpException( - `Unreachable: Team with ID ${teamId} has multiple records.`, - HttpStatus.INTERNAL_SERVER_ERROR, - ); - } - return res[0]; - } - /** * @param userId, organizationId, startTerm, endTerm * @returns TeamMemeberT 해당 학기 해당 단체에 해당하는 TeamMemberT 객체를 리턴합니다. diff --git a/packages/api/src/feature/organization/team/controller/team.controller.ts b/packages/api/src/feature/organization/team/controller/team.controller.ts index 982ae09..3141c4e 100644 --- a/packages/api/src/feature/organization/team/controller/team.controller.ts +++ b/packages/api/src/feature/organization/team/controller/team.controller.ts @@ -6,8 +6,12 @@ import { ApiOrg008RequestBody, ApiOrg008RequestUrl, ApiOrg008ResponseCreated, + ApiOrg009RequestBody, + ApiOrg009RequestUrl, + ApiOrg009ResponseCreated, apiOrg007, apiOrg008, + apiOrg009, } from "@sparcs-students/interface/api/organization/index"; import { ZodPipe } from "@sparcs-students/api/common/pipes/zod-pipe"; @@ -32,4 +36,12 @@ export class TeamController { ): Promise { return this.teamService.postTeamMember(body); } + + @Post(ApiOrg009RequestUrl) + @UsePipes(new ZodPipe(apiOrg009)) + async postTeamLeader( + @Body() body: ApiOrg009RequestBody, + ): Promise { + return this.teamService.postTeamLeader(body); + } } diff --git a/packages/api/src/feature/organization/team/repository/team.repository.ts b/packages/api/src/feature/organization/team/repository/team.repository.ts index 65d5564..dfb12b5 100644 --- a/packages/api/src/feature/organization/team/repository/team.repository.ts +++ b/packages/api/src/feature/organization/team/repository/team.repository.ts @@ -1,8 +1,15 @@ import { Injectable, Inject } from "@nestjs/common"; -import { Team, TeamMember, TeamT } from "@sparcs-students/api/drizzle/schema"; +import { + Team, + TeamMember, + TeamLeader, + TeamT, + TeamMemberT, + TeamLeaderT, +} from "@sparcs-students/api/drizzle/schema"; import { ApiOrg007RequestBody } from "@sparcs-students/interface/api/organization/index"; -import { and, eq, isNull } from "drizzle-orm"; +import { and, eq, isNull, or, gte, lte } from "drizzle-orm"; import { MySql2Database } from "drizzle-orm/mysql2"; import { DrizzleAsyncProvider } from "src/drizzle/drizzle.provider"; @@ -69,12 +76,155 @@ export class TeamRepository { return res; } - async selectTeamById(teamId: number): Promise { - const res = await this.db - .select() - .from(Team) - .where(and(eq(Team.id, teamId), isNull(Team.deletedAt))) + async selectTeam(target: Partial): Promise { + const { id, organizationId, semesterId, name, detail } = target; + + let query = this.db.select().from(Team).$dynamic(); + + const whereConditions = []; + + if (id) { + whereConditions.push(eq(Team.id, id)); + } + + if (organizationId) { + whereConditions.push(eq(Team.organizationId, organizationId)); + } + + if (semesterId) { + whereConditions.push(eq(Team.semesterId, semesterId)); + } + + if (name) { + whereConditions.push(eq(Team.name, name)); + } + + if (detail) { + whereConditions.push(eq(Team.detail, detail)); + } + + // 삭제된 항목 제외 + whereConditions.push(isNull(Team.deletedAt)); + + // 조건이 하나라도 있으면 AND로 묶어서 처리 + if (whereConditions.length > 0) { + query = query.where(and(...whereConditions)); + } + + // 쿼리 실행 + const res = await query.execute(); + + return res; + } + + async selectTeamMember(target: Partial) { + const { id, userId, startTerm, endTerm, teamId } = target; + let query = this.db.select().from(TeamMember).$dynamic(); + + const whereConditions = []; + + if (id) { + whereConditions.push(eq(TeamMember.id, id)); + } + + if (userId) { + whereConditions.push(eq(TeamMember.userId, userId)); + } + + if (startTerm) { + whereConditions.push( + or(gte(TeamMember.endTerm, startTerm), isNull(TeamMember.endTerm)), + ); + } + + if (endTerm) { + whereConditions.push(lte(TeamMember.startTerm, endTerm)); + } + + if (teamId) { + whereConditions.push(eq(TeamMember.teamId, teamId)); + } + + // 삭제된 항목 제외 + whereConditions.push(isNull(TeamMember.deletedAt)); + + // 조건이 하나라도 있으면 AND로 묶어서 처리 + if (whereConditions.length > 0) { + query = query.where(and(...whereConditions)); + } + + // 쿼리 실행 + const res = await query.execute(); + + return res; + } + + async selectTeamLeader(target: Partial) { + const { id, userId, startTerm, endTerm, teamId, role } = target; + let query = this.db.select().from(TeamLeader).$dynamic(); + + const whereConditions = []; + + if (id) { + whereConditions.push(eq(TeamLeader.id, id)); + } + + if (userId) { + whereConditions.push(eq(TeamLeader.userId, userId)); + } + + if (startTerm) { + whereConditions.push( + or(gte(TeamLeader.endTerm, startTerm), isNull(TeamLeader.endTerm)), + ); + } + + if (endTerm) { + whereConditions.push(lte(TeamLeader.startTerm, endTerm)); + } + + if (teamId) { + whereConditions.push(eq(TeamLeader.teamId, teamId)); + } + + if (role) { + whereConditions.push(eq(TeamLeader.role, role)); + } + + // 삭제된 항목 제외 + whereConditions.push(isNull(TeamLeader.deletedAt)); + + // 조건이 하나라도 있으면 AND로 묶어서 처리 + if (whereConditions.length > 0) { + query = query.where(and(...whereConditions)); + } + + // 쿼리 실행 + const res = await query.execute(); + + return res; + } + + async insertTeamLeader( + userId: number, + teamId: number, + role: string, + startTerm: Date, + endTerm: Date, + ): Promise { + await this.db + .insert(TeamLeader) + .values({ userId, teamId, role, startTerm, endTerm }) .execute(); + const res = await this.ckTeamLeaderBeforeCreate(userId, teamId); return res; } + + async ckTeamLeaderBeforeCreate(userId, teamId): Promise { + const res = await this.selectTeamLeader({ userId, teamId }); + if (res.length === 0) { + return 0; + } + return res[0].id; + } } diff --git a/packages/api/src/feature/organization/team/service/team.public.service.ts b/packages/api/src/feature/organization/team/service/team.public.service.ts new file mode 100644 index 0000000..e2fecb2 --- /dev/null +++ b/packages/api/src/feature/organization/team/service/team.public.service.ts @@ -0,0 +1,31 @@ +import { + HttpException, + HttpStatus, + Injectable, + NotFoundException, +} from "@nestjs/common"; +import { TeamT } from "@sparcs-students/api/drizzle/schema"; +import { TeamRepository } from "../repository/team.repository"; + +@Injectable() +export class TeamPublicService { + constructor(private readonly teamRepository: TeamRepository) {} + + /** + * @param teamId + * @returns TeamT id에 해당하는 TeamT 객체를 리턴합니다. + * @description 해당 id의 Team이 없으면 404 exception을 throw 합니다. + */ + async getTeamById(teamId: number): Promise { + const res = await this.teamRepository.selectTeam({ id: teamId }); + if (res.length === 0) { + throw new NotFoundException(`Team with ID ${teamId} not found.`); + } else if (res.length > 1) { + throw new HttpException( + `Unreachable: Team with ID ${teamId} has multiple records.`, + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + return res[0]; + } +} diff --git a/packages/api/src/feature/organization/team/service/team.service.ts b/packages/api/src/feature/organization/team/service/team.service.ts index 24003c3..a1d0409 100644 --- a/packages/api/src/feature/organization/team/service/team.service.ts +++ b/packages/api/src/feature/organization/team/service/team.service.ts @@ -9,6 +9,8 @@ import { ApiOrg007ResponseCreated, ApiOrg008RequestBody, ApiOrg008ResponseCreated, + ApiOrg009RequestBody, + ApiOrg009ResponseCreated, } from "@sparcs-students/interface/api/organization/index"; import { UserPublicService } from "@sparcs-students/api/feature/user/service/user.public.service"; import { SemesterPublicService } from "@sparcs-students/api/feature/semester/semester.public.service"; @@ -46,7 +48,7 @@ export class TeamService { ): Promise { // userId와 teamId의 유효성 체크 await this.userPublicService.getUserById(body.userId); - const ckTeam = await this.teamRepository.selectTeamById(body.teamId); + const ckTeam = await this.teamRepository.selectTeam({ id: body.teamId }); if (ckTeam.length === 0) { throw new NotFoundException("Team not found"); } else if (ckTeam.length > 1) { @@ -91,4 +93,63 @@ export class TeamService { } return { teamMemberId }; } + + async postTeamLeader( + body: ApiOrg009RequestBody, + ): Promise { + // userId와 teamId의 유효성 체크 + await this.userPublicService.getUserById(body.userId); + const ckTeam = await this.teamRepository.selectTeam({ id: body.teamId }); + if (ckTeam.length === 0) { + throw new NotFoundException("Team not found"); + } else if (ckTeam.length > 1) { + throw new HttpException( + "Unreachable: Team has multiple records", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + const { startTerm, endTerm } = + await this.semesterPublicService.getSemesterById(ckTeam[0].semesterId); + + // team leader user 가 TeamMember인지 체크 + const countTeamMember = await this.teamRepository.selectTeamMember({ + userId: body.userId, + teamId: body.teamId, + }); + if (countTeamMember.length === 0) { + throw new NotFoundException("Team member not found"); + } else if (countTeamMember.length > 1) { + throw new HttpException( + "Unreachable: Team member has multiple records", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + + // team leader 중복 체크 + const ckLeader = await this.teamRepository.ckTeamLeaderBeforeCreate( + body.userId, + body.teamId, + ); + if (ckLeader !== 0) { + throw new HttpException( + "Team leader already exists", + HttpStatus.BAD_REQUEST, + ); + } + // team leader 생성 + const teamLeaderId = await this.teamRepository.insertTeamLeader( + body.userId, + body.teamId, + body.role, + startTerm, + endTerm, + ); + if (teamLeaderId === 0) { + throw new HttpException( + "Team leader creation failed", + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + return { teamLeaderId }; + } } diff --git a/packages/api/src/feature/organization/team/team.module.ts b/packages/api/src/feature/organization/team/team.module.ts index 5941999..3a3de19 100644 --- a/packages/api/src/feature/organization/team/team.module.ts +++ b/packages/api/src/feature/organization/team/team.module.ts @@ -8,6 +8,7 @@ import { TeamController } from "./controller/team.controller"; import { TeamRepository } from "./repository/team.repository"; /* eslint-disable import/no-cycle */ import { OrganizationModule } from "../organization.module"; +import { TeamPublicService } from "./service/team.public.service"; /* eslint-disable import/no-cycle */ @Module({ @@ -18,7 +19,7 @@ import { OrganizationModule } from "../organization.module"; forwardRef(() => OrganizationModule), ], controllers: [TeamController], - providers: [TeamService, TeamRepository], - exports: [], + providers: [TeamService, TeamRepository, TeamPublicService], + exports: [TeamPublicService], }) export class TeamModule {} 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 7f37bda..c5b9529 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 @@ -3,12 +3,13 @@ import { Module } from "@nestjs/common"; import { DrizzleModule } from "src/drizzle/drizzle.module"; import { OrganizationModule } from "src/feature/organization/organization.module"; import { UserModule } from "src/feature/user/user.module"; +import { TeamModule } from "src/feature/organization/team/team.module"; import { ProjectProposalRepository } from "./repository/project-proposal.repository"; import { ProjectProposalService } from "./service/project-proposal.service"; import { ProjectProposalController } from "./controller/project-proposal.controller"; @Module({ - imports: [OrganizationModule, DrizzleModule, UserModule], + imports: [TeamModule, DrizzleModule, UserModule, OrganizationModule], providers: [ProjectProposalRepository, ProjectProposalService], controllers: [ProjectProposalController], }) diff --git a/packages/api/src/feature/proposal/project-proposal/service/project-proposal.service.ts b/packages/api/src/feature/proposal/project-proposal/service/project-proposal.service.ts index e5fce0b..bbb0dcf 100644 --- a/packages/api/src/feature/proposal/project-proposal/service/project-proposal.service.ts +++ b/packages/api/src/feature/proposal/project-proposal/service/project-proposal.service.ts @@ -12,6 +12,7 @@ import { } from "@sparcs-students/interface/api/proposal/index"; import { UserPublicService } from "@sparcs-students/api/feature/user/service/user.public.service"; import { OrganizationPublicService } from "src/feature/organization/service/organization.public.service"; +import { TeamPublicService } from "src/feature/organization/team/service/team.public.service"; import { ProjectProposalRepository } from "../repository/project-proposal.repository"; @Injectable() @@ -20,6 +21,7 @@ export class ProjectProposalService { private readonly projectProposalRepository: ProjectProposalRepository, private readonly organizationPublicService: OrganizationPublicService, private readonly userPublicService: UserPublicService, + private readonly teamPublicService: TeamPublicService, ) {} async getProjectProposalsForStudentsBySemesterId( @@ -82,7 +84,7 @@ export class ProjectProposalService { } const projectProposalRevision = prpRevs[0]; - const team = await this.organizationPublicService.getTeamById( + const team = await this.teamPublicService.getTeamById( projectProposalRevision.teamId, ); const manager = await this.userPublicService.getUserById(