From c8c98be6eec89ab5e5b8f22b24bd78f859f14005 Mon Sep 17 00:00:00 2001 From: Matin Date: Sun, 26 Jan 2025 10:57:20 +0330 Subject: [PATCH] chore: annoucement module with user-group --- src/app.module.ts | 2 + .../DTO/create-announcement.dto.ts | 45 +++++++++++++ .../announcements/announcement.controller.ts | 8 +++ .../announcements/announcement.module.ts | 14 ++++ .../announcements/announcement.service.ts | 6 ++ .../entities/announcement.entity.ts | 33 +++++++++ .../entities/user-announcement.entity.ts | 0 src/modules/users/DTO/user-group.dto.ts | 67 +++++++++++++++++++ .../users/entities/user-group.entity.ts | 22 ++++++ src/modules/users/entities/user.entity.ts | 6 +- .../users/providers/user-group.repository.ts | 12 ++++ src/modules/users/providers/users.service.ts | 23 ++++++- src/modules/users/users.module.ts | 6 +- 13 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 src/modules/announcements/DTO/create-announcement.dto.ts create mode 100644 src/modules/announcements/announcement.controller.ts create mode 100644 src/modules/announcements/announcement.module.ts create mode 100644 src/modules/announcements/announcement.service.ts create mode 100644 src/modules/announcements/entities/announcement.entity.ts create mode 100644 src/modules/announcements/entities/user-announcement.entity.ts create mode 100644 src/modules/users/DTO/user-group.dto.ts create mode 100644 src/modules/users/entities/user-group.entity.ts create mode 100644 src/modules/users/providers/user-group.repository.ts diff --git a/src/app.module.ts b/src/app.module.ts index 19f2f08..6ba8d2e 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -8,6 +8,7 @@ import { cacheConfig } from "./configs/cache.config"; import { rateLimitConfig } from "./configs/rateLimit.config"; import { databaseConfigs } from "./configs/typeorm.config"; import { HTTPLogger } from "./core/middlewares/logger.middleware"; +import { AnnoucementsModule } from "./modules/announcements/announcement.module"; import { AuthModule } from "./modules/auth/auth.module"; import { DanakServicesModule } from "./modules/danak-services/danak-services.module"; import { TicketsModule } from "./modules/tickets/tickets.module"; @@ -23,6 +24,7 @@ import { UsersModule } from "./modules/users/users.module"; TicketsModule, AuthModule, DanakServicesModule, + AnnoucementsModule, ], controllers: [], providers: [], diff --git a/src/modules/announcements/DTO/create-announcement.dto.ts b/src/modules/announcements/DTO/create-announcement.dto.ts new file mode 100644 index 0000000..0a8dc34 --- /dev/null +++ b/src/modules/announcements/DTO/create-announcement.dto.ts @@ -0,0 +1,45 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { ArrayMinSize, IsArray, IsBoolean, IsNotEmpty, IsOptional, IsString, IsUUID, MinLength } from "class-validator"; + +export class CreateAnnouncementDto { + @IsNotEmpty({ message: "عنوان نمی‌تواند خالی باشد" }) + @IsString({ message: "عنوان باید رشته باشد" }) + @ApiProperty({ + type: String, + description: "Title of the announcement", + example: "اطلاعیه جدید", + }) + title: string; + + @IsNotEmpty({ message: "متن اطلاعیه نمی‌تواند خالی باشد" }) + @IsString({ message: "متن اطلاعیه باید رشته باشد" }) + @MinLength(10, { message: "متن اطلاعیه باید حداقل ۱۰ کاراکتر باشد" }) + @ApiProperty({ + type: String, + description: "Content of the announcement", + example: "اطلاعیه جدید برای کاربران", + }) + content: string; + + @IsBoolean() + @ApiProperty({ + type: Boolean, + description: "Is this announcement important?", + example: false, + }) + isPublic: boolean; + + @IsOptional() + @IsArray() + @ArrayMinSize(1, { message: "حداقل یک گروه باید انتخاب شود" }) + @IsUUID("4", { + each: true, + message: "شناسه هر گروه باید یک UUID معتبر نسخه ۴ باشد", + }) + @ApiProperty({ + type: [String], + description: "Array of group ids", + example: ["f7b1b3b1-1b1b-4b1b-8b1b-1b1b1b1b1b1b"], + }) + groupIds: string[]; +} diff --git a/src/modules/announcements/announcement.controller.ts b/src/modules/announcements/announcement.controller.ts new file mode 100644 index 0000000..ef9c438 --- /dev/null +++ b/src/modules/announcements/announcement.controller.ts @@ -0,0 +1,8 @@ +import { Controller } from "@nestjs/common"; +import { ApiTags } from "@nestjs/swagger"; + +@ApiTags("Announcements") +@Controller("announcements") +export class AnnouncementController { + constructor() {} +} diff --git a/src/modules/announcements/announcement.module.ts b/src/modules/announcements/announcement.module.ts new file mode 100644 index 0000000..cb648ed --- /dev/null +++ b/src/modules/announcements/announcement.module.ts @@ -0,0 +1,14 @@ +import { Module } from "@nestjs/common"; +import { TypeOrmModule } from "@nestjs/typeorm"; + +import { AnnouncementController } from "./announcement.controller"; +import { AnnouncementService } from "./announcement.service"; +import { Announcement } from "./entities/announcement.entity"; + +@Module({ + imports: [TypeOrmModule.forFeature([Announcement])], + providers: [AnnouncementService], + controllers: [AnnouncementController], + exports: [TypeOrmModule], +}) +export class AnnoucementsModule {} diff --git a/src/modules/announcements/announcement.service.ts b/src/modules/announcements/announcement.service.ts new file mode 100644 index 0000000..7888eb6 --- /dev/null +++ b/src/modules/announcements/announcement.service.ts @@ -0,0 +1,6 @@ +import { Injectable } from "@nestjs/common"; + +@Injectable() +export class AnnouncementService { + constructor() {} +} diff --git a/src/modules/announcements/entities/announcement.entity.ts b/src/modules/announcements/entities/announcement.entity.ts new file mode 100644 index 0000000..a9b465a --- /dev/null +++ b/src/modules/announcements/entities/announcement.entity.ts @@ -0,0 +1,33 @@ +import { Column, Entity, Index, ManyToMany } from "typeorm"; + +import { BaseEntity } from "../../../common/entities/base.entity"; +import { UserGroup } from "../../users/entities/user-group.entity"; + +@Entity() +@Index(["isPublic", "createdAt"]) +export class Announcement extends BaseEntity { + @Column({ type: "varchar", length: 100 }) + title: string; + + @Column({ type: "text" }) + content: string; + + @Column({ type: "timestamp", default: null, nullable: true }) + publishAt: Date; + + //TODO: Add service here + //TODO: Add customer here + + @Column({ type: "boolean", default: false }) + isImportant: boolean; + + @ManyToMany(() => UserGroup, (group) => group.announcements) + targetGroups: UserGroup[]; + + @Column({ default: false }) + isPublic: boolean; + + isPublicAnnouncement(): boolean { + return this.isPublic; + } +} diff --git a/src/modules/announcements/entities/user-announcement.entity.ts b/src/modules/announcements/entities/user-announcement.entity.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/modules/users/DTO/user-group.dto.ts b/src/modules/users/DTO/user-group.dto.ts new file mode 100644 index 0000000..e42cc7b --- /dev/null +++ b/src/modules/users/DTO/user-group.dto.ts @@ -0,0 +1,67 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { ArrayMinSize, IsArray, IsNotEmpty, IsOptional, IsString, IsUUID, MaxLength } from "class-validator"; + +export class CreateUserGroupDto { + @IsNotEmpty({ message: "نام گروه نمی‌تواند خالی باشد" }) + @IsString({ message: "نام گروه باید رشته باشد" }) + @MaxLength(100, { message: "نام گروه نمی‌تواند بیش از ۱۰۰ کاراکتر باشد" }) + @ApiProperty({ + example: "گروه کاربران ویژه", + description: "نام گروه", + }) + name: string; + + @IsOptional() + @IsString({ message: "توضیحات باید رشته باشد" }) + @ApiProperty({ + example: "گروه ویژه برای کاربران منتخب", + description: "توضیحات اختیاری گروه", + required: false, + }) + description?: string; + + @IsNotEmpty() + @IsArray({ message: "لیست کاربران باید آرایه باشد" }) + @ArrayMinSize(0, { message: "لیست کاربران نمی‌تواند کوچکتر از ۰ باشد" }) + @IsUUID("4", { each: true, message: "شناسه هر کاربر باید UUID معتبر باشد" }) + @ApiProperty({ + type: [String], + example: ["d3d29a70-5c0a-4aae-a587-8a212c3c8f26"], + description: "آرایه ای از شناسه های کاربران", + required: false, + }) + userIds: string[]; +} + +export class UpdateUserGroupDto { + @IsOptional() + @IsNotEmpty({ message: "نام گروه نمی‌تواند خالی باشد" }) + @IsString({ message: "نام گروه باید رشته باشد" }) + @MaxLength(100, { message: "نام گروه نمی‌تواند بیش از ۱۰۰ کاراکتر باشد" }) + @ApiProperty({ + example: "گروه کاربران بروزرسانی شده", + description: "نام جدید گروه", + required: false, + }) + name?: string; + + @IsOptional() + @IsString({ message: "توضیحات باید رشته باشد" }) + @ApiProperty({ + example: "توضیحات جدید برای گروه", + description: "توضیحات اختیاری گروه", + required: false, + }) + description?: string; + + @IsOptional() + @IsArray({ message: "لیست کاربران باید آرایه باشد" }) + @IsUUID("4", { each: true, message: "شناسه هر کاربر باید UUID معتبر باشد" }) + @ApiProperty({ + type: [String], + example: ["d3d29a70-5c0a-4aae-a587-8a212c3c8f26"], + description: "آرایه ای از شناسه های کاربران", + required: false, + }) + userIds?: string[]; +} diff --git a/src/modules/users/entities/user-group.entity.ts b/src/modules/users/entities/user-group.entity.ts new file mode 100644 index 0000000..eb1826d --- /dev/null +++ b/src/modules/users/entities/user-group.entity.ts @@ -0,0 +1,22 @@ +import { Column, Entity, JoinTable, ManyToMany } from "typeorm"; + +import { User } from "./user.entity"; +import { BaseEntity } from "../../../common/entities/base.entity"; +import { Announcement } from "../../announcements/entities/announcement.entity"; + +@Entity() +export class UserGroup extends BaseEntity { + @Column({ type: "varchar", length: 100 }) + name: string; + + @Column({ nullable: true }) + description?: string; + + @ManyToMany(() => User, (user) => user.groups) + @JoinTable() + users: User[]; + + @ManyToMany(() => Announcement, (announcement) => announcement.targetGroups) + @JoinTable() + announcements: Announcement[]; +} diff --git a/src/modules/users/entities/user.entity.ts b/src/modules/users/entities/user.entity.ts index 53be104..fafcd14 100644 --- a/src/modules/users/entities/user.entity.ts +++ b/src/modules/users/entities/user.entity.ts @@ -1,6 +1,7 @@ -import { Column, Entity, ManyToOne, OneToMany } from "typeorm"; +import { Column, Entity, ManyToMany, ManyToOne, OneToMany } from "typeorm"; import { Role } from "./role.entity"; +import { UserGroup } from "./user-group.entity"; import { BaseEntity } from "../../../common/entities/base.entity"; import { TicketMessage } from "../../tickets/entities/ticket-message.entity"; import { Ticket } from "../../tickets/entities/ticket.entity"; @@ -39,4 +40,7 @@ export class User extends BaseEntity { @OneToMany(() => TicketMessage, (ticketMessage) => ticketMessage.author) ticketMessage: TicketMessage[]; + + @ManyToMany(() => UserGroup, (group) => group.users) + groups: UserGroup[]; } diff --git a/src/modules/users/providers/user-group.repository.ts b/src/modules/users/providers/user-group.repository.ts new file mode 100644 index 0000000..5133b69 --- /dev/null +++ b/src/modules/users/providers/user-group.repository.ts @@ -0,0 +1,12 @@ +import { Injectable } from "@nestjs/common"; +import { InjectRepository } from "@nestjs/typeorm"; +import { Repository } from "typeorm"; + +import { UserGroup } from "../entities/user-group.entity"; + +@Injectable() +export class UserGroupRepository extends Repository { + constructor(@InjectRepository(UserGroup) userGroupRepository: Repository) { + super(userGroupRepository.target, userGroupRepository.manager, userGroupRepository.queryRunner); + } +} diff --git a/src/modules/users/providers/users.service.ts b/src/modules/users/providers/users.service.ts index d6a3bae..6fe69a0 100644 --- a/src/modules/users/providers/users.service.ts +++ b/src/modules/users/providers/users.service.ts @@ -1,17 +1,18 @@ import { BadRequestException, Injectable, Logger } from "@nestjs/common"; import slugify from "slugify"; -import { Not } from "typeorm"; +import { In, Not } from "typeorm"; import { RoleRepository } from "./roles.repository"; -import { UserRepository } from "../repositories/users.repository"; // import { roles } from "../../../../database/seeders/role.seeder"; import { CommonMessage, UserMessage } from "../../../common/enums/message.enum"; import { CompleteRegistrationDto } from "../../auth/DTO/complete-register.dto"; import { CheckValidityDTO } from "../DTO/check-validity.dto"; import { UpdateProfileDto } from "../DTO/update-profile.dto"; +import { CreateUserGroupDto } from "../DTO/user-group.dto"; import { User } from "../entities/user.entity"; import { RoleEnum } from "../enums/role.enum"; import { ValidityType } from "../enums/validity-type.enum"; +import { UserRepository } from "../repositories/users.repository"; @Injectable() export class UsersService { @@ -113,4 +114,22 @@ export class UsersService { // this.logger.log({ createdRoles }); // return createdRoles; // } + + async createUserGroup(createUserGroupDto: CreateUserGroupDto) { + const users = await this.userRepository.find({ where: { id: In(createUserGroupDto.userIds) } }); + const userGroup = this.userRepository.create({ + ...createUserGroupDto, + ...users, + }); + return await this.userRepository.save(userGroup); + } + + ///****************************************************** */ + // async roleSeeder() { + // const countRole = await this.roleRepository.count(); + // if (countRole > 0) return; + // const createdRoles = await this.roleRepository.insert(roles); + // this.logger.log({ createdRoles }); + // return createdRoles; + // } } diff --git a/src/modules/users/users.module.ts b/src/modules/users/users.module.ts index e23b216..4b06f11 100644 --- a/src/modules/users/users.module.ts +++ b/src/modules/users/users.module.ts @@ -2,15 +2,17 @@ import { Module, OnApplicationBootstrap } from "@nestjs/common"; import { TypeOrmModule } from "@nestjs/typeorm"; import { Role } from "./entities/role.entity"; +import { UserGroup } from "./entities/user-group.entity"; import { User } from "./entities/user.entity"; import { RoleRepository } from "./providers/roles.repository"; +import { UserGroupRepository } from "./providers/user-group.repository"; import { UsersService } from "./providers/users.service"; import { UserRepository } from "./repositories/users.repository"; import { UsersController } from "./users.controller"; @Module({ - imports: [TypeOrmModule.forFeature([User, Role])], - providers: [UsersService, UserRepository, RoleRepository], + imports: [TypeOrmModule.forFeature([User, Role, UserGroup])], + providers: [UsersService, UserRepository, RoleRepository, UserGroupRepository], controllers: [UsersController], exports: [UsersService, TypeOrmModule], })