mirror of
https://github.com/labring/FastGPT.git
synced 2026-05-02 01:02:05 +08:00
refactor: merge standardConstants and standard in team plan (#6549)
* refactor: merge standardConstants and standard in team plan * Update packages/service/support/wallet/sub/utils.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: remove type assertion * chore: type * test: test buildStandardPlan * fix: type * perf: code perf * add test code --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: archer <545436317@qq.com>
This commit is contained in:
@@ -2,7 +2,9 @@ import z from 'zod';
|
||||
import { StandardSubLevelEnum, SubModeEnum, SubTypeEnum } from './constants';
|
||||
import { ObjectIdSchema } from '../../../common/type/mongo';
|
||||
|
||||
// Content of plan
|
||||
/**
|
||||
* Static plan config, stored in global.subPlans
|
||||
*/
|
||||
export const TeamStandardSubPlanItemSchema = z.object({
|
||||
name: z.string().optional(),
|
||||
desc: z.string().optional(),
|
||||
@@ -71,6 +73,10 @@ export const SubPlanSchema = z.object({
|
||||
});
|
||||
export type SubPlanType = z.infer<typeof SubPlanSchema>;
|
||||
|
||||
/**
|
||||
* TeamSub Schema in DB.
|
||||
* Configs are optional
|
||||
*/
|
||||
export const TeamSubSchema = z.object({
|
||||
_id: ObjectIdSchema,
|
||||
teamId: ObjectIdSchema,
|
||||
@@ -105,9 +111,30 @@ export const TeamSubSchema = z.object({
|
||||
});
|
||||
export type TeamSubSchemaType = z.infer<typeof TeamSubSchema>;
|
||||
|
||||
/**
|
||||
* Merged plan type: combines DB subscription record metadata with effective plan limits
|
||||
*
|
||||
* Omits:
|
||||
* - maxApp/maxDataset from TeamSubSchema: 这些字段在 DB 中存储,但在合并后的类型中使用 maxAppAmount/maxDatasetAmount
|
||||
* - pointPrice from TeamStandardSubPlanItemSchema: 避免与 price 字段冲突
|
||||
*
|
||||
* Field priority: TeamStandardSubPlanItemSchema fields override TeamSubSchema fields when both exist
|
||||
*/ export const TeamPlanStandardSchema = z.object({
|
||||
...TeamSubSchema.omit({
|
||||
maxApp: true,
|
||||
maxDataset: true
|
||||
}).shape,
|
||||
...TeamStandardSubPlanItemSchema.omit({
|
||||
pointPrice: true,
|
||||
price: true
|
||||
}).shape,
|
||||
price: z.number().optional()
|
||||
});
|
||||
|
||||
export type TeamPlanStandardType = z.infer<typeof TeamPlanStandardSchema>;
|
||||
|
||||
export const TeamPlanStatusSchema = z.object({
|
||||
[SubTypeEnum.standard]: TeamSubSchema.optional(),
|
||||
standardConstants: TeamStandardSubPlanItemSchema.optional(),
|
||||
[SubTypeEnum.standard]: TeamPlanStandardSchema.optional(),
|
||||
totalPoints: z.int(),
|
||||
usedPoints: z.int(),
|
||||
datasetMaxSize: z.int()
|
||||
|
||||
@@ -25,7 +25,7 @@ export const checkTeamAIPoints = async (teamId: string) => {
|
||||
};
|
||||
|
||||
export const checkTeamMemberLimit = async (teamId: string, newCount: number) => {
|
||||
const [{ standardConstants }, memberCount] = await Promise.all([
|
||||
const [{ standard }, memberCount] = await Promise.all([
|
||||
getTeamStandPlan({
|
||||
teamId
|
||||
}),
|
||||
@@ -35,7 +35,7 @@ export const checkTeamMemberLimit = async (teamId: string, newCount: number) =>
|
||||
})
|
||||
]);
|
||||
|
||||
if (standardConstants && newCount + memberCount > standardConstants.maxTeamMember) {
|
||||
if (standard?.maxTeamMember && newCount + memberCount > standard.maxTeamMember) {
|
||||
return Promise.reject(TeamErrEnum.teamOverSize);
|
||||
}
|
||||
};
|
||||
@@ -50,7 +50,7 @@ export const checkTeamAppTypeLimit = async ({
|
||||
amount?: number;
|
||||
}) => {
|
||||
if (appCheckType === 'app') {
|
||||
const [{ standardConstants }, appCount] = await Promise.all([
|
||||
const [{ standard }, appCount] = await Promise.all([
|
||||
getTeamStandPlan({ teamId }),
|
||||
MongoApp.countDocuments({
|
||||
teamId,
|
||||
@@ -60,7 +60,7 @@ export const checkTeamAppTypeLimit = async ({
|
||||
})
|
||||
]);
|
||||
|
||||
if (standardConstants && appCount + amount > standardConstants.maxAppAmount) {
|
||||
if (standard?.maxAppAmount && appCount + amount > standard.maxAppAmount) {
|
||||
return Promise.reject(TeamErrEnum.appAmountNotEnough);
|
||||
}
|
||||
|
||||
@@ -107,10 +107,10 @@ export const checkDatasetIndexLimit = async ({
|
||||
teamId: string;
|
||||
insertLen?: number;
|
||||
}) => {
|
||||
const [{ standardConstants, totalPoints, usedPoints, datasetMaxSize }, usedDatasetIndexSize] =
|
||||
const [{ standard, totalPoints, usedPoints, datasetMaxSize }, usedDatasetIndexSize] =
|
||||
await Promise.all([getTeamPlanStatus({ teamId }), getVectorCountByTeamId(teamId)]);
|
||||
|
||||
if (!standardConstants) return;
|
||||
if (!standard) return;
|
||||
|
||||
if (usedDatasetIndexSize + insertLen >= datasetMaxSize) {
|
||||
return Promise.reject(TeamErrEnum.datasetSizeNotEnough);
|
||||
@@ -123,7 +123,7 @@ export const checkDatasetIndexLimit = async ({
|
||||
};
|
||||
|
||||
export const checkTeamDatasetLimit = async (teamId: string) => {
|
||||
const [{ standardConstants }, datasetCount] = await Promise.all([
|
||||
const [{ standard }, datasetCount] = await Promise.all([
|
||||
getTeamStandPlan({ teamId }),
|
||||
MongoDataset.countDocuments({
|
||||
teamId,
|
||||
@@ -132,7 +132,7 @@ export const checkTeamDatasetLimit = async (teamId: string) => {
|
||||
]);
|
||||
|
||||
// User check
|
||||
if (standardConstants && datasetCount >= standardConstants.maxDatasetAmount) {
|
||||
if (standard?.maxDatasetAmount && datasetCount >= standard.maxDatasetAmount) {
|
||||
return Promise.reject(TeamErrEnum.datasetAmountNotEnough);
|
||||
}
|
||||
|
||||
@@ -148,11 +148,11 @@ export const checkTeamDatasetLimit = async (teamId: string) => {
|
||||
};
|
||||
|
||||
export const checkTeamDatasetSyncPermission = async (teamId: string) => {
|
||||
const { standardConstants } = await getTeamStandPlan({
|
||||
const { standard } = await getTeamStandPlan({
|
||||
teamId
|
||||
});
|
||||
|
||||
if (standardConstants && !standardConstants?.websiteSyncPerDataset) {
|
||||
if (standard && !standard?.websiteSyncPerDataset) {
|
||||
return Promise.reject(TeamErrEnum.websiteSyncNotEnough);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,9 +5,11 @@ import {
|
||||
standardSubLevelMap
|
||||
} from '@fastgpt/global/support/wallet/sub/constants';
|
||||
import { MongoTeamSub } from './schema';
|
||||
import {
|
||||
type TeamPlanStatusType,
|
||||
type TeamSubSchemaType
|
||||
import type {
|
||||
TeamStandardSubPlanItemType,
|
||||
TeamPlanStatusType,
|
||||
TeamPlanStandardType,
|
||||
TeamSubSchemaType
|
||||
} from '@fastgpt/global/support/wallet/sub/type';
|
||||
import dayjs from 'dayjs';
|
||||
import { type ClientSession } from '../../../common/mongo';
|
||||
@@ -38,55 +40,33 @@ export const sortStandPlans = (plans: TeamSubSchemaType[]) => {
|
||||
standardSubLevelMap[b.currentSubLevel].weight - standardSubLevelMap[a.currentSubLevel].weight
|
||||
);
|
||||
};
|
||||
export const getTeamStandPlan = async ({ teamId }: { teamId: string }) => {
|
||||
const plans = await MongoTeamSub.find(
|
||||
{
|
||||
teamId,
|
||||
type: SubTypeEnum.standard
|
||||
},
|
||||
undefined,
|
||||
{
|
||||
...readFromSecondary
|
||||
}
|
||||
);
|
||||
sortStandPlans(plans);
|
||||
|
||||
const standardPlans = global.subPlans?.standard;
|
||||
const standard = plans[0];
|
||||
|
||||
const standardConstants =
|
||||
standard.currentSubLevel && standardPlans
|
||||
? standardPlans[
|
||||
standard.currentSubLevel === StandardSubLevelEnum.custom
|
||||
? StandardSubLevelEnum.advanced
|
||||
: standard.currentSubLevel
|
||||
]
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
[SubTypeEnum.standard]: standard,
|
||||
standardConstants: standardConstants
|
||||
? {
|
||||
...standardConstants,
|
||||
maxTeamMember: standard?.maxTeamMember ?? standardConstants.maxTeamMember,
|
||||
maxAppAmount: standard?.maxApp ?? standardConstants.maxAppAmount,
|
||||
maxDatasetAmount: standard?.maxDataset ?? standardConstants.maxDatasetAmount,
|
||||
requestsPerMinute: standard?.requestsPerMinute ?? standardConstants.requestsPerMinute,
|
||||
chatHistoryStoreDuration:
|
||||
standard?.chatHistoryStoreDuration ?? standardConstants.chatHistoryStoreDuration,
|
||||
maxDatasetSize: standard?.maxDatasetSize ?? standardConstants.maxDatasetSize,
|
||||
websiteSyncPerDataset:
|
||||
standard?.websiteSyncPerDataset ?? standardConstants.websiteSyncPerDataset,
|
||||
appRegistrationCount:
|
||||
standard?.appRegistrationCount ?? standardConstants.appRegistrationCount,
|
||||
auditLogStoreDuration:
|
||||
standard?.auditLogStoreDuration ?? standardConstants.auditLogStoreDuration,
|
||||
ticketResponseTime: standard?.ticketResponseTime ?? standardConstants.ticketResponseTime,
|
||||
customDomain: standard?.customDomain ?? standardConstants.customDomain
|
||||
}
|
||||
: undefined
|
||||
};
|
||||
};
|
||||
export const buildStandardPlan = (
|
||||
standard: TeamSubSchemaType,
|
||||
standardConstants: TeamStandardSubPlanItemType
|
||||
): TeamPlanStandardType => ({
|
||||
...standard,
|
||||
name: standardConstants.name,
|
||||
desc: standardConstants.desc,
|
||||
price: standardConstants.price,
|
||||
priceDescription: standardConstants.priceDescription,
|
||||
customFormUrl: standardConstants.customFormUrl,
|
||||
customDescriptions: standardConstants.customDescriptions,
|
||||
wecom: standardConstants.wecom,
|
||||
maxTeamMember: standard?.maxTeamMember ?? standardConstants.maxTeamMember,
|
||||
maxAppAmount: standard?.maxApp ?? standardConstants.maxAppAmount,
|
||||
maxDatasetAmount: standard?.maxDataset ?? standardConstants.maxDatasetAmount,
|
||||
requestsPerMinute: standard?.requestsPerMinute ?? standardConstants.requestsPerMinute,
|
||||
chatHistoryStoreDuration:
|
||||
standard?.chatHistoryStoreDuration ?? standardConstants.chatHistoryStoreDuration,
|
||||
maxDatasetSize: standard?.maxDatasetSize ?? standardConstants.maxDatasetSize,
|
||||
websiteSyncPerDataset: standard?.websiteSyncPerDataset ?? standardConstants.websiteSyncPerDataset,
|
||||
appRegistrationCount: standard?.appRegistrationCount ?? standardConstants.appRegistrationCount,
|
||||
auditLogStoreDuration: standard?.auditLogStoreDuration ?? standardConstants.auditLogStoreDuration,
|
||||
ticketResponseTime: standard?.ticketResponseTime ?? standardConstants.ticketResponseTime,
|
||||
customDomain: standard?.customDomain ?? standardConstants.customDomain,
|
||||
maxUploadFileSize: standard?.maxUploadFileSize ?? standardConstants.maxUploadFileSize,
|
||||
maxUploadFileCount: standard?.maxUploadFileCount ?? standardConstants.maxUploadFileCount
|
||||
});
|
||||
|
||||
export const initTeamFreePlan = async ({
|
||||
teamId,
|
||||
@@ -176,11 +156,46 @@ export const initTeamFreePlan = async ({
|
||||
);
|
||||
};
|
||||
|
||||
// 获取团队标准套餐
|
||||
export const getTeamStandPlan = async ({ teamId }: { teamId: string }) => {
|
||||
const plans = await MongoTeamSub.find(
|
||||
{
|
||||
teamId,
|
||||
type: SubTypeEnum.standard
|
||||
},
|
||||
undefined,
|
||||
{
|
||||
...readFromSecondary
|
||||
}
|
||||
);
|
||||
sortStandPlans(plans);
|
||||
|
||||
const standardPlans = global.subPlans?.standard;
|
||||
const standard = plans[0];
|
||||
|
||||
const standardConstants =
|
||||
standard.currentSubLevel && standardPlans
|
||||
? standardPlans[
|
||||
standard.currentSubLevel === StandardSubLevelEnum.custom
|
||||
? StandardSubLevelEnum.advanced
|
||||
: standard.currentSubLevel
|
||||
]
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
[SubTypeEnum.standard]: standardConstants
|
||||
? buildStandardPlan(standard, standardConstants)
|
||||
: undefined
|
||||
};
|
||||
};
|
||||
|
||||
// 获取团队所有套餐内容
|
||||
export const getTeamPlanStatus = async ({
|
||||
teamId
|
||||
}: {
|
||||
teamId: string;
|
||||
}): Promise<TeamPlanStatusType> => {
|
||||
/** 配置里的套餐 */
|
||||
const standardPlans = global.subPlans?.standard;
|
||||
|
||||
/* Get all plans and datasetSize */
|
||||
@@ -190,6 +205,7 @@ export const getTeamPlanStatus = async ({
|
||||
const teamStandardPlans = sortStandPlans(
|
||||
plans.filter((plan) => plan.type === SubTypeEnum.standard)
|
||||
);
|
||||
/** 数据库里的,用户目前 active 的套餐 */
|
||||
const standardPlan = teamStandardPlans[0];
|
||||
|
||||
const extraDatasetSize = plans.filter((plan) => plan.type === SubTypeEnum.extraDatasetSize);
|
||||
@@ -230,6 +246,7 @@ export const getTeamPlanStatus = async ({
|
||||
standardMaxDatasetSize +
|
||||
extraDatasetSize.reduce((acc, cur) => acc + (cur.currentExtraDatasetSize || 0), 0);
|
||||
|
||||
/** 静态的套餐配置,如果是 custom 则返回 advanced */
|
||||
const standardConstants =
|
||||
standardPlan?.currentSubLevel && standardPlans
|
||||
? standardPlans[
|
||||
@@ -242,56 +259,8 @@ export const getTeamPlanStatus = async ({
|
||||
teamPoint.updateTeamPointsCache({ teamId, totalPoints, surplusPoints });
|
||||
|
||||
return {
|
||||
standard:
|
||||
standardPlan.currentSubLevel === StandardSubLevelEnum.custom && standardConstants
|
||||
? {
|
||||
...standardPlan,
|
||||
maxTeamMember: standardPlan?.maxTeamMember ?? standardConstants.maxTeamMember,
|
||||
maxApp: standardPlan?.maxApp ?? standardConstants.maxAppAmount,
|
||||
maxDataset: standardPlan?.maxDataset ?? standardConstants.maxDatasetAmount,
|
||||
requestsPerMinute:
|
||||
standardPlan?.requestsPerMinute ?? standardConstants.requestsPerMinute,
|
||||
chatHistoryStoreDuration:
|
||||
standardPlan?.chatHistoryStoreDuration ?? standardConstants.chatHistoryStoreDuration,
|
||||
maxDatasetSize: standardPlan?.maxDatasetSize ?? standardConstants.maxDatasetSize,
|
||||
websiteSyncPerDataset:
|
||||
standardPlan?.websiteSyncPerDataset || standardConstants.websiteSyncPerDataset,
|
||||
appRegistrationCount:
|
||||
standardPlan?.appRegistrationCount ?? standardConstants.appRegistrationCount,
|
||||
auditLogStoreDuration:
|
||||
standardPlan?.auditLogStoreDuration ?? standardConstants.auditLogStoreDuration,
|
||||
ticketResponseTime:
|
||||
standardPlan?.ticketResponseTime ?? standardConstants.ticketResponseTime,
|
||||
customDomain: standardPlan?.customDomain ?? standardConstants.customDomain,
|
||||
maxUploadFileSize:
|
||||
standardPlan?.maxUploadFileSize ?? standardConstants.maxUploadFileSize,
|
||||
maxUploadFileCount:
|
||||
standardPlan?.maxUploadFileCount ?? standardConstants.maxUploadFileCount
|
||||
}
|
||||
: standardPlan,
|
||||
standardConstants: standardConstants
|
||||
? {
|
||||
...standardConstants,
|
||||
maxTeamMember: standardPlan?.maxTeamMember ?? standardConstants.maxTeamMember,
|
||||
maxAppAmount: standardPlan?.maxApp ?? standardConstants.maxAppAmount,
|
||||
maxDatasetAmount: standardPlan?.maxDataset ?? standardConstants.maxDatasetAmount,
|
||||
requestsPerMinute: standardPlan?.requestsPerMinute ?? standardConstants.requestsPerMinute,
|
||||
chatHistoryStoreDuration:
|
||||
standardPlan?.chatHistoryStoreDuration ?? standardConstants.chatHistoryStoreDuration,
|
||||
maxDatasetSize: standardPlan?.maxDatasetSize ?? standardConstants.maxDatasetSize,
|
||||
websiteSyncPerDataset:
|
||||
standardPlan?.websiteSyncPerDataset || standardConstants.websiteSyncPerDataset,
|
||||
appRegistrationCount:
|
||||
standardPlan?.appRegistrationCount ?? standardConstants.appRegistrationCount,
|
||||
auditLogStoreDuration:
|
||||
standardPlan?.auditLogStoreDuration ?? standardConstants.auditLogStoreDuration,
|
||||
ticketResponseTime:
|
||||
standardPlan?.ticketResponseTime ?? standardConstants.ticketResponseTime,
|
||||
customDomain: standardPlan?.customDomain ?? standardConstants.customDomain,
|
||||
maxUploadFileSize: standardPlan?.maxUploadFileSize ?? standardConstants.maxUploadFileSize,
|
||||
maxUploadFileCount:
|
||||
standardPlan?.maxUploadFileCount ?? standardConstants.maxUploadFileCount
|
||||
}
|
||||
[SubTypeEnum.standard]: standardConstants
|
||||
? buildStandardPlan(standardPlan, standardConstants)
|
||||
: undefined,
|
||||
|
||||
totalPoints,
|
||||
@@ -301,6 +270,7 @@ export const getTeamPlanStatus = async ({
|
||||
};
|
||||
};
|
||||
|
||||
/* ===== Buffer controller ===== */
|
||||
export const teamPoint = {
|
||||
getTeamPoints: async ({ teamId }: { teamId: string }) => {
|
||||
const surplusCacheKey = `${CacheKeyEnum.team_point_surplus}:${teamId}`;
|
||||
@@ -368,9 +338,7 @@ export const teamQPM = {
|
||||
|
||||
// 2. Computed
|
||||
const teamPlanStatus = await getTeamPlanStatus({ teamId });
|
||||
const limit =
|
||||
teamPlanStatus[SubTypeEnum.standard]?.requestsPerMinute ??
|
||||
teamPlanStatus.standardConstants?.requestsPerMinute;
|
||||
const limit = teamPlanStatus[SubTypeEnum.standard]?.requestsPerMinute;
|
||||
|
||||
if (!limit) {
|
||||
if (process.env.CHAT_MAX_QPM) return Number(process.env.CHAT_MAX_QPM);
|
||||
|
||||
Reference in New Issue
Block a user