chore: refactor the announcement module and service and add queue

This commit is contained in:
mahyargdz
2025-03-04 10:51:16 +03:30
parent e234f98998
commit bf991914e4
9 changed files with 265 additions and 105 deletions
@@ -1,7 +1,13 @@
import { InjectQueue } from "@nestjs/bullmq";
import { BadRequestException, Injectable } from "@nestjs/common";
import { Queue } from "bullmq";
import dayjs from "dayjs";
import { AnnouncementMessage } from "../../../common/enums/message.enum";
import { DanakServicesService } from "../../danak-services/providers/danak-services.service";
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 { AnnouncementRepository } from "../repositories/announcement.repository";
import { UserAnnouncementRepository } from "../repositories/user-announcement.repository";
@@ -9,33 +15,78 @@ import { UserAnnouncementRepository } from "../repositories/user-announcement.re
@Injectable()
export class AnnouncementService {
constructor(
@InjectQueue(ANNOUNCEMENT.ANNOUNCEMENT_QUEUE_NAME) private readonly announcementQueue: Queue,
private readonly announcementRepository: AnnouncementRepository,
private readonly userAnnouncementRepository: UserAnnouncementRepository,
private readonly danakServicesService: DanakServicesService,
) {}
/***************************************** */
async getAllAnnouncements(queryDto: SearchAnnouncementQueryDto, userId: string) {
async createAnnouncement(createAnnouncementDto: CreateAnnouncementDto) {
const { serviceId, userIds } = createAnnouncementDto;
let isPublic = true;
const announcement = this.announcementRepository.create({ ...createAnnouncementDto });
if (!announcement.publishAt) {
announcement.publishAt = new Date();
}
if (dayjs(announcement.publishAt).isBefore(dayjs().subtract(1, "day")))
throw new BadRequestException(AnnouncementMessage.PUBLISH_AT_MUST_BE_FUTURE_DATE);
if (serviceId) {
announcement.targetService = await this.danakServicesService.findServiceById(serviceId);
isPublic = false;
}
if (userIds && userIds.length > 0) isPublic = false;
await this.announcementRepository.save(announcement);
await this.announcementQueue.add(
ANNOUNCEMENT.ANNOUNCEMENT_SEND_JOB_NAME,
{
announcementId: announcement.id,
isPublic,
serviceId,
userIds: userIds,
},
{
delay: dayjs(announcement.publishAt).diff(dayjs(), "millisecond"),
attempts: ANNOUNCEMENT.ANNOUNCEMENT_SEND_JOB_ATTEMPTS,
backoff: ANNOUNCEMENT.ANNOUNCEMENT_SEND_JOB_BACKOFF,
},
);
return {
message: AnnouncementMessage.CREATED_AND_SENT_TO_USERS_AFTER_PUBLISH,
announcement,
};
}
/***************************************** */
async getAnnouncements(queryDto: SearchAnnouncementQueryDto) {
const { limit, skip } = PaginationUtils(queryDto);
const queryBuilder = this.announcementRepository
.createQueryBuilder("announcement")
.leftJoin("announcement.service", "danak_service")
.addSelect(["danak_service.id", "danak_service.name"])
.leftJoin("announcement.targetGroups", "group")
.addSelect(["group.id", "group.name"])
.leftJoinAndSelect("announcement.userAnnouncements", "userAnnouncements", "userAnnouncements.userId = :userId", { userId });
.leftJoin("announcement.targetService", "danak_service")
.addSelect(["danak_service.id", "danak_service.name"]);
// .leftJoinAndSelect("announcement.userAnnouncements", "userAnnouncements", "userAnnouncements.userId = :userId", { userId });
if (queryDto.since) {
queryBuilder.andWhere("announcement.createdAt >= :since", { since: queryDto.since });
queryBuilder.andWhere("announcement.createdAt >= :since", { since: dayjs(queryDto.since).toDate() });
}
if (queryDto.publishAt) {
queryBuilder.andWhere("announcement.publishAt >= :publishAt", { publishAt: queryDto.publishAt });
queryBuilder.andWhere("announcement.publishAt >= :publishAt", { publishAt: dayjs(queryDto.publishAt).toDate() });
}
if (queryDto.q) {
queryBuilder.andWhere("announcement.title LIKE :q", { q: `%${queryDto.q}%` });
queryBuilder.andWhere("announcement.title ILIKE :q", { q: `%${queryDto.q}%` });
}
queryBuilder.orderBy("announcement.createdAt", "DESC");
@@ -47,89 +98,60 @@ export class AnnouncementService {
/***************************************** */
async getAllAnnouncementsByUser(queryDto: SearchAnnouncementQueryDto, userId: string) {
async getAnnouncementsByUser(queryDto: SearchAnnouncementQueryDto, userId: string) {
const { limit, skip } = PaginationUtils(queryDto);
const queryBuilder = this.announcementRepository
.createQueryBuilder("announcement")
.leftJoin("announcement.service", "service")
const queryBuilder = this.userAnnouncementRepository
.createQueryBuilder("userAnnouncement")
.leftJoin("userAnnouncement.announcement", "announcement")
.addSelect([
"announcement.id",
"announcement.title",
"announcement.isImportant",
"announcement.createdAt",
"announcement.publishAt",
"announcement.content",
])
.leftJoin("userAnnouncement.user", "user")
.leftJoin("announcement.targetService", "service")
.addSelect(["service.id", "service.name"])
.leftJoin("announcement.users", "announcementUser")
.leftJoin("announcement.targetGroups", "group")
.addSelect(["group.id", "group.name"])
.leftJoin("group.users", "groupUser")
.leftJoin("service.subscriptionPlans", "subscriptionPlan")
.leftJoin(
"UserSubscription",
"userSubscription",
"userSubscription.planId = subscriptionPlan.id AND userSubscription.userId = :userId",
{ userId },
)
.leftJoinAndSelect("announcement.userAnnouncements", "userAnnouncements", "userAnnouncements.userId = :userId", { userId })
.where("announcement.isPublic = :isPublic", { isPublic: true })
.orWhere("groupUser.id = :userId", { userId })
.orWhere("userSubscription.userId = :userId", { userId })
.orWhere("announcementUser.id = :userId", { userId });
.where("user.id = :userId", { userId });
if (queryDto.since) {
queryBuilder.andWhere("announcement.createdAt >= :since", { since: queryDto.since });
queryBuilder.andWhere("announcement.createdAt >= :since", { since: dayjs(queryDto.since).toDate() });
}
if (queryDto.publishAt) {
queryBuilder.andWhere("announcement.publishAt >= :publishAt", { publishAt: queryDto.publishAt });
queryBuilder.andWhere("announcement.publishAt >= :publishAt", { publishAt: dayjs(queryDto.publishAt).toDate() });
}
if (queryDto.q) {
queryBuilder.andWhere("announcement.title LIKE :q", { q: `%${queryDto.q}%` });
queryBuilder.andWhere("announcement.title ILIKE :q", { q: `%${queryDto.q}%` });
}
queryBuilder.orderBy("announcement.createdAt", "DESC");
const rawResults = await queryBuilder.getRawMany();
const [, count] = await queryBuilder.skip(skip).take(limit).getManyAndCount();
//TODO: fix later
const announcements = rawResults.map((raw) => ({
id: raw.announcement_id,
createdAt: raw.announcement_createdAt,
updatedAt: raw.announcement_updatedAt,
title: raw.announcement_title,
content: raw.announcement_content,
publishAt: raw.announcement_publishAt,
service: {
id: raw.service_id,
name: raw.service_name,
},
isImportant: raw.announcement_isImportant,
targetGroups: raw.group_id ? [{ id: raw.group_id, name: raw.group_name }] : [],
isPublic: raw.announcement_isPublic,
isRead: raw.userAnnouncements_isRead ? true : false,
}));
const [announcements, count] = await queryBuilder.skip(skip).take(limit).getManyAndCount();
return { announcements, count, paginate: true };
}
/***************************************** */
async getOneAnnouncement(id: string, userId: string) {
async getAnnouncementById(id: string, userId: string) {
const announcement = await this.announcementRepository.findOneBy({ id });
if (!announcement) throw new BadRequestException(AnnouncementMessage.NOT_FOUND);
const userAnnouncement = this.userAnnouncementRepository.create({
announcement: { id: announcement.id },
user: { id: userId },
isRead: true,
});
const userAnnouncement = await this.userAnnouncementRepository.findOneBy({ announcement: { id }, user: { id: userId } });
if (!userAnnouncement) throw new BadRequestException(AnnouncementMessage.NOT_FOUND);
userAnnouncement.isRead = true;
await this.userAnnouncementRepository.save(userAnnouncement);
return { announcement };
}
/***************************************** */
async getPublicAnnouncements() {
const announcements = await this.announcementRepository.find({ where: { isPublic: true }, relations: ["service", "targetGroups"] });
return { announcements };
}
/***************************************** */
async getAnnouncementByService(queryDto: SearchAnnouncementQueryDto, serviceId: string) {
const { limit, skip } = PaginationUtils(queryDto);