Skip to content

Commit

Permalink
Merge pull request #58 from se-gam/feat/notice
Browse files Browse the repository at this point in the history
feat: 공지사항 API 추가
  • Loading branch information
therealjamesjung authored Jan 5, 2025
2 parents aaa1306 + 2a63d56 commit 40c677a
Show file tree
Hide file tree
Showing 12 changed files with 339 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/workflows/dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ jobs:
git pull origin dev
yarn install
yarn build
yarn db:save:dev
dotenv -e .dev.env -- pm2 restart main
11 changes: 11 additions & 0 deletions prisma/migrations/20250105131123_add_notice/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- CreateTable
CREATE TABLE "notice" (
"id" INTEGER NOT NULL,
"title" TEXT NOT NULL,
"content" TEXT NOT NULL,
"is_popup" BOOLEAN NOT NULL DEFAULT false,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"deleted_at" TIMESTAMP(3),

CONSTRAINT "notice_pkey" PRIMARY KEY ("id")
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- AlterTable
CREATE SEQUENCE notice_id_seq;
ALTER TABLE "notice" ALTER COLUMN "id" SET DEFAULT nextval('notice_id_seq');
ALTER SEQUENCE notice_id_seq OWNED BY "notice"."id";
10 changes: 10 additions & 0 deletions prisma/schema/notice/notice.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
model Notice {
id Int @id @default(autoincrement())
title String
content String
isPopup Boolean @default(false) @map("is_popup")
createdAt DateTime @default(now()) @map("created_at")
deletedAt DateTime? @map("deleted_at")
@@map("notice")
}
4 changes: 3 additions & 1 deletion src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { AttendanceModule } from 'src/attendance/attendance.module';
import { AuthModule } from 'src/auth/auth.module';
import { CommonModule } from 'src/common/common.module';
import { LoggerMiddleware } from 'src/common/middlewares/logger.middleware';
import { GodokModule } from 'src/godok/godok.module';
import { NoticeModule } from 'src/notice/notice.module';
import { RestaurantModule } from 'src/restaurant/restaurant.module';
import { StudyroomModule } from 'src/studyroom/studyroom.module';
import { UserModule } from 'src/user/user.module';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { configModule } from './modules/config.module';
import { GodokModule } from 'src/godok/godok.module';

@Module({
imports: [
Expand All @@ -21,6 +22,7 @@ import { GodokModule } from 'src/godok/godok.module';
UserModule,
RestaurantModule,
GodokModule,
NoticeModule,
],
controllers: [AppController],
providers: [AppService],
Expand Down
55 changes: 55 additions & 0 deletions src/notice/dto/notice.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { ApiProperty } from '@nestjs/swagger';
import { Notice } from '@prisma/client';

export class NoticeDto {
@ApiProperty({
description: '공지사항 id',
example: 1,
type: Number,
})
id!: number;

@ApiProperty({
description: '공지사항 제목',
example: '공지사항 제목',
type: String,
})
title!: string;

@ApiProperty({
description: '공지사항 내용',
example: '공지사항 내용',
type: String,
})
content!: string;

@ApiProperty({
description: '팝업 공지사항 여부',
example: false,
type: Boolean,
})
isPopup!: boolean;

@ApiProperty({
description: '공지사항 생성 일자',
type: Date,
})
createdAt!: Date;

@ApiProperty({
description: '공지사항 삭제 일자',
type: Date,
})
deletedAt?: Date;

static from(notice: Notice): NoticeDto {
return {
id: notice.id,
title: notice.title,
content: notice.content,
isPopup: notice.isPopup,
createdAt: notice.createdAt,
deletedAt: notice.deletedAt,
};
}
}
86 changes: 86 additions & 0 deletions src/notice/notice.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
Version,
} from '@nestjs/common';
import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import { NoticeDto } from './dto/notice.dto';
import { NoticeService } from './notice.service';
import { CreateUpdateNoticePayload } from './payload/create-update-notice.payload';

@ApiTags('공지사항 API')
@Controller('notice')
export class NoticeController {
constructor(private readonly noticeService: NoticeService) {}

@Version('1')
@ApiOkResponse({ type: [NoticeDto] })
@ApiOperation({
summary: '공지사항 조회 API',
description: '공지사항을 조회합니다.',
})
@Get('')
async getNotice(): Promise<NoticeDto[]> {
return this.noticeService.getNotice();
}

@Version('1')
@ApiOperation({
summary: '팝업 공지사항 조회 API',
description: '팝업 공지사항을 조회합니다.',
})
@Get('popup')
async getPopupNotice(): Promise<NoticeDto> {
return this.noticeService.getPopupNotice();
}

@Version('1')
@ApiOperation({
summary: '팝업 공지사항 등록 API',
description: '팝업 공지사항을 등록합니다.',
})
@Post('popup/:id')
async createPopupNotice(@Param('id') id: number): Promise<void> {
return this.noticeService.createPopupNotice(id);
}

@Version('1')
@ApiOperation({
summary: '공지사항 생성 API',
description: '공지사항을 생성합니다.',
})
@Post('')
async createNotice(
@Body() payload: CreateUpdateNoticePayload,
): Promise<void> {
return this.noticeService.createNotice(payload);
}

@Version('1')
@ApiOperation({
summary: '공지사항 수정 API',
description: '공지사항을 수정합니다.',
})
@Put(':id')
async updateNotice(
@Param('id') id: number,
@Body() payload: CreateUpdateNoticePayload,
): Promise<void> {
return this.noticeService.updateNotice(id, payload);
}

@Version('1')
@ApiOperation({
summary: '공지사항 삭제 API',
description: '공지사항을 삭제합니다.',
})
@Delete(':id')
async deleteNotice(@Param('id') id: number): Promise<void> {
return this.noticeService.deleteNotice(id);
}
}
10 changes: 10 additions & 0 deletions src/notice/notice.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { NoticeController } from './notice.controller';
import { NoticeRepository } from './notice.repository';
import { NoticeService } from './notice.service';

@Module({
controllers: [NoticeController],
providers: [NoticeService, NoticeRepository],
})
export class NoticeModule {}
79 changes: 79 additions & 0 deletions src/notice/notice.repository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from 'src/common/services/prisma.service';
import { CreateUpdateNoticePayload } from './payload/create-update-notice.payload';
import { Notice } from './types/notice.type';

@Injectable()
export class NoticeRepository {
constructor(private readonly prisma: PrismaService) {}

async getNotice(): Promise<Notice[]> {
return this.prisma.notice.findMany({
where: {
deletedAt: null,
},
orderBy: {
createdAt: 'desc',
},
});
}

async getPopupNotice(): Promise<Notice | null> {
return this.prisma.notice.findFirst({
where: {
isPopup: true,
deletedAt: null,
},
orderBy: {
createdAt: 'desc',
},
});
}

async getNoticeById(id: number): Promise<Notice | null> {
return this.prisma.notice.findUnique({
where: { id, deletedAt: null },
});
}

async createNotice(payload: CreateUpdateNoticePayload): Promise<void> {
await this.prisma.notice.create({
data: {
title: payload.title,
content: payload.content,
},
});
}

async updateNotice(
id: number,
payload: CreateUpdateNoticePayload,
): Promise<void> {
await this.prisma.notice.update({
where: { id },
data: {
title: payload.title,
content: payload.content,
},
});
}

async deleteNotice(id: number): Promise<void> {
await this.prisma.notice.update({
where: { id },
data: { deletedAt: new Date() },
});
}

async createPopupNotice(id: number): Promise<void> {
await this.prisma.notice.updateMany({
where: { isPopup: true },
data: { isPopup: false },
});

await this.prisma.notice.update({
where: { id },
data: { isPopup: true },
});
}
}
54 changes: 54 additions & 0 deletions src/notice/notice.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Injectable, NotFoundException } from '@nestjs/common';
import { NoticeDto } from './dto/notice.dto';
import { NoticeRepository } from './notice.repository';
import { CreateUpdateNoticePayload } from './payload/create-update-notice.payload';

@Injectable()
export class NoticeService {
constructor(private readonly noticeRepository: NoticeRepository) {}

async getNotice(): Promise<NoticeDto[]> {
const notices = await this.noticeRepository.getNotice();

return notices.map(NoticeDto.from);
}

async getPopupNotice(): Promise<NoticeDto> {
const notice = await this.noticeRepository.getPopupNotice();

if (!notice) throw new NotFoundException('팝업 공지사항이 없습니다.');

return NoticeDto.from(notice);
}

async createNotice(payload: CreateUpdateNoticePayload): Promise<void> {
return this.noticeRepository.createNotice(payload);
}

async updateNotice(
id: number,
payload: CreateUpdateNoticePayload,
): Promise<void> {
const notice = await this.noticeRepository.getNoticeById(id);

if (!notice) throw new NotFoundException('공지사항을 찾을 수 없습니다.');

return this.noticeRepository.updateNotice(id, payload);
}

async deleteNotice(id: number): Promise<void> {
const notice = await this.noticeRepository.getNoticeById(id);

if (!notice) throw new NotFoundException('공지사항을 찾을 수 없습니다.');

return this.noticeRepository.deleteNotice(id);
}

async createPopupNotice(id: number): Promise<void> {
const notice = await this.noticeRepository.getNoticeById(id);

if (!notice) throw new NotFoundException('공지사항을 찾을 수 없습니다.');

return this.noticeRepository.createPopupNotice(id);
}
}
18 changes: 18 additions & 0 deletions src/notice/payload/create-update-notice.payload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';

export class CreateUpdateNoticePayload {
@ApiProperty({
description: '공지사항 제목',
example: '공지사항 제목',
})
@IsString()
title!: string;

@ApiProperty({
description: '공지사항 내용',
example: '공지사항 내용',
})
@IsString()
content!: string;
}
8 changes: 8 additions & 0 deletions src/notice/types/notice.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export type Notice = {
id: number;
title: string;
content: string;
isPopup: boolean;
createdAt: Date;
deletedAt: Date | null;
};

0 comments on commit 40c677a

Please sign in to comment.