城市合伙人端的修改(后台)
This commit is contained in:
@@ -168,15 +168,15 @@ export class AuthService {
|
||||
}
|
||||
|
||||
private trackPartnerEvent(
|
||||
partnerAccountId: bigint | undefined,
|
||||
partnerId: bigint,
|
||||
actorAccountId: bigint | undefined,
|
||||
primaryAccountId: bigint,
|
||||
clientApp: ClientApp | string,
|
||||
eventName: string,
|
||||
extraJson?: Record<string, unknown>,
|
||||
ref?: { refType?: string; refId?: bigint },
|
||||
) {
|
||||
this.analyticsService.trackPartnerOneSafe(partnerAccountId, clientApp, {
|
||||
partnerId,
|
||||
this.analyticsService.trackPartnerOneSafe(actorAccountId, clientApp, {
|
||||
partnerAccountId: primaryAccountId,
|
||||
eventName,
|
||||
refType: ref?.refType,
|
||||
refId: ref?.refId,
|
||||
@@ -184,10 +184,42 @@ export class AuthService {
|
||||
});
|
||||
}
|
||||
|
||||
private async resolvePrimaryAccount(accountId: bigint) {
|
||||
const account = await this.prisma.partnerAccount.findUnique({ where: { id: accountId } });
|
||||
if (!account) throw new NotFoundException('合伙人账号不存在');
|
||||
if (account.isPrimary === 1) return account;
|
||||
if (!account.parentAccountId) {
|
||||
throw new BadRequestException('子账号缺少主账号');
|
||||
}
|
||||
return this.prisma.partnerAccount.findUniqueOrThrow({ where: { id: account.parentAccountId } });
|
||||
}
|
||||
|
||||
private partnerTokenPayload(
|
||||
account: {
|
||||
id: bigint;
|
||||
name: string;
|
||||
phone: string;
|
||||
isPrimary: number;
|
||||
staffRole: string | null;
|
||||
permissions?: unknown;
|
||||
},
|
||||
primary: { id: bigint; companyName: string | null },
|
||||
) {
|
||||
return {
|
||||
id: account.id.toString(),
|
||||
primaryAccountId: primary.id.toString(),
|
||||
name: account.name,
|
||||
phone: account.phone,
|
||||
isPrimary: account.isPrimary === 1,
|
||||
staffRole: account.staffRole ?? undefined,
|
||||
companyName: primary.companyName ?? undefined,
|
||||
permissions: Array.isArray(account.permissions) ? account.permissions : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
private async assertPartnerAccountByPhone(phone: string) {
|
||||
const account = await this.prisma.partnerAccount.findUnique({
|
||||
where: { phone },
|
||||
include: { partner: true },
|
||||
});
|
||||
if (!account) throw new BadRequestException('未找到合伙人账号');
|
||||
if (account.status !== 'ACTIVE') throw new BadRequestException('合伙人账号已停用');
|
||||
@@ -197,11 +229,12 @@ export class AuthService {
|
||||
async checkPartnerPhone(phone: string) {
|
||||
const normalizedPhone = this.assertMobilePhone(phone);
|
||||
const account = await this.assertPartnerAccountByPhone(normalizedPhone);
|
||||
const primary = await this.resolvePrimaryAccount(account.id);
|
||||
return {
|
||||
ok: true,
|
||||
maskedPhone: this.maskPhone(normalizedPhone),
|
||||
name: account.name,
|
||||
companyName: account.partner.companyName,
|
||||
companyName: primary.companyName,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -306,12 +339,13 @@ export class AuthService {
|
||||
) {
|
||||
const partnerAccount = await this.prisma.partnerAccount.findUnique({
|
||||
where: { id: actorRef.refId },
|
||||
select: { id: true, partnerId: true },
|
||||
select: { id: true, isPrimary: true, parentAccountId: true },
|
||||
});
|
||||
if (partnerAccount) {
|
||||
const primary = await this.resolvePrimaryAccount(partnerAccount.id);
|
||||
this.trackPartnerEvent(
|
||||
partnerAccount.id,
|
||||
partnerAccount.partnerId,
|
||||
primary.id,
|
||||
clientApp,
|
||||
'partner_sms_send',
|
||||
{
|
||||
@@ -415,20 +449,12 @@ export class AuthService {
|
||||
private async buildPartnerSessionResponse(accountId: bigint, clientApp: ClientApp) {
|
||||
const account = await this.prisma.partnerAccount.findUnique({
|
||||
where: { id: accountId },
|
||||
include: { partner: true },
|
||||
});
|
||||
if (!account || account.status !== 'ACTIVE') {
|
||||
throw new UnauthorizedException('Invalid refresh token');
|
||||
}
|
||||
return this.issueToken('PARTNER', account.id, clientApp, false, undefined, undefined, {
|
||||
id: account.id.toString(),
|
||||
partnerId: account.partnerId.toString(),
|
||||
name: account.name,
|
||||
phone: account.phone,
|
||||
isPrimary: account.isPrimary === 1,
|
||||
staffRole: account.staffRole ?? undefined,
|
||||
companyName: account.partner.companyName,
|
||||
});
|
||||
const primary = await this.resolvePrimaryAccount(account.id);
|
||||
return this.issueToken('PARTNER', account.id, clientApp, false, undefined, undefined, this.partnerTokenPayload(account, primary));
|
||||
}
|
||||
|
||||
async loginUser(phone: string, code: string, clientApp: ClientApp, guestId?: bigint) {
|
||||
@@ -601,7 +627,8 @@ export class AuthService {
|
||||
} catch (err) {
|
||||
const account = await this.prisma.partnerAccount.findUnique({ where: { phone: normalizedPhone } });
|
||||
if (account) {
|
||||
this.trackPartnerEvent(account.id, account.partnerId, clientApp, 'partner_sms_verify_fail', {
|
||||
const primary = await this.resolvePrimaryAccount(account.id);
|
||||
this.trackPartnerEvent(account.id, primary.id, clientApp, 'partner_sms_verify_fail', {
|
||||
phone: this.maskPhone(normalizedPhone),
|
||||
reason: err instanceof BadRequestException ? err.message : '验证码错误',
|
||||
});
|
||||
@@ -610,29 +637,21 @@ export class AuthService {
|
||||
}
|
||||
const account = await this.prisma.partnerAccount.findUnique({
|
||||
where: { phone: normalizedPhone },
|
||||
include: { partner: true },
|
||||
});
|
||||
if (!account) throw new BadRequestException('未找到合伙人账号');
|
||||
if (account.status !== 'ACTIVE') throw new BadRequestException('合伙人账号已停用');
|
||||
const primary = await this.resolvePrimaryAccount(account.id);
|
||||
await this.prisma.partnerAccount.update({
|
||||
where: { id: account.id },
|
||||
data: { lastLoginAt: new Date() },
|
||||
});
|
||||
this.trackPartnerEvent(account.id, account.partnerId, clientApp, 'partner_sms_login', {
|
||||
this.trackPartnerEvent(account.id, primary.id, clientApp, 'partner_sms_login', {
|
||||
phone: this.maskPhone(normalizedPhone),
|
||||
});
|
||||
this.trackPartnerEvent(account.id, account.partnerId, clientApp, 'partner_login_success', {
|
||||
this.trackPartnerEvent(account.id, primary.id, clientApp, 'partner_login_success', {
|
||||
method: 'sms',
|
||||
});
|
||||
return this.issueToken('PARTNER', account.id, clientApp, false, undefined, undefined, {
|
||||
id: account.id.toString(),
|
||||
partnerId: account.partnerId.toString(),
|
||||
name: account.name,
|
||||
phone: account.phone,
|
||||
isPrimary: account.isPrimary === 1,
|
||||
staffRole: account.staffRole ?? undefined,
|
||||
companyName: account.partner.companyName,
|
||||
});
|
||||
return this.issueToken('PARTNER', account.id, clientApp, false, undefined, undefined, this.partnerTokenPayload(account, primary));
|
||||
}
|
||||
|
||||
async loginHq(phone: string, code: string, clientApp: ClientApp) {
|
||||
@@ -734,9 +753,14 @@ export class AuthService {
|
||||
if (actorType === 'PARTNER') {
|
||||
const account = await this.prisma.partnerAccount.findUnique({
|
||||
where: { id: actorId },
|
||||
include: { partner: true },
|
||||
});
|
||||
return serializeBigInt(account);
|
||||
if (!account) return null;
|
||||
const primary = await this.resolvePrimaryAccount(account.id);
|
||||
return serializeBigInt({
|
||||
...account,
|
||||
primaryAccountId: primary.id,
|
||||
companyName: primary.companyName,
|
||||
});
|
||||
}
|
||||
if (actorType === 'HQ') {
|
||||
const account = await this.prisma.hqAccount.findUnique({ where: { id: actorId } });
|
||||
@@ -1102,7 +1126,6 @@ export class AuthService {
|
||||
|
||||
const account = await this.prisma.partnerAccount.findUnique({
|
||||
where: { id: partnerAccountId },
|
||||
include: { partner: true },
|
||||
});
|
||||
if (!account) throw new BadRequestException('合伙人账号不存在');
|
||||
|
||||
@@ -1120,19 +1143,12 @@ export class AuthService {
|
||||
wxUnionId: session.unionId ?? account.wxUnionId,
|
||||
lastLoginAt: new Date(),
|
||||
},
|
||||
include: { partner: true },
|
||||
});
|
||||
const primary = await this.resolvePrimaryAccount(updated.id);
|
||||
|
||||
this.trackPartnerEvent(updated.id, updated.partnerId, clientApp, 'partner_wechat_bind', { platform });
|
||||
this.trackPartnerEvent(updated.id, primary.id, clientApp, 'partner_wechat_bind', { platform });
|
||||
|
||||
return this.issueToken('PARTNER', updated.id, clientApp, false, undefined, undefined, {
|
||||
id: updated.id.toString(),
|
||||
partnerId: updated.partnerId.toString(),
|
||||
name: updated.name,
|
||||
phone: updated.phone,
|
||||
isPrimary: updated.isPrimary === 1,
|
||||
companyName: updated.partner.companyName,
|
||||
});
|
||||
return this.issueToken('PARTNER', updated.id, clientApp, false, undefined, undefined, this.partnerTokenPayload(updated, primary));
|
||||
}
|
||||
|
||||
async loginPartnerWechat(code: string, clientApp: ClientApp, platform: 'h5' | 'mini' = 'h5') {
|
||||
@@ -1144,7 +1160,6 @@ export class AuthService {
|
||||
|
||||
let account = await this.prisma.partnerAccount.findFirst({
|
||||
where: { wxOpenId: session.openId },
|
||||
include: { partner: true },
|
||||
});
|
||||
|
||||
if (!account && this.wechatProvider.isMock()) {
|
||||
@@ -1152,7 +1167,6 @@ export class AuthService {
|
||||
account = await this.prisma.partnerAccount.findFirst({
|
||||
where: { status: 'ACTIVE' },
|
||||
orderBy: [{ isPrimary: 'desc' }, { id: 'asc' }],
|
||||
include: { partner: true },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1167,23 +1181,15 @@ export class AuthService {
|
||||
wxUnionId: session.unionId ?? account.wxUnionId,
|
||||
lastLoginAt: new Date(),
|
||||
},
|
||||
include: { partner: true },
|
||||
});
|
||||
const primary = await this.resolvePrimaryAccount(account.id);
|
||||
|
||||
this.trackPartnerEvent(account.id, account.partnerId, clientApp, 'partner_wechat_login', { platform });
|
||||
this.trackPartnerEvent(account.id, account.partnerId, clientApp, 'partner_login_success', {
|
||||
this.trackPartnerEvent(account.id, primary.id, clientApp, 'partner_wechat_login', { platform });
|
||||
this.trackPartnerEvent(account.id, primary.id, clientApp, 'partner_login_success', {
|
||||
method: 'wechat',
|
||||
});
|
||||
|
||||
return this.issueToken('PARTNER', account.id, clientApp, false, undefined, undefined, {
|
||||
id: account.id.toString(),
|
||||
partnerId: account.partnerId.toString(),
|
||||
name: account.name,
|
||||
phone: account.phone,
|
||||
isPrimary: account.isPrimary === 1,
|
||||
staffRole: account.staffRole ?? undefined,
|
||||
companyName: account.partner.companyName,
|
||||
});
|
||||
return this.issueToken('PARTNER', account.id, clientApp, false, undefined, undefined, this.partnerTokenPayload(account, primary));
|
||||
}
|
||||
|
||||
private async mergeUsers(guestId: bigint, primaryId: bigint): Promise<UserRow> {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IsIn, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
import { IsArray, IsIn, IsNotEmpty, IsOptional, IsString } from 'class-validator';
|
||||
import { AccountStatus, PartnerStaffRole } from '@dukang/shared-types';
|
||||
|
||||
export class CreatePartnerStaffDto {
|
||||
@@ -15,6 +15,11 @@ export class CreatePartnerStaffDto {
|
||||
@IsString()
|
||||
@IsIn(Object.values(PartnerStaffRole))
|
||||
staffRole?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
permissions?: string[];
|
||||
}
|
||||
|
||||
export class UpdatePartnerStaffDto {
|
||||
@@ -27,6 +32,11 @@ export class UpdatePartnerStaffDto {
|
||||
@IsOptional()
|
||||
staffRole?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
permissions?: string[];
|
||||
|
||||
@IsString()
|
||||
@IsIn(Object.values(AccountStatus))
|
||||
@IsOptional()
|
||||
|
||||
@@ -17,7 +17,7 @@ export class PartnerStaffController {
|
||||
|
||||
@Post()
|
||||
create(@CurrentUser() user: AuthUser, @Body() dto: CreatePartnerStaffDto) {
|
||||
return this.staffService.createStaff(user.actorId, dto);
|
||||
return this.staffService.createStaff(user, dto);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
@@ -26,11 +26,11 @@ export class PartnerStaffController {
|
||||
@Param('id') id: string,
|
||||
@Body() dto: UpdatePartnerStaffDto,
|
||||
) {
|
||||
return this.staffService.updateStaff(user.actorId, BigInt(id), dto);
|
||||
return this.staffService.updateStaff(user, BigInt(id), dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
remove(@CurrentUser() user: AuthUser, @Param('id') id: string) {
|
||||
return this.staffService.deleteStaff(user.actorId, BigInt(id));
|
||||
return this.staffService.deleteStaff(user, BigInt(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +1,182 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { PartnerStaffRole } from '@dukang/shared-types';
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
import { CreatePartnerStaffDto, UpdatePartnerStaffDto } from './dto/partner-staff.dto';
|
||||
|
||||
@Injectable()
|
||||
export class PartnerStaffService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async listStaff(parentAccountId: bigint) {
|
||||
const rows = await this.prisma.partnerAccount.findMany({
|
||||
where: { parentAccountId },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
});
|
||||
return rows.map((row) => this.toStaffItem(row));
|
||||
}
|
||||
|
||||
async createStaff(parentAccountId: bigint, dto: CreatePartnerStaffDto) {
|
||||
const parent = await this.prisma.partnerAccount.findUniqueOrThrow({
|
||||
where: { id: parentAccountId },
|
||||
});
|
||||
if (parent.isPrimary !== 1) {
|
||||
throw new BadRequestException('仅主账号可添加子账号');
|
||||
}
|
||||
|
||||
const phone = dto.phone.trim();
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) {
|
||||
throw new BadRequestException('请输入正确的手机号码');
|
||||
}
|
||||
|
||||
const existing = await this.prisma.partnerAccount.findUnique({ where: { phone } });
|
||||
if (existing) throw new BadRequestException('该手机号已被使用');
|
||||
|
||||
const name = dto.name.trim();
|
||||
if (!name) throw new BadRequestException('请填写真实姓名');
|
||||
|
||||
const account = await this.prisma.partnerAccount.create({
|
||||
data: {
|
||||
partnerId: parent.partnerId,
|
||||
phone,
|
||||
name,
|
||||
staffRole: (dto.staffRole as PartnerStaffRole | undefined) ?? PartnerStaffRole.INTERNAL,
|
||||
isPrimary: 0,
|
||||
parentAccountId: parent.id,
|
||||
status: 'DISABLED',
|
||||
},
|
||||
});
|
||||
|
||||
return this.toStaffItem(account);
|
||||
}
|
||||
|
||||
async updateStaff(parentAccountId: bigint, staffId: bigint, dto: UpdatePartnerStaffDto) {
|
||||
const staff = await this.assertStaffOwned(parentAccountId, staffId);
|
||||
const data: Record<string, unknown> = {};
|
||||
if (dto.name !== undefined) {
|
||||
const name = dto.name.trim();
|
||||
if (!name) throw new BadRequestException('请填写真实姓名');
|
||||
data.name = name;
|
||||
}
|
||||
if (dto.staffRole !== undefined) {
|
||||
data.staffRole = dto.staffRole as PartnerStaffRole;
|
||||
}
|
||||
if (dto.status !== undefined) {
|
||||
data.status = dto.status;
|
||||
}
|
||||
const updated = await this.prisma.partnerAccount.update({
|
||||
where: { id: staff.id },
|
||||
data,
|
||||
});
|
||||
return this.toStaffItem(updated);
|
||||
}
|
||||
|
||||
async deleteStaff(parentAccountId: bigint, staffId: bigint) {
|
||||
const staff = await this.assertStaffOwned(parentAccountId, staffId);
|
||||
await this.prisma.partnerAccount.delete({ where: { id: staff.id } });
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
private async assertStaffOwned(parentAccountId: bigint, staffId: bigint) {
|
||||
const staff = await this.prisma.partnerAccount.findFirst({
|
||||
where: { id: staffId, parentAccountId },
|
||||
});
|
||||
if (!staff) throw new NotFoundException('子账号不存在');
|
||||
return staff;
|
||||
}
|
||||
|
||||
private toStaffItem(row: {
|
||||
id: bigint;
|
||||
name: string;
|
||||
phone: string;
|
||||
staffRole: string | null;
|
||||
status: string;
|
||||
lastLoginAt: Date | null;
|
||||
}) {
|
||||
return serializeBigInt({
|
||||
id: row.id.toString(),
|
||||
name: row.name,
|
||||
phone: this.maskPhone(row.phone),
|
||||
staffRole: row.staffRole ?? PartnerStaffRole.INTERNAL,
|
||||
status: row.status,
|
||||
lastLoginAt: row.lastLoginAt?.toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
private maskPhone(phone: string): string {
|
||||
if (phone.length !== 11) return phone;
|
||||
return `${phone.slice(0, 3)} **** ${phone.slice(7)}`;
|
||||
}
|
||||
}
|
||||
import {
|
||||
|
||||
BadRequestException,
|
||||
|
||||
Injectable,
|
||||
|
||||
NotFoundException,
|
||||
|
||||
} from '@nestjs/common';
|
||||
|
||||
import { PartnerStaffRole } from '@dukang/shared-types';
|
||||
|
||||
import { PrismaService } from '../../common/prisma/prisma.module';
|
||||
|
||||
import { serializeBigInt } from '../../common/decorators/current-user.decorator';
|
||||
|
||||
import type { AuthUser } from '../../common/guards/jwt-auth.guard';
|
||||
|
||||
import { AnalyticsService } from '../analytics/analytics.service';
|
||||
|
||||
import { CreatePartnerStaffDto, UpdatePartnerStaffDto } from './dto/partner-staff.dto';
|
||||
|
||||
|
||||
|
||||
@Injectable()
|
||||
|
||||
export class PartnerStaffService {
|
||||
|
||||
constructor(
|
||||
|
||||
private readonly prisma: PrismaService,
|
||||
|
||||
private readonly analytics: AnalyticsService,
|
||||
|
||||
) {}
|
||||
|
||||
|
||||
|
||||
async listStaff(parentAccountId: bigint) {
|
||||
|
||||
const rows = await this.prisma.partnerAccount.findMany({
|
||||
|
||||
where: { parentAccountId },
|
||||
|
||||
orderBy: { createdAt: 'desc' },
|
||||
|
||||
});
|
||||
|
||||
return rows.map((row) => this.toStaffItem(row));
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
async createStaff(actor: AuthUser, dto: CreatePartnerStaffDto) {
|
||||
|
||||
const parentAccountId = actor.actorId;
|
||||
|
||||
const parent = await this.prisma.partnerAccount.findUniqueOrThrow({
|
||||
|
||||
where: { id: parentAccountId },
|
||||
|
||||
});
|
||||
|
||||
if (parent.isPrimary !== 1) {
|
||||
|
||||
throw new BadRequestException('仅主账号可添加子账号');
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const phone = dto.phone.trim();
|
||||
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) {
|
||||
|
||||
throw new BadRequestException('请输入正确的手机号码');
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
const existing = await this.prisma.partnerAccount.findUnique({ where: { phone } });
|
||||
|
||||
if (existing) throw new BadRequestException('该手机号已被使用');
|
||||
|
||||
|
||||
|
||||
const name = dto.name.trim();
|
||||
|
||||
if (!name) throw new BadRequestException('请填写真实姓名');
|
||||
|
||||
|
||||
|
||||
const staffRole = (dto.staffRole as PartnerStaffRole | undefined) ?? PartnerStaffRole.INTERNAL;
|
||||
|
||||
|
||||
|
||||
const account = await this.prisma.partnerAccount.create({
|
||||
|
||||
data: {
|
||||
|
||||
phone,
|
||||
|
||||
name,
|
||||
|
||||
staffRole,
|
||||
|
||||
permissions: dto.permissions ?? undefined,
|
||||
|
||||
isPrimary: 0,
|
||||
|
||||
parentAccountId: parent.id,
|
||||
|
||||
status: 'DISABLED',
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
this.trackStaffEvent(actor, parent.id, 'partner_staff_create', account.id, {
|
||||
|
||||
name,
|
||||
|
||||
phone: this.maskPhone(phone),
|
||||
|
||||
staffRole,
|
||||
|
||||
status: account.status,
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
return this.toStaffItem(account);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
async updateStaff(actor: AuthUser, staffId: bigint, dto: UpdatePartnerStaffDto) {
|
||||
|
||||
const parentAccountId = actor.actorId;
|
||||
|
||||
const staff = await this.assertStaffOwned(parentAccountId, staffId);
|
||||
|
||||
const before = {
|
||||
|
||||
name: staff.name,
|
||||
|
||||
staffRole: staff.staffRole,
|
||||
|
||||
status: staff.status,
|
||||
|
||||
};
|
||||
|
||||
const data: Record<string, unknown> = {};
|
||||
|
||||
if (dto.name !== undefined) {
|
||||
|
||||
const name = dto.name.trim();
|
||||
|
||||
if (!name) throw new BadRequestException('请填写真实姓名');
|
||||
|
||||
data.name = name;
|
||||
|
||||
}
|
||||
|
||||
if (dto.staffRole !== undefined) {
|
||||
|
||||
data.staffRole = dto.staffRole as PartnerStaffRole;
|
||||
|
||||
}
|
||||
|
||||
if (dto.permissions !== undefined) {
|
||||
|
||||
data.permissions = dto.permissions;
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user