feat: more sub plan info;fix: emprt index (#4997)

* feat: more sub plan info

* fix: emprt index

* doc
This commit is contained in:
Archer
2025-06-10 19:01:10 +08:00
committed by GitHub
parent ccae96a981
commit a848c2e3ba
26 changed files with 333 additions and 166 deletions

View File

@@ -112,7 +112,7 @@ const RechargeModal = ({
<Box>{`${t('common:support.user.team.Dataset usage')}:`}</Box>
<Box
ml={2}
>{`${teamPlanStatus?.usedDatasetSize} / ${teamPlanStatus?.datasetMaxSize || t('account_info:unlimited')}`}</Box>
>{`${teamPlanStatus?.usedDatasetIndexSize} / ${teamPlanStatus?.datasetMaxSize || t('account_info:unlimited')}`}</Box>
<Box ml={5}>{`${t('common:support.wallet.subscription.AI points usage')}:`}</Box>
<Box
ml={2}

View File

@@ -8,6 +8,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
import { useTranslation } from 'next-i18next';
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import dynamic from 'next/dynamic';
import type { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
const ModelPriceModal = dynamic(() =>
import('@/components/core/ai/ModelTable').then((mod) => mod.ModelPriceModal)
@@ -15,10 +16,12 @@ const ModelPriceModal = dynamic(() =>
const StandardPlanContentList = ({
level,
mode
mode,
standplan
}: {
level: `${StandardSubLevelEnum}`;
mode: `${SubModeEnum}`;
standplan?: TeamSubSchema;
}) => {
const { t } = useTranslation();
const { subPlans } = useSystemStore();
@@ -31,9 +34,9 @@ const StandardPlanContentList = ({
price: plan.price * (mode === SubModeEnum.month ? 1 : 10),
level: level as `${StandardSubLevelEnum}`,
...standardSubLevelMap[level as `${StandardSubLevelEnum}`],
maxTeamMember: plan.maxTeamMember,
maxAppAmount: plan.maxAppAmount,
maxDatasetAmount: plan.maxDatasetAmount,
maxTeamMember: standplan?.maxTeamMember || plan.maxTeamMember,
maxAppAmount: standplan?.maxApp || plan.maxAppAmount,
maxDatasetAmount: standplan?.maxDataset || plan.maxDatasetAmount,
chatHistoryStoreDuration: plan.chatHistoryStoreDuration,
maxDatasetSize: plan.maxDatasetSize,
permissionCustomApiKey: plan.permissionCustomApiKey,
@@ -43,7 +46,14 @@ const StandardPlanContentList = ({
permissionWebsiteSync: plan.permissionWebsiteSync,
permissionTeamOperationLog: plan.permissionTeamOperationLog
};
}, [subPlans?.standard, level, mode]);
}, [
subPlans?.standard,
level,
mode,
standplan?.maxTeamMember,
standplan?.maxApp,
standplan?.maxDataset
]);
return planContent ? (
<Grid gap={4} fontSize={'sm'} fontWeight={500}>

View File

@@ -1,5 +1,5 @@
'use client';
import React, { useCallback, useEffect, useMemo } from 'react';
import React, { useCallback, useMemo } from 'react';
import {
Box,
Flex,
@@ -70,10 +70,6 @@ const Info = () => {
const standardPlan = teamPlanStatus?.standardConstants;
const { isOpen: isOpenContact, onClose: onCloseContact, onOpen: onOpenContact } = useDisclosure();
useMount(() => {
initUserInfo();
});
return (
<AccountContainer>
<Box py={[3, '28px']} px={[5, 10]} mx={'auto'}>
@@ -402,55 +398,77 @@ const PlanUsage = () => {
}
});
const datasetUsageMap = useMemo(() => {
const valueColorSchema = useCallback((val: number) => {
if (val < 50) return 'green';
if (val < 80) return 'yellow';
return 'red';
}, []);
const datasetIndexUsageMap = useMemo(() => {
if (!teamPlanStatus) {
return {
colorScheme: 'green',
value: 0,
maxSize: t('account_info:unlimited'),
usedSize: 0
max: t('account_info:unlimited'),
rate: 0
};
}
const rate = teamPlanStatus.usedDatasetSize / teamPlanStatus.datasetMaxSize;
const colorScheme = (() => {
if (rate < 0.5) return 'green';
if (rate < 0.8) return 'yellow';
return 'red';
})();
const rate = teamPlanStatus.usedDatasetIndexSize / teamPlanStatus.datasetMaxSize;
return {
colorScheme,
value: rate * 100,
maxSize: teamPlanStatus.datasetMaxSize || t('account_info:unlimited'),
usedSize: teamPlanStatus.usedDatasetSize
value: teamPlanStatus.usedDatasetIndexSize,
rate: rate * 100,
max: teamPlanStatus.datasetMaxSize || 1
};
}, [teamPlanStatus, t]);
}, [t, teamPlanStatus]);
const aiPointsUsageMap = useMemo(() => {
if (!teamPlanStatus) {
return {
colorScheme: 'green',
value: 0,
maxSize: t('account_info:unlimited'),
usedSize: 0
max: t('account_info:unlimited'),
rate: 0
};
}
const rate = teamPlanStatus.usedPoints / teamPlanStatus.totalPoints;
const colorScheme = (() => {
if (rate < 0.5) return 'green';
if (rate < 0.8) return 'yellow';
return 'red';
})();
return {
colorScheme,
value: rate * 100,
max: teamPlanStatus.totalPoints ? teamPlanStatus.totalPoints : t('account_info:unlimited'),
used: teamPlanStatus.usedPoints ? Math.round(teamPlanStatus.usedPoints) : 0
value: Math.round(teamPlanStatus.usedPoints),
max: teamPlanStatus.totalPoints,
rate: (teamPlanStatus.usedPoints / teamPlanStatus.totalPoints) * 100
};
}, [teamPlanStatus, t]);
}, [t, teamPlanStatus]);
const limitData = useMemo(() => {
if (!teamPlanStatus) {
return [];
}
return [
{
label: t('account_info:member_amount'),
value: teamPlanStatus.usedMember,
max: teamPlanStatus?.standardConstants?.maxTeamMember || t('account_info:unlimited'),
rate:
(teamPlanStatus.usedMember / (teamPlanStatus?.standardConstants?.maxTeamMember || 1)) *
100
},
{
label: t('account_info:app_amount'),
value: teamPlanStatus.usedAppAmount,
max: teamPlanStatus?.standardConstants?.maxAppAmount || t('account_info:unlimited'),
rate:
(teamPlanStatus.usedAppAmount / (teamPlanStatus?.standardConstants?.maxAppAmount || 1)) *
100
},
{
label: t('account_info:dataset_amount'),
value: teamPlanStatus.usedDatasetSize,
max: teamPlanStatus?.standardConstants?.maxDatasetAmount || t('account_info:unlimited'),
rate:
(teamPlanStatus.usedDatasetSize /
(teamPlanStatus?.standardConstants?.maxDatasetAmount || 1)) *
100
}
];
}, [t, teamPlanStatus]);
return standardPlan ? (
<Box mt={[6, 0]}>
@@ -531,6 +549,7 @@ const PlanUsage = () => {
<StandardPlanContentList
level={standardPlan?.currentSubLevel}
mode={standardPlan.currentMode}
standplan={standardPlan}
/>
</Box>
</Box>
@@ -574,15 +593,15 @@ const PlanUsage = () => {
{t('account_info:knowledge_base_capacity')}
</Box>
<Box color={'myGray.600'} ml={2}>
{datasetUsageMap.usedSize}/{datasetUsageMap.maxSize}
{datasetIndexUsageMap.value}/{datasetIndexUsageMap.max}
</Box>
</Flex>
</Flex>
<Box mt={3}>
<Box mt={1}>
<Progress
size={'sm'}
value={datasetUsageMap.value}
colorScheme={datasetUsageMap.colorScheme}
value={datasetIndexUsageMap.rate}
colorScheme={valueColorSchema(datasetIndexUsageMap.rate)}
borderRadius={'md'}
isAnimated
hasStripe
@@ -591,7 +610,7 @@ const PlanUsage = () => {
/>
</Box>
</Box>
<Box mt="9" width={'100%'} fontSize={'sm'}>
<Box mt="6" width={'100%'} fontSize={'sm'}>
<Flex alignItems={'center'}>
<Flex alignItems={'center'}>
<Box fontWeight={'bold'} color={'myGray.900'}>
@@ -599,15 +618,15 @@ const PlanUsage = () => {
</Box>
<QuestionTip ml={1} label={t('account_info:ai_points_usage_tip')}></QuestionTip>
<Box color={'myGray.600'} ml={2}>
{aiPointsUsageMap.used}/{aiPointsUsageMap.max}
{aiPointsUsageMap.value}/{aiPointsUsageMap.max}
</Box>
</Flex>
</Flex>
<Box mt={3}>
<Box mt={1}>
<Progress
size={'sm'}
value={aiPointsUsageMap.value}
colorScheme={aiPointsUsageMap.colorScheme}
value={aiPointsUsageMap.rate}
colorScheme={valueColorSchema(aiPointsUsageMap.rate)}
borderRadius={'md'}
isAnimated
hasStripe
@@ -616,6 +635,42 @@ const PlanUsage = () => {
/>
</Box>
</Box>
<MyDivider />
{limitData.map((item) => {
return (
<Box
key={item.label}
_notFirst={{
mt: 4
}}
width={'100%'}
fontSize={'sm'}
>
<Flex alignItems={'center'}>
<Box fontWeight={'bold'} color={'myGray.900'}>
{item.label}
</Box>
<Box color={'myGray.600'} ml={2}>
{item.value}/{item.max}
</Box>
</Flex>
<Box mt={1}>
<Progress
size={'sm'}
value={item.rate}
colorScheme={valueColorSchema(item.rate)}
borderRadius={'md'}
isAnimated
hasStripe
borderWidth={'1px'}
borderColor={'borderColor.low'}
/>
</Box>
</Box>
);
})}
</Box>
{isOpenStandardModal && <StandDetailModal onClose={onCloseStandardModal} />}
{isOpenRedeemCouponModal && (

View File

@@ -12,7 +12,7 @@ import { getCollectionWithDataset } from '@fastgpt/service/core/dataset/controll
import { pushGenerateVectorUsage } from '@/service/support/wallet/usage/push';
import type { InsertOneDatasetDataProps } from '@/global/core/dataset/api';
import { simpleText } from '@fastgpt/global/common/string/tools';
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { checkDatasetIndexLimit } from '@fastgpt/service/support/permission/teamLimit';
import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
@@ -41,9 +41,9 @@ async function handler(req: NextApiRequest) {
per: WritePermissionVal
});
await checkDatasetLimit({
await checkDatasetIndexLimit({
teamId,
insertLen: 1
insertLen: 1 + (indexes?.length || 0)
});
const [

View File

@@ -2,7 +2,7 @@
import type { NextApiResponse } from 'next';
import type { PushDatasetDataProps } from '@fastgpt/global/core/dataset/api.d';
import { authDatasetCollection } from '@fastgpt/service/support/permission/dataset/auth';
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { checkDatasetIndexLimit } from '@fastgpt/service/support/permission/teamLimit';
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
import { pushDataListToTrainingQueue } from '@fastgpt/service/core/dataset/training/controller';
import { NextAPI } from '@/service/middleware/entry';
@@ -35,7 +35,7 @@ async function handler(req: ApiRequestProps<PushDatasetDataProps>, res: NextApiR
});
// auth dataset limit
await checkDatasetLimit({
await checkDatasetIndexLimit({
teamId,
insertLen: predictDataLimitLength(getTrainingModeByCollection(collection), data)
});

View File

@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { checkDatasetIndexLimit } from '@fastgpt/service/support/permission/teamLimit';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
@@ -19,7 +19,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const numberSize = Number(size);
await checkDatasetLimit({
await checkDatasetIndexLimit({
teamId,
insertLen: numberSize
});

View File

@@ -2,17 +2,53 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { getTeamPlanStatus } from '@fastgpt/service/support/wallet/sub/utils';
import { NextAPI } from '@/service/middleware/entry';
import type { ClientTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type';
import { MongoApp } from '@fastgpt/service/core/app/schema';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { getVectorCountByTeamId } from '@fastgpt/service/common/vectorDB/controller';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant';
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
async function handler(
req: NextApiRequest,
res: NextApiResponse<any>
): Promise<ClientTeamPlanStatusType | undefined> {
try {
const { teamId } = await authCert({
req,
authToken: true
});
return getTeamPlanStatus({
teamId
});
const [planStatus, usedMember, usedAppAmount, usedDatasetSize, usedDatasetIndexSize] =
await Promise.all([
getTeamPlanStatus({
teamId
}),
MongoTeamMember.countDocuments({
teamId,
status: { $ne: TeamMemberStatusEnum.leave }
}),
MongoApp.countDocuments({
teamId,
type: {
$in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool]
}
}),
MongoDataset.countDocuments({
type: { $ne: DatasetTypeEnum.folder }
}),
getVectorCountByTeamId(teamId)
]);
return {
...planStatus,
usedMember,
usedAppAmount,
usedDatasetSize,
usedDatasetIndexSize
};
} catch (error) {}
}

View File

@@ -73,13 +73,11 @@ const formatIndexes = async ({
};
// If index not type, set it to custom
indexes = indexes
.map((item) => ({
text: typeof item.text === 'string' ? item.text : String(item.text),
type: item.type || DatasetDataIndexTypeEnum.custom,
dataId: item.dataId
}))
.filter((item) => !!item.text.trim());
indexes = indexes.map((item) => ({
text: typeof item.text === 'string' ? item.text : String(item.text),
type: item.type || DatasetDataIndexTypeEnum.custom,
dataId: item.dataId
}));
// Recompute default indexes, Merge ids of the same index, reduce the number of rebuilds
const defaultIndexes = await getDefaultIndex({ q, a, indexSize });
@@ -134,7 +132,7 @@ const formatIndexes = async ({
)
).flat();
return chekcIndexes;
return chekcIndexes.filter((item) => !!item.text.trim());
};
/* insert data.
* 1. create data id

View File

@@ -20,7 +20,7 @@ import { delay } from '@fastgpt/service/common/bullmq';
import { rawText2Chunks, readDatasetSourceRawText } from '@fastgpt/service/core/dataset/read';
import { getLLMModel } from '@fastgpt/service/core/ai/model';
import { getLLMMaxChunkSize } from '@fastgpt/global/core/dataset/training/utils';
import { checkDatasetLimit } from '@fastgpt/service/support/permission/teamLimit';
import { checkDatasetIndexLimit } from '@fastgpt/service/support/permission/teamLimit';
import { predictDataLimitLength } from '@fastgpt/global/core/dataset/utils';
import { getTrainingModeByCollection } from '@fastgpt/service/core/dataset/collection/utils';
import { pushDataListToTrainingQueue } from '@fastgpt/service/core/dataset/training/controller';
@@ -258,7 +258,7 @@ export const datasetParseQueue = async (): Promise<any> => {
// Check dataset limit
try {
await checkDatasetLimit({
await checkDatasetIndexLimit({
teamId: data.teamId,
insertLen: predictDataLimitLength(trainingMode, chunks)
});

View File

@@ -15,7 +15,10 @@ import type {
TeamMemberItemType,
TeamMemberSchema
} from '@fastgpt/global/support/user/team/type.d';
import type { FeTeamPlanStatusType, TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
import type {
ClientTeamPlanStatusType,
TeamSubSchema
} from '@fastgpt/global/support/wallet/sub/type';
import type { TeamInvoiceHeaderType } from '@fastgpt/global/support/user/team/type';
import type { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type';
import type {
@@ -100,7 +103,7 @@ export const checkTeamDatasetSizeLimit = (size: number) =>
/* plans */
export const getTeamPlanStatus = () =>
GET<FeTeamPlanStatusType>(`/support/user/team/plan/getTeamPlanStatus`, { maxQuantity: 1 });
GET<ClientTeamPlanStatusType>(`/support/user/team/plan/getTeamPlanStatus`, { maxQuantity: 1 });
export const getTeamPlans = () =>
GET<TeamSubSchema[]>(`/proApi/support/user/team/plan/getTeamPlans`);

View File

@@ -1,13 +1,11 @@
import { create, devtools, persist, immer } from '@fastgpt/web/common/zustand';
import type { UserUpdateParams } from '@/types/user';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { getTokenLogin, putUserInfo } from '@/web/support/user/api';
import type { OrgType } from '@fastgpt/global/support/user/team/org/type';
import type { UserType } from '@fastgpt/global/support/user/type.d';
import type { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type';
import type { ClientTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type';
import { getTeamPlanStatus } from './team/api';
import { getGroupList } from './team/group/api';
type State = {
systemMsgReadId: string;
@@ -22,7 +20,7 @@ type State = {
setUserInfo: (user: UserType | null) => void;
updateUserInfo: (user: UserUpdateParams) => Promise<void>;
teamPlanStatus: FeTeamPlanStatusType | null;
teamPlanStatus: ClientTeamPlanStatusType | null;
initTeamPlanStatus: () => Promise<any>;
teamOrgs: OrgType[];