Files
dmail-api/src/modules/businesses/services/businesses.service.ts
T
morteza 0c095b681c
Build and Deploy Docker Images / build_and_deploy (push) Has been cancelled
add : super admin routes to get list and active/deactivate
2026-03-17 23:48:52 +03:30

184 lines
7.2 KiB
TypeScript

import { EntityManager } from "@mikro-orm/postgresql";
import { HttpService } from "@nestjs/axios";
import { BadRequestException, Inject, Injectable, InternalServerErrorException, Logger } from "@nestjs/common";
import { AxiosError } from "axios";
import { catchError, firstValueFrom } from "rxjs";
import { QuotaPurchaseService } from "./quota-purchase.service";
import { BusinessMessage } from "../../../common/enums/message.enum";
import { DANAKCORP_CONFIG, IDanakCorpConfig } from "../../../configs/danakcorp.config";
import { QUOTA_CONSTANTS, SUBSCRIPTIONS } from "../constant";
import { PurchaseQuotaDto } from "../DTO/purchase-quota.dto";
import { UpdateBusinessSettingsDto } from "../DTO/update-business-settings.dto";
import { BusinessStaff } from "../entities/business-staff.entity";
import { Business } from "../entities/business.entity";
import { IDanakCheckAccessResponse } from "../interfaces/IBusiness";
import { BusinessRepository } from "../repositories/business.repository";
import { FindBusinessesDto } from "../DTO/find-businesses.dto";
@Injectable()
export class BusinessesService {
private readonly logger = new Logger(BusinessesService.name);
constructor(
private readonly businessRepository: BusinessRepository,
@Inject(DANAKCORP_CONFIG) private readonly danakCorpConfig: IDanakCorpConfig,
private readonly httpService: HttpService,
private readonly em: EntityManager,
private readonly quotaPurchaseService: QuotaPurchaseService,
) { }
async getBusinessByDanakSubscriptionId(danakSubscriptionId: string) {
const business = await this.businessRepository.findOne({ danakSubscriptionId, deletedAt: null });
if (!business) throw new BadRequestException(BusinessMessage.NOT_FOUND);
return business;
}
//************************************************** */
async hasAccessToService(business: Business, userId: string) {
try {
const { data } = await firstValueFrom(
this.httpService
.get<IDanakCheckAccessResponse>(`${this.danakCorpConfig.apiUrl}/subscriptions/check-access/${SUBSCRIPTIONS.DMAIL_SERVICE_ID}`, {
params: {
userId,
danakSubscriptionId: business.danakSubscriptionId,
},
headers: {
"X-API-Key": this.danakCorpConfig.apiKey,
},
timeout: this.danakCorpConfig.timeout,
})
.pipe(
catchError((error: AxiosError) => {
this.logger.error(`Failed to check access to service for business ${business.id}:`, error.message);
throw new InternalServerErrorException(BusinessMessage.USER_DOES_NOT_HAVE_ACCESS_TO_SERVICE);
}),
),
);
if (!data.success || !data.data?.hasAccess) {
this.logger.error(`DanakCorp API returned error for business ${business.id}:`, data.error);
throw new InternalServerErrorException(BusinessMessage.QUOTA_PURCHASE_FAILED);
}
return data.data?.hasAccess;
} catch (error) {
this.logger.error(`Failed to check access to service for business ${business.id}:`, error);
throw new InternalServerErrorException(BusinessMessage.USER_DOES_NOT_HAVE_ACCESS_TO_SERVICE);
}
}
//************************************************** */
async getBusinessBySlug(slug: string) {
const business = await this.businessRepository.findOne({ slug, deletedAt: null });
if (!business) throw new BadRequestException(BusinessMessage.NOT_FOUND);
return { business };
}
//************************************************** */
async updateBusinessSettings(businessId: string, settingsDto: UpdateBusinessSettingsDto) {
const business = await this.businessRepository.findOne({ id: businessId, deletedAt: null });
if (!business) throw new BadRequestException(BusinessMessage.NOT_FOUND);
this.businessRepository.assign(business, settingsDto);
await this.em.flush();
return {
message: BusinessMessage.BUSINESS_SETTINGS_UPDATED,
business,
};
}
//************************************************** */
async getBusinessSettings(businessId: string) {
const business = await this.businessRepository.findOne({ id: businessId, deletedAt: null });
if (!business) throw new BadRequestException(BusinessMessage.NOT_FOUND);
return { business };
}
//************************************************** */
async getBusinessById(id: string) {
const business = await this.businessRepository.findOne({ id, deletedAt: null });
if (!business) throw new BadRequestException(BusinessMessage.NOT_FOUND);
return business;
}
//************************************************** */
async findAdminWithLeastTickets() {
// First query to get the ID of the business staff with the least tickets
const result = await this.em.getConnection().execute(
`
SELECT bs.id
FROM business_staff bs
WHERE bs.deleted_at IS NULL
ORDER BY (
SELECT COUNT(*) FROM ticket t WHERE t.assigned_to_id = bs.id
) ASC
LIMIT 1
`,
);
if (result.length === 0) return null;
return this.em.findOne(BusinessStaff, { id: result[0].id });
}
/*******************************/
async findOneByIdWithEntityManager(staffId: string, em: EntityManager) {
const staff = await em.findOne(BusinessStaff, { id: staffId });
if (!staff) throw new BadRequestException(BusinessMessage.STAFF_NOT_FOUND);
return staff;
}
//************************************************** */
async getBusinessQuota(businessId: string) {
const business = await this.businessRepository.findOne({ id: businessId, deletedAt: null });
if (!business) throw new BadRequestException(BusinessMessage.NOT_FOUND);
return {
quota: {
quotaPricePerGB: QUOTA_CONSTANTS.QUOTA_PRICE_PER_GB,
total: Number(business.quota),
used: Number(business.usedQuota),
remaining: Number(business.remainingQuota),
totalInMB: Math.round(Number(business.quota) / (1024 * 1024)),
totalInGB: Math.round(Number(business.quota) / (1024 * 1024 * 1024)),
usedInMB: Math.round(Number(business.usedQuota) / (1024 * 1024)),
usedInGB: Math.round(Number(business.usedQuota) / (1024 * 1024 * 1024)),
remainingInMB: Math.round(Number(business.remainingQuota) / (1024 * 1024)),
remainingInGB: Math.round(Number(business.remainingQuota) / (1024 * 1024 * 1024)),
usagePercentage: Math.round((Number(business.usedQuota) / Number(business.quota)) * 100),
},
};
}
//************************************************** */
async purchaseQuota(businessId: string, purchaseDto: PurchaseQuotaDto) {
const business = await this.businessRepository.findOne({ id: businessId, deletedAt: null });
if (!business) throw new BadRequestException(BusinessMessage.NOT_FOUND);
return this.quotaPurchaseService.purchaseQuota(business, purchaseDto);
}
//********************************************* */
async findBusinesses(query: FindBusinessesDto) {
return this.businessRepository.findAllPaginated(query)
}
//********************************************* */
async updateBusinesStatus(businessId: string, isActive: boolean) {
const business = await this.getBusinessById(businessId)
business.isActive = isActive
await this.em.flush()
return business
}
}