chore: update referral logic
This commit is contained in:
@@ -348,6 +348,9 @@ export const enum ContactUsMessage {
|
||||
TITLE_IS_REQUIRED = "عنوان مورد نیاز است",
|
||||
TITLE_STRING = "عنوان باید یک رشته باشد",
|
||||
NOT_FOUND = "پیامی با این ایدی یافت نشد",
|
||||
BUSINESS_NAME_IS_REQUIRED = "نام شرکت مورد نیاز است",
|
||||
BUSINESS_NAME_STRING = "نام شرکت باید یک رشته باشد",
|
||||
BUSINESS_NAME_LENGTH = "نام شرکت باید بین ۲ تا ۱۰۰ کاراکتر باشد",
|
||||
}
|
||||
|
||||
export const enum NotificationMessage {
|
||||
@@ -630,7 +633,7 @@ export const enum BlogMessage {
|
||||
TITLE_STRING = "عنوان باید یک رشته باشد",
|
||||
DESCRIPTION_REQUIRED = "توضیحات الزامی است",
|
||||
DESCRIPTION_STRING = "توضیحات باید یک رشته باشد",
|
||||
TITLE_MAX_LENGTH = "حداکثر طول عنوان باید ۱۰۰ کاراکتر باشد",
|
||||
TITLE_MAX_LENGTH = "حداکثر طول عنوان باید ۲۵۵ کاراکتر باشد",
|
||||
DESCRIPTION_MAX_LENGTH = "حداکثر طول توضیحات باید ۲۵۵ کاراکتر باشد",
|
||||
ICON_URL_REQUIRED = "آدرس آیکون الزامی است",
|
||||
ICON_URL_INVALID = "آدرس آیکون نامعتبر است",
|
||||
@@ -643,7 +646,7 @@ export const enum BlogMessage {
|
||||
BLOG_NOT_FOUND = "بلاگ مورد نظر یافت نشد",
|
||||
PREVIEW_CONTENT_REQUIRED = "محتوای پیشنمایش الزامی است",
|
||||
PREVIEW_CONTENT_STRING = "محتوای پیشنمایش باید یک رشته باشد",
|
||||
PREVIEW_CONTENT_MAX_LENGTH = "حداکثر طول محتوای پیشنمایش باید ۱۵۰ کاراکتر باشد",
|
||||
PREVIEW_CONTENT_MAX_LENGTH = "حداکثر طول محتوای پیشنمایش باید 600 کاراکتر باشد",
|
||||
CONTENT_REQUIRED = "محتوای بلاگ الزامی است",
|
||||
CONTENT_STRING = "محتوای بلاگ باید یک رشته باشد",
|
||||
CONTENT_MAX_LENGTH = "حداکثر طول محتوای بلاگ باید ۶۰۰ کاراکتر باشد",
|
||||
@@ -674,7 +677,7 @@ export const enum BlogMessage {
|
||||
SEARCH_QUERY_STRING = "رشته جستجو باید یک رشته باشد",
|
||||
IS_PINNED_SHOULD_BE_A_BOOLEAN = "وضعیت سنجاق بودن باید یک بولین باشد",
|
||||
SLUG_REQUIRED = "اسلاگ بلاگ الزامی است",
|
||||
SLUG_MAX_LENGTH = "حداکثر طول اسلاگ بلاگ باید ۱۵۰ کاراکتر باشد",
|
||||
SLUG_MAX_LENGTH = "حداکثر طول اسلاگ بلاگ باید ۲۵۵ کاراکتر باشد",
|
||||
}
|
||||
|
||||
export const enum SliderMessage {
|
||||
|
||||
@@ -13,7 +13,7 @@ export function databaseConfigs(): TypeOrmModuleAsyncOptions {
|
||||
username: configService.getOrThrow<string>("DB_USER"),
|
||||
password: configService.getOrThrow<string>("DB_PASS"),
|
||||
autoLoadEntities: true,
|
||||
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : false,
|
||||
synchronize: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
||||
logging: configService.getOrThrow<string>("NODE_ENV") == "production" ? false : true,
|
||||
migrationsTableName: "typeorm_migrations",
|
||||
migrationsRun: false,
|
||||
|
||||
@@ -67,12 +67,7 @@ export class AuthService {
|
||||
const hashedPassword = await this.passwordService.hashPassword(completeRegistrationDto.password);
|
||||
const user = await this.usersService.createUser(completeRegistrationDto, hashedPassword, queryRunner);
|
||||
|
||||
if (referralCode) {
|
||||
await this.referralsService.useReferralCode({
|
||||
referralCode,
|
||||
userId: user.id,
|
||||
});
|
||||
}
|
||||
if (referralCode) await this.referralsService.useReferralCode({ referralCode, userId: user.id }, queryRunner);
|
||||
|
||||
const tokens = await this.tokensService.generateTokens(user, queryRunner);
|
||||
|
||||
|
||||
@@ -6,25 +6,24 @@ import { BlogMessage } from "../../../common/enums/message.enum";
|
||||
export class CreateBlogDto {
|
||||
@IsNotEmpty({ message: BlogMessage.TITLE_REQUIRED })
|
||||
@IsString({ message: BlogMessage.TITLE_STRING })
|
||||
@MaxLength(150, { message: BlogMessage.TITLE_MAX_LENGTH })
|
||||
@MaxLength(250, { message: BlogMessage.TITLE_MAX_LENGTH })
|
||||
@ApiProperty({ description: "Title of the blog", example: "Blog Title" })
|
||||
title: string;
|
||||
|
||||
@IsNotEmpty({ message: BlogMessage.SLUG_REQUIRED })
|
||||
@IsString({ message: BlogMessage.SLUG_STRING })
|
||||
@MaxLength(150, { message: BlogMessage.SLUG_MAX_LENGTH })
|
||||
@MaxLength(250, { message: BlogMessage.SLUG_MAX_LENGTH })
|
||||
@ApiProperty({ description: "Slug of the blog", example: "blog-title" })
|
||||
slug: string;
|
||||
|
||||
@IsNotEmpty({ message: BlogMessage.PREVIEW_CONTENT_REQUIRED })
|
||||
@IsString({ message: BlogMessage.PREVIEW_CONTENT_STRING })
|
||||
@MaxLength(150, { message: BlogMessage.PREVIEW_CONTENT_MAX_LENGTH })
|
||||
@MaxLength(600, { message: BlogMessage.PREVIEW_CONTENT_MAX_LENGTH })
|
||||
@ApiProperty({ description: "Preview content of the blog", example: "Preview content of the blog" })
|
||||
previewContent: string;
|
||||
|
||||
@IsNotEmpty({ message: BlogMessage.CONTENT_REQUIRED })
|
||||
@IsString({ message: BlogMessage.CONTENT_STRING })
|
||||
@MaxLength(600, { message: BlogMessage.CONTENT_MAX_LENGTH })
|
||||
@ApiProperty({ description: "Content of the blog", example: "Content of the blog" })
|
||||
content: string;
|
||||
|
||||
@@ -36,7 +35,7 @@ export class CreateBlogDto {
|
||||
|
||||
@IsNotEmpty({ message: BlogMessage.META_TITLE_REQUIRED })
|
||||
@IsString({ message: BlogMessage.META_TITLE_STRING })
|
||||
@MaxLength(60, { message: BlogMessage.META_TITLE_MAX_LENGTH })
|
||||
@MaxLength(250, { message: BlogMessage.META_TITLE_MAX_LENGTH })
|
||||
@ApiProperty({ description: "Meta title of the blog", example: "Meta title of the blog" })
|
||||
metaTitle: string;
|
||||
|
||||
|
||||
@@ -20,6 +20,6 @@ export class BlogCategory extends BaseEntity {
|
||||
@OneToMany(() => Blog, (blog) => blog.category)
|
||||
blogs: Blog[];
|
||||
|
||||
@DeleteDateColumn({ type: "timestamptz" })
|
||||
@DeleteDateColumn()
|
||||
deletedAt?: Date;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,6 @@ export class BlogComment extends BaseEntity {
|
||||
@ManyToOne(() => User, (user) => user.blogComments)
|
||||
user: User;
|
||||
|
||||
@ManyToOne(() => Blog, (blog) => blog.comments)
|
||||
@ManyToOne(() => Blog, (blog) => blog.comments, { onDelete: "CASCADE" })
|
||||
blog: Blog;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import { User } from "../../users/entities/user.entity";
|
||||
|
||||
@Entity()
|
||||
export class Blog extends BaseEntity {
|
||||
@Column({ type: "varchar", length: 150, unique: true })
|
||||
@Column({ type: "varchar", length: 250, unique: true })
|
||||
title: string;
|
||||
|
||||
@Column({ type: "varchar", length: 150, unique: true })
|
||||
@Column({ type: "varchar", length: 250, unique: true })
|
||||
slug: string;
|
||||
|
||||
@Column({ type: "text" })
|
||||
@@ -19,13 +19,13 @@ export class Blog extends BaseEntity {
|
||||
@Column({ type: "text" })
|
||||
content: string;
|
||||
|
||||
@Column({ type: "varchar", length: 150, nullable: true })
|
||||
@Column({ type: "varchar", length: 250, nullable: true })
|
||||
imageUrl: string;
|
||||
|
||||
@Column({ type: "varchar", length: 100, nullable: true })
|
||||
@Column({ type: "varchar", length: 250, nullable: true })
|
||||
metaTitle: string;
|
||||
|
||||
@Column({ type: "varchar", length: 160, nullable: true })
|
||||
@Column({ type: "varchar", length: 250, nullable: true })
|
||||
metaDescription: string;
|
||||
|
||||
@Column({ type: "simple-array", nullable: true })
|
||||
@@ -40,15 +40,15 @@ export class Blog extends BaseEntity {
|
||||
@Column({ type: "boolean", default: false })
|
||||
isPinned: boolean;
|
||||
|
||||
@ManyToOne(() => User, (user) => user.blogs)
|
||||
@ManyToOne(() => User, (user) => user.blogs, { onDelete: "CASCADE", nullable: false })
|
||||
author: User;
|
||||
|
||||
@ManyToOne(() => BlogCategory, (category) => category.blogs)
|
||||
@ManyToOne(() => BlogCategory, (category) => category.blogs, { nullable: false })
|
||||
category: BlogCategory;
|
||||
|
||||
@OneToMany(() => BlogComment, (comment) => comment.blog)
|
||||
comments: BlogComment[];
|
||||
|
||||
@DeleteDateColumn({ type: "timestamptz" })
|
||||
@DeleteDateColumn()
|
||||
deletedAt?: Date;
|
||||
}
|
||||
|
||||
@@ -56,9 +56,13 @@ export class BlogsService {
|
||||
//*********************************** */
|
||||
|
||||
async deleteCategory(id: string) {
|
||||
await this.findCategoryById(id);
|
||||
const category = await this.blogCategoriesRepository.findOneBy({ id });
|
||||
if (!category) throw new BadRequestException(BlogMessage.CATEGORY_NOT_FOUND);
|
||||
|
||||
await this.blogCategoriesRepository.softDelete(id);
|
||||
await this.blogCategoriesRepository.softDelete({ id });
|
||||
return {
|
||||
message: CommonMessage.DELETED,
|
||||
};
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
@@ -70,7 +74,10 @@ export class BlogsService {
|
||||
await this.blogCategoriesRepository.save(category);
|
||||
|
||||
//
|
||||
return { message: CommonMessage.UPDATE_SUCCESS, category };
|
||||
return {
|
||||
message: CommonMessage.UPDATE_SUCCESS,
|
||||
category,
|
||||
};
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
@@ -146,21 +153,24 @@ export class BlogsService {
|
||||
//*********************************** */
|
||||
|
||||
async deleteBlog(id: string) {
|
||||
await this.findBlogById(id);
|
||||
const blog = await this.blogsRepository.findOneBy({ id });
|
||||
if (!blog) throw new BadRequestException(BlogMessage.BLOG_NOT_FOUND);
|
||||
|
||||
await this.blogsRepository.softDelete(id);
|
||||
return { message: CommonMessage.DELETED };
|
||||
await this.blogsRepository.softDelete({ id });
|
||||
return {
|
||||
message: CommonMessage.DELETED,
|
||||
};
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
async getBlogById(id: string, isAdmin: boolean = false) {
|
||||
const blog = await this.findBlogById(id);
|
||||
const comments = await this.blogCommentsRepository.findBlogCommentsByBlogId(id);
|
||||
|
||||
if (!isAdmin) {
|
||||
blog.viewCount += 1;
|
||||
await this.blogsRepository.save(blog);
|
||||
}
|
||||
if (isAdmin) return { blog };
|
||||
|
||||
blog.viewCount += 1;
|
||||
await this.blogsRepository.save(blog);
|
||||
const comments = await this.blogCommentsRepository.findBlogCommentsByBlogId(id);
|
||||
|
||||
return { blog, comments };
|
||||
}
|
||||
@@ -170,12 +180,13 @@ export class BlogsService {
|
||||
const blog = await this.blogsRepository.findOneBySlug(slug);
|
||||
if (!blog) throw new BadRequestException(BlogMessage.BLOG_NOT_FOUND);
|
||||
|
||||
if (!isAdmin) {
|
||||
blog.viewCount += 1;
|
||||
await this.blogsRepository.save(blog);
|
||||
}
|
||||
if (!isAdmin) return { blog };
|
||||
|
||||
return { blog };
|
||||
blog.viewCount += 1;
|
||||
await this.blogsRepository.save(blog);
|
||||
const comments = await this.blogCommentsRepository.findBlogCommentsByBlogId(blog.id);
|
||||
|
||||
return { blog, comments };
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
|
||||
@@ -13,73 +13,66 @@ export class BlogsRepository extends Repository<Blog> {
|
||||
}
|
||||
|
||||
async findOneById(id: string): Promise<Blog | null> {
|
||||
return this.findOne({
|
||||
where: { id, deletedAt: IsNull() },
|
||||
relations: {
|
||||
author: true,
|
||||
category: true,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
previewContent: true,
|
||||
metaTitle: true,
|
||||
metaDescription: true,
|
||||
imageUrl: true,
|
||||
slug: true,
|
||||
createdAt: true,
|
||||
content: true,
|
||||
tags: true,
|
||||
viewCount: true,
|
||||
isActive: true,
|
||||
isPinned: true,
|
||||
category: {
|
||||
id: true,
|
||||
title: true,
|
||||
iconUrl: true,
|
||||
description: true,
|
||||
},
|
||||
author: {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
const queryBuilder = this.createQueryBuilder("blog")
|
||||
.leftJoinAndSelect("blog.author", "author")
|
||||
.leftJoinAndSelect("blog.category", "category")
|
||||
.where("blog.id = :id", { id })
|
||||
.andWhere("blog.deletedAt IS NULL")
|
||||
.select([
|
||||
"blog.id",
|
||||
"blog.title",
|
||||
"blog.previewContent",
|
||||
"blog.metaTitle",
|
||||
"blog.metaDescription",
|
||||
"blog.imageUrl",
|
||||
"blog.slug",
|
||||
"blog.createdAt",
|
||||
"blog.content",
|
||||
"blog.tags",
|
||||
"blog.viewCount",
|
||||
"blog.isActive",
|
||||
"blog.isPinned",
|
||||
"category.id",
|
||||
"category.title",
|
||||
"category.iconUrl",
|
||||
"category.description",
|
||||
"author.id",
|
||||
"author.firstName",
|
||||
"author.lastName",
|
||||
]);
|
||||
|
||||
return queryBuilder.getOne();
|
||||
}
|
||||
|
||||
async findOneBySlug(slug: string): Promise<Blog | null> {
|
||||
return this.findOne({
|
||||
where: { slug, deletedAt: IsNull(), isActive: true },
|
||||
relations: {
|
||||
author: true,
|
||||
category: true,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
title: true,
|
||||
previewContent: true,
|
||||
metaTitle: true,
|
||||
metaDescription: true,
|
||||
imageUrl: true,
|
||||
slug: true,
|
||||
createdAt: true,
|
||||
content: true,
|
||||
tags: true,
|
||||
viewCount: true,
|
||||
category: {
|
||||
id: true,
|
||||
title: true,
|
||||
iconUrl: true,
|
||||
description: true,
|
||||
},
|
||||
author: {
|
||||
id: true,
|
||||
firstName: true,
|
||||
lastName: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
const queryBuilder = this.createQueryBuilder("blog")
|
||||
.leftJoinAndSelect("blog.author", "author")
|
||||
.leftJoinAndSelect("blog.category", "category")
|
||||
.where("blog.slug = :slug", { slug })
|
||||
.andWhere("blog.deletedAt IS NULL")
|
||||
.andWhere("blog.isActive = :isActive", { isActive: true })
|
||||
.select([
|
||||
"blog.id",
|
||||
"blog.title",
|
||||
"blog.previewContent",
|
||||
"blog.metaTitle",
|
||||
"blog.metaDescription",
|
||||
"blog.imageUrl",
|
||||
"blog.slug",
|
||||
"blog.createdAt",
|
||||
"blog.content",
|
||||
"blog.tags",
|
||||
"blog.viewCount",
|
||||
"category.id",
|
||||
"category.title",
|
||||
"category.iconUrl",
|
||||
"category.description",
|
||||
"author.id",
|
||||
"author.firstName",
|
||||
"author.lastName",
|
||||
]);
|
||||
|
||||
return queryBuilder.getOne();
|
||||
}
|
||||
|
||||
async fineOneByTitle(title: string): Promise<Blog | null> {
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsEmail, IsNotEmpty, IsString } from "class-validator";
|
||||
import { IsEmail, IsMobilePhone, IsNotEmpty, IsString, Length } from "class-validator";
|
||||
|
||||
import { ContactUsMessage } from "../../../common/enums/message.enum";
|
||||
import { AuthMessage, ContactUsMessage } from "../../../common/enums/message.enum";
|
||||
|
||||
export class CreateContactUsDto {
|
||||
@IsNotEmpty({ message: ContactUsMessage.FULLNAME_IS_REQUIRED })
|
||||
@IsString({ message: ContactUsMessage.FULLNAME_STRING })
|
||||
@ApiProperty({ description: "full name of person need to create contact us", example: "متین جمشیدی" })
|
||||
@ApiProperty({ description: "Full name of the person creating the contact request", example: "mahyar gdz" })
|
||||
fullName: string;
|
||||
|
||||
@IsNotEmpty({ message: ContactUsMessage.FULLNAME_STRING })
|
||||
@IsEmail()
|
||||
@ApiProperty({ description: "email of person need to contact us", example: "matin@gmail.com" })
|
||||
@IsNotEmpty({ message: AuthMessage.EMAIL_NOT_EMPTY })
|
||||
@IsEmail({}, { message: AuthMessage.INVALID_EMAIL_FORMAT })
|
||||
@ApiProperty({ description: "Email address", example: "ma@gmail.com" })
|
||||
email: string;
|
||||
|
||||
@IsNotEmpty({ message: ContactUsMessage.TITLE_IS_REQUIRED })
|
||||
@IsString({ message: ContactUsMessage.TITLE_STRING })
|
||||
@ApiProperty({ description: "title of contact us", example: "عنوان پیام" })
|
||||
title: string;
|
||||
@IsNotEmpty({ message: AuthMessage.PHONE_NOT_EMPTY })
|
||||
@IsMobilePhone("fa-IR", {}, { message: AuthMessage.INVALID_PHONE_FORMAT })
|
||||
@Length(11, 11, { message: AuthMessage.PHONE_SHOULD_BE_11_DIGIT })
|
||||
@ApiProperty({ description: "Phone number", default: "09922320740" })
|
||||
phone: string;
|
||||
|
||||
@IsNotEmpty({ message: ContactUsMessage.BUSINESS_NAME_IS_REQUIRED })
|
||||
@IsString({ message: ContactUsMessage.BUSINESS_NAME_STRING })
|
||||
@Length(2, 100, { message: ContactUsMessage.BUSINESS_NAME_LENGTH })
|
||||
@ApiProperty({ description: "Business name", example: "Tech Corp" })
|
||||
businessName: string;
|
||||
|
||||
@IsNotEmpty({ message: ContactUsMessage.CONTENT_IS_REQUIRED })
|
||||
@IsString({ message: ContactUsMessage.CONTENT_STRING })
|
||||
@ApiProperty({ description: "content of contact us", example: "متن پیام" })
|
||||
@ApiProperty({ description: "Content of the contact request", example: "متن پیام" })
|
||||
content: string;
|
||||
}
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
import { Body, Controller, Get, Param, Post, Query } from "@nestjs/common";
|
||||
import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||
import { ApiOperation } from "@nestjs/swagger";
|
||||
|
||||
import { CreateContactUsDto } from "./DTO/create-contact-us.dto";
|
||||
import { SearchContactUsQueryDto } from "./DTO/search-contact-us-query.dto";
|
||||
import { ContactUsService } from "./providers/contact-us.service";
|
||||
import { AdminRoute } from "../../common/decorators/admin.decorator";
|
||||
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||
import { PermissionsDec } from "../../common/decorators/permission.decorator";
|
||||
import { SkipAuth } from "../../common/decorators/skip-auth.decorator";
|
||||
import { ParamDto } from "../../common/DTO/param.dto";
|
||||
import { PermissionEnum } from "../users/enums/permission.enum";
|
||||
|
||||
@Controller("contact-us")
|
||||
@ApiTags("ContactUs")
|
||||
@AuthGuards()
|
||||
export class ContactUsController {
|
||||
constructor(private readonly contactUsService: ContactUsService) {}
|
||||
|
||||
@ApiOperation({ summary: "Create a new contact us" })
|
||||
@SkipAuth()
|
||||
@Post()
|
||||
createContactUs(@Body() createContactUsDto: CreateContactUsDto) {
|
||||
return this.contactUsService.createContactUs(createContactUsDto);
|
||||
@@ -22,9 +25,9 @@ export class ContactUsController {
|
||||
|
||||
//******************** */
|
||||
|
||||
@AuthGuards()
|
||||
@PermissionsDec(PermissionEnum.CONTACTS_US)
|
||||
@ApiOperation({ summary: "Get all contact us ===> login as admin" })
|
||||
@AdminRoute()
|
||||
@ApiOperation({ summary: "Get all contact us (admin route)" })
|
||||
@Get()
|
||||
getAllContactUs(@Query() queryDto: SearchContactUsQueryDto) {
|
||||
return this.contactUsService.getAllContactUs(queryDto);
|
||||
@@ -32,9 +35,9 @@ export class ContactUsController {
|
||||
|
||||
//******************** */
|
||||
|
||||
@AuthGuards()
|
||||
@AdminRoute()
|
||||
@PermissionsDec(PermissionEnum.CONTACTS_US)
|
||||
@ApiOperation({ summary: "Get one contact us with id ===> login as admin" })
|
||||
@ApiOperation({ summary: "Get one contact us with id (admin route)" })
|
||||
@Get(":id")
|
||||
getOneContactUs(@Param() paramDto: ParamDto) {
|
||||
return this.contactUsService.getOneContactUs(paramDto.id);
|
||||
|
||||
@@ -7,18 +7,18 @@ export class ContactUs extends BaseEntity {
|
||||
@Column({ type: "int", generated: "identity", insert: false })
|
||||
numericId: number;
|
||||
|
||||
@Column({ type: "varchar", length: 150, nullable: false })
|
||||
title: string;
|
||||
@Column({ type: "varchar", length: 150 })
|
||||
businessName: string;
|
||||
|
||||
@Column({ type: "varchar", length: 350 })
|
||||
@Column({ type: "varchar", length: 250 })
|
||||
fullName: string;
|
||||
|
||||
@Column({ type: "varchar", length: 11 })
|
||||
phone: string;
|
||||
|
||||
@Column({ type: "varchar", length: 150 })
|
||||
email: string;
|
||||
|
||||
@Column({ type: "text", nullable: false })
|
||||
content: string;
|
||||
|
||||
@Column({ type: "boolean", default: false })
|
||||
isRead: boolean;
|
||||
}
|
||||
|
||||
@@ -13,9 +13,7 @@ export class ContactUsService {
|
||||
//****************************** */
|
||||
|
||||
async createContactUs(createContactUsDto: CreateContactUsDto) {
|
||||
const contactUs = this.contactUsRepository.create({
|
||||
...createContactUsDto,
|
||||
});
|
||||
const contactUs = this.contactUsRepository.create({ ...createContactUsDto });
|
||||
await this.contactUsRepository.save(contactUs);
|
||||
|
||||
return {
|
||||
@@ -56,15 +54,10 @@ export class ContactUsService {
|
||||
//****************************** */
|
||||
|
||||
async getOneContactUs(id: string) {
|
||||
const contactUs = await this.contactUsRepository.findOneBy({
|
||||
id,
|
||||
});
|
||||
const contactUs = await this.contactUsRepository.findOneBy({ id });
|
||||
|
||||
if (!contactUs) throw new BadRequestException(ContactUsMessage.NOT_FOUND);
|
||||
|
||||
contactUs.isRead = true;
|
||||
await this.contactUsRepository.save(contactUs);
|
||||
|
||||
return { contactUs };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +129,6 @@ export class DanakServicesController {
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "get services for user side" })
|
||||
@AuthGuards()
|
||||
@Get("suggested")
|
||||
getDanakSuggestServices() {
|
||||
return this.danakServicesService.getDanakSuggestServices();
|
||||
|
||||
@@ -2,6 +2,7 @@ import { InjectQueue } from "@nestjs/bullmq";
|
||||
import { BadRequestException, HttpException, HttpStatus, Injectable, InternalServerErrorException } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { Queue } from "bullmq";
|
||||
import dayjs from "dayjs";
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Decimal from "decimal.js";
|
||||
import { FastifyReply } from "fastify";
|
||||
@@ -10,11 +11,9 @@ import { DataSource, Not, QueryRunner } from "typeorm";
|
||||
import { PaginationDto } from "../../../common/DTO/pagination.dto";
|
||||
import { CommonMessage, PaymentMessage, UserMessage, WalletMessage } from "../../../common/enums/message.enum";
|
||||
import { NotificationsService } from "../../notifications/providers/notifications.service";
|
||||
import { ReferralReward } from "../../referrals/entities/referral-reward.entity";
|
||||
import { Referral } from "../../referrals/entities/referral.entity";
|
||||
import { ReferralRewardStatus } from "../../referrals/enums/referral-reward-status.enum";
|
||||
import { ReferralStatus } from "../../referrals/enums/referral-status.enum";
|
||||
// import { ReferralsService } from "../../referrals/providers/referrals.service";
|
||||
import { ReferralsService } from "../../referrals/providers/referrals.service";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { PaginationUtils } from "../../utils/providers/pagination.utils";
|
||||
import { WalletsService } from "../../wallets/providers/wallets.service";
|
||||
@@ -51,8 +50,8 @@ export class PaymentsService {
|
||||
private readonly bankAccountsRepository: BankAccountsRepository,
|
||||
private readonly walletsService: WalletsService,
|
||||
private readonly depositRequestsRepository: DepositRequestsRepository,
|
||||
private readonly referralsService: ReferralsService,
|
||||
private dataSource: DataSource,
|
||||
// private readonly referralsService: ReferralsService,
|
||||
) {}
|
||||
|
||||
//*********************************** */
|
||||
@@ -261,55 +260,38 @@ export class PaymentsService {
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
private async isFirstPurchase(userId: string, queryRunner: QueryRunner): Promise<boolean> {
|
||||
private async isFirstPurchase(userId: string, queryRunner: QueryRunner) {
|
||||
const paymentCount = await queryRunner.manager.count(Payment, {
|
||||
where: { user: { id: userId }, status: PaymentStatus.COMPLETED },
|
||||
});
|
||||
return paymentCount === 0;
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
private async getPendingReferral(userId: string, queryRunner: QueryRunner): Promise<Referral | null> {
|
||||
return queryRunner.manager.findOne(Referral, {
|
||||
where: {
|
||||
referredUser: { id: userId },
|
||||
status: ReferralStatus.PENDING,
|
||||
},
|
||||
relations: { referrer: true },
|
||||
});
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
private async handleReferralReward(payment: Payment, queryRunner: QueryRunner) {
|
||||
const isFirstPurchase = await this.isFirstPurchase(payment.user.id, queryRunner);
|
||||
if (!isFirstPurchase) return;
|
||||
|
||||
const referral = await this.getPendingReferral(payment.user.id, queryRunner);
|
||||
const referral = await this.referralsService.getPendingReferral(payment.user.id, queryRunner);
|
||||
if (!referral) return;
|
||||
|
||||
// Reward amount is 10% of the purchase amount
|
||||
// 10%
|
||||
const rewardAmount = new Decimal(payment.amount).mul(0.1);
|
||||
|
||||
// Create reward record
|
||||
const reward = queryRunner.manager.create(ReferralReward, {
|
||||
user: { id: referral.referrer.id },
|
||||
referral: { id: referral.id },
|
||||
amount: rewardAmount,
|
||||
status: ReferralRewardStatus.PENDING,
|
||||
});
|
||||
await queryRunner.manager.save(reward);
|
||||
const reward = await this.referralsService.createReferralReward(referral.referrer.id, referral.id, rewardAmount, queryRunner);
|
||||
|
||||
// Charge referrer's wallet
|
||||
await this.walletsService.createReferralRewardTransaction(referral.referrer.id, rewardAmount, queryRunner);
|
||||
|
||||
// Update reward status
|
||||
reward.status = ReferralRewardStatus.PAID;
|
||||
reward.paidAt = new Date();
|
||||
reward.paidAt = dayjs().toDate();
|
||||
await queryRunner.manager.save(reward);
|
||||
|
||||
// Update referral status
|
||||
referral.status = ReferralStatus.COMPLETED;
|
||||
referral.completedAt = new Date();
|
||||
referral.completedAt = dayjs().toDate();
|
||||
await queryRunner.manager.save(referral);
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
@@ -16,8 +16,8 @@ export class ReferralReward extends BaseEntity {
|
||||
@ManyToOne(() => Referral, (referral) => referral.rewards)
|
||||
referral: Referral;
|
||||
|
||||
@Column({ type: "decimal", precision: 10, scale: 2, nullable: false, transformer: new DecimalTransformer() })
|
||||
amount: Decimal;
|
||||
@Column({ type: "decimal", precision: 10, scale: 2, nullable: true, transformer: new DecimalTransformer() })
|
||||
amount?: Decimal;
|
||||
|
||||
@Column({ type: "enum", enum: ReferralRewardStatus, default: ReferralRewardStatus.PENDING })
|
||||
status: ReferralRewardStatus;
|
||||
|
||||
@@ -3,13 +3,11 @@ import { randomBytes } from "node:crypto";
|
||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
// eslint-disable-next-line import/no-named-as-default
|
||||
import Decimal from "decimal.js";
|
||||
import { DataSource, QueryRunner } from "typeorm";
|
||||
import { QueryRunner } from "typeorm";
|
||||
|
||||
import { ReferralMessage, UserMessage } from "../../../common/enums/message.enum";
|
||||
import { User } from "../../users/entities/user.entity";
|
||||
import { WalletsService } from "../../wallets/providers/wallets.service";
|
||||
import { UseReferralCodeDto } from "../DTO/use-referral-code.dto";
|
||||
import { ReferralRewardStatus } from "../enums/referral-reward-status.enum";
|
||||
import { ReferralStatus } from "../enums/referral-status.enum";
|
||||
import { ReferralCodesRepository } from "../repositories/referral-codes.repository";
|
||||
import { ReferralRewardsRepository } from "../repositories/referral-rewards.repository";
|
||||
@@ -20,8 +18,6 @@ export class ReferralsService {
|
||||
private readonly referralsRepository: ReferralsRepository,
|
||||
private readonly referralCodesRepository: ReferralCodesRepository,
|
||||
private readonly referralRewardsRepository: ReferralRewardsRepository,
|
||||
private readonly walletsService: WalletsService,
|
||||
private readonly dataSource: DataSource,
|
||||
) {}
|
||||
|
||||
async generateReferralCode(userId: string, queryRunner: QueryRunner) {
|
||||
@@ -54,75 +50,76 @@ export class ReferralsService {
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
async useReferralCode(dto: UseReferralCodeDto) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
async useReferralCode(dto: UseReferralCodeDto, queryRunner: QueryRunner) {
|
||||
const referredUser = await queryRunner.manager.findOne(User, { where: { id: dto.userId } });
|
||||
if (!referredUser) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
|
||||
try {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
const referralCode = await queryRunner.manager.findOne(this.referralCodesRepository.target, {
|
||||
where: { code: dto.referralCode, isActive: true },
|
||||
relations: { user: true },
|
||||
});
|
||||
if (!referralCode || !referralCode.user) throw new BadRequestException(ReferralMessage.INVALID_OR_INACTIVE_REFERRAL_CODE);
|
||||
|
||||
const referredUser = await queryRunner.manager.findOne(User, { where: { id: dto.userId } });
|
||||
if (!referredUser) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
const existingReferral = await queryRunner.manager.findOne(this.referralsRepository.target, {
|
||||
where: { referredUser: { id: referredUser.id } },
|
||||
});
|
||||
if (existingReferral) throw new BadRequestException(ReferralMessage.USER_ALREADY_REFERRED);
|
||||
|
||||
const referralCode = await queryRunner.manager.findOne(this.referralCodesRepository.target, {
|
||||
where: { code: dto.referralCode, isActive: true },
|
||||
relations: { user: true },
|
||||
});
|
||||
if (!referralCode || !referralCode.user) throw new BadRequestException(ReferralMessage.INVALID_OR_INACTIVE_REFERRAL_CODE);
|
||||
const referral = queryRunner.manager.create(this.referralsRepository.target, {
|
||||
referrer: { id: referralCode.user.id },
|
||||
referredUser: { id: referredUser.id },
|
||||
referralCode: { id: referralCode.id },
|
||||
});
|
||||
|
||||
// Check if user has already been referred
|
||||
const existingReferral = await queryRunner.manager.findOne(this.referralsRepository.target, {
|
||||
where: { referredUser: { id: referredUser.id } },
|
||||
});
|
||||
if (existingReferral) throw new BadRequestException(ReferralMessage.USER_ALREADY_REFERRED);
|
||||
await queryRunner.manager.save(referral);
|
||||
|
||||
// Create referral record
|
||||
const referral = queryRunner.manager.create(this.referralsRepository.target, {
|
||||
referrer: { id: referralCode.user.id },
|
||||
referredUser: { id: referredUser.id },
|
||||
referralCode: { id: referralCode.id },
|
||||
status: ReferralStatus.PENDING,
|
||||
});
|
||||
referralCode.usedCount += 1;
|
||||
await queryRunner.manager.save(referralCode);
|
||||
|
||||
await queryRunner.manager.save(referral);
|
||||
// const reward = queryRunner.manager.create(this.referralRewardsRepository.target, {
|
||||
// user: { id: referralCode.user.id },
|
||||
// referral: { id: referral.id },
|
||||
// });
|
||||
|
||||
// Increment usedCount
|
||||
referralCode.usedCount += 1;
|
||||
await queryRunner.manager.save(referralCode);
|
||||
// await queryRunner.manager.save(reward);
|
||||
|
||||
// Create pending reward
|
||||
const reward = queryRunner.manager.create(this.referralRewardsRepository.target, {
|
||||
user: { id: referralCode.user.id },
|
||||
referral: { id: referral.id },
|
||||
amount: 100, // Set your reward amount here
|
||||
status: ReferralRewardStatus.PENDING,
|
||||
});
|
||||
|
||||
await queryRunner.manager.save(reward);
|
||||
|
||||
// Charge the referred user's wallet
|
||||
const chargeAmount = new Decimal(100);
|
||||
await this.walletsService.createReferralRewardTransaction(referredUser.id, chargeAmount, queryRunner);
|
||||
|
||||
// Update referral status to COMPLETED
|
||||
referral.status = ReferralStatus.COMPLETED;
|
||||
await queryRunner.manager.save(referral);
|
||||
|
||||
// Update reward status to PAID
|
||||
reward.status = ReferralRewardStatus.PAID;
|
||||
reward.paidAt = new Date();
|
||||
await queryRunner.manager.save(reward);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
return { referral, reward };
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
return referral;
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
async getPendingReferral(userId: string, queryRunner: QueryRunner) {
|
||||
return queryRunner.manager.findOne(this.referralsRepository.target, {
|
||||
where: {
|
||||
referredUser: { id: userId },
|
||||
status: ReferralStatus.PENDING,
|
||||
},
|
||||
relations: { referrer: true },
|
||||
});
|
||||
}
|
||||
//*********************************** */
|
||||
|
||||
async createReferral(userId: string, queryRunner: QueryRunner) {
|
||||
const referral = queryRunner.manager.create(this.referralsRepository.target, {
|
||||
referrer: { id: userId },
|
||||
referredUser: { id: userId },
|
||||
});
|
||||
await queryRunner.manager.save(referral);
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
|
||||
async createReferralReward(userId: string, referralId: string, amount: Decimal, queryRunner: QueryRunner) {
|
||||
const reward = queryRunner.manager.create(this.referralRewardsRepository.target, {
|
||||
user: { id: userId },
|
||||
referral: { id: referralId },
|
||||
amount,
|
||||
});
|
||||
await queryRunner.manager.save(reward);
|
||||
|
||||
return reward;
|
||||
}
|
||||
|
||||
//*********************************** */
|
||||
private generateRandomCode(): string {
|
||||
return randomBytes(6).toString("hex");
|
||||
}
|
||||
|
||||
@@ -1,31 +1,23 @@
|
||||
import { Body, Controller, Post } from "@nestjs/common";
|
||||
import { ApiOperation } from "@nestjs/swagger";
|
||||
// import { Body, Controller, Post } from "@nestjs/common";
|
||||
|
||||
import { UseReferralCodeDto } from "./DTO/use-referral-code.dto";
|
||||
import { ReferralsService } from "./providers/referrals.service";
|
||||
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||
// import { UserDec } from "../../common/decorators/user.decorator";
|
||||
// import { ReferralsService } from "./providers/referrals.service";
|
||||
// import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
|
||||
// // import { UserDec } from "../../common/decorators/user.decorator";
|
||||
|
||||
@Controller("referrals")
|
||||
@AuthGuards()
|
||||
export class ReferralsController {
|
||||
constructor(private readonly referralsService: ReferralsService) {}
|
||||
// @Controller("referrals")
|
||||
// @AuthGuards()
|
||||
// export class ReferralsController {
|
||||
// constructor(private readonly referralsService: ReferralsService) {}
|
||||
|
||||
@ApiOperation({ summary: "Use a referral code" })
|
||||
@Post("use-code")
|
||||
useReferralCode(@Body() dto: UseReferralCodeDto) {
|
||||
return this.referralsService.useReferralCode(dto);
|
||||
}
|
||||
// // @ApiOperation({ summary: "Get referral statistics for a user" })
|
||||
// // @Get("stats/:userId")
|
||||
// // getReferralStats(@Param("userId") userId: string) {
|
||||
// // return this.referralsService.getReferralStats(userId);
|
||||
// // }
|
||||
|
||||
// @ApiOperation({ summary: "Get referral statistics for a user" })
|
||||
// @Get("stats/:userId")
|
||||
// getReferralStats(@Param("userId") userId: string) {
|
||||
// return this.referralsService.getReferralStats(userId);
|
||||
// }
|
||||
|
||||
// @Get("my-stats")
|
||||
// @ApiOperation({ summary: "Get current user's referral statistics" })
|
||||
// getMyReferralStats(@UserDec("id") userId: string) {
|
||||
// return this.referralsService.getReferralStats(userId);
|
||||
// }
|
||||
}
|
||||
// // @Get("my-stats")
|
||||
// // @ApiOperation({ summary: "Get current user's referral statistics" })
|
||||
// // getMyReferralStats(@UserDec("id") userId: string) {
|
||||
// // return this.referralsService.getReferralStats(userId);
|
||||
// // }
|
||||
// }
|
||||
|
||||
@@ -5,16 +5,14 @@ import { ReferralCode } from "./entities/referral-code.entity";
|
||||
import { ReferralReward } from "./entities/referral-reward.entity";
|
||||
import { Referral } from "./entities/referral.entity";
|
||||
import { ReferralsService } from "./providers/referrals.service";
|
||||
import { ReferralsController } from "./referrals.controller";
|
||||
import { WalletsModule } from "../wallets/wallets.module";
|
||||
import { ReferralCodesRepository } from "./repositories/referral-codes.repository";
|
||||
import { ReferralRewardsRepository } from "./repositories/referral-rewards.repository";
|
||||
import { ReferralsRepository } from "./repositories/referrals.repository";
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Referral, ReferralReward, ReferralCode]), WalletsModule],
|
||||
imports: [TypeOrmModule.forFeature([Referral, ReferralReward, ReferralCode])],
|
||||
providers: [ReferralsService, ReferralsRepository, ReferralCodesRepository, ReferralRewardsRepository],
|
||||
controllers: [ReferralsController],
|
||||
controllers: [],
|
||||
exports: [ReferralsService],
|
||||
})
|
||||
export class ReferralsModule {}
|
||||
|
||||
Reference in New Issue
Block a user