fix: pagination bug (#3577)

This commit is contained in:
Finley Ge
2025-01-14 11:47:47 +08:00
committed by archer
parent 19abfd1a3e
commit f468ba2f30
7 changed files with 129 additions and 156 deletions

View File

@@ -1,4 +1,7 @@
import { SourceMemberType } from '@fastgpt/global/support/user/type';
import { MongoTeam } from './team/teamSchema'; import { MongoTeam } from './team/teamSchema';
import { MongoTeamMember } from './team/teamMemberSchema';
import { ClientSession } from '../../common/mongo';
/* export dataset limit */ /* export dataset limit */
export const updateExportDatasetLimit = async (teamId: string) => { export const updateExportDatasetLimit = async (teamId: string) => {
@@ -67,3 +70,43 @@ export const checkWebSyncLimit = async ({
return Promise.reject(`每个团队,每 ${limitMinutes} 分钟仅使用一次同步功能。`); return Promise.reject(`每个团队,每 ${limitMinutes} 分钟仅使用一次同步功能。`);
} }
}; };
/**
* This function will add a property named sourceMember to the list passed in.
* @param list The list to add the sourceMember property to. [TmbId] property is required.
* @error If member is not found, this item will be skipped.
* @returns The list with the sourceMember property added.
*/
export async function addSourceMember<T extends { tmbId: string }>({
list,
teamId,
session
}: {
list: T[];
teamId?: string;
session?: ClientSession;
}): Promise<Array<T & { sourceMember: SourceMemberType }>> {
if (!list.length) return [];
const tmbList = await MongoTeamMember.find(
{
_id: { $in: list.map((item) => String(item.tmbId)) },
...(teamId && { teamId })
},
'tmbId name avatar status',
{
session
}
).lean();
return list
.map((item) => {
const tmb = tmbList.find((tmb) => String(tmb._id) === String(item.tmbId));
if (!tmb) return;
return {
...item,
...(tmb && { sourceMember: { name: tmb.name, avatar: tmb.avatar, status: tmb.status } })
};
})
.filter(Boolean) as Array<T & { sourceMember: SourceMemberType }>;
}

View File

@@ -44,7 +44,8 @@ function GroupEditModal({ onClose, editGroupId }: { onClose: () => void; editGro
members: allMembers, members: allMembers,
refetchGroups, refetchGroups,
groups, groups,
refetchMembers refetchMembers,
MemberScrollData
} = useContextSelector(TeamContext, (v) => v); } = useContextSelector(TeamContext, (v) => v);
const group = useMemo(() => { const group = useMemo(() => {
@@ -156,31 +157,33 @@ function GroupEditModal({ onClose, editGroupId }: { onClose: () => void; editGro
}} }}
/> />
<Flex flexDirection="column" mt={3} flexGrow="1" overflow={'auto'} maxH={'400px'}> <Flex flexDirection="column" mt={3} flexGrow="1" overflow={'auto'} maxH={'400px'}>
{filtered.map((member) => { <MemberScrollData>
return ( {filtered.map((member) => {
<HStack return (
py="2" <HStack
px={3} py="2"
borderRadius={'md'} px={3}
alignItems="center" borderRadius={'md'}
key={member.tmbId} alignItems="center"
cursor={'pointer'} key={member.tmbId}
_hover={{ cursor={'pointer'}
bg: 'myGray.50', _hover={{
...(!isSelected(member.tmbId) ? { svg: { color: 'myGray.50' } } : {}) bg: 'myGray.50',
}} ...(!isSelected(member.tmbId) ? { svg: { color: 'myGray.50' } } : {})
_notLast={{ mb: 2 }} }}
onClick={() => handleToggleSelect(member.tmbId)} _notLast={{ mb: 2 }}
> onClick={() => handleToggleSelect(member.tmbId)}
<Checkbox >
isChecked={!!isSelected(member.tmbId)} <Checkbox
icon={<MyIcon name={'common/check'} w={'12px'} />} isChecked={!!isSelected(member.tmbId)}
/> icon={<MyIcon name={'common/check'} w={'12px'} />}
<Avatar src={member.avatar} w="1.5rem" borderRadius={'50%'} /> />
<Box>{member.memberName}</Box> <Avatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
</HStack> <Box>{member.memberName}</Box>
); </HStack>
})} );
})}
</MemberScrollData>
</Flex> </Flex>
</Flex> </Flex>
<Flex borderLeft="1px" borderColor="myGray.200" flexDirection="column" p="4" h={'100%'}> <Flex borderLeft="1px" borderColor="myGray.200" flexDirection="column" p="4" h={'100%'}>

View File

@@ -22,7 +22,6 @@ import { useMemo, useState } from 'react';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { TeamContext } from '../context'; import { TeamContext } from '../context';
import { OrgType } from '@fastgpt/global/support/user/team/org/type'; import { OrgType } from '@fastgpt/global/support/user/team/org/type';
import dynamic from 'next/dynamic';
export type GroupFormType = { export type GroupFormType = {
members: { members: {
@@ -51,7 +50,7 @@ function OrgMemberManageModal({
onClose: () => void; onClose: () => void;
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
const allMembers = useContextSelector(TeamContext, (v) => v.members); const { members: allMembers, MemberScrollData } = useContextSelector(TeamContext, (v) => v);
const [selectedMembers, setSelectedMembers] = useState<string[]>( const [selectedMembers, setSelectedMembers] = useState<string[]>(
currentOrg.members.map((item) => item.tmbId) currentOrg.members.map((item) => item.tmbId)
@@ -124,32 +123,34 @@ function OrgMemberManageModal({
}} }}
/> />
<Flex flexDirection="column" mt={3} flexGrow="1" overflow={'auto'} maxH={'400px'}> <Flex flexDirection="column" mt={3} flexGrow="1" overflow={'auto'} maxH={'400px'}>
{filterMembers.map((member) => { <MemberScrollData>
return ( {filterMembers.map((member) => {
<HStack return (
py="2" <HStack
px={3} py="2"
borderRadius={'md'} px={3}
alignItems="center" borderRadius={'md'}
key={member.tmbId} alignItems="center"
cursor={'pointer'} key={member.tmbId}
_hover={{ cursor={'pointer'}
bg: 'myGray.50', _hover={{
...(!isSelected(member.tmbId) ? { svg: { color: 'myGray.50' } } : {}) bg: 'myGray.50',
}} ...(!isSelected(member.tmbId) ? { svg: { color: 'myGray.50' } } : {})
_notLast={{ mb: 2 }} }}
onClick={() => handleToggleSelect(member.tmbId)} _notLast={{ mb: 2 }}
> onClick={() => handleToggleSelect(member.tmbId)}
<Checkbox >
isChecked={!!isSelected(member.tmbId)} <Checkbox
icon={<CheckboxIcon name={'common/check'} />} isChecked={!!isSelected(member.tmbId)}
pointerEvents="none" icon={<CheckboxIcon name={'common/check'} />}
/> pointerEvents="none"
<Avatar src={member.avatar} w="1.5rem" borderRadius={'50%'} /> />
<Box>{member.memberName}</Box> <Avatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
</HStack> <Box>{member.memberName}</Box>
); </HStack>
})} );
})}
</MemberScrollData>
</Flex> </Flex>
</Flex> </Flex>
<Flex borderLeft="1px" borderColor="myGray.200" flexDirection="column" p="4" h={'100%'}> <Flex borderLeft="1px" borderColor="myGray.200" flexDirection="column" p="4" h={'100%'}>

View File

@@ -10,8 +10,8 @@ import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { readFromSecondary } from '@fastgpt/service/common/mongo/utils'; import { readFromSecondary } from '@fastgpt/service/common/mongo/utils';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant';
import { PaginationResponse } from '@fastgpt/web/common/fetch/type'; import { PaginationResponse } from '@fastgpt/web/common/fetch/type';
import { addSourceMember } from '@fastgpt/service/support/user/utils';
async function handler( async function handler(
req: NextApiRequest, req: NextApiRequest,
@@ -82,14 +82,6 @@ async function handler(
as: 'chatitems' as: 'chatitems'
} }
}, },
{
$lookup: {
from: TeamMemberCollectionName,
localField: 'tmbId',
foreignField: '_id',
as: 'member'
}
},
{ {
$addFields: { $addFields: {
userGoodFeedbackCount: { userGoodFeedbackCount: {
@@ -143,12 +135,7 @@ async function handler(
customFeedbacksCount: 1, customFeedbacksCount: 1,
markCount: 1, markCount: 1,
outLinkUid: 1, outLinkUid: 1,
tmbId: 1, tmbId: 1
sourceMember: {
name: '$member.name',
avatar: '$member.avatar',
status: '$member.status'
}
} }
} }
], ],
@@ -159,8 +146,15 @@ async function handler(
MongoChat.countDocuments(where, { ...readFromSecondary }) MongoChat.countDocuments(where, { ...readFromSecondary })
]); ]);
const listWithSourceMember = await addSourceMember({
list: list,
teamId
});
const listWithoutTmbId = list.filter((item) => !item.tmbId);
return { return {
list, list: listWithSourceMember.concat(listWithoutTmbId),
total total
}; };
} }

View File

@@ -18,7 +18,7 @@ import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { concatPer } from '@fastgpt/service/support/permission/controller'; import { concatPer } from '@fastgpt/service/support/permission/controller';
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers'; import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers'; import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; import { addSourceMember } from '@fastgpt/service/support/user/utils';
export type ListAppBody = { export type ListAppBody = {
parentId?: ParentIdType; parentId?: ParentIdType;
@@ -202,32 +202,9 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
}) })
.filter((app) => app.permission.hasReadPer); .filter((app) => app.permission.hasReadPer);
// get member info return addSourceMember({
const memberInfo = await MongoTeamMember.find( list: formatApps,
{ _id: { $in: formatApps.map((app) => app.tmbId) } }, teamId
'_id name avatar status'
).lean();
return formatApps.map((app) => {
const member = memberInfo.find((item) => String(item._id) === String(app.tmbId))!;
return {
_id: app._id,
tmbId: app.tmbId,
avatar: app.avatar,
type: app.type,
name: app.name,
intro: app.intro,
updateTime: app.updateTime,
permission: app.permission,
pluginData: app.pluginData,
inheritPermission: app.inheritPermission ?? true,
private: app.privateApp,
sourceMember: {
name: member.name,
avatar: member.avatar,
status: member.status
}
};
}); });
} }

