add single endpoint

This commit is contained in:
2025-12-27 13:04:35 +03:30
parent e56d9888e5
commit 3cb59a0ca8
6 changed files with 170 additions and 6 deletions
@@ -0,0 +1,11 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsNotEmpty, IsUUID } from "class-validator";
import { SubscriptionMessage } from "../../../common/enums/message.enum";
export class BuyPremiumPlanDto {
@IsNotEmpty({ message: SubscriptionMessage.PLAN_ID_REQUIRED })
@IsUUID(4, { message: SubscriptionMessage.PLAN_ID_SHOULD_BE_UUID })
@ApiProperty({ description: "The restaurant subscription ID to upgrade", example: "f7b3b3b3-7b3b-4b3b-8b3b-3b3b3b3b3b3b" })
userSubscriptionId: string;
}
@@ -22,6 +22,6 @@ export class CreateRestaurantDto {
@ApiProperty({ example: 'sub_1234567890', description: 'شناسه اشتراک' })
@IsString()
subscriptionId!: string;
userSubscriptionId!: string;
}
+10 -2
View File
@@ -13,6 +13,7 @@ import { CreateRestaurantDto } from "./DTO/create-restaurant.dto";
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { PermissionsDec } from "../../common/decorators/permission.decorator";
import { PermissionEnum } from "../users/enums/permission.enum";
import { UserDec } from "../../common/decorators/user.decorator";
@ApiTags("d-menu")
@Controller("admin/dmenu")
@@ -112,7 +113,14 @@ export class DmenuController {
@Post('restaurants')
@ApiOperation({ summary: "Create restaurant" })
createRestaurant(@Body() dto: CreateRestaurantDto) {
return this.restaurantService.createRestaurant(dto);
createRestaurant(@Body() dto: CreateRestaurantDto, @UserDec("id") userId: string) {
return this.restaurantService.createRestaurant(dto, userId);
}
@Get('/super-admin/restaurants/subscription/:subscriptionId')
@ApiOperation({ summary: "Get restaurant subscription by subscription ID" })
@ApiParam({ name: "subscriptionId", required: true, type: String })
getRestaurantSubscription(@Param("subscriptionId") subscriptionId: string) {
return this.restaurantService.getRestaurantSubscription(subscriptionId);
}
}
@@ -0,0 +1,94 @@
import { BadRequestException, Injectable } from "@nestjs/common";
import dayjs from "dayjs";
import Decimal from "decimal.js";
import { SubscriptionMessage } from "../../../common/enums/message.enum";
import { InvoicesService } from "../../invoices/providers/invoices.service";
import { UserSubscription } from "../../subscriptions/entities/user-subscription.entity";
import { UserSubscriptionsRepository } from "../../subscriptions/repositories/user-subscriptions.repository";
import { BuyPremiumPlanDto } from "../DTO/buy-premium-plan.dto";
@Injectable()
export class PremiumService {
constructor(
private readonly userSubscriptionsRepository: UserSubscriptionsRepository,
private readonly invoicesService: InvoicesService,
) {}
async buyPremiumPlan(dto: BuyPremiumPlanDto, userId: string) {
// Find the user's current subscription
const userSubscription = await this.userSubscriptionsRepository.findOne({
where: { id: dto.userSubscriptionId, user: { id: userId } },
relations: {
user: true,
plan: { service: true },
},
});
if (!userSubscription) {
throw new BadRequestException(SubscriptionMessage.NOT_FOUND);
}
// Calculate remaining days
const remainingDays = this.calculateRemainingDays(userSubscription);
// Extract duration from current plan and convert to months
const planDurationInMonths = Math.round(userSubscription.plan.duration / 30);
// Get premium pricing based on extracted duration
const premiumPrice = this.getPremiumPrice(planDurationInMonths);
// Create invoice items for premium purchase
const invoiceItems = [{
name: `Premium Plan - ${planDurationInMonths} Months`,
count: 1,
unitPrice: premiumPrice.toNumber(),
discount: 0,
}];
// Create invoice directly
const createInvoiceDto = {
userId: userSubscription.user.id,
items: invoiceItems,
isRecurring: false,
};
const invoice = await this.invoicesService.createInvoiceAdmin(createInvoiceDto, {
ip: '',
headers: {},
userId: userSubscription.user.id
});
return {
message: "Premium plan purchased successfully",
userSubscription,
invoice,
remainingDays,
premiumPrice: premiumPrice.toNumber(),
};
}
//************************************ */
private calculateRemainingDays(userSubscription: UserSubscription): number {
const now = dayjs();
const endDate = dayjs(userSubscription.endDate);
if (endDate.isBefore(now)) {
return 0;
}
return endDate.diff(now, 'day');
}
//************************************ */
private getPremiumPrice(durationInMonths: number): Decimal {
switch (durationInMonths) {
case 6:
return new Decimal(5000000);
case 12:
return new Decimal(10000000);
default:
throw new BadRequestException("Invalid premium plan duration. Only 6 and 12 month plans are supported.");
}
}
}
@@ -1,5 +1,5 @@
import { HttpService } from "@nestjs/axios";
import { Injectable, Logger } from "@nestjs/common";
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
import { Inject } from "@nestjs/common";
import { AxiosError } from "axios";
import { catchError, firstValueFrom, throwError } from "rxjs";
@@ -8,6 +8,9 @@ import { IDmenuConfig } from "../../../configs/icons.config";
import { DMENU_CONFIG } from "../constants";
import { FindRestaurantsDto } from "../DTO/find-restaurants.dto";
import { CreateRestaurantDto } from "../DTO/create-restaurant.dto";
import { PlanEnum } from "../interface/plan.interface";
import { SubscriptionsService } from "../../subscriptions/providers/subscriptions.service";
import { SubscriptionMessage } from "../../../common/enums/message.enum";
@Injectable()
export class RestaurantService {
@@ -16,6 +19,7 @@ export class RestaurantService {
constructor(
@Inject(DMENU_CONFIG) private readonly config: IDmenuConfig,
private readonly httpService: HttpService,
private readonly subscriptionService: SubscriptionsService,
) {}
private getHeaders(): Record<string, string> {
@@ -67,11 +71,18 @@ export class RestaurantService {
}
}
async createRestaurant(dto: CreateRestaurantDto) {
async createRestaurant(dto: CreateRestaurantDto, userId: string) {
const { userSubscription } = await this.subscriptionService.getUserSubscriptionById(dto.userSubscriptionId, userId);
if (!userSubscription) {
throw new BadRequestException(SubscriptionMessage.USER_SUBS_NOT_FOUND);
}
try {
const { data } = await firstValueFrom(
this.httpService
.post(`${this.config.baseUrl}/super-admin/restaurants`, dto, {
.post(`${this.config.baseUrl}/super-admin/restaurants`, {
...dto,
plan: PlanEnum.Base,
}, {
headers: this.getHeaders(),
})
.pipe(
@@ -87,5 +98,20 @@ export class RestaurantService {
throw error;
}
}
async getRestaurantSubscription(subscriptionId: string) {
// TODO: Implement the business logic to get restaurant subscription
// This could involve querying the database, making external API calls, etc.
this.logger.log(`Getting restaurant subscription for ID: ${subscriptionId}`);
// For now, return a placeholder response
// You should implement the actual logic based on your business requirements
return {
subscriptionId,
message: "Restaurant subscription endpoint created successfully",
// Add actual data structure based on your requirements
};
}
}
@@ -0,0 +1,25 @@
import { Controller, Get, Param } from "@nestjs/common";
import { ApiBearerAuth, ApiOperation, ApiParam, ApiTags } from "@nestjs/swagger";
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { PermissionsDec } from "../../common/decorators/permission.decorator";
import { PermissionEnum } from "../users/enums/permission.enum";
import { RestaurantService } from "./providers/restaurant.service";
@ApiTags("super-admin")
@Controller("super-admin")
@AuthGuards()
@PermissionsDec(PermissionEnum.DMENU)
@ApiBearerAuth()
export class SuperAdminController {
constructor(
private readonly restaurantService: RestaurantService,
) {}
@Get('restaurants/subscription/:subscriptionId')
@ApiOperation({ summary: "Get restaurant subscription by subscription ID" })
@ApiParam({ name: "subscriptionId", required: true, type: String })
getRestaurantSubscription(@Param("subscriptionId") subscriptionId: string) {
return this.restaurantService.getRestaurantSubscription(subscriptionId);
}
}