Skip to content

Commit

Permalink
Merge pull request #1104 from academic-relations/dev
Browse files Browse the repository at this point in the history
Merge dev into main
  • Loading branch information
pbc1017 authored Sep 25, 2024
2 parents c49c3a8 + f27c12d commit 4260e79
Show file tree
Hide file tree
Showing 16 changed files with 258 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,13 @@ export default class ActivityRepository {
clubId: contents.clubId,
originalName: contents.name,
name: contents.name,
activityStatusEnumId: Number(contents.activityTypeEnumId),
activityStatusEnumId: Number(ActivityStatusEnum.Applied),
location: contents.location,
purpose: contents.purpose,
detail: contents.detail,
evidence: contents.evidence,
activityDId: contents.activityDId,
activityTypeEnumId: Number(ActivityStatusEnum.Applied),
activityTypeEnumId: Number(contents.activityTypeEnumId),
});
if (activityInsertResult.affectedRows !== 1) {
logger.debug("[insertActivity] rollback occurs");
Expand Down
5 changes: 3 additions & 2 deletions packages/api/src/feature/club/club.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { DrizzleModule } from "src/drizzle/drizzle.module";

import UserModule from "../user/user.module";

import ClubDelegateController from "./controller/club-delegate.controller";
import { ClubController } from "./controller/club.controller";
import ClubDelegateController from "./delegate/delegate.controller";
import ClubDelegateService from "./delegate/delegate.service";
import { ClubDelegateDRepository } from "./repository/club.club-delegate-d.repository";
import { ClubRoomTRepository } from "./repository/club.club-room-t.repository";
import ClubStudentTRepository from "./repository/club.club-student-t.repository";
Expand All @@ -15,11 +16,11 @@ import { ClubGetStudentClubBrief } from "./repository/club.get-student-club-brie
import { ClubPutStudentClubBrief } from "./repository/club.put-student-club-brief";
import ClubRepository from "./repository/club.repository";
import SemesterDRepository from "./repository/club.semester-d.repository";
import ClubDelegateService from "./service/club-delegate.service";

import ClubPublicService from "./service/club.public.service";
import { ClubService } from "./service/club.service";

// TODO: delegate 관련 모듈화하기
@Module({
imports: [DrizzleModule, UserModule],
controllers: [ClubController, ClubDelegateController],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { ZodPipe } from "@sparcs-clubs/api/common/pipe/zod-pipe";
import { Student } from "@sparcs-clubs/api/common/util/decorators/method-decorator";
import { GetStudent } from "@sparcs-clubs/api/common/util/decorators/param-decorator";

import ClubDelegateService from "../service/club-delegate.service";
import ClubDelegateService from "./delegate.service";

import type {
ApiClb006RequestParam,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import { ClubDelegateEnum } from "@sparcs-clubs/interface/common/enum/club.enum"
import logger from "@sparcs-clubs/api/common/util/logger";
import { getKSTDate } from "@sparcs-clubs/api/common/util/util";

import ClubPublicService from "@sparcs-clubs/api/feature/club/service/club.public.service";
import UserPublicService from "@sparcs-clubs/api/feature/user/service/user.public.service";

import { ClubDelegateDRepository } from "../repository/club.club-delegate-d.repository";

import ClubPublicService from "./club.public.service";

import type {
ApiClb006RequestParam,
ApiClb006ResponseOK,
Expand Down Expand Up @@ -84,39 +83,47 @@ export default class ClubDelegateService {
* @param clubId 동아리 Id
* @param clubDelegateEnumId 대표자 지위 Id
*
* @description 동아리 대표자의 변경을 적용합니다.
* @description putStudentClubDelegate의 서비스 진입점입니다.
* 동아리 대표자의 변경을 적용합니다.
*/
async putStudentClubDelegate(param: {
studentId: number;
targetStudentId: number;
clubId: number;
clubDelegateEnumId: number;
}) {
}): Promise<void> {
const currentDelegates =
await this.clubDelegateDRepository.findDelegateByClubId(param.clubId);

// clubDelegateEnumId가 대표자일 경우, 신청자가 현재 동아리의 대표자가 맞는지 검사합니다.
// clubDelegateEnumId가 대의원일 경우, 신청자가 현재 동아리의 대의원이 맞는지 검사합니다.
// 24.09.24 변경사항: 동아리 대표자만 사용 가능하도록 변경합니다.
const studentStatus = currentDelegates.find(
e => e.studentId === param.studentId,
);
if (
studentStatus === undefined ||
(param.clubDelegateEnumId === ClubDelegateEnum.Representative &&
studentStatus.ClubDelegateEnumId !== ClubDelegateEnum.Representative)
)
// if (
// studentStatus === undefined ||
// (param.clubDelegateEnumId === ClubDelegateEnum.Representative &&
// studentStatus.ClubDelegateEnumId !== ClubDelegateEnum.Representative)
// )
// throw new HttpException(
// "This api is allowed for delegates",
// HttpStatus.FORBIDDEN,
// );
if (studentStatus.ClubDelegateEnumId !== ClubDelegateEnum.Representative)
throw new HttpException(
"This api is allowed for delegates",
"This api is allowed for the club representative",
HttpStatus.FORBIDDEN,
);

// 신청자가 이미 다른 요청을 생성하지 않았는지 검사합니다.
// 대의원 변경의 경우 요청을 생성하지 않고 바로 변경되기에 항상 통과해야 합니다.
const requests =
await this.clubDelegateDRepository.findDelegateChangeRequestByPrevStudentId(
{ studentId: param.studentId },
);
if (requests.length > 1)
throw new HttpException("unreahable", HttpStatus.INTERNAL_SERVER_ERROR);
throw new HttpException("unreachable", HttpStatus.INTERNAL_SERVER_ERROR);
if (requests.length === 1)
throw new HttpException(
"You already made a request",
Expand Down Expand Up @@ -149,18 +156,46 @@ export default class ClubDelegateService {
HttpStatus.BAD_REQUEST,
);

// 대표자 변경 신청의 경우 targetStudentId가 undefined인지 검사합니다.
if (
!(await this.clubDelegateDRepository.insertClubDelegateChangeRequest({
clubId: param.clubId,
clubDelegateEnumId: param.clubDelegateEnumId,
studentId: param.studentId,
targetStudentId: param.targetStudentId,
}))
param.clubDelegateEnumId === ClubDelegateEnum.Representative &&
param.targetStudentId === undefined
)
throw new HttpException(
"Failed to insert request",
HttpStatus.INTERNAL_SERVER_ERROR,
"representative can;t be empty",
HttpStatus.BAD_REQUEST,
);

// 대의원의 경우 즉시 변경하고, 대표자의 경우 변경 요청을 생성합니다.
switch (param.clubDelegateEnumId) {
case ClubDelegateEnum.Representative:
if (
!(await this.clubDelegateDRepository.insertClubDelegateChangeRequest({
clubId: param.clubId,
clubDelegateEnumId: param.clubDelegateEnumId,
studentId: param.studentId,
targetStudentId: param.targetStudentId,
}))
)
throw new HttpException(
"Failed to insert request",
HttpStatus.INTERNAL_SERVER_ERROR,
);
break;
default:
if (
!this.clubDelegateDRepository.updateDelegate({
clubId: param.clubId,
clubDelegateEnumId: param.clubDelegateEnumId,
studentId: param.studentId,
})
)
throw new HttpException(
"Failed to change delegate",
HttpStatus.INTERNAL_SERVER_ERROR,
);
break;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ export class ClubDelegateDRepository {
/**
* @param clubId 동아리 id
* @param clubDelegateEnumId 대표자 분류 id
* @param studentId 지정할 학생 id
* @param studentId 지정할 학생 id. undefined인 경우 지위만 해제시킵니다.
*
* @description 해당 학생을 동아리의 대표자로 지정합니다.
* 기존에 해당 지위로 지정되었던 학생이 존재할 경우 지위가 해제됩니다.
Expand All @@ -289,7 +289,7 @@ export class ClubDelegateDRepository {
async updateDelegate(param: {
clubId: number;
clubDelegateEnumId: number;
studentId: number;
studentId?: number;
}): Promise<boolean> {
const now = getKSTDate();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ import { getKSTDate, takeUnique } from "@sparcs-clubs/api/common/util/util";
import { DrizzleAsyncProvider } from "@sparcs-clubs/api/drizzle/drizzle.provider";

import { Student } from "@sparcs-clubs/api/drizzle/schema/user.schema";
import { Club, ClubStudentT, SemesterD } from "src/drizzle/schema/club.schema";
import {
Club,
ClubDelegateD,
ClubStudentT,
SemesterD,
} from "src/drizzle/schema/club.schema";

@Injectable()
export default class ClubStudentTRepository {
Expand Down Expand Up @@ -163,11 +168,25 @@ export default class ClubStudentTRepository {
.execute();
}

// ** 주의: delegate 또는 일반 부원을 제거합니다.
// ** 제거 시 hard deletion이 이루어집니다.
async removeStudentFromClub(
studentId: number,
clubId: number,
semesterId: number,
isTargetStudentDelegate: boolean,
): Promise<void> {
if (isTargetStudentDelegate)
await this.db
.delete(ClubDelegateD)
.where(
and(
eq(ClubDelegateD.studentId, studentId),
eq(ClubDelegateD.clubId, clubId),
isNull(ClubDelegateD.deletedAt),
),
)
.execute();
await this.db
.delete(ClubStudentT)
.where(
Expand Down
5 changes: 5 additions & 0 deletions packages/api/src/feature/club/service/club.public.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,11 +238,16 @@ export default class ClubPublicService {
);
}

const isTargetStudentDelegate = await this.isStudentDelegate(
studentId,
clubId,
);
// 신입 부원 제거
await this.clubStudentTRepository.removeStudentFromClub(
studentId,
clubId,
semesterId,
isTargetStudentDelegate,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,27 @@ export class MemberRegistrationService {
"Not a member registration available",
HttpStatus.BAD_REQUEST,
);

// 해당 동아리가 존재하는지 확인
const club = await this.clubPublicService.getClubByClubId({ clubId });
if (club.length === 0) {
throw new HttpException("The club does not exist.", HttpStatus.NOT_FOUND);
}

// 해당 동아리가 이번 학기에 활동중이어서 신청이 가능한 지 확인
const clubExistedSemesters =
await this.clubPublicService.getClubsExistedSemesters({ clubId });
const isClubOperatingThisSemester = clubExistedSemesters.some(
semester => semester.id === semesterId,
);

if (!isClubOperatingThisSemester) {
throw new HttpException(
"The club is not operating in the current semester.",
HttpStatus.BAD_REQUEST,
);
}

// 이미 해당 동아리에 해당 학생의 반려되지 않은 신청이 존재하는지 확인하기
const isAlreadyApplied =
await this.memberRegistrationRepository.getMemberClubRegistrationExceptRejected(
Expand Down
45 changes: 36 additions & 9 deletions packages/web/src/common/components/Modal/AgreeModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const TextButtonContainer = styled.div`
gap: 16px;
`;

const ResponsiveTypograpy = styled(Typography)`
const ResponsiveTypography = styled(Typography)`
font-size: 16px;
line-height: 24px;
font-weight: 400;
Expand All @@ -48,7 +48,8 @@ const ResponsiveTypograpy = styled(Typography)`
`;

const StyledOl = styled.ol`
margin-top: 0px;
margin: 0px;
margin-top: 8px;
padding-left: 16px;
`;
const AgreementModal: React.FC<AgreementModalProps> = ({
Expand Down Expand Up @@ -80,11 +81,11 @@ const AgreementModal: React.FC<AgreementModalProps> = ({
return (
<Modal isOpen={isOpen} onClose={() => {}}>
<StyledModalContainer>
<ResponsiveTypograpy>
<ResponsiveTypography>
KAIST 학부 동아리연합회의 회원이 되기 위해서는 아래 개인정보 수집 및
이용 약관과 제3자 제공 약관에 동의해야 합니다. 동의하지 않을 경우
Clubs 서비스 사용에 제약이 있을 수 있습니다.
</ResponsiveTypograpy>
</ResponsiveTypography>
<Card gap={16} padding="16px" outline>
<Toggle
label={
Expand All @@ -95,20 +96,20 @@ const AgreementModal: React.FC<AgreementModalProps> = ({
>
<Typography fs={isMobile ? 14 : 16} lh={isMobile ? 24 : 28}>
KAIST 학부 동아리연합회는 개인정보보호법 제15조의 규정에 따라
개인정보를 수집,이용합니다.
개인정보를 수집, 이용합니다.
<StyledOl>
<li>
개인정보의 수집,이용 목적 : KAIST 학부 동아리연합회 회원 정보
관리, 사무 처리 및 활동인증서 발급을 위한 기록 보유
</li>
<li>
수집하려는 개인정보의 항목 : 성명, KAIST 학번, 소속 학과, 소속
동아리
동아리, KAIST 이메일 주소
</li>
<li>
개인정보의 보유 및 이용 기간 : 영구 (KAIST 학부 동아리연합회
회원인 기간 및 활동확인서 발급에 필요한 기초 기록의 보유를
위함입니다.)
위한 기간)
</li>
<li>
귀하는 개인정보의 수집,이용의 동의를 거부할 수 있으며, 동의를
Expand All @@ -122,7 +123,9 @@ const AgreementModal: React.FC<AgreementModalProps> = ({
<Card gap={16} padding="16px" outline>
<Toggle
label={
<Typography fs={isMobile ? 14 : 16}>제3자 이용 약관 </Typography>
<Typography fs={isMobile ? 14 : 16}>
개인정보 제3자 제공 약관
</Typography>
}
>
<Typography fs={isMobile ? 14 : 16} lh={isMobile ? 24 : 28}>
Expand All @@ -136,7 +139,7 @@ const AgreementModal: React.FC<AgreementModalProps> = ({
</li>
<li>
제공하는 개인정보의 항목 : 성명, KAIST 학번, 소속 학과, 소속
동아리
동아리, KAIST 이메일 주소
</li>
<li>
개인정보를 제공받는 자의 개인정보 보유 및 이용 기간 : 영구
Expand All @@ -149,6 +152,30 @@ const AgreementModal: React.FC<AgreementModalProps> = ({
없습니다.
</li>
</StyledOl>
<StyledOl>
<li>
개인정보를 제공받는 자 : KAIST 학부 총학생회 및
중앙선거관리위원회를 포함한 KAIST 학부 총학생회 산하 기구
</li>
<li>
개인정보를 제공받는 자의 개인정보 이용 목적 : 본회 총선거 시행
및 KAIST 학부 총학생회 사무 처리
</li>
<li>
제공하는 개인정보의 항목 : 성명, KAIST 학번, 소속 학과, KAIST
이메일 주소
</li>
<li>
개인정보를 제공받는 자의 개인정보 보유 및 이용 기간 : 영구
(KAIST 학부 총학생회 사무처리 및 사무기록 보존에 필요한
기간입니다.)
</li>
<li>
귀하는 개인정보의 제3자 제공의 동의를 거부할 수 있으며, 동의를
거부하는 경우 KAIST 학부 동아리연합회의 회원이 되실 수
없습니다.
</li>
</StyledOl>
</Typography>
</Toggle>
</Card>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export const useGetClubDetail = (club_id: string) =>
switch (status) {
case 200:
case 304:
return apiClb002.responseBodyMap[200].parse(data);
// return apiClb002.responseBodyMap[200].parse(data);
return data;
default:
throw new UnexpectedAPIResponseError();
}
Expand Down
Loading

0 comments on commit 4260e79

Please sign in to comment.