diff --git a/prisma/schema.prisma b/prisma/schema.prisma index b971d49..f63c336 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -53,9 +53,9 @@ model Process { studentId Int? @unique headReviewerId Int? phaseId Int? - headReviewer User? @relation("headReviewer", fields: [headReviewerId], references: [id]) + headReviewer User? @relation("headReviewer", fields: [headReviewerId], references: [id], onDelete: SetNull) phase Phase? @relation(fields: [phaseId], references: [id]) - student User? @relation("student", fields: [studentId], references: [id]) + student User? @relation("student", fields: [studentId], references: [id], onDelete: Cascade) reviewers Reviewer[] thesisInfos ThesisInfo[] @@ -87,7 +87,7 @@ model ThesisInfo { processId Int? reviews Review[] thesisFiles ThesisFile[] - process Process? @relation(fields: [processId], references: [id]) + process Process? @relation(fields: [processId], references: [id], onDelete: Cascade) @@index([processId], map: "thesis_info_processId_fkey") @@map("thesis_info") @@ -99,8 +99,8 @@ model Reviewer { reviewerId Int? processId Int? role Role - process Process? @relation(fields: [processId], references: [id]) - reviewer User? @relation(fields: [reviewerId], references: [id]) + process Process? @relation(fields: [processId], references: [id], onDelete: Cascade) + reviewer User? @relation(fields: [reviewerId], references: [id], onDelete: Cascade) @@index([processId], map: "reviewer_processId_fkey") @@index([reviewerId], map: "reviewer_reviewerId_fkey") @@ -120,8 +120,8 @@ model Review { thesisInfoId Int? fileId String? @unique file File? @relation(fields: [fileId], references: [uuid]) - reviewer User? @relation(fields: [reviewerId], references: [id]) - thesisInfo ThesisInfo? @relation(fields: [thesisInfoId], references: [id]) + reviewer User? @relation(fields: [reviewerId], references: [id], onDelete: SetNull) + thesisInfo ThesisInfo? @relation(fields: [thesisInfoId], references: [id], onDelete: Cascade) @@index([reviewerId], map: "review_reviewerId_fkey") @@index([thesisInfoId], map: "review_thesisInfoId_fkey") @@ -151,7 +151,7 @@ model ThesisFile { thesisInfoId Int? fileId String? @unique file File? @relation(fields: [fileId], references: [uuid]) - ThesisInfo ThesisInfo? @relation(fields: [thesisInfoId], references: [id]) + ThesisInfo ThesisInfo? @relation(fields: [thesisInfoId], references: [id], onDelete: Cascade) @@index([thesisInfoId], map: "thesis_file_thesisInfoId_fkey") @@map("thesis_file") @@ -167,7 +167,7 @@ model Achievements { authorType AuthorType authorNumbers Int userId Int? - User User? @relation(fields: [userId], references: [id]) + User User? @relation(fields: [userId], references: [id], onDelete: Cascade) @@index([userId], map: "achievements_userId_fkey") @@map("achievements") diff --git a/src/modules/departments/departments.controller.ts b/src/modules/departments/departments.controller.ts index ca6e086..ceb4f18 100644 --- a/src/modules/departments/departments.controller.ts +++ b/src/modules/departments/departments.controller.ts @@ -63,7 +63,7 @@ export class DepartmentsController { @Delete(":id") @ApiOperation({ summary: "학과 삭제", - description: "학과 삭제", + description: "학과 삭제, 해당 학과 소속 학생들은 Cascade로 삭제됩니다.", }) @UseUserTypeGuard([UserType.ADMIN]) @ApiUnauthorizedResponse({ description: "[관리자] 로그인 후 접근 가능" }) diff --git a/src/modules/departments/departments.module.ts b/src/modules/departments/departments.module.ts index cbe56a1..32fcc41 100644 --- a/src/modules/departments/departments.module.ts +++ b/src/modules/departments/departments.module.ts @@ -1,9 +1,11 @@ import { Module } from "@nestjs/common"; import { DepartmentsService } from "./departments.service"; import { DepartmentsController } from "./departments.controller"; +import { StudentsModule } from "../students/students.module"; @Module({ controllers: [DepartmentsController], providers: [DepartmentsService], + imports: [StudentsModule], }) export class DepartmentsModule {} diff --git a/src/modules/departments/departments.service.ts b/src/modules/departments/departments.service.ts index 2cc4095..8f9fab2 100644 --- a/src/modules/departments/departments.service.ts +++ b/src/modules/departments/departments.service.ts @@ -2,10 +2,15 @@ import { PrismaService } from "src/config/database/prisma.service"; import { BadRequestException, Injectable, InternalServerErrorException } from "@nestjs/common"; import { CreateDepartmentDto } from "./dtos/create-department.dto"; import { UpdateDepartmentQuery } from "./dtos/update-department-query.dto"; +import { UserType } from "@prisma/client"; +import { StudentsService } from "../students/students.service"; @Injectable() export class DepartmentsService { - constructor(private readonly prismaService: PrismaService) {} + constructor( + private readonly prismaService: PrismaService, + private readonly studentService: StudentsService + ) {} async getAllDepartments() { const departments = await this.prismaService.department.findMany({ include: { @@ -39,12 +44,24 @@ export class DepartmentsService { async deleteDepartment(id: number) { const department = await this.prismaService.department.findUnique({ where: { id }, + include: { users: true }, }); - if (!department) { throw new BadRequestException("존재하지 않는 학과입니다."); } + // 소속 학생 삭제 + const studentIds = department.users.filter((user) => user.type === UserType.STUDENT).map((student) => student.id); + for (const studentId of studentIds) { + try { + await this.studentService.deleteStudent(studentId); + } catch (error) { + console.log(error); + throw new InternalServerErrorException("해당 학과의 학생을 삭제하는 과정에서 오류 발생"); + } + } + + // 학과 삭제 return this.prismaService.department.delete({ where: { id }, }); diff --git a/src/modules/files/files.module.ts b/src/modules/files/files.module.ts index 39b548b..89415b2 100644 --- a/src/modules/files/files.module.ts +++ b/src/modules/files/files.module.ts @@ -5,5 +5,6 @@ import { FilesService } from "./files.service"; @Module({ controllers: [FilesController], providers: [FilesService], + exports: [FilesService], }) export class FilesModule {} diff --git a/src/modules/students/students.controller.ts b/src/modules/students/students.controller.ts index 75ad21a..7a4d709 100644 --- a/src/modules/students/students.controller.ts +++ b/src/modules/students/students.controller.ts @@ -395,6 +395,7 @@ export class StudentsController { return new CommonResponseDto(reviewersDto); } + // 삭제 @Delete("") @ApiOperation({ summary: "학생 목록 삭제", @@ -408,4 +409,17 @@ export class StudentsController { return new CommonResponseDto(); } + + @Delete("/:id") + @ApiOperation({ + summary: "학생 삭제 API", + description: "아이디에 해당하는 학생을 DB에서 삭제하고, 관련 데이터와 파일도 모두 삭제한다.", + }) + @UseUserTypeGuard([UserType.ADMIN]) + @ApiUnauthorizedResponse({ description: "[관리자] 로그인 후 접근 가능" }) + @ApiInternalServerErrorResponse({ description: "서버 내부 오류" }) + async deleteStudent(@Param("id", PositiveIntPipe) studentId: number) { + await this.studentsService.deleteStudent(studentId); + return new CommonResponseDto(); + } } diff --git a/src/modules/students/students.module.ts b/src/modules/students/students.module.ts index 159c870..3f4fef6 100644 --- a/src/modules/students/students.module.ts +++ b/src/modules/students/students.module.ts @@ -2,10 +2,10 @@ import { Module } from "@nestjs/common"; import { StudentsController } from "./students.controller"; import { StudentsService } from "./students.service"; import { AuthModule } from "../auth/auth.module"; - @Module({ controllers: [StudentsController], providers: [StudentsService], imports: [AuthModule], + exports: [StudentsService], }) export class StudentsModule {} diff --git a/src/modules/students/students.service.ts b/src/modules/students/students.service.ts index 062a596..89f5573 100644 --- a/src/modules/students/students.service.ts +++ b/src/modules/students/students.service.ts @@ -2038,18 +2038,45 @@ export class StudentsService { } async deleteStudentsList() { + // 논문 파일, 서명 파일 가져오기 + + // 학생 데이터 모두 삭제 (Hard delete) + // cascade로 삭제되는 연관된 데이터들 : Achivements, Process, Reviewers, ThesisInfos, Reviews, ThesisFiles + try { + await this.prismaService.user.deleteMany({ + where: { type: UserType.STUDENT }, + }); + } catch (error) { + console.log(error); + throw new InternalServerErrorException("학생 삭제 오류 발생"); + } + } + + async deleteStudent(studentId: number) { + // 학생 아이디 확인 + const student = await this.prismaService.user.findUnique({ + where: { + id: studentId, + type: UserType.STUDENT, + deletedAt: null, + }, + }); + if (!student) { + throw new BadRequestException("해당하는 학생을 찾을 수 없습니다."); + } + + // 학생 데이터 모두 삭제 (Hard delete) + // cascade로 삭제되는 연관된 데이터들 : Achivements, Process, Reviewers, ThesisInfos, Reviews, ThesisFiles try { - return await this.prismaService.user.updateMany({ + await this.prismaService.user.delete({ where: { + id: studentId, type: UserType.STUDENT, - deletedAt: null, - }, - data: { - deletedAt: DateUtil.getCurrentTime().fullDateTime, }, }); - } catch (e) { - throw new InternalServerErrorException("학생 목록 삭제에 실패했습니다."); + } catch (error) { + console.log(error); + throw new InternalServerErrorException("학생 삭제 오류 발생"); } } }