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