webadmin增加显示子账号显示列表
修复api服务端报错
This commit is contained in:
@@ -6,6 +6,7 @@ export const HqOperationAction = {
|
||||
PARTNER_UPDATE: 'PARTNER_UPDATE',
|
||||
PARTNER_ACCOUNT_CREATE: 'PARTNER_ACCOUNT_CREATE',
|
||||
PARTNER_ACCOUNT_UPDATE: 'PARTNER_ACCOUNT_UPDATE',
|
||||
PARTNER_ACCOUNT_DELETE: 'PARTNER_ACCOUNT_DELETE',
|
||||
HQ_ACCOUNT_CREATE: 'HQ_ACCOUNT_CREATE',
|
||||
HQ_ACCOUNT_UPDATE: 'HQ_ACCOUNT_UPDATE',
|
||||
HQ_PERMISSION_UPDATE: 'HQ_PERMISSION_UPDATE',
|
||||
@@ -50,6 +51,7 @@ export const HQ_OPERATION_ACTION_LABELS: Record<string, string> = {
|
||||
[HqOperationAction.PARTNER_UPDATE]: '编辑城市合伙人',
|
||||
[HqOperationAction.PARTNER_ACCOUNT_CREATE]: '新增合伙人账户',
|
||||
[HqOperationAction.PARTNER_ACCOUNT_UPDATE]: '编辑合伙人账户',
|
||||
[HqOperationAction.PARTNER_ACCOUNT_DELETE]: '删除合伙人子账号',
|
||||
[HqOperationAction.HQ_ACCOUNT_CREATE]: '新增 HQ 管理员',
|
||||
[HqOperationAction.HQ_ACCOUNT_UPDATE]: '编辑 HQ 管理员',
|
||||
[HqOperationAction.HQ_PERMISSION_UPDATE]: '配置 HQ 权限',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Body, Controller, Get, Param, Post, Put, Query, UseGuards } from '@nestjs/common';
|
||||
import { Body, Controller, Delete, Get, Param, Post, Put, Query, UseGuards } from '@nestjs/common';
|
||||
import { HqAuthGuard } from '../../common/guards/hq-auth.guard';
|
||||
import { HqOperation } from '../../common/hq-operation/hq-operation.decorator';
|
||||
import { HqOperationAction } from '../../common/hq-operation/hq-operation.constants';
|
||||
@@ -54,6 +54,11 @@ export class AdminPartnersController {
|
||||
export class AdminPartnerAccountsController {
|
||||
constructor(private readonly service: AdminPartnersService) {}
|
||||
|
||||
@Get('tree')
|
||||
tree(@Query('partnerId') partnerId?: string) {
|
||||
return this.service.listPartnerAccountTree(partnerId ? BigInt(partnerId) : undefined);
|
||||
}
|
||||
|
||||
@Get()
|
||||
list(@Query() query: AdminPartnerAccountsQueryDto) {
|
||||
return this.service.listPartnerAccounts(query);
|
||||
@@ -85,4 +90,14 @@ export class AdminPartnerAccountsController {
|
||||
update(@Param('id') id: string, @Body() dto: UpdatePartnerAccountDto) {
|
||||
return this.service.updatePartnerAccount(BigInt(id), dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@HqOperation({
|
||||
action: HqOperationAction.PARTNER_ACCOUNT_DELETE,
|
||||
refType: 'PARTNER_ACCOUNT',
|
||||
refIdParam: 'id',
|
||||
})
|
||||
remove(@Param('id') id: string) {
|
||||
return this.service.deletePartnerSubAccount(BigInt(id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,10 +94,65 @@ export class AdminPartnersService {
|
||||
return serializeBigInt({ items, total, page, pageSize });
|
||||
}
|
||||
|
||||
async listPartnerAccountTree(partnerId?: bigint) {
|
||||
const where: Prisma.PartnerAccountWhereInput = {};
|
||||
if (partnerId) where.partnerId = partnerId;
|
||||
|
||||
const accounts = await this.prisma.partnerAccount.findMany({
|
||||
where,
|
||||
orderBy: [{ isPrimary: 'desc' }, { createdAt: 'asc' }],
|
||||
include: { partner: { select: { id: true, companyName: true } } },
|
||||
});
|
||||
|
||||
type TreeNode = (typeof accounts)[number] & { children: TreeNode[] };
|
||||
const nodeMap = new Map<string, TreeNode>();
|
||||
const roots: TreeNode[] = [];
|
||||
|
||||
for (const account of accounts) {
|
||||
nodeMap.set(account.id.toString(), { ...account, children: [] });
|
||||
}
|
||||
|
||||
for (const account of accounts) {
|
||||
const node = nodeMap.get(account.id.toString())!;
|
||||
if (account.parentAccountId) {
|
||||
const parent = nodeMap.get(account.parentAccountId.toString());
|
||||
if (parent) parent.children.push(node);
|
||||
else roots.push(node);
|
||||
} else {
|
||||
roots.push(node);
|
||||
}
|
||||
}
|
||||
|
||||
const mapNode = (node: TreeNode) => ({
|
||||
id: node.id,
|
||||
phone: node.phone,
|
||||
name: node.name,
|
||||
status: node.status,
|
||||
isPrimary: node.isPrimary,
|
||||
staffRole: node.staffRole,
|
||||
parentAccountId: node.parentAccountId,
|
||||
partner: node.partner,
|
||||
createdAt: node.createdAt,
|
||||
lastLoginAt: node.lastLoginAt,
|
||||
children: node.children.length ? node.children.map(mapNode) : undefined,
|
||||
});
|
||||
|
||||
return serializeBigInt(roots.map(mapNode));
|
||||
}
|
||||
|
||||
async detailPartnerAccount(id: bigint) {
|
||||
const account = await this.prisma.partnerAccount.findUnique({
|
||||
where: { id },
|
||||
include: { partner: true },
|
||||
include: {
|
||||
partner: {
|
||||
select: {
|
||||
id: true,
|
||||
companyName: true,
|
||||
contactPhone: true,
|
||||
address: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!account) throw new NotFoundException('开城合伙人账号不存在');
|
||||
|
||||
@@ -126,16 +181,49 @@ export class AdminPartnersService {
|
||||
}
|
||||
|
||||
async createPartnerAccount(dto: CreatePartnerAccountDto) {
|
||||
const partner = await this.prisma.partner.findUnique({ where: { id: BigInt(dto.partnerId) } });
|
||||
const phone = dto.phone.trim();
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) {
|
||||
throw new BadRequestException('请输入正确的手机号码');
|
||||
}
|
||||
const phoneTaken = await this.prisma.partnerAccount.findUnique({ where: { phone } });
|
||||
if (phoneTaken) throw new BadRequestException('该手机号已被使用');
|
||||
|
||||
if (dto.parentAccountId) {
|
||||
const parent = await this.prisma.partnerAccount.findUnique({
|
||||
where: { id: BigInt(dto.parentAccountId) },
|
||||
});
|
||||
if (!parent) throw new BadRequestException('主账号不存在');
|
||||
if (parent.isPrimary !== 1) throw new BadRequestException('仅可向主账号添加子账号');
|
||||
if (dto.partnerId && dto.partnerId !== parent.partnerId.toString()) {
|
||||
throw new BadRequestException('开城合伙人与主账号不匹配');
|
||||
}
|
||||
|
||||
const account = await this.prisma.partnerAccount.create({
|
||||
data: {
|
||||
partnerId: parent.partnerId,
|
||||
phone,
|
||||
name: dto.name.trim(),
|
||||
staffRole: (dto.staffRole ?? 'INTERNAL') as 'PARTNER' | 'INTERNAL' | 'PROMOTER',
|
||||
isPrimary: 0,
|
||||
parentAccountId: parent.id,
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
include: { partner: { select: { id: true, companyName: true } } },
|
||||
});
|
||||
return serializeBigInt(account);
|
||||
}
|
||||
|
||||
const partner = await this.prisma.partner.findUnique({ where: { id: BigInt(dto.partnerId!) } });
|
||||
if (!partner) throw new BadRequestException('开城合伙人不存在');
|
||||
const account = await this.prisma.partnerAccount.create({
|
||||
data: {
|
||||
partnerId: partner.id,
|
||||
phone: dto.phone,
|
||||
name: dto.name,
|
||||
phone,
|
||||
name: dto.name.trim(),
|
||||
staffRole: dto.staffRole ? (dto.staffRole as 'PARTNER' | 'INTERNAL' | 'PROMOTER') : undefined,
|
||||
isPrimary: 0,
|
||||
},
|
||||
include: { partner: { select: { id: true, companyName: true } } },
|
||||
});
|
||||
return serializeBigInt(account);
|
||||
}
|
||||
@@ -162,4 +250,14 @@ export class AdminPartnersService {
|
||||
const account = await this.prisma.partnerAccount.update({ where: { id }, data });
|
||||
return serializeBigInt(account);
|
||||
}
|
||||
|
||||
async deletePartnerSubAccount(id: bigint) {
|
||||
const account = await this.prisma.partnerAccount.findUnique({ where: { id } });
|
||||
if (!account) throw new NotFoundException('开城合伙人账号不存在');
|
||||
if (!account.parentAccountId) {
|
||||
throw new BadRequestException('仅可删除子账号');
|
||||
}
|
||||
await this.prisma.partnerAccount.delete({ where: { id } });
|
||||
return { ok: true };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,9 +211,10 @@ export class UpdatePartnerAccountDto {
|
||||
}
|
||||
|
||||
export class CreatePartnerAccountDto {
|
||||
@ValidateIf((o: CreatePartnerAccountDto) => !o.parentAccountId)
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
partnerId: string;
|
||||
partnerId?: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@@ -226,6 +227,11 @@ export class CreatePartnerAccountDto {
|
||||
@IsOptional()
|
||||
@IsIn(['PARTNER', 'INTERNAL', 'PROMOTER'])
|
||||
staffRole?: string;
|
||||
|
||||
/** 主账号 ID;传入则创建子账号 */
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
parentAccountId?: string;
|
||||
}
|
||||
|
||||
export class CreateCityDto {
|
||||
|
||||
Reference in New Issue
Block a user