View File

@@ -7,7 +7,7 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant'; import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { VersionListItemType } from '@fastgpt/global/core/app/version'; import { VersionListItemType } from '@fastgpt/global/core/app/version';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; import { addSourceMember } from '@fastgpt/service/support/user/utils';
export type versionListBody = PaginationProps<{ export type versionListBody = PaginationProps<{
appId: string; appId: string;
@@ -36,43 +36,21 @@ async function handler(
.limit(pageSize) .limit(pageSize)
.lean(); .lean();
const memberList = await MongoTeamMember.find( return (
{ await addSourceMember({
_id: { $in: versions.map((item) => item.tmbId) } list: versions
}, })
'_id name avatar status' ).map((item) => ({
).lean(); ...item,
isPublish: !!item.isPublish
return versions.map((item) => { }));
const member = memberList.find((member) => String(member._id) === String(item.tmbId));
return {
...item,
sourceMember: {
name: member?.name || '',
avatar: member?.avatar || '',
status: member?.status || ''
}
};
});
})(), })(),
MongoAppVersion.countDocuments({ appId }) MongoAppVersion.countDocuments({ appId })
]); ]);
const versionList = result.map((item) => {
return {
_id: item._id,
appId: item.appId,
versionName: item.versionName,
time: item.time,
isPublish: item.isPublish,
tmbId: item.tmbId,
sourceMember: item.sourceMember
};
});
return { return {
total, total,
list: versionList list: result
}; };
} }

View File

@@ -1,4 +1,3 @@
import type { DatasetListItemType } from '@fastgpt/global/core/dataset/type.d';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth'; import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
@@ -18,8 +17,7 @@ import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers'; import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
import { concatPer } from '@fastgpt/service/support/permission/controller'; import { concatPer } from '@fastgpt/service/support/permission/controller';
import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers'; import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; import { addSourceMember } from '@fastgpt/service/support/user/utils';
import { getVectorModel } from '@fastgpt/service/core/ai/model';
export type GetDatasetListBody = { export type GetDatasetListBody = {
parentId: ParentIdType; parentId: ParentIdType;
@@ -175,30 +173,9 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
}) })
.filter((app) => app.permission.hasReadPer); .filter((app) => app.permission.hasReadPer);
const tmbIds = formatDatasets.map((item) => String(item.tmbId)); return addSourceMember({
const memberInfo = await MongoTeamMember.find({ _id: { $in: tmbIds } }, '_id name avatar').lean(); list: formatDatasets,
teamId
return formatDatasets.map((item) => {
const member =
memberInfo.find((member) => String(member._id) === String(item.tmbId)) ?? memberInfo[0];
return {
_id: item._id,
avatar: item.avatar,
name: item.name,
intro: item.intro,
type: item.type,
permission: item.permission,
vectorModel: getVectorModel(item.vectorModel),
inheritPermission: item.inheritPermission,
tmbId: item.tmbId,
updateTime: item.updateTime,
private: item.privateDataset,
sourceMember: {
name: member!.name,
avatar: member!.avatar
}
};
}); });
} }