Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impl Entity API (MEE013~027) #1298

Merged
merged 18 commits into from
Feb 17, 2025
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: impl mee023 to 027
ChaeyeonAhn committed Jan 20, 2025
commit 5e58089ad446030f4c5ed8e840de0007967f0e7f
26 changes: 25 additions & 1 deletion packages/api/src/feature/meeting/entity/entity.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import {
Body,
Controller,
// Get, Query,
Get,
// Query,
Param,
Put,
UsePipes,
@@ -13,6 +14,11 @@ import apiMee021, {
ApiMee021ResponseOk,
} from "@sparcs-clubs/interface/api/meeting/apiMee021";

import apiMee027, {
ApiMee027RequestParam,
ApiMee027ResponseOk,
} from "@sparcs-clubs/interface/api/meeting/apiMee027";

import { ZodPipe } from "@sparcs-clubs/api/common/pipe/zod-pipe";
import { Executive } from "@sparcs-clubs/api/common/util/decorators/method-decorator";
import { GetExecutive } from "@sparcs-clubs/api/common/util/decorators/param-decorator";
@@ -42,4 +48,22 @@ export default class EntityController {

return result;
}

@Executive()
@Get(
"/executive/meetings/meeting/:meetingId/agendas/agenda/:agendaId/entities",
)
@UsePipes(new ZodPipe(apiMee027))
async getExecutiveMeetingAgendaEntities(
@GetExecutive() user: GetExecutive,
@Param() { meetingId, agendaId }: ApiMee027RequestParam,
): Promise<ApiMee027ResponseOk> {
const result = await this.entityService.getExecutiveMeetingAgendaEntities(
user.executiveId,
meetingId,
agendaId,
);

return result;
}
}
263 changes: 262 additions & 1 deletion packages/api/src/feature/meeting/entity/entity.repository.ts
Original file line number Diff line number Diff line change
@@ -4,7 +4,17 @@ import {
Injectable,
} from "@nestjs/common";

import { and, count, eq, isNull, max, or, sql } from "drizzle-orm";
import {
and,
count,
countDistinct,
eq,
inArray,
isNull,
max,
or,
sql,
} from "drizzle-orm";
import { MySql2Database } from "drizzle-orm/mysql2";

import logger from "@sparcs-clubs/api/common/util/logger";
@@ -570,4 +580,255 @@ export class EntityRepository {

return isDeleteVoteSuccess;
}

async deleteMeetingAgendaVoteForUser(
userId: number,
meetingId: number,
agendaId: number,
voteId: number,
) {
const isDeleteVoteSuccess = await this.db.transaction(async tx => {
const [deleteFromVoteResult] = await tx
.update(MeetingVoteResult)
.set({ deletedAt: sql<Date>`Now()` })
.where(
and(
eq(MeetingVoteResult.id, voteId),
eq(MeetingVoteResult.id, userId),
),
);

if (deleteFromVoteResult.affectedRows !== 1) {
logger.debug(
"[EntityRepository] Failed to soft delete meeting agenda vote result for user.",
);
return false;
}

logger.debug(
`[EntityRepository] Soft deleted meeting agenda vote result for user: ${meetingId}, ${agendaId}, ${voteId}, ${userId}`,
);

return deleteFromVoteResult;
});

return isDeleteVoteSuccess;
}

async getMeetingAgendaVote(
userId: number,
meetingId: number,
agendaId: number,
voteId: number,
) {
const isGetVoteSuccess = await this.db
.select({
title: MeetingAgendaVote.title,
description: MeetingAgendaVote.description,
})
.from(MeetingAgendaVote)
.where(
and(
eq(MeetingAgendaVote.id, voteId),
isNull(MeetingAgendaVote.deletedAt),
),
);

if (isGetVoteSuccess.length !== 1) {
logger.debug("[EntityRepository] Failed to retrieve one vote from table");
return false;
}

const title = isGetVoteSuccess[0]?.title;
const description = isGetVoteSuccess[0]?.description;

const choices = await this.db
.select({
id: MeetingVoteChoice.id,
choice: MeetingVoteChoice.choice,
})
.from(MeetingVoteChoice)
.where(
and(
eq(MeetingVoteChoice.voteId, voteId),
isNull(MeetingVoteChoice.deletedAt),
),
);

return { title, description, choices };
}

async getMeetingAgendaVoteWithResults(
userId: number,
meetingId: number,
agendaId: number,
voteId: number,
) {
const isGetVoteSuccess = await this.db
.select({
title: MeetingAgendaVote.title,
description: MeetingAgendaVote.description,
})
.from(MeetingAgendaVote)
.where(
and(
eq(MeetingAgendaVote.id, voteId),
isNull(MeetingAgendaVote.deletedAt),
),
);

if (isGetVoteSuccess.length !== 1) {
logger.debug("[EntityRepository] Failed to retrieve one vote from table");
return false;
}

const title = isGetVoteSuccess[0]?.title;
const description = isGetVoteSuccess[0]?.description;

const choices = await this.db
.select({
id: MeetingVoteChoice.id,
choice: MeetingVoteChoice.choice,
})
.from(MeetingVoteChoice)
.where(
and(
eq(MeetingVoteChoice.voteId, voteId),
isNull(MeetingVoteChoice.deletedAt),
),
);

const getChoiceForUser = await this.db
.select({
id: MeetingVoteResult.choiceId,
})
.from(MeetingVoteResult)
.where(
and(
eq(MeetingVoteResult.userId, userId),
eq(MeetingVoteResult.voteId, voteId),
isNull(MeetingVoteResult.deletedAt),
),
);

const choiceId = getChoiceForUser[0]?.id;

// if getChoiceForUser.length === 0: user cancelled vote.

return { title, description, choices, choiceId };
}

async getMeetingAgendaVoteFinal(
userId: number,
meetingId: number,
agendaId: number,
voteId: number,
) {
const isGetVoteSuccess = await this.db
.select({
title: MeetingAgendaVote.title,
description: MeetingAgendaVote.description,
})
.from(MeetingAgendaVote)
.where(
and(
eq(MeetingAgendaVote.id, voteId),
isNull(MeetingAgendaVote.deletedAt),
),
);

if (isGetVoteSuccess.length !== 1) {
logger.debug("[EntityRepository] Failed to retrieve one vote from table");
return false;
}

const title = isGetVoteSuccess[0]?.title;
const description = isGetVoteSuccess[0]?.description;

const getChoices = await this.db
.select({
id: MeetingVoteChoice.id,
choice: MeetingVoteChoice.choice,
})
.from(MeetingVoteChoice)
.where(
and(
eq(MeetingVoteChoice.voteId, voteId),
isNull(MeetingVoteChoice.deletedAt),
),
);

const choiceArray = getChoices.map(e => e.id);

const results = await this.db
.select({
id: MeetingVoteResult.choiceId,
votes: countDistinct(MeetingVoteResult.userId),
})
.from(MeetingVoteResult)
.where(
and(
inArray(MeetingVoteResult.choiceId, choiceArray),
isNull(MeetingVoteResult.deletedAt),
),
)
.groupBy(MeetingVoteResult.choiceId); // choiceId 별 그룹화

return { title, description, results };
}

async getMeetingAgendaEntities(
userId: number,
meetingId: number,
agendaId: number,
) {
const getContents = await this.db
.select({
meetingAgendaEntityTypeEnum: MeetingMapping.meetingAgendaEntityType,
content: MeetingAgendaContent.content,
position: MeetingMapping.meetingAgendaEntityPosition,
})
.from(MeetingMapping)
.innerJoin(
MeetingAgendaContent,
eq(MeetingAgendaContent.id, MeetingMapping.meetingAgendaContentId),
)
.where(
and(
eq(MeetingMapping.meetingId, meetingId),
eq(MeetingMapping.meetingAgendaId, agendaId),
eq(MeetingMapping.meetingAgendaEntityType, 1),
isNull(MeetingMapping.deletedAt),
),
);

const getVotes = await this.db
.select({
meetingAgendaEntityTypeEnum: MeetingMapping.meetingAgendaEntityType,
title: MeetingAgendaVote.title,
description: MeetingAgendaVote.description,
position: MeetingMapping.meetingAgendaEntityPosition,
})
.from(MeetingMapping)
.innerJoin(
MeetingAgendaVote,
eq(MeetingAgendaVote.id, MeetingMapping.meetingAgendaVoteId),
)
.where(
and(
eq(MeetingMapping.meetingId, meetingId),
eq(MeetingMapping.meetingAgendaId, agendaId),
eq(MeetingMapping.meetingAgendaEntityType, 2),
isNull(MeetingMapping.deletedAt),
),
);

const mergedArray = [...getVotes, ...getContents].sort(
(a, b) => a.position - b.position,
);

const resultArray = mergedArray.map(({ position: _, ...rest }) => rest);

return resultArray;
}
}
27 changes: 27 additions & 0 deletions packages/api/src/feature/meeting/entity/entity.service.ts
Original file line number Diff line number Diff line change
@@ -41,4 +41,31 @@ export class EntityService {
}
return result;
}

async getExecutiveMeetingAgendaEntities(
userId: number,
meetingId: number,
agendaId: number,
) {
const user = await this.userPublicService.getExecutiveById({
id: userId,
});

if (!user) {
throw new HttpException("Executive not found", HttpStatus.NOT_FOUND);
}

const result = await this.entityRepository.getMeetingAgendaEntities(
userId,
meetingId,
agendaId,
);
if (!result) {
throw new HttpException(
"Failed get meeting agenda entities",
HttpStatus.BAD_REQUEST,
);
}
return result;
}
}
Loading