2026-04-27 10:45:39 +03:30
|
|
|
import { UploadedFileTypes } from '@/common/enums/enums'
|
2026-05-08 18:09:13 +03:30
|
|
|
import { translateEnumValue } from '@/common/utils'
|
2026-03-16 00:33:40 +03:30
|
|
|
import { PasswordUtil } from '@/common/utils/password.util'
|
|
|
|
|
import {
|
|
|
|
|
AccountStatus,
|
|
|
|
|
AccountType,
|
|
|
|
|
PartnerRole,
|
|
|
|
|
PartnerStatus,
|
|
|
|
|
} from '@/generated/prisma/enums'
|
2026-04-16 22:19:20 +03:30
|
|
|
import { PartnerSelect } from '@/generated/prisma/models'
|
2026-04-27 10:45:39 +03:30
|
|
|
import { UploaderService } from '@/modules/uploader/uploader.service'
|
2026-03-07 11:25:11 +03:30
|
|
|
import { PrismaService } from '@/prisma/prisma.service'
|
|
|
|
|
import { Injectable } from '@nestjs/common'
|
|
|
|
|
import { ResponseMapper } from 'common/response/response-mapper'
|
2026-04-16 22:19:20 +03:30
|
|
|
import { CreatePartnerDto, UpdatePartnerDto } from './dto/partner.dto'
|
2026-03-07 11:25:11 +03:30
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
|
export class PartnersService {
|
2026-04-24 23:02:05 +03:30
|
|
|
private now = new Date().getTime()
|
2026-04-27 10:45:39 +03:30
|
|
|
constructor(
|
|
|
|
|
private readonly prisma: PrismaService,
|
|
|
|
|
private readonly uploaderService: UploaderService,
|
|
|
|
|
) {}
|
2026-03-07 11:25:11 +03:30
|
|
|
|
2026-04-16 22:19:20 +03:30
|
|
|
private readonly defaultSelect: PartnerSelect = {
|
|
|
|
|
id: true,
|
|
|
|
|
name: true,
|
|
|
|
|
code: true,
|
|
|
|
|
status: true,
|
2026-04-27 10:45:39 +03:30
|
|
|
logo_url: true,
|
2026-05-03 16:23:17 +03:30
|
|
|
tsp_provider: true,
|
2026-04-16 22:19:20 +03:30
|
|
|
created_at: true,
|
2026-04-24 23:02:05 +03:30
|
|
|
license_charge_transactions: {
|
|
|
|
|
select: {
|
|
|
|
|
activation_expires_at: true,
|
|
|
|
|
purchased_count: true,
|
|
|
|
|
_count: {
|
|
|
|
|
select: {
|
|
|
|
|
licenses: {
|
|
|
|
|
where: {
|
|
|
|
|
activation: {
|
|
|
|
|
isNot: null,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
license_renew_charge_transactions: {
|
2026-04-23 20:59:39 +03:30
|
|
|
select: {
|
2026-04-24 23:02:05 +03:30
|
|
|
activation_expires_at: true,
|
|
|
|
|
purchased_count: true,
|
|
|
|
|
_count: {
|
2026-04-23 20:59:39 +03:30
|
|
|
select: {
|
2026-04-24 23:02:05 +03:30
|
|
|
license_renews: {
|
|
|
|
|
where: {
|
|
|
|
|
activation: {
|
|
|
|
|
isNot: undefined,
|
2026-04-23 20:59:39 +03:30
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2026-04-24 23:02:05 +03:30
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
account_quota_charge_transactions: {
|
|
|
|
|
select: {
|
|
|
|
|
activation_expires_at: true,
|
|
|
|
|
purchased_count: true,
|
|
|
|
|
_count: {
|
2026-04-23 20:59:39 +03:30
|
|
|
select: {
|
2026-04-24 23:02:05 +03:30
|
|
|
credits: {
|
|
|
|
|
where: {
|
2026-04-25 15:16:59 +03:30
|
|
|
allocation: {
|
|
|
|
|
isNot: null,
|
2026-04-23 20:59:39 +03:30
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2026-04-24 23:02:05 +03:30
|
|
|
},
|
|
|
|
|
}
|
2026-04-23 20:59:39 +03:30
|
|
|
|
2026-04-24 23:02:05 +03:30
|
|
|
private readonly mapPartner = (partner: any) => {
|
|
|
|
|
const {
|
|
|
|
|
license_charge_transactions,
|
|
|
|
|
account_quota_charge_transactions,
|
|
|
|
|
license_renew_charge_transactions,
|
2026-05-08 18:09:13 +03:30
|
|
|
status,
|
2026-04-24 23:02:05 +03:30
|
|
|
...rest
|
|
|
|
|
} = partner
|
2026-04-23 20:59:39 +03:30
|
|
|
|
|
|
|
|
const account_quota_status = { total: 0, used: 0, expired: 0 }
|
2026-04-24 23:02:05 +03:30
|
|
|
account_quota_charge_transactions.forEach((account_quota_charge_transaction: any) => {
|
|
|
|
|
const total = account_quota_charge_transaction.purchased_count
|
|
|
|
|
const used = account_quota_charge_transaction._count.credits
|
|
|
|
|
account_quota_status.total += total
|
|
|
|
|
account_quota_status.used += used
|
2026-04-23 20:59:39 +03:30
|
|
|
account_quota_status.expired +=
|
2026-04-24 23:02:05 +03:30
|
|
|
account_quota_charge_transaction.activation_expires_at.getTime() <= this.now
|
|
|
|
|
? total - used
|
|
|
|
|
: 0
|
2026-04-23 20:59:39 +03:30
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const licenses_status = { total: 0, used: 0, expired: 0 }
|
2026-04-24 23:02:05 +03:30
|
|
|
license_charge_transactions.forEach((license_charge_transaction: any) => {
|
|
|
|
|
const total = license_charge_transaction.purchased_count
|
|
|
|
|
const used = license_charge_transaction._count.licenses
|
|
|
|
|
licenses_status.total += total
|
|
|
|
|
licenses_status.used += used
|
2026-04-23 20:59:39 +03:30
|
|
|
licenses_status.expired +=
|
2026-04-24 23:02:05 +03:30
|
|
|
license_charge_transaction.activation_expires_at.getTime() <= this.now
|
|
|
|
|
? total - used
|
|
|
|
|
: 0
|
2026-03-07 11:25:11 +03:30
|
|
|
})
|
2026-04-16 22:19:20 +03:30
|
|
|
|
2026-04-24 23:02:05 +03:30
|
|
|
const license_renew_status = { total: 0, used: 0, expired: 0 }
|
|
|
|
|
license_renew_charge_transactions.forEach((license_renew_charge_transaction: any) => {
|
|
|
|
|
const total = license_renew_charge_transaction.purchased_count
|
|
|
|
|
const used = license_renew_charge_transaction._count.license_renews
|
|
|
|
|
license_renew_status.total += total
|
|
|
|
|
license_renew_status.used += used
|
|
|
|
|
license_renew_status.expired +=
|
|
|
|
|
license_renew_charge_transaction.activation_expires_at.getTime() <= this.now
|
|
|
|
|
? total - used
|
|
|
|
|
: 0
|
|
|
|
|
})
|
|
|
|
|
return {
|
2026-04-16 22:19:20 +03:30
|
|
|
...rest,
|
2026-05-08 18:09:13 +03:30
|
|
|
status: translateEnumValue('PartnerStatus', status),
|
2026-04-23 20:59:39 +03:30
|
|
|
licenses_status,
|
2026-04-24 23:02:05 +03:30
|
|
|
license_renew_status,
|
2026-04-23 20:59:39 +03:30
|
|
|
account_quota_status,
|
2026-04-16 22:19:20 +03:30
|
|
|
}
|
2026-04-24 23:02:05 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async findAll() {
|
|
|
|
|
const partners = await this.prisma.partner.findMany({
|
|
|
|
|
select: this.defaultSelect,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const mappedPartners = partners.map(this.mapPartner)
|
|
|
|
|
|
|
|
|
|
return ResponseMapper.list(mappedPartners)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async findOne(id: string) {
|
|
|
|
|
const partner = await this.prisma.partner.findUniqueOrThrow({
|
|
|
|
|
where: { id },
|
|
|
|
|
select: this.defaultSelect,
|
|
|
|
|
})
|
2026-04-16 22:19:20 +03:30
|
|
|
|
2026-04-24 23:02:05 +03:30
|
|
|
return ResponseMapper.single(this.mapPartner(partner))
|
2026-03-07 11:25:11 +03:30
|
|
|
}
|
|
|
|
|
|
2026-04-27 10:45:39 +03:30
|
|
|
async create(data: CreatePartnerDto, logo?: any) {
|
|
|
|
|
const { username, password, ...rest } = data as any
|
|
|
|
|
delete rest.logo
|
|
|
|
|
|
|
|
|
|
const logo_url = logo
|
|
|
|
|
? await this.uploaderService.uploadFile(logo, UploadedFileTypes.PARTNER_LOGO)
|
|
|
|
|
: undefined
|
|
|
|
|
|
2026-03-16 00:33:40 +03:30
|
|
|
const partner = await this.prisma.partner.create({
|
|
|
|
|
data: {
|
|
|
|
|
...rest,
|
|
|
|
|
status: PartnerStatus.ACTIVE,
|
2026-04-27 10:45:39 +03:30
|
|
|
...(logo_url ? { logo_url } : {}),
|
|
|
|
|
accounts: {
|
2026-03-16 00:33:40 +03:30
|
|
|
create: {
|
|
|
|
|
role: PartnerRole.OWNER,
|
|
|
|
|
account: {
|
|
|
|
|
create: {
|
|
|
|
|
username,
|
2026-04-27 10:45:39 +03:30
|
|
|
password: await PasswordUtil.hash(password),
|
2026-03-16 00:33:40 +03:30
|
|
|
status: AccountStatus.ACTIVE,
|
|
|
|
|
type: AccountType.PARTNER,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
},
|
2026-04-16 22:19:20 +03:30
|
|
|
select: this.defaultSelect,
|
2026-03-16 00:33:40 +03:30
|
|
|
})
|
2026-03-07 11:25:11 +03:30
|
|
|
return ResponseMapper.create(partner)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-27 10:45:39 +03:30
|
|
|
async update(id: string, data: UpdatePartnerDto, logo?: any) {
|
|
|
|
|
const rest = { ...(data as any) }
|
|
|
|
|
delete rest.logo
|
|
|
|
|
|
|
|
|
|
const partner = await this.prisma.$transaction(async tx => {
|
|
|
|
|
let logo_url = ''
|
|
|
|
|
if (logo) {
|
|
|
|
|
const uploadedUrl = await this.uploaderService.uploadFile(
|
|
|
|
|
logo,
|
|
|
|
|
UploadedFileTypes.PARTNER_LOGO,
|
|
|
|
|
)
|
|
|
|
|
logo_url = uploadedUrl || ''
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const prevLogo = await tx.partner.findUnique({
|
|
|
|
|
where: { id },
|
|
|
|
|
select: { logo_url: true },
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (logo_url && prevLogo?.logo_url) {
|
|
|
|
|
this.uploaderService.deleteFile(prevLogo.logo_url, UploadedFileTypes.PARTNER_LOGO)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return tx.partner.update({
|
|
|
|
|
where: { id },
|
|
|
|
|
data: {
|
|
|
|
|
...rest,
|
|
|
|
|
...(logo_url ? { logo_url } : {}),
|
|
|
|
|
},
|
|
|
|
|
select: this.defaultSelect,
|
|
|
|
|
})
|
2026-04-06 18:33:04 +03:30
|
|
|
})
|
2026-04-27 10:45:39 +03:30
|
|
|
|
2026-04-06 18:33:04 +03:30
|
|
|
return ResponseMapper.update(partner)
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-07 11:25:11 +03:30
|
|
|
async delete(id: string) {
|
|
|
|
|
await this.prisma.partner.delete({ where: { id } })
|
|
|
|
|
return ResponseMapper.delete()
|
|
|
|
|
}
|
|
|
|
|
}
|