init to partner module

This commit is contained in:
2026-04-13 15:47:59 +03:30
parent 388aa25de4
commit 59da9585c4
21 changed files with 553 additions and 12 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ export class ConsumerMiddleware implements NestMiddleware {
return doForbidden()
}
req.consumerData = {
req.partnerData = {
account_id: consumerAccount.id,
id: consumerAccount.consumer_id,
}
@@ -0,0 +1,39 @@
import { TokenAccount } from '@/common/decorators/tokenInfo.decorator'
import { Body, Controller, Get, Param, Patch } from '@nestjs/common'
import { ApiTags } from '@nestjs/swagger'
import { AccountsService } from './accounts.service'
import { UpdateAccountDto } from './dto/account.dto'
@ApiTags('PartnerAccounts')
@Controller('partner/accounts')
export class AccountsController {
constructor(private readonly service: AccountsService) {}
@Get()
async findAll(@TokenAccount('userId') consumerId: string) {
return this.service.findAll(consumerId)
}
@Get(':id')
async findOne(@Param('id') id: string) {
return this.service.findOne(id)
}
// @Post()
// async create(
// @TokenAccount('userId') consumerId: string,
// @Body() data: CreateConsumerAccountDto,
// ) {
// return this.service.create(consumerId, data)
// }
@Patch(':id')
async update(@Param('id') id: string, @Body() data: UpdateAccountDto) {
return this.service.update(id, data)
}
// @Delete(':id')
// async delete(@Param('id') id: string) {
// return this.service.delete(id)
// }
}
@@ -0,0 +1,12 @@
import { PrismaModule } from '@/prisma/prisma.module'
import { Module } from '@nestjs/common'
import { AccountsController } from './accounts.controller'
import { AccountsService } from './accounts.service'
@Module({
imports: [PrismaModule],
controllers: [AccountsController],
providers: [AccountsService],
exports: [AccountsService],
})
export class PartnerAccountsModule {}
@@ -0,0 +1,79 @@
import { PasswordUtil } from '@/common/utils/password.util'
import { AccountStatus, AccountType } from '@/generated/prisma/enums'
import { PrismaService } from '@/prisma/prisma.service'
import { Injectable } from '@nestjs/common'
import { ResponseMapper } from 'common/response/response-mapper'
import { CreatePartnerAccountDto, UpdateAccountDto } from './dto/account.dto'
@Injectable()
export class AccountsService {
constructor(private readonly prisma: PrismaService) {}
async findAll(partner_id: string) {
const accounts = await this.prisma.partnerAccount.findMany({
where: { partner_id },
select: {
id: true,
role: true,
created_at: true,
account: {
select: {
username: true,
status: true,
},
},
},
})
return ResponseMapper.list(accounts)
}
async findOne(id: string) {
const account = await this.prisma.partnerAccount.findUniqueOrThrow({
where: { id },
select: {
id: true,
role: true,
created_at: true,
account: {
select: {
username: true,
status: true,
},
},
},
})
return ResponseMapper.single(account)
}
async create(partnerId: string, data: CreatePartnerAccountDto) {
const account = await this.prisma.partnerAccount.create({
data: {
role: data.role,
account: {
create: {
password: await PasswordUtil.hash(data.password),
status: AccountStatus.ACTIVE,
type: AccountType.PARTNER,
username: data.username,
},
},
partner: {
connect: {
id: partnerId,
},
},
},
})
return ResponseMapper.create(account)
}
async update(id: string, data: UpdateAccountDto) {
const account = await this.prisma.account.update({ where: { id }, data })
return ResponseMapper.update(account)
}
// async delete(id: string) {
// await this.prisma.account.delete({ where: { id } })
// return ResponseMapper.delete()
// }
}
@@ -0,0 +1,24 @@
import { ApiProperty, PartialType } from '@nestjs/swagger'
import { IsEnum, IsOptional, IsString } from 'class-validator'
import { AccountStatus, PartnerRole } from 'generated/prisma/enums'
export class CreatePartnerAccountDto {
@IsString()
@ApiProperty({ required: true })
username: string
@IsString()
@ApiProperty({ required: true })
password: string
@IsEnum(PartnerRole)
@ApiProperty({ required: true, enum: PartnerRole })
role: PartnerRole
}
export class UpdateAccountDto extends PartialType(CreatePartnerAccountDto) {
@IsOptional()
@IsEnum(AccountStatus)
@ApiProperty({ enum: AccountStatus })
status?: AccountStatus
}
@@ -0,0 +1,18 @@
import { PartnerInfo } from '@/common/decorators/partnerInfo.decorator'
import { Controller, Get, Param } from '@nestjs/common'
import { PartnerCustomersService } from './customers.service'
@Controller('partner/customers')
export class PartnerCustomersController {
constructor(private readonly service: PartnerCustomersService) {}
@Get()
async findAll(@PartnerInfo('id') partnerId: string) {
return this.service.findAll(partnerId)
}
@Get(':id')
async findOne(@PartnerInfo('id') partnerId: string, @Param('id') id: string) {
return this.service.findOne(partnerId, id)
}
}
@@ -0,0 +1,11 @@
import { PrismaModule } from '@/prisma/prisma.module'
import { Module } from '@nestjs/common'
import { PartnerCustomersController } from './customers.controller'
import { PartnerCustomersService } from './customers.service'
@Module({
imports: [PrismaModule],
controllers: [PartnerCustomersController],
providers: [PartnerCustomersService],
})
export class PartnerCustomersModule {}
@@ -0,0 +1,61 @@
import { ConsumerSelect, ConsumerWhereInput } from '@/generated/prisma/models'
import { PrismaService } from '@/prisma/prisma.service'
import { Injectable } from '@nestjs/common'
import { ResponseMapper } from 'common/response/response-mapper'
@Injectable()
export class PartnerCustomersService {
constructor(private readonly prisma: PrismaService) {}
defaultSelect: ConsumerSelect = {
id: true,
first_name: true,
last_name: true,
status: true,
license: {
select: {
id: true,
starts_at: true,
status: true,
expires_at: true,
created_at: true,
},
},
created_at: true,
}
private defaultWhere = (partner_id: string): ConsumerWhereInput => ({
license: {
partner_id,
},
})
async findAll(partner_id: string, page = 1, pageSize = 10) {
const [partners, count] = await this.prisma.$transaction(async tx => [
await tx.consumer.findMany({
where: this.defaultWhere(partner_id),
select: this.defaultSelect,
skip: (page - 1) * pageSize,
take: 10,
}),
await tx.consumer.count({
where: this.defaultWhere(partner_id),
}),
])
return ResponseMapper.paginate(partners, { count, page, pageSize })
}
async findOne(partner_id: string, consumer_id: string) {
const partner = await this.prisma.consumer.findUniqueOrThrow({
where: {
license: {
partner_id,
},
id: consumer_id,
},
select: this.defaultSelect,
})
return ResponseMapper.single(partner)
}
}
@@ -0,0 +1,74 @@
import { ApiProperty } from '@nestjs/swagger'
import {
IsBoolean,
IsNumber,
IsOptional,
IsString,
MaxLength,
MinLength,
} from 'class-validator'
export class UpdateCustomerLegalDto {
@IsString()
@IsOptional()
@ApiProperty({ required: true })
company_name?: string
@IsString()
@IsOptional()
@ApiProperty({ required: true })
economic_code?: string
@IsString()
@IsOptional()
@ApiProperty({ required: true })
registration_number?: string
@IsNumber()
@MaxLength(10)
@MinLength(10)
@IsOptional()
@ApiProperty({ maxLength: 10, minLength: 10 })
postal_code?: string
@IsBoolean()
@IsOptional()
@ApiProperty({ required: false })
is_favorite?: boolean
}
export class UpdateCustomerIndividualDto {
@IsString()
@IsOptional()
@ApiProperty({ required: true })
first_name?: string
@IsString()
@IsOptional()
@ApiProperty({ required: true })
last_name?: string
@IsNumber()
@MaxLength(10)
@MinLength(10)
@IsOptional()
@ApiProperty({ required: true, maxLength: 10, minLength: 10 })
national_code?: string
@IsNumber()
@MaxLength(10)
@MinLength(10)
@IsOptional()
@ApiProperty({ maxLength: 10, minLength: 10 })
postal_code?: string
@IsBoolean()
@IsOptional()
@ApiProperty({ required: false })
is_favorite?: boolean
@IsNumber()
@IsOptional()
@ApiProperty({ required: false })
economic_code?: string
}
@@ -0,0 +1,15 @@
import { ConsumerInfo } from '@/common/decorators/consumerInfo.decorator'
import { Controller, Get } from '@nestjs/common'
import { ApiTags } from '@nestjs/swagger'
import { PartnerService } from './partners.service'
@ApiTags('Partner')
@Controller('partner')
export class PartnerController {
constructor(private service: PartnerService) {}
@Get('')
async findOne(@ConsumerInfo('id') consumerId: string) {
return this.service.getInfo(consumerId)
}
}
@@ -0,0 +1,61 @@
import { checkAndDecodeJwtToken } from '@/common/utils/jwt-user.util'
import { PrismaService } from '@/prisma/prisma.service'
import {
ForbiddenException,
Injectable,
NestMiddleware,
UnauthorizedException,
} from '@nestjs/common'
import { JwtService } from '@nestjs/jwt'
import { NextFunction, Request, Response } from 'express'
@Injectable()
export class PartnerMiddleware implements NestMiddleware {
constructor(
private prisma: PrismaService,
private jwtService: JwtService,
) {}
async use(req: Request, _res: Response, next: NextFunction) {
const doForbidden = () => {
console.log('doForbidden')
throw new ForbiddenException('شما دسترسی لازم را ندارید')
}
try {
const tokenAccount = checkAndDecodeJwtToken(req, this.jwtService)
if (tokenAccount?.type !== 'PARTNER') {
throw new UnauthorizedException()
}
const partnerAccount = await this.prisma.partnerAccount.findUnique({
where: {
id: tokenAccount.account_id,
},
select: {
partner_id: true,
id: true,
},
})
if (!partnerAccount) {
return doForbidden()
}
req.partnerData = {
account_id: partnerAccount.id,
id: partnerAccount.partner_id,
}
req.decodedToken = tokenAccount
return next()
} catch (error) {
if (error && typeof error === 'object') {
throw error
}
return doForbidden()
}
}
}
+27
View File
@@ -0,0 +1,27 @@
import { MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common'
import { JwtService } from '@nestjs/jwt'
import { PartnerAccountsModule } from './accounts/accounts.module'
import { PartnerCustomersModule } from './customers/customers.module'
import { PartnerController } from './partners.controller'
import { PartnerMiddleware } from './partners.middleware'
import { PartnerService } from './partners.service'
@Module({
controllers: [PartnerController],
imports: [PartnerAccountsModule, PartnerCustomersModule],
providers: [JwtService, PartnerService],
})
export class PartnerModule implements NestModule {
configure(partner: MiddlewareConsumer) {
partner.apply(PartnerMiddleware).forRoutes({
path: '/partner*path',
method: RequestMethod.ALL,
version: '1',
})
partner.apply(PartnerMiddleware).forRoutes({
path: '/partner',
method: RequestMethod.ALL,
version: '1',
})
}
}
+23
View File
@@ -0,0 +1,23 @@
import { ResponseMapper } from '@/common/response/response-mapper'
import { PrismaService } from '@/prisma/prisma.service'
import { Injectable } from '@nestjs/common'
@Injectable()
export class PartnerService {
constructor(private readonly prisma: PrismaService) {}
async getInfo(partner_id: string) {
const partner = await this.prisma.partner.findUniqueOrThrow({
where: {
id: partner_id,
},
select: {
id: true,
name: true,
status: true,
},
})
return ResponseMapper.single(partner)
}
}