From 48d8d3e74af3370d1df971365ffadff8d28647e1 Mon Sep 17 00:00:00 2001 From: SeungJong Ha Date: Sun, 2 Feb 2025 19:12:48 +0900 Subject: [PATCH 1/2] fix: add expiration logic --- .../club.club-delegate-d.repository.ts | 7 +--- .../feature/club/delegate/delegate.service.ts | 40 ++++++++++++++++++- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/packages/api/src/feature/club/delegate/club.club-delegate-d.repository.ts b/packages/api/src/feature/club/delegate/club.club-delegate-d.repository.ts index 669d69ece..dc16bcbd5 100644 --- a/packages/api/src/feature/club/delegate/club.club-delegate-d.repository.ts +++ b/packages/api/src/feature/club/delegate/club.club-delegate-d.repository.ts @@ -38,7 +38,7 @@ export class ClubDelegateDRepository { /** * @param id 삭제할 변경 요청의 id */ - async deleteDelegatChangeRequestById(param: { + async deleteDelegateChangeRequestById(param: { id: number; }): Promise { const [result] = await this.db @@ -77,18 +77,13 @@ export class ClubDelegateDRepository { * 3일 이내에 신청된 요청만을 조회합니다. * 최근에 신청된 요청이 가장 위에 위치합니다. */ - // TODO: 만료 enum 추가 async findDelegateChangeRequestByClubId(param: { clubId: number }) { - const threeDaysAgo = new Date(); - threeDaysAgo.setDate(threeDaysAgo.getDate() - 3); - const result = await this.db .select() .from(ClubDelegateChangeRequest) .where( and( eq(ClubDelegateChangeRequest.clubId, param.clubId), - gte(ClubDelegateChangeRequest.createdAt, threeDaysAgo), isNull(ClubDelegateChangeRequest.deletedAt), ), ) diff --git a/packages/api/src/feature/club/delegate/delegate.service.ts b/packages/api/src/feature/club/delegate/delegate.service.ts index 7496c16c0..160e263b0 100644 --- a/packages/api/src/feature/club/delegate/delegate.service.ts +++ b/packages/api/src/feature/club/delegate/delegate.service.ts @@ -48,6 +48,40 @@ export default class ClubDelegateService { private clubPublicService: ClubPublicService, ) {} + /** + * @param clubId 동아리 Id + * @description 해당 동아리의 대표자 변경 요청 중 3일이 지난 요청을 만료(soft delete)합니다. + * **_모든 변경 요청 조회 관련 로직에서 조회 이전에 호출되어야 합니다._** + */ + private async cleanExpiredChangeRequests(param: { + clubId: number; + }): Promise { + const requests = + await this.clubDelegateDRepository.findDelegateChangeRequestByClubId({ + clubId: param.clubId, + }); + + const threeDaysAgo = getKSTDate(); + threeDaysAgo.setDate(threeDaysAgo.getDate() - 3); + + await Promise.all( + requests.map(async request => { + if ( + request.clubDelegateChangeRequestStatusEnumId === + ClubDelegateChangeRequestStatusEnum.Applied && + request.createdAt < threeDaysAgo + ) { + logger.debug( + `Found expired change request created on ${request.createdAt}`, + ); + await this.clubDelegateDRepository.deleteDelegateChangeRequestById({ + id: request.id, + }); + } + }), + ); + } + async getStudentClubDelegates( param: { studentId: number } & ApiClb006RequestParam, ): Promise { @@ -225,6 +259,7 @@ export default class ClubDelegateService { * * @description getStudentClubDelegateRequests의 서비스 진입점입니다. * 동아리 대표자 변경 요청을 조회합니다. + * 조회한 대표자 변경 요청이 3일이 지났다면 soft delete합니다. */ async getStudentClubDelegateRequests(param: { param: ApiClb011RequestParam; @@ -243,6 +278,9 @@ export default class ClubDelegateService { HttpStatus.FORBIDDEN, ); + // 3일이 지난 요청은 soft delete합니다. + await this.cleanExpiredChangeRequests({ clubId: param.param.clubId }); + const result = await this.clubDelegateDRepository.findDelegateChangeRequestByClubId({ clubId: param.param.clubId, @@ -360,7 +398,7 @@ export default class ClubDelegateService { requests.map(request => { if (request === undefined) throw new HttpException("No request", HttpStatus.BAD_REQUEST); - return this.clubDelegateDRepository.deleteDelegatChangeRequestById({ + return this.clubDelegateDRepository.deleteDelegateChangeRequestById({ id: request.id, }); }), From 12b385216abd965b9f3af6a20464989695a30ac4 Mon Sep 17 00:00:00 2001 From: Woojeong Choi Date: Tue, 4 Feb 2025 21:35:49 +0900 Subject: [PATCH 2/2] chore: use ClubDelegateEnum --- .../components/ChangeRepresentativeCard.tsx | 45 +++++++++++++------ .../_atomic/RepresentativeLoadFrame.tsx | 7 ++- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/packages/web/src/features/manage-club/components/ChangeRepresentativeCard.tsx b/packages/web/src/features/manage-club/components/ChangeRepresentativeCard.tsx index d89e0dd7c..c4b1edc82 100644 --- a/packages/web/src/features/manage-club/components/ChangeRepresentativeCard.tsx +++ b/packages/web/src/features/manage-club/components/ChangeRepresentativeCard.tsx @@ -54,38 +54,51 @@ const ChangeRepresentativeCard: React.FC<{ item => item.value !== delegatesNow?.delegates - .find(delegate => delegate.delegateEnumId === 1) + .find( + delegate => + delegate.delegateEnumId === ClubDelegateEnum.Representative, + ) ?.studentId?.toString(), ), ); const representative = delegatesNow?.delegates - .find(delegate => delegate.delegateEnumId === 1) + .find( + delegate => delegate.delegateEnumId === ClubDelegateEnum.Representative, + ) ?.studentId?.toString() ?? ""; const representativeName = - delegatesNow?.delegates.find(delegate => delegate.delegateEnumId === 1) - ?.name ?? ""; + delegatesNow?.delegates.find( + delegate => delegate.delegateEnumId === ClubDelegateEnum.Representative, + )?.name ?? ""; const representativeStudentNumber = - delegatesNow?.delegates.find(delegate => delegate.delegateEnumId === 1) - ?.studentNumber ?? ""; + delegatesNow?.delegates.find( + delegate => delegate.delegateEnumId === ClubDelegateEnum.Representative, + )?.studentNumber ?? ""; const [delegate1, setDelegate1] = useState( - delegatesNow?.delegates.find(delegate => delegate.delegateEnumId === 2) - ?.studentId === 0 + delegatesNow?.delegates.find( + delegate => delegate.delegateEnumId === ClubDelegateEnum.Delegate1, + )?.studentId === 0 ? "" : delegatesNow?.delegates - .find(delegate => delegate.delegateEnumId === 2) + .find( + delegate => delegate.delegateEnumId === ClubDelegateEnum.Delegate1, + ) ?.studentId.toString() ?? "", ); const [delegate2, setDelegate2] = useState( - delegatesNow?.delegates.find(delegate => delegate.delegateEnumId === 3) - ?.studentId === 0 + delegatesNow?.delegates.find( + delegate => delegate.delegateEnumId === ClubDelegateEnum.Delegate2, + )?.studentId === 0 ? "" : delegatesNow?.delegates - .find(delegate => delegate.delegateEnumId === 3) + .find( + delegate => delegate.delegateEnumId === ClubDelegateEnum.Delegate2, + ) ?.studentId.toString() ?? "", ); @@ -138,7 +151,9 @@ const ChangeRepresentativeCard: React.FC<{ if ( delegate1 !== delegatesNow?.delegates - .find(delegate => delegate.delegateEnumId === 2) + .find( + delegate => delegate.delegateEnumId === ClubDelegateEnum.Delegate1, + ) ?.studentId?.toString() && type !== "Applied" && delegate1 !== "" @@ -158,7 +173,9 @@ const ChangeRepresentativeCard: React.FC<{ if ( delegate2 !== delegatesNow?.delegates - .find(delegate => delegate.delegateEnumId === 3) + .find( + delegate => delegate.delegateEnumId === ClubDelegateEnum.Delegate2, + ) ?.studentId?.toString() && type !== "Applied" && delegate2 !== "" diff --git a/packages/web/src/features/manage-club/frames/_atomic/RepresentativeLoadFrame.tsx b/packages/web/src/features/manage-club/frames/_atomic/RepresentativeLoadFrame.tsx index a3a1b2fb3..2db9a1684 100644 --- a/packages/web/src/features/manage-club/frames/_atomic/RepresentativeLoadFrame.tsx +++ b/packages/web/src/features/manage-club/frames/_atomic/RepresentativeLoadFrame.tsx @@ -1,5 +1,7 @@ import React from "react"; +import { ClubDelegateEnum } from "@sparcs-clubs/interface/common/enum/club.enum"; + import AsyncBoundary from "@sparcs-clubs/web/common/components/AsyncBoundary"; import { useGetClubDetail } from "@sparcs-clubs/web/features/clubs/services/getClubDetail"; import ChangeRepresentativeCard from "@sparcs-clubs/web/features/manage-club/components/ChangeRepresentativeCard"; @@ -19,7 +21,10 @@ const RepresentativeLoadFrame: React.FC<{ data: clubMembers, isLoading: clubMembersIsLoading, isError: clubMembersIsError, - } = useGetDelegateCandidates({ clubId, delegateEnumId: 1 }); + } = useGetDelegateCandidates({ + clubId, + delegateEnumId: ClubDelegateEnum.Representative, + }); const { data: clubInfo,