Add for leave family
This commit is contained in:
@@ -212,12 +212,76 @@ export class FamilyService {
|
||||
|
||||
for (const member of memberships) {
|
||||
|
||||
await this.removeMember(userId, member.id);
|
||||
await this.leaveFamily(userId, member.groupId);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async leaveFamily(requesterId: string, groupId: string, newOwnerUserId?: string) {
|
||||
|
||||
const group = await this.getGroupWithMembers(groupId);
|
||||
|
||||
this.assertMember(group, requesterId);
|
||||
|
||||
const member = group.members.find((item) => item.userId === requesterId);
|
||||
|
||||
if (!member) {
|
||||
|
||||
throw new NotFoundException('Участник не найден');
|
||||
|
||||
}
|
||||
|
||||
const isOwner = group.ownerId === requesterId;
|
||||
|
||||
const otherHumans = this.getHumanMembers(group.members).filter((item) => item.userId !== requesterId);
|
||||
|
||||
if (isOwner && otherHumans.length === 0) {
|
||||
|
||||
await this.destroyGroupWithCleanup(group);
|
||||
|
||||
return { count: 1, deleted: true };
|
||||
|
||||
}
|
||||
|
||||
if (isOwner && otherHumans.length > 0) {
|
||||
|
||||
if (!newOwnerUserId) {
|
||||
|
||||
throw new BadRequestException('Перед выходом передайте управление семьёй другому участнику');
|
||||
|
||||
}
|
||||
|
||||
if (!otherHumans.some((item) => item.userId === newOwnerUserId)) {
|
||||
|
||||
throw new BadRequestException('Новый глава семьи должен быть участником семьи');
|
||||
|
||||
}
|
||||
|
||||
await this.transferOwnership(group, requesterId, newOwnerUserId);
|
||||
|
||||
const updatedGroup = await this.getGroupWithMembers(groupId);
|
||||
|
||||
const updatedMember = updatedGroup.members.find((item) => item.userId === requesterId);
|
||||
|
||||
if (!updatedMember) {
|
||||
|
||||
throw new NotFoundException('Участник не найден');
|
||||
|
||||
}
|
||||
|
||||
await this.executeMemberLeave(updatedGroup, updatedMember, { voluntary: true });
|
||||
|
||||
return { count: 1, deleted: false };
|
||||
|
||||
}
|
||||
|
||||
await this.executeMemberLeave(group, member, { voluntary: true });
|
||||
|
||||
return { count: 1, deleted: false };
|
||||
|
||||
}
|
||||
|
||||
private async destroyGroupWithCleanup(group: {
|
||||
|
||||
id: string;
|
||||
@@ -399,7 +463,7 @@ export class FamilyService {
|
||||
|
||||
where: { id: memberId },
|
||||
|
||||
include: { group: { include: { members: true } } }
|
||||
include: { group: { include: { members: { include: this.memberUserInclude() } } } }
|
||||
|
||||
});
|
||||
|
||||
@@ -413,7 +477,13 @@ export class FamilyService {
|
||||
|
||||
const isOwner = member.group.ownerId === requesterId;
|
||||
|
||||
if (!isSelf && !isOwner) {
|
||||
if (isSelf) {
|
||||
|
||||
throw new BadRequestException('Для выхода из семьи используйте соответствующую функцию');
|
||||
|
||||
}
|
||||
|
||||
if (!isOwner) {
|
||||
|
||||
throw new ForbiddenException('Недостаточно прав для удаления участника');
|
||||
|
||||
@@ -425,17 +495,175 @@ export class FamilyService {
|
||||
|
||||
}
|
||||
|
||||
await this.executeMemberLeave(member.group, member, { voluntary: false });
|
||||
|
||||
return { count: 1 };
|
||||
|
||||
}
|
||||
|
||||
private async transferOwnership(
|
||||
|
||||
group: {
|
||||
|
||||
id: string;
|
||||
|
||||
name: string;
|
||||
|
||||
members: Array<{ userId: string; user?: { displayName: string } }>;
|
||||
|
||||
},
|
||||
|
||||
fromUserId: string,
|
||||
|
||||
toUserId: string
|
||||
|
||||
) {
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
|
||||
await tx.chatRoomMember.deleteMany({ where: { userId: member.userId, room: { groupId: member.groupId } } });
|
||||
await tx.familyGroup.update({ where: { id: group.id }, data: { ownerId: toUserId } });
|
||||
|
||||
await tx.familyMember.delete({ where: { id: memberId } });
|
||||
await tx.familyMember.updateMany({ where: { groupId: group.id, userId: fromUserId }, data: { role: 'member' } });
|
||||
|
||||
await tx.familyMember.updateMany({ where: { groupId: group.id, userId: toUserId }, data: { role: 'owner' } });
|
||||
|
||||
});
|
||||
|
||||
return { count: 1 };
|
||||
const oldOwnerName = group.members.find((item) => item.userId === fromUserId)?.user?.displayName ?? 'Участник';
|
||||
|
||||
const title = 'Вы стали главой семьи';
|
||||
|
||||
const body = `${oldOwnerName} передал(а) вам управление семьёй «${group.name}»`;
|
||||
|
||||
await this.notifications.create(
|
||||
|
||||
toUserId,
|
||||
|
||||
'family_ownership_transferred',
|
||||
|
||||
title,
|
||||
|
||||
body,
|
||||
|
||||
{ groupId: group.id, groupName: group.name, previousOwnerId: fromUserId }
|
||||
|
||||
);
|
||||
|
||||
await this.notifications.publishRealtime(
|
||||
|
||||
toUserId,
|
||||
|
||||
'family_ownership_transferred',
|
||||
|
||||
title,
|
||||
|
||||
body,
|
||||
|
||||
{ groupId: group.id, groupName: group.name, previousOwnerId: fromUserId }
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
private async executeMemberLeave(
|
||||
|
||||
group: {
|
||||
|
||||
id: string;
|
||||
|
||||
name: string;
|
||||
|
||||
members: Array<{ id: string; userId: string; role: string; user?: { displayName: string; linkedBotId?: string | null } }>;
|
||||
|
||||
},
|
||||
|
||||
member: { id: string; userId: string; user?: { displayName: string } },
|
||||
|
||||
options: { voluntary: boolean }
|
||||
|
||||
) {
|
||||
|
||||
const displayName = member.user?.displayName ?? (await this.loadMemberUser(member.userId))?.displayName ?? 'Участник';
|
||||
|
||||
await this.prisma.$transaction(async (tx) => {
|
||||
|
||||
await tx.chatRoomMember.deleteMany({ where: { userId: member.userId, room: { groupId: group.id } } });
|
||||
|
||||
await tx.familyMember.delete({ where: { id: member.id } });
|
||||
|
||||
});
|
||||
|
||||
const recipients = group.members.filter(
|
||||
|
||||
(item) => item.userId !== member.userId && item.role !== 'bot' && !item.user?.linkedBotId
|
||||
|
||||
);
|
||||
|
||||
const eventType = options.voluntary ? 'family_member_left' : 'family_member_removed';
|
||||
|
||||
const title = options.voluntary ? 'Участник покинул семью' : 'Участник удалён из семьи';
|
||||
|
||||
const body = options.voluntary
|
||||
|
||||
? `${displayName} покинул(а) семью «${group.name}»`
|
||||
|
||||
: `${displayName} был(а) удалён(а) из семьи «${group.name}»`;
|
||||
|
||||
for (const recipient of recipients) {
|
||||
|
||||
await this.notifications.create(
|
||||
|
||||
recipient.userId,
|
||||
|
||||
eventType,
|
||||
|
||||
title,
|
||||
|
||||
body,
|
||||
|
||||
{ groupId: group.id, groupName: group.name, userId: member.userId, userName: displayName }
|
||||
|
||||
);
|
||||
|
||||
await this.notifications.publishRealtime(
|
||||
|
||||
recipient.userId,
|
||||
|
||||
eventType,
|
||||
|
||||
title,
|
||||
|
||||
body,
|
||||
|
||||
{ groupId: group.id, groupName: group.name, userId: member.userId, userName: displayName }
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
await this.notifications.publishRealtime(
|
||||
|
||||
member.userId,
|
||||
|
||||
'family_left_self',
|
||||
|
||||
'Вы покинули семью',
|
||||
|
||||
`Вы покинули семью «${group.name}»`,
|
||||
|
||||
{ groupId: group.id, groupName: group.name }
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
private getHumanMembers(
|
||||
|
||||
members: Array<{ userId: string; role: string; user?: { linkedBotId?: string | null } }>
|
||||
|
||||
) {
|
||||
|
||||
return members.filter((member) => member.role !== 'bot' && !member.user?.linkedBotId);
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user