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

1229 impl mee007 009 011 #1232

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
85 changes: 70 additions & 15 deletions packages/api/src/feature/meeting/agenda/agenda.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,33 @@ import apiMee006, {
ApiMee006ResponseCreated,
} from "@sparcs-clubs/interface/api/meeting/apiMee006";

import apiMee007, {
ApiMee007RequestParam,
ApiMee007ResponseCreated,
} from "@sparcs-clubs/interface/api/meeting/apiMee007";

import apiMee008, {
ApiMee008RequestBody,
ApiMee008RequestParam,
ApiMee008ResponseOk,
} from "@sparcs-clubs/interface/api/meeting/apiMee008";

import apiMee009, {
ApiMee009RequestBody,
ApiMee009RequestParam,
ApiMee009ResponseCreated,
} from "@sparcs-clubs/interface/api/meeting/apiMee009";

import apiMee010, {
ApiMee010RequestParam,
ApiMee010ResponseOk,
} from "@sparcs-clubs/interface/api/meeting/apiMee010";

import apiMee011, {
ApiMee011RequestParam,
ApiMee011ResponseCreated,
} from "@sparcs-clubs/interface/api/meeting/apiMee011";

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";
Expand All @@ -40,33 +56,58 @@ export default class AgendaController {
@UsePipes(new ZodPipe(apiMee006))
async postExecutiveMeetingAgenda(
@GetExecutive() user: GetExecutive,
@Param() { meetingId }: ApiMee006RequestParam,
@Body() { meetingEnumId, description, title }: ApiMee006RequestBody,
@Param() param: ApiMee006RequestParam,
@Body() body: ApiMee006RequestBody,
): Promise<ApiMee006ResponseCreated> {
const result = await this.meetingService.postExecutiveMeetingAgenda(
user.executiveId,
meetingId,
meetingEnumId,
description,
title,
param.meetingId,
body.meetingEnumId,
body.description,
body.title,
);
return result;
}

@Post("/executive/meetings/meeting/:meetingId/agendas")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❗ Post가 아니라 Get인것 같아요. 혹시 테스트중에는 문제가 없었나요?

@UsePipes(new ZodPipe(apiMee007))
async getExecutiveMeetingAgendas(
@Param() param: ApiMee007RequestParam,
): Promise<ApiMee007ResponseCreated> {
const result = await this.meetingService.getMeetingAgendas(param);
return result;
}

@Executive()
@Patch("/executive/meetings/meeting/:meetingId/agendas/agenda/:agendaId")
@UsePipes(new ZodPipe(apiMee008))
async patchExecutiveMeetingAgenda(
@GetExecutive() user: GetExecutive,
@Param() { agendaId }: ApiMee008RequestParam,
@Body() { agendaEnumId, description, title }: ApiMee008RequestBody,
@Param() param: ApiMee008RequestParam,
@Body() body: ApiMee008RequestBody,
): Promise<ApiMee008ResponseOk> {
const result = await this.meetingService.patchExecutiveMeetingAgenda(
user.executiveId,
agendaId,
agendaEnumId,
description,
title,
param.agendaId,
body.agendaEnumId,
body.description,
body.title,
);
return result;
}

@Executive()
@Patch("/executive/meetings/meeting/:meetingId/agendas")
@UsePipes(new ZodPipe(apiMee009))
async patchExecutiveMeetingAgendasOrder(
@GetExecutive() user: GetExecutive,
@Param() param: ApiMee009RequestParam,
@Body() body: ApiMee009RequestBody,
): Promise<ApiMee009ResponseCreated> {
const result = await this.meetingService.patchExecutiveMeetingAgendasOrder(
user.executiveId,
param,
body,
);
return result;
}
Expand All @@ -76,12 +117,26 @@ export default class AgendaController {
@UsePipes(new ZodPipe(apiMee010))
async deleteExecutiveMeetingAgenda(
@GetExecutive() user: GetExecutive,
@Param() { meetingId, agendaId }: ApiMee010RequestParam,
@Param() param: ApiMee010RequestParam,
): Promise<ApiMee010ResponseOk> {
const result = await this.meetingService.deleteExecutiveMeetingAgenda(
user.executiveId,
meetingId,
agendaId,
param.meetingId,
param.agendaId,
);
return result;
}

@Executive()
@Delete("/executive/meetings/meeting/:meetingId/agendas/agenda/:agendaId")
@UsePipes(new ZodPipe(apiMee011))
async postExecutiveMeetingAgendaImport(
@GetExecutive() user: GetExecutive,
@Param() param: ApiMee011RequestParam,
): Promise<ApiMee011ResponseCreated> {
const result = await this.meetingService.postExecutiveMeetingAgendaImport(
user.executiveId,
param,
Comment on lines +130 to +139
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❗ 컨벤션에 맞지 않는것 같아요. 확인해주세요!

);
return result;
}
Expand Down
64 changes: 64 additions & 0 deletions packages/api/src/feature/meeting/agenda/agenda.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,26 @@ import {

import { ApiMee006ResponseCreated } from "@sparcs-clubs/interface/api/meeting/apiMee006";

import {
ApiMee007RequestParam,
ApiMee007ResponseCreated,
} from "@sparcs-clubs/interface/api/meeting/apiMee007";

import { ApiMee008ResponseOk } from "@sparcs-clubs/interface/api/meeting/apiMee008";

import {
ApiMee009RequestBody,
ApiMee009RequestParam,
ApiMee009ResponseCreated,
} from "@sparcs-clubs/interface/api/meeting/apiMee009";

import { ApiMee010ResponseOk } from "@sparcs-clubs/interface/api/meeting/apiMee010";

import {
ApiMee011RequestParam,
ApiMee011ResponseCreated,
} from "@sparcs-clubs/interface/api/meeting/apiMee011";

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

import { MeetingRepository } from "../meeting.repository";
Expand Down Expand Up @@ -47,6 +63,13 @@ export class AgendaService {
return {};
}

async getMeetingAgendas(
param: ApiMee007RequestParam,
): Promise<ApiMee007ResponseCreated> {
const res = await this.meetingRepository.getMeetingAgendas(param.meetingId);
return res;
}

async patchExecutiveMeetingAgenda(
executiveId: number,
agendaId: number,
Expand All @@ -72,6 +95,27 @@ export class AgendaService {
return {};
}

async patchExecutiveMeetingAgendasOrder(
executiveId: number,
param: ApiMee009RequestParam,
body: ApiMee009RequestBody,
): Promise<ApiMee009ResponseCreated> {
const user = await this.userPublicService.getExecutiveById({
id: executiveId,
});

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

await this.meetingRepository.updateMeetingAgendasOrder(
param.meetingId,
body.agendaIdList,
);

return {};
}

async deleteExecutiveMeetingAgenda(
executiveId: number,
meetingId: number,
Expand All @@ -92,4 +136,24 @@ export class AgendaService {

return {};
}

async postExecutiveMeetingAgendaImport(
executiveId: number,
param: ApiMee011RequestParam,
): Promise<ApiMee011ResponseCreated> {
const user = await this.userPublicService.getExecutiveById({
id: executiveId,
});

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

await this.meetingRepository.addMeetingAgendaMapping(
param.meetingId,
param.agendaId,
);

return {};
}
}
129 changes: 129 additions & 0 deletions packages/api/src/feature/meeting/meeting.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,40 @@ export class MeetingRepository {
return isInsertAgendaAndMappingSuccess;
}

async getMeetingAgendas(meetingId: number) {
const rawAgendas = await this.db
.select({
agendaId: MeetingAgenda.id,
agendaEnumId: MeetingAgenda.MeetingAgendaEnum,
title: MeetingAgenda.title,
description: MeetingAgenda.description,
})
.from(MeetingMapping)
.innerJoin(
MeetingAgenda,
eq(MeetingMapping.meetingAgendaId, MeetingAgenda.id),
)
.where(eq(MeetingMapping.meetingId, meetingId));

if (rawAgendas.length === 0) {
logger.debug(
`[MeetingRepository] No agendas found for meeting ID: ${meetingId}`,
);
return null;
}

const response = {
agendas: rawAgendas.map(agenda => ({
agendaId: agenda.agendaId,
agendaEnumId: agenda.agendaEnumId,
title: agenda.title,
description: agenda.description,
})),
};

return response;
}

async updateMeetingAgenda(
agendaId: number,
agendaEnumId: number,
Expand Down Expand Up @@ -486,4 +520,99 @@ export class MeetingRepository {

return meetingAgendaMappingDeleteResult;
}

async updateMeetingAgendasOrder(meetingId: number, agendaIdList: number[]) {
if (agendaIdList.length === 0) {
logger.debug(
`[MeetingRepository] No agenda IDs provided for meeting ID: ${meetingId}`,
);
return null; // No need to update if the list is empty
}

await this.db.transaction(async trx => {
const updatePromises = agendaIdList.map((agendaId, index) =>
// Create a promise for each agenda update
trx
.update(MeetingMapping)
.set({ meetingAgendaPosition: index }) // Set the new position based on the index
.where(
and(
eq(MeetingMapping.meetingAgendaId, agendaId),
eq(MeetingMapping.meetingId, meetingId),
),
)
.then(result => {
Comment on lines +538 to +544
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ 여기선 soft deletion을 필터링할 필요가 없나요?

// Log a warning if no row was updated
if (result[0].affectedRows === 0) {
logger.warn(
`[MeetingRepository] No MeetingMapping found for meeting ID: ${meetingId} and agenda ID: ${agendaId}`,
);
}
}),
);

// Wait for all updates to complete concurrently
await Promise.all(updatePromises);
});

logger.info(
`[MeetingRepository] Successfully updated agenda positions for meeting ID: ${meetingId}`,
);
return { success: true };
}

async addMeetingAgendaMapping(meetingId: number, agendaId: number) {
// Check if a mapping already exists to avoid duplicates
const existingMapping = await this.db
.select()
.from(MeetingMapping)
.where(
and(
eq(MeetingMapping.meetingId, meetingId),
eq(MeetingMapping.meetingAgendaId, agendaId),
),
)
.execute();

if (existingMapping.length > 0) {
logger.debug(
`[MeetingRepository] Mapping already exists for meetingId: ${meetingId} and agendaId: ${agendaId}`,
);
return { success: false, message: "Mapping already exists" };
}

// Get the current maximum meetingAgendaPosition for the given meetingId
const [maxPositionResult] = await this.db
.select({
maxPosition: sql<number>`MAX(${MeetingMapping.meetingAgendaPosition})`,
})
.from(MeetingMapping)
.where(eq(MeetingMapping.meetingId, meetingId))
.execute();

const newPosition = (maxPositionResult?.maxPosition || 0) + 1;

// Insert the new mapping into the MeetingMapping table
const result = await this.db
.insert(MeetingMapping)
.values({
meetingId,
meetingAgendaId: agendaId,
meetingAgendaPosition: newPosition, // Set position to the next available value
meetingAgendaEntityType: 1, // Entity type is fixed to 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💊 EntityType이 무엇인지 Enum을 통해 정의하고 주석이 있으면 이해하기 편할거 같아요

})
.execute();

// Check if insertion was successful
if (result[0].affectedRows === 1) {
logger.info(
`[MeetingRepository] Successfully added mapping for meetingId: ${meetingId} and agendaId: ${agendaId} with position: ${newPosition}`,
);
return { success: true };
}
logger.error(
`[MeetingRepository] Failed to add mapping for meetingId: ${meetingId} and agendaId: ${agendaId}`,
);
return { success: false, message: "Failed to add mapping" };
}
}
Loading