From a848c2e3baa7fc30b492e0f6ab58d65bd66db080 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Tue, 10 Jun 2025 19:01:10 +0800 Subject: [PATCH] feat: more sub plan info;fix: emprt index (#4997) * feat: more sub plan info * fix: emprt index * doc --- .../zh-cn/docs/development/upgrading/4912.md | 8 +- packages/global/support/wallet/sub/type.d.ts | 13 +- .../core/dataset/collection/controller.ts | 4 +- packages/service/core/dataset/read.ts | 2 +- .../service/core/dataset/training/schema.ts | 3 +- .../service/support/permission/teamLimit.ts | 121 ++++++++------ packages/service/support/wallet/sub/schema.ts | 3 + packages/service/support/wallet/sub/utils.ts | 47 ++++-- packages/service/worker/utils.ts | 5 +- packages/web/i18n/en/account_info.json | 3 + packages/web/i18n/en/common.json | 8 +- packages/web/i18n/zh-CN/account_info.json | 9 +- packages/web/i18n/zh-CN/common.json | 6 +- packages/web/i18n/zh-Hant/account_info.json | 3 + packages/web/i18n/zh-Hant/common.json | 6 +- .../wallet/NotSufficientModal/index.tsx | 2 +- .../wallet/StandardPlanContentList.tsx | 20 ++- projects/app/src/pages/account/info/index.tsx | 147 ++++++++++++------ .../pages/api/core/dataset/data/insertData.ts | 6 +- .../pages/api/core/dataset/data/pushData.ts | 4 +- .../user/team/limit/datasetSizeLimit.ts | 4 +- .../user/team/plan/getTeamPlanStatus.ts | 44 +++++- .../service/core/dataset/data/controller.ts | 14 +- .../core/dataset/queues/datasetParse.ts | 4 +- projects/app/src/web/support/user/team/api.ts | 7 +- .../app/src/web/support/user/useUserStore.ts | 6 +- 26 files changed, 333 insertions(+), 166 deletions(-) diff --git a/docSite/content/zh-cn/docs/development/upgrading/4912.md b/docSite/content/zh-cn/docs/development/upgrading/4912.md index cfac18765..6ea79d5c0 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/4912.md +++ b/docSite/content/zh-cn/docs/development/upgrading/4912.md @@ -1,10 +1,10 @@ --- -title: 'V4.9.11(进行中)' +title: 'V4.9.12(进行中)' description: 'FastGPT V4.9.12 更新说明' icon: 'upgrade' draft: false toc: true -weight: 789 +weight: 788 --- ## 🚀 新增内容 @@ -17,7 +17,9 @@ weight: 789 1. 密码校验时,增加更多的特殊字符 2. 后端全量计算知识库 chunk 参数,避免自动模式下部分参数未正确使用默认值。 3. 将文本分块移至 worker 线程,避免阻塞。 +4. 展示更多套餐用量信息。 ## 🐛 修复 -1. 自定义问答提取提示词被覆盖。 \ No newline at end of file +1. 自定义问答提取提示词被覆盖。 +2. 模板导入时,存在空 indexes 时,导致数据插入失败。 \ No newline at end of file diff --git a/packages/global/support/wallet/sub/type.d.ts b/packages/global/support/wallet/sub/type.d.ts index 0bd7cf73e..2f89b7c95 100644 --- a/packages/global/support/wallet/sub/type.d.ts +++ b/packages/global/support/wallet/sub/type.d.ts @@ -45,6 +45,9 @@ export type TeamSubSchema = { nextMode: `${SubModeEnum}`; currentSubLevel: StandardSubLevelEnum; nextSubLevel: StandardSubLevelEnum; + maxTeamMember?: number; + maxApp?: number; + maxDataset?: number; totalPoints: number; surplusPoints: number; @@ -52,7 +55,7 @@ export type TeamSubSchema = { currentExtraDatasetSize: number; }; -export type FeTeamPlanStatusType = { +export type TeamPlanStatusType = { [SubTypeEnum.standard]?: TeamSubSchema; standardConstants?: TeamStandardSubPlanItemType; @@ -61,5 +64,11 @@ export type FeTeamPlanStatusType = { // standard + extra datasetMaxSize: number; - usedDatasetSize: number; +}; + +export type ClientTeamPlanStatusType = TeamPlanStatusType & { + usedMember: number; + usedAppAmount: number; + usedDatasetSize: number; + usedDatasetIndexSize: number; }; diff --git a/packages/service/core/dataset/collection/controller.ts b/packages/service/core/dataset/collection/controller.ts index 9a032b5f3..a88ee4ae9 100644 --- a/packages/service/core/dataset/collection/controller.ts +++ b/packages/service/core/dataset/collection/controller.ts @@ -18,7 +18,7 @@ import { BucketNameEnum } from '@fastgpt/global/common/file/constants'; import type { ClientSession } from '../../../common/mongo'; import { createOrGetCollectionTags } from './utils'; import { rawText2Chunks } from '../read'; -import { checkDatasetLimit } from '../../../support/permission/teamLimit'; +import { checkDatasetIndexLimit } from '../../../support/permission/teamLimit'; import { predictDataLimitLength } from '../../../../global/core/dataset/utils'; import { mongoSessionRun } from '../../../common/mongo/sessionRun'; import { createTrainingUsage } from '../../../support/wallet/usage/controller'; @@ -166,7 +166,7 @@ export const createCollectionAndInsertData = async ({ })(); // 2. auth limit - await checkDatasetLimit({ + await checkDatasetIndexLimit({ teamId, insertLen: predictDataLimitLength(trainingMode, chunks) }); diff --git a/packages/service/core/dataset/read.ts b/packages/service/core/dataset/read.ts index f2a4a9c1c..bd7d43f9b 100644 --- a/packages/service/core/dataset/read.ts +++ b/packages/service/core/dataset/read.ts @@ -199,7 +199,7 @@ export const rawText2Chunks = async ({ .map((item) => ({ q: item[0] || '', a: item[1] || '', - indexes: item.slice(2), + indexes: item.slice(2).filter((item) => item.trim()), imageIdList })) .filter((item) => item.q || item.a); diff --git a/packages/service/core/dataset/training/schema.ts b/packages/service/core/dataset/training/schema.ts index eaf85ef3d..b8ca7300d 100644 --- a/packages/service/core/dataset/training/schema.ts +++ b/packages/service/core/dataset/training/schema.ts @@ -83,8 +83,7 @@ const TrainingDataSchema = new Schema({ enum: Object.values(DatasetDataIndexTypeEnum) }, text: { - type: String, - required: true + type: String } } ], diff --git a/packages/service/support/permission/teamLimit.ts b/packages/service/support/permission/teamLimit.ts index a897428d5..40eea3f1b 100644 --- a/packages/service/support/permission/teamLimit.ts +++ b/packages/service/support/permission/teamLimit.ts @@ -5,28 +5,9 @@ import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { TeamErrEnum } from '@fastgpt/global/common/error/code/team'; import { SystemErrEnum } from '@fastgpt/global/common/error/code/system'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; - -export const checkDatasetLimit = async ({ - teamId, - insertLen = 0 -}: { - teamId: string; - insertLen?: number; -}) => { - const { standardConstants, totalPoints, usedPoints, datasetMaxSize, usedDatasetSize } = - await getTeamPlanStatus({ teamId }); - - if (!standardConstants) return; - - if (usedDatasetSize + insertLen >= datasetMaxSize) { - return Promise.reject(TeamErrEnum.datasetSizeNotEnough); - } - - if (usedPoints >= totalPoints) { - return Promise.reject(TeamErrEnum.aiPointsNotEnough); - } - return; -}; +import { MongoTeamMember } from '../user/team/teamMemberSchema'; +import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant'; +import { getVectorCountByTeamId } from '../../common/vectorDB/controller'; export const checkTeamAIPoints = async (teamId: string) => { const { standardConstants, totalPoints, usedPoints } = await getTeamPlanStatus({ @@ -45,6 +26,72 @@ export const checkTeamAIPoints = async (teamId: string) => { }; }; +export const checkTeamMemberLimit = async (teamId: string, newCount: number) => { + const [{ standardConstants }, memberCount] = await Promise.all([ + getTeamStandPlan({ + teamId + }), + MongoTeamMember.countDocuments({ + teamId, + status: { $ne: TeamMemberStatusEnum.leave } + }) + ]); + + if (standardConstants && newCount + memberCount > standardConstants.maxTeamMember) { + return Promise.reject(TeamErrEnum.teamOverSize); + } +}; + +export const checkTeamAppLimit = async (teamId: string, amount = 1) => { + const [{ standardConstants }, appCount] = await Promise.all([ + getTeamStandPlan({ teamId }), + MongoApp.countDocuments({ + teamId, + type: { + $in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool] + } + }) + ]); + + if (standardConstants && appCount + amount >= standardConstants.maxAppAmount) { + return Promise.reject(TeamErrEnum.appAmountNotEnough); + } + + // System check + if (global?.licenseData?.maxApps && typeof global?.licenseData?.maxApps === 'number') { + const totalApps = await MongoApp.countDocuments({ + type: { + $in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool] + } + }); + if (totalApps >= global.licenseData.maxApps) { + return Promise.reject(SystemErrEnum.licenseAppAmountLimit); + } + } +}; + +export const checkDatasetIndexLimit = async ({ + teamId, + insertLen = 0 +}: { + teamId: string; + insertLen?: number; +}) => { + const [{ standardConstants, totalPoints, usedPoints, datasetMaxSize }, usedDatasetIndexSize] = + await Promise.all([getTeamPlanStatus({ teamId }), getVectorCountByTeamId(teamId)]); + + if (!standardConstants) return; + + if (usedDatasetIndexSize + insertLen >= datasetMaxSize) { + return Promise.reject(TeamErrEnum.datasetSizeNotEnough); + } + + if (usedPoints >= totalPoints) { + return Promise.reject(TeamErrEnum.aiPointsNotEnough); + } + return; +}; + export const checkTeamDatasetLimit = async (teamId: string) => { const [{ standardConstants }, datasetCount] = await Promise.all([ getTeamStandPlan({ teamId }), @@ -74,30 +121,12 @@ export const checkTeamDatasetLimit = async (teamId: string) => { } }; -export const checkTeamAppLimit = async (teamId: string, amount = 1) => { - const [{ standardConstants }, appCount] = await Promise.all([ - getTeamStandPlan({ teamId }), - MongoApp.countDocuments({ - teamId, - type: { - $in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool] - } - }) - ]); +export const checkTeamWebSyncPermission = async (teamId: string) => { + const { standardConstants } = await getTeamStandPlan({ + teamId + }); - if (standardConstants && appCount + amount >= standardConstants.maxAppAmount) { - return Promise.reject(TeamErrEnum.appAmountNotEnough); - } - - // System check - if (global?.licenseData?.maxApps && typeof global?.licenseData?.maxApps === 'number') { - const totalApps = await MongoApp.countDocuments({ - type: { - $in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool] - } - }); - if (totalApps >= global.licenseData.maxApps) { - return Promise.reject(SystemErrEnum.licenseAppAmountLimit); - } + if (standardConstants && !standardConstants?.permissionWebsiteSync) { + return Promise.reject(TeamErrEnum.websiteSyncNotEnough); } }; diff --git a/packages/service/support/wallet/sub/schema.ts b/packages/service/support/wallet/sub/schema.ts index 8cd1d1bdc..723199b75 100644 --- a/packages/service/support/wallet/sub/schema.ts +++ b/packages/service/support/wallet/sub/schema.ts @@ -52,6 +52,9 @@ const SubSchema = new Schema({ type: String, enum: Object.values(StandardSubLevelEnum) }, + maxTeamMember: Number, + maxApp: Number, + maxDataset: Number, // stand sub and extra points sub. Plan total points totalPoints: { diff --git a/packages/service/support/wallet/sub/utils.ts b/packages/service/support/wallet/sub/utils.ts index d24e709c0..b6be0e662 100644 --- a/packages/service/support/wallet/sub/utils.ts +++ b/packages/service/support/wallet/sub/utils.ts @@ -6,10 +6,9 @@ import { } from '@fastgpt/global/support/wallet/sub/constants'; import { MongoTeamSub } from './schema'; import { - type FeTeamPlanStatusType, + type TeamPlanStatusType, type TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type.d'; -import { getVectorCountByTeamId } from '../../../common/vectorDB/controller'; import dayjs from 'dayjs'; import { type ClientSession } from '../../../common/mongo'; import { addMonths } from 'date-fns'; @@ -44,12 +43,21 @@ export const getTeamStandPlan = async ({ teamId }: { teamId: string }) => { const standardPlans = global.subPlans?.standard; const standard = plans[0]; + const standardConstants = + standard?.currentSubLevel && standardPlans + ? standardPlans[standard.currentSubLevel] + : undefined; + return { [SubTypeEnum.standard]: standard, - standardConstants: - standard?.currentSubLevel && standardPlans - ? standardPlans[standard.currentSubLevel] - : undefined + standardConstants: standardConstants + ? { + ...standardConstants, + maxTeamMember: standard?.maxTeamMember || standardConstants.maxTeamMember, + maxAppAmount: standard?.maxApp || standardConstants.maxAppAmount, + maxDatasetAmount: standard?.maxDataset || standardConstants.maxDatasetAmount + } + : undefined }; }; @@ -111,14 +119,11 @@ export const getTeamPlanStatus = async ({ teamId }: { teamId: string; -}): Promise => { +}): Promise => { const standardPlans = global.subPlans?.standard; /* Get all plans and datasetSize */ - const [plans, usedDatasetSize] = await Promise.all([ - MongoTeamSub.find({ teamId }).lean(), - getVectorCountByTeamId(teamId) - ]); + const plans = await MongoTeamSub.find({ teamId }).lean(); /* Get all standardPlans and active standardPlan */ const teamStandardPlans = sortStandPlans( @@ -158,17 +163,25 @@ export const getTeamPlanStatus = async ({ standardMaxDatasetSize + extraDatasetSize.reduce((acc, cur) => acc + (cur.currentExtraDatasetSize || 0), 0); + const standardConstants = + standardPlan?.currentSubLevel && standardPlans + ? standardPlans[standardPlan.currentSubLevel] + : undefined; + return { [SubTypeEnum.standard]: standardPlan, - standardConstants: - standardPlan?.currentSubLevel && standardPlans - ? standardPlans[standardPlan.currentSubLevel] - : undefined, + standardConstants: standardConstants + ? { + ...standardConstants, + maxTeamMember: standardPlan?.maxTeamMember || standardConstants.maxTeamMember, + maxAppAmount: standardPlan?.maxApp || standardConstants.maxAppAmount, + maxDatasetAmount: standardPlan?.maxDataset || standardConstants.maxDatasetAmount + } + : undefined, totalPoints, usedPoints: totalPoints - surplusPoints, - datasetMaxSize: totalDatasetSize, - usedDatasetSize + datasetMaxSize: totalDatasetSize }; }; diff --git a/packages/service/worker/utils.ts b/packages/service/worker/utils.ts index fb541ae90..83bde7e38 100644 --- a/packages/service/worker/utils.ts +++ b/packages/service/worker/utils.ts @@ -14,7 +14,10 @@ export const getSafeEnv = () => { return { LOG_LEVEL: process.env.LOG_LEVEL, STORE_LOG_LEVEL: process.env.STORE_LOG_LEVEL, - NODE_ENV: process.env.NODE_ENV + NODE_ENV: process.env.NODE_ENV, + HTTP_PROXY: process.env.HTTP_PROXY, + HTTPS_PROXY: process.env.HTTPS_PROXY, + NO_PROXY: process.env.NO_PROXY }; }; diff --git a/packages/web/i18n/en/account_info.json b/packages/web/i18n/en/account_info.json index a1e3a83dc..15a520732 100644 --- a/packages/web/i18n/en/account_info.json +++ b/packages/web/i18n/en/account_info.json @@ -6,6 +6,7 @@ "ai_points_calculation_standard": "AI points", "ai_points_usage": "AI points", "ai_points_usage_tip": "Each time the AI ​​model is called, a certain amount of AI points will be consumed. \nFor specific calculation standards, please refer to the \"Billing Standards\" above.", + "app_amount": "App amount", "avatar": "Avatar", "avatar_selection_exception": "Abnormal avatar selection", "balance": "balance", @@ -21,6 +22,7 @@ "contact_us": "Contact us", "current_package": "Current plan", "current_token_price": "Current points price", + "dataset_amount": "Dataset amount", "effective_time": "Effective time", "email_label": "Mail", "exchange": "Exchange", @@ -34,6 +36,7 @@ "help_document": "Help documentation", "knowledge_base_capacity": "Dataset usages", "manage": "Manage", + "member_amount": "Member amount", "member_name": "Name", "month": "moon", "new_password": "New Password", diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index 39b350d9b..89a0273bd 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -215,7 +215,6 @@ "core.app.Interval timer run": "Scheduled Execution", "core.app.Interval timer tip": "Can Execute App on Schedule", "core.app.Make a brief introduction of your app": "Give Your AI App an Introduction", - "core.app.name": "name", "core.app.Name and avatar": "Avatar & Name", "core.app.Publish": "Publish", "core.app.Publish Confirm": "Confirm to Publish App? This Will Immediately Update the App Status on All Publishing Channels.", @@ -261,6 +260,7 @@ "core.app.have_saved": "Saved", "core.app.logs.Source And Time": "Source & Time", "core.app.more": "View More", + "core.app.name": "name", "core.app.no_app": "No Apps Yet, Create One Now!", "core.app.not_saved": "Not Saved", "core.app.outLink.Can Drag": "Icon Can Be Dragged", @@ -1145,10 +1145,10 @@ "support.wallet.subscription.Upgrade plan": "Upgrade Package", "support.wallet.subscription.ai_model": "AI Language Model", "support.wallet.subscription.function.History store": "{{amount}} Days of Chat History Retention", - "support.wallet.subscription.function.Max app": "{{amount}} Apps & Plugins", - "support.wallet.subscription.function.Max dataset": "{{amount}} Datasets", + "support.wallet.subscription.function.Max app": "{{amount}} App limit", + "support.wallet.subscription.function.Max dataset": "{{amount}} Dataset limit", "support.wallet.subscription.function.Max dataset size": "{{amount}} Dataset Indexes", - "support.wallet.subscription.function.Max members": "{{amount}} Team Members", + "support.wallet.subscription.function.Max members": "{{amount}} Member limit", "support.wallet.subscription.function.Points": "{{amount}} AI Points", "support.wallet.subscription.mode.Month": "Month", "support.wallet.subscription.mode.Period": "Subscription Period", diff --git a/packages/web/i18n/zh-CN/account_info.json b/packages/web/i18n/zh-CN/account_info.json index e923c1e61..cd20076cd 100644 --- a/packages/web/i18n/zh-CN/account_info.json +++ b/packages/web/i18n/zh-CN/account_info.json @@ -6,6 +6,7 @@ "ai_points_calculation_standard": "AI 积分", "ai_points_usage": "AI 积分使用量", "ai_points_usage_tip": "每次调用 AI 模型时,都会消耗一定的 AI 积分。具体的计算标准可参考上方的“计费标准”。", + "app_amount": "应用数量", "avatar": "头像", "avatar_selection_exception": "头像选择异常", "balance": "余额", @@ -21,6 +22,7 @@ "contact_us": "联系我们", "current_package": "当前套餐", "current_token_price": "当前积分价格", + "dataset_amount": "知识库数量", "effective_time": "生效时间", "email_label": "邮箱", "exchange": "兑换", @@ -34,6 +36,7 @@ "help_document": "帮助文档", "knowledge_base_capacity": "知识库容量", "manage": "管理", + "member_amount": "成员数量", "member_name": "成员名", "month": "月", "new_password": "新密码", @@ -53,7 +56,9 @@ "please_bind_contact": "请绑定联系方式", "please_bind_notification_receiving_path": "请先绑定通知接收途径", "purchase_extra_package": "购买额外套餐", + "redeem_coupon": "兑换码", "reminder_create_bound_notification_account": "提醒创建者绑定通知账号", + "reset_password": "重置密码", "resource_usage": "资源用量", "select_avatar": "点击选择头像", "standard_package_and_extra_resource_package": "包含标准套餐与额外资源包", @@ -65,7 +70,6 @@ "type": "类型", "unlimited": "无限制", "update_password": "修改密码", - "reset_password": "重置密码", "update_success_tip": "更新数据成功", "upgrade_package": "升级套餐", "usage_balance": "使用余额: 使用余额", @@ -74,6 +78,5 @@ "user_team_team_name": "团队", "verification_code": "验证码", "you_can_convert": "您可以兑换", - "yuan": "元", - "redeem_coupon": "兑换码" + "yuan": "元" } diff --git a/packages/web/i18n/zh-CN/common.json b/packages/web/i18n/zh-CN/common.json index 7cfc615c2..22577be6c 100644 --- a/packages/web/i18n/zh-CN/common.json +++ b/packages/web/i18n/zh-CN/common.json @@ -1145,10 +1145,10 @@ "support.wallet.subscription.Upgrade plan": "升级套餐", "support.wallet.subscription.ai_model": "AI语言模型", "support.wallet.subscription.function.History store": "{{amount}} 天对话记录保留", - "support.wallet.subscription.function.Max app": "{{amount}} 个应用&插件", - "support.wallet.subscription.function.Max dataset": "{{amount}} 个知识库", + "support.wallet.subscription.function.Max app": "{{amount}} 个应用上限", + "support.wallet.subscription.function.Max dataset": "{{amount}} 个知识库上限", "support.wallet.subscription.function.Max dataset size": "{{amount}} 组知识库索引", - "support.wallet.subscription.function.Max members": "{{amount}} 个团队成员", + "support.wallet.subscription.function.Max members": "{{amount}} 个团队成员上限", "support.wallet.subscription.function.Points": "{{amount}} AI 积分", "support.wallet.subscription.mode.Month": "按月", "support.wallet.subscription.mode.Period": "订阅周期", diff --git a/packages/web/i18n/zh-Hant/account_info.json b/packages/web/i18n/zh-Hant/account_info.json index 05da4a404..9c5560d48 100644 --- a/packages/web/i18n/zh-Hant/account_info.json +++ b/packages/web/i18n/zh-Hant/account_info.json @@ -6,6 +6,7 @@ "ai_points_calculation_standard": "AI 積分", "ai_points_usage": "AI 積分使用量", "ai_points_usage_tip": "每次呼叫 AI 模型時,都會消耗一定的 AI 積分。\n具體的計算標準可參考上方的「計費標準」。", + "app_amount": "應用數量", "avatar": "頭像", "avatar_selection_exception": "頭像選擇異常", "balance": "餘額", @@ -21,6 +22,7 @@ "contact_us": "聯絡我們", "current_package": "目前套餐", "current_token_price": "目前積分價格", + "dataset_amount": "知識庫數量", "effective_time": "生效時間", "email_label": "信箱", "exchange": "兌換", @@ -34,6 +36,7 @@ "help_document": "幫助文件", "knowledge_base_capacity": "知識庫容量", "manage": "管理", + "member_amount": "成員數量", "member_name": "成員名", "month": "月", "new_password": "新密碼", diff --git a/packages/web/i18n/zh-Hant/common.json b/packages/web/i18n/zh-Hant/common.json index 10d4690c0..e2b120b6f 100644 --- a/packages/web/i18n/zh-Hant/common.json +++ b/packages/web/i18n/zh-Hant/common.json @@ -215,7 +215,6 @@ "core.app.Interval timer run": "排程執行", "core.app.Interval timer tip": "可排程執行應用程式", "core.app.Make a brief introduction of your app": "為您的 AI 應用程式寫一段介紹", - "core.app.name": "名稱", "core.app.Name and avatar": "頭像與名稱", "core.app.Publish": "發布", "core.app.Publish Confirm": "確認發布應用程式?這將立即更新所有發布管道的應用程式狀態。", @@ -261,6 +260,7 @@ "core.app.have_saved": "已儲存", "core.app.logs.Source And Time": "來源與時間", "core.app.more": "檢視更多", + "core.app.name": "名稱", "core.app.no_app": "還沒有應用程式,快來建立一個吧!", "core.app.not_saved": "未儲存", "core.app.outLink.Can Drag": "圖示可拖曳", @@ -1145,8 +1145,8 @@ "support.wallet.subscription.Upgrade plan": "升級方案", "support.wallet.subscription.ai_model": "AI 語言模型", "support.wallet.subscription.function.History store": "{{amount}} 天對話紀錄保留", - "support.wallet.subscription.function.Max app": "{{amount}} 個應用程式與外掛程式", - "support.wallet.subscription.function.Max dataset": "{{amount}} 個知識庫", + "support.wallet.subscription.function.Max app": "{{amount}} 個應用上限", + "support.wallet.subscription.function.Max dataset": "{{amount}} 個知識庫上限", "support.wallet.subscription.function.Max dataset size": "{{amount}} 組知識庫索引", "support.wallet.subscription.function.Max members": "{{amount}} 個團隊成員", "support.wallet.subscription.function.Points": "{{amount}} AI 點數", diff --git a/projects/app/src/components/support/wallet/NotSufficientModal/index.tsx b/projects/app/src/components/support/wallet/NotSufficientModal/index.tsx index e283bafc7..f0d3a7747 100644 --- a/projects/app/src/components/support/wallet/NotSufficientModal/index.tsx +++ b/projects/app/src/components/support/wallet/NotSufficientModal/index.tsx @@ -112,7 +112,7 @@ const RechargeModal = ({ {`${t('common:support.user.team.Dataset usage')}:`} {`${teamPlanStatus?.usedDatasetSize} / ${teamPlanStatus?.datasetMaxSize || t('account_info:unlimited')}`} + >{`${teamPlanStatus?.usedDatasetIndexSize} / ${teamPlanStatus?.datasetMaxSize || t('account_info:unlimited')}`} {`${t('common:support.wallet.subscription.AI points usage')}:`} 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 ? ( diff --git a/projects/app/src/pages/account/info/index.tsx b/projects/app/src/pages/account/info/index.tsx index 440baec28..c01cf9187 100644 --- a/projects/app/src/pages/account/info/index.tsx +++ b/projects/app/src/pages/account/info/index.tsx @@ -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 ( @@ -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 ? ( @@ -531,6 +549,7 @@ const PlanUsage = () => { @@ -574,15 +593,15 @@ const PlanUsage = () => { {t('account_info:knowledge_base_capacity')} - {datasetUsageMap.usedSize}/{datasetUsageMap.maxSize} + {datasetIndexUsageMap.value}/{datasetIndexUsageMap.max} - + { /> - + @@ -599,15 +618,15 @@ const PlanUsage = () => { - {aiPointsUsageMap.used}/{aiPointsUsageMap.max} + {aiPointsUsageMap.value}/{aiPointsUsageMap.max} - + { /> + + + + {limitData.map((item) => { + return ( + + + + {item.label} + + + {item.value}/{item.max} + + + + + + + ); + })} {isOpenStandardModal && } {isOpenRedeemCouponModal && ( diff --git a/projects/app/src/pages/api/core/dataset/data/insertData.ts b/projects/app/src/pages/api/core/dataset/data/insertData.ts index ee0deb7b9..950a382b3 100644 --- a/projects/app/src/pages/api/core/dataset/data/insertData.ts +++ b/projects/app/src/pages/api/core/dataset/data/insertData.ts @@ -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 [ diff --git a/projects/app/src/pages/api/core/dataset/data/pushData.ts b/projects/app/src/pages/api/core/dataset/data/pushData.ts index 23a484c58..971e5724b 100644 --- a/projects/app/src/pages/api/core/dataset/data/pushData.ts +++ b/projects/app/src/pages/api/core/dataset/data/pushData.ts @@ -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, res: NextApiR }); // auth dataset limit - await checkDatasetLimit({ + await checkDatasetIndexLimit({ teamId, insertLen: predictDataLimitLength(getTrainingModeByCollection(collection), data) }); diff --git a/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts b/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts index 5bb6b0b51..489936a36 100644 --- a/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts +++ b/projects/app/src/pages/api/support/user/team/limit/datasetSizeLimit.ts @@ -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) { 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 }); diff --git a/projects/app/src/pages/api/support/user/team/plan/getTeamPlanStatus.ts b/projects/app/src/pages/api/support/user/team/plan/getTeamPlanStatus.ts index 41a2d7605..4612f7a84 100644 --- a/projects/app/src/pages/api/support/user/team/plan/getTeamPlanStatus.ts +++ b/projects/app/src/pages/api/support/user/team/plan/getTeamPlanStatus.ts @@ -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) { +async function handler( + req: NextApiRequest, + res: NextApiResponse +): Promise { 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) {} } diff --git a/projects/app/src/service/core/dataset/data/controller.ts b/projects/app/src/service/core/dataset/data/controller.ts index 336455c24..e5cccf07e 100644 --- a/projects/app/src/service/core/dataset/data/controller.ts +++ b/projects/app/src/service/core/dataset/data/controller.ts @@ -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 diff --git a/projects/app/src/service/core/dataset/queues/datasetParse.ts b/projects/app/src/service/core/dataset/queues/datasetParse.ts index f68abfbb4..2156bea0b 100644 --- a/projects/app/src/service/core/dataset/queues/datasetParse.ts +++ b/projects/app/src/service/core/dataset/queues/datasetParse.ts @@ -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 => { // Check dataset limit try { - await checkDatasetLimit({ + await checkDatasetIndexLimit({ teamId: data.teamId, insertLen: predictDataLimitLength(trainingMode, chunks) }); diff --git a/projects/app/src/web/support/user/team/api.ts b/projects/app/src/web/support/user/team/api.ts index 563f92c95..5231f1204 100644 --- a/projects/app/src/web/support/user/team/api.ts +++ b/projects/app/src/web/support/user/team/api.ts @@ -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(`/support/user/team/plan/getTeamPlanStatus`, { maxQuantity: 1 }); + GET(`/support/user/team/plan/getTeamPlanStatus`, { maxQuantity: 1 }); export const getTeamPlans = () => GET(`/proApi/support/user/team/plan/getTeamPlans`); diff --git a/projects/app/src/web/support/user/useUserStore.ts b/projects/app/src/web/support/user/useUserStore.ts index 9f84ba982..91d588ac3 100644 --- a/projects/app/src/web/support/user/useUserStore.ts +++ b/projects/app/src/web/support/user/useUserStore.ts @@ -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; - teamPlanStatus: FeTeamPlanStatusType | null; + teamPlanStatus: ClientTeamPlanStatusType | null; initTeamPlanStatus: () => Promise; teamOrgs: OrgType[];