diff --git a/src/common/enums/message.enum.ts b/src/common/enums/message.enum.ts index a15774f..2698177 100755 --- a/src/common/enums/message.enum.ts +++ b/src/common/enums/message.enum.ts @@ -212,6 +212,13 @@ export const enum AnnouncementMessage { CREATED_AND_SENT_TO_USERS_AFTER_PUBLISH = "اطلاعیه با موفقیت ایجاد شد و پس از انتشار به کاربران ارسال خواهد شد", CREATED_AND_SENT_TO_USERS = "اطلاعیه با موفقیت ایجاد شد و به کاربران ارسال شد", PUBLISH_AT_MUST_BE_FUTURE_DATE = "تاریخ انتشار باید در آینده باشد", + UPDATED_SUCCESSFULLY = "اطلاعیه با موفقیت به‌روزرسانی شد", + DELETED_SUCCESSFULLY = "اطلاعیه با موفقیت حذف شد", + CANNOT_DELETE_PUBLISHED = "نمی‌توان اطلاعیه منتشر شده را حذف کرد", + CANNOT_UPDATE_PUBLISHED = "نمی‌توان اطلاعیه منتشر شده را ویرایش کرد", + PUBLISH_AT_CANNOT_BE_IN_PAST = "تاریخ انتشار نمی‌تواند در گذشته باشد", + USER_ANNOUNCEMENTS_DELETED = "اطلاعیه‌های کاربر با موفقیت حذف شدند", + QUEUE_JOBS_DELETED = "وظایف صف با موفقیت حذف شدند", } export const enum CriticismMessage { diff --git a/src/modules/announcements/DTO/update-announcement.dto.ts b/src/modules/announcements/DTO/update-announcement.dto.ts new file mode 100644 index 0000000..028e7cb --- /dev/null +++ b/src/modules/announcements/DTO/update-announcement.dto.ts @@ -0,0 +1,26 @@ +import { IsDate, IsOptional, IsString, IsUUID } from "class-validator"; + +export class UpdateAnnouncementDto { + @IsOptional() + @IsString() + title?: string; + + @IsOptional() + @IsString() + content?: string; + + @IsOptional() + @IsDate() + publishAt?: Date; + + @IsOptional() + @IsUUID() + serviceId?: string; + + @IsOptional() + @IsUUID(undefined, { each: true }) + userIds?: string[]; + + @IsOptional() + isImportant?: boolean; +} diff --git a/src/modules/announcements/announcement.controller.ts b/src/modules/announcements/announcement.controller.ts index 1f3065d..21517da 100755 --- a/src/modules/announcements/announcement.controller.ts +++ b/src/modules/announcements/announcement.controller.ts @@ -1,8 +1,9 @@ -import { Body, Controller, Get, Param, Post, Query } from "@nestjs/common"; -import { ApiProperty, ApiTags } from "@nestjs/swagger"; +import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from "@nestjs/common"; +import { ApiProperty } from "@nestjs/swagger"; import { CreateAnnouncementDto } from "./DTO/create-announcement.dto"; import { SearchAnnouncementQueryDto } from "./DTO/search-announcement-query.dto"; +import { UpdateAnnouncementDto } from "./DTO/update-announcement.dto"; import { AnnouncementService } from "./providers/announcement.service"; import { AuthGuards } from "../../common/decorators/auth-guard.decorator"; import { PermissionsDec } from "../../common/decorators/permission.decorator"; @@ -10,52 +11,48 @@ import { UserDec } from "../../common/decorators/user.decorator"; import { ParamDto } from "../../common/DTO/param.dto"; import { PermissionEnum } from "../users/enums/permission.enum"; -@ApiTags("Announcements") @Controller("announcements") +@AuthGuards() export class AnnouncementController { constructor(private readonly announcementService: AnnouncementService) {} - @AuthGuards() @PermissionsDec(PermissionEnum.ANNOUNCEMENTS) - @ApiProperty({ description: "Create a new announcement ===> login as admin" }) + @ApiProperty({ description: "Create a new announcement (admin route)" }) @Post() - async create(@Body() createAnnouncementDto: CreateAnnouncementDto) { - return await this.announcementService.createAnnouncement(createAnnouncementDto); + create(@Body() createAnnouncementDto: CreateAnnouncementDto) { + return this.announcementService.createAnnouncement(createAnnouncementDto); } - @AuthGuards() - @ApiProperty({ description: "Get all announcements ===> login as admin" }) + @PermissionsDec(PermissionEnum.ANNOUNCEMENTS) + @ApiProperty({ description: "Update an announcement (admin route)" }) + @Patch(":id") + update(@Param() paramDto: ParamDto, @Body() updateAnnouncementDto: UpdateAnnouncementDto) { + return this.announcementService.updateAnnouncement(paramDto.id, updateAnnouncementDto); + } + + @PermissionsDec(PermissionEnum.ANNOUNCEMENTS) + @ApiProperty({ description: "Delete an announcement (admin route)" }) + @Delete(":id") + delete(@Param() paramDto: ParamDto) { + return this.announcementService.deleteAnnouncement(paramDto.id); + } + + @ApiProperty({ description: "Get all announcements (admin route)" }) @PermissionsDec(PermissionEnum.ANNOUNCEMENTS) @Get() - async getAnnouncements(@Query() queryDto: SearchAnnouncementQueryDto) { - return await this.announcementService.getAnnouncements(queryDto); + getAnnouncements(@Query() queryDto: SearchAnnouncementQueryDto) { + return this.announcementService.getAnnouncements(queryDto); } - @AuthGuards() - @ApiProperty({ description: "Get all announcements for user ===> login as user" }) + @ApiProperty({ description: "Get all announcements for user " }) @Get("user") - async getAnnouncementsByUser(@Query() queryDto: SearchAnnouncementQueryDto, @UserDec("id") userId: string) { - return await this.announcementService.getAnnouncementsByUser(queryDto, userId); + getAnnouncementsByUser(@Query() queryDto: SearchAnnouncementQueryDto, @UserDec("id") userId: string) { + return this.announcementService.getAnnouncementsByUser(queryDto, userId); } - @AuthGuards() - @ApiProperty({ description: "Get one announcements with id" }) + @ApiProperty({ description: "Get one announcements with id " }) @Get(":id") getAnnouncementById(@Param() paramDto: ParamDto, @UserDec("id") userId: string) { return this.announcementService.getAnnouncementById(paramDto.id, userId); } - - // @AuthGuards() - // @ApiProperty({ description: "Get all public announcements" }) - // @Get("public") - // async getPublicAnnouncements() { - // return await this.announcementService.getPublicAnnouncements(); - // } - - // @AuthGuards() - // @ApiProperty({ description: "Get all public announcements" }) - // @Get("/service/:serviceId") - // async getAnnouncementsByService(@Query() queryDto: SearchAnnouncementQueryDto, @Param("serviceId") serviceId: string) { - // return await this.announcementService.getAnnouncementByService(queryDto, serviceId); - // } } diff --git a/src/modules/announcements/providers/announcement.service.ts b/src/modules/announcements/providers/announcement.service.ts index 764c2ba..7c154b4 100755 --- a/src/modules/announcements/providers/announcement.service.ts +++ b/src/modules/announcements/providers/announcement.service.ts @@ -9,6 +9,7 @@ import { PaginationUtils } from "../../utils/providers/pagination.utils"; import { ANNOUNCEMENT } from "../constants"; import { CreateAnnouncementDto } from "../DTO/create-announcement.dto"; import { SearchAnnouncementQueryDto } from "../DTO/search-announcement-query.dto"; +import { UpdateAnnouncementDto } from "../DTO/update-announcement.dto"; import { AnnouncementRepository } from "../repositories/announcement.repository"; import { UserAnnouncementRepository } from "../repositories/user-announcement.repository"; @@ -185,4 +186,75 @@ export class AnnouncementService { async countUserAnnouncements(userId: string) { return this.userAnnouncementRepository.countBy({ user: { id: userId }, isRead: false }); } + //************************************ */ + + async updateAnnouncement(id: string, updateAnnouncementDto: UpdateAnnouncementDto) { + const announcement = await this.announcementRepository.findOneBy({ id }); + if (!announcement) throw new BadRequestException(AnnouncementMessage.NOT_FOUND); + + const { serviceId, userIds } = updateAnnouncementDto; + let isPublic = true; + + if (serviceId) { + announcement.targetService = await this.danakServicesService.findServiceById(serviceId); + isPublic = false; + } + + if (userIds && userIds.length > 0) isPublic = false; + + if (updateAnnouncementDto.publishAt) { + if (dayjs(updateAnnouncementDto.publishAt).isBefore(dayjs().subtract(1, "day"))) { + throw new BadRequestException(AnnouncementMessage.PUBLISH_AT_MUST_BE_FUTURE_DATE); + } + } + + await this.announcementRepository.save({ ...announcement, ...updateAnnouncementDto }); + + if (updateAnnouncementDto.publishAt) { + const jobs = await this.announcementQueue.getJobs(); + const existingJob = jobs.find((job) => job.data.announcementId === id); + + // + if (existingJob) await existingJob.remove(); + + await this.announcementQueue.add( + ANNOUNCEMENT.ANNOUNCEMENT_SEND_JOB_NAME, + { + announcementId: announcement.id, + isPublic, + serviceId, + userIds, + }, + { + delay: dayjs(announcement.publishAt).diff(dayjs(), "millisecond"), + attempts: ANNOUNCEMENT.ANNOUNCEMENT_SEND_JOB_ATTEMPTS, + backoff: { type: "exponential", delay: ANNOUNCEMENT.ANNOUNCEMENT_SEND_JOB_BACKOFF }, + }, + ); + } + + return { + message: AnnouncementMessage.UPDATED_SUCCESSFULLY, + announcement, + }; + } + + //************************************ */ + + async deleteAnnouncement(id: string) { + const announcement = await this.announcementRepository.findOneBy({ id }); + if (!announcement) throw new BadRequestException(AnnouncementMessage.NOT_FOUND); + + const jobs = await this.announcementQueue.getJobs(); + const existingJob = jobs.find((job) => job.data.announcementId === id); + if (existingJob) await existingJob.remove(); + + await this.userAnnouncementRepository.delete({ announcement: { id } }); + + await this.announcementRepository.delete(id); + + return { + message: AnnouncementMessage.DELETED_SUCCESSFULLY, + }; + } } diff --git a/src/modules/sliders/sliders.controller.ts b/src/modules/sliders/sliders.controller.ts index 369e9fd..b5f6571 100644 --- a/src/modules/sliders/sliders.controller.ts +++ b/src/modules/sliders/sliders.controller.ts @@ -23,28 +23,28 @@ export class SlidersController { return this.slidersService.create(createSliderDto); } - @ApiOperation({ summary: "Get all sliders" }) + @ApiOperation({ summary: "Get all sliders (admin route)" }) @AdminRoute() @Get() getSlidersList(@Query() queryDto: PaginationDto) { return this.slidersService.getSlidersList(queryDto); } - @ApiOperation({ summary: "Get a slider by id" }) + @ApiOperation({ summary: "Get a slider by id (admin route)" }) @AdminRoute() @Get(":id") getSliderById(@Param() paramDto: ParamDto) { return this.slidersService.getSliderById(paramDto.id); } - @ApiOperation({ summary: "Update a slider" }) + @ApiOperation({ summary: "Update a slider (admin route)" }) @AdminRoute() @Patch(":id") update(@Param() paramDto: ParamDto, @Body() updateSliderDto: UpdateSliderDto) { return this.slidersService.update(paramDto.id, updateSliderDto); } - @ApiOperation({ summary: "Delete a slider" }) + @ApiOperation({ summary: "Delete a slider (admin route)" }) @AdminRoute() @Delete(":id") remove(@Param() paramDto: ParamDto) {