mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 21:13:50 +00:00
V4.6.9-first commit (#899)
* perf: insert mongo dataset data session * perf: dataset data index * remove delay * rename bill schema * rename bill record * perf: bill table * perf: prompt * perf: sub plan * change the usage count * feat: usage bill * publish usages * doc * 新增团队聊天功能 (#20) * perf: doc * feat 添加标签部分 feat 信息团队标签配置 feat 新增团队同步管理 feat team分享页面 feat 完成team分享页面 feat 实现模糊搜索 style 格式化 fix 修复迷糊匹配 style 样式修改 fix 团队标签功能修复 * fix 修复鉴权功能 * merge 合并代码 * fix 修复引用错误 * fix 修复pr问题 * fix 修复ts格式问题 --------- Co-authored-by: archer <545436317@qq.com> Co-authored-by: liuxingwan <liuxingwan.lxw@alibaba-inc.com> * update extra plan * fix: ts * format * perf: bill field * feat: standard plan * fix: ts * feat 个人账号页面修改 (#22) * feat 添加标签部分 feat 信息团队标签配置 feat 新增团队同步管理 feat team分享页面 feat 完成team分享页面 feat 实现模糊搜索 style 格式化 fix 修复迷糊匹配 style 样式修改 fix 团队标签功能修复 * fix 修复鉴权功能 * merge 合并代码 * fix 修复引用错误 * fix 修复pr问题 * fix 修复ts格式问题 * feat 修改个人账号页 --------- Co-authored-by: liuxingwan <liuxingwan.lxw@alibaba-inc.com> * sub plan page (#23) * fix chunk index; error page text * feat: dataset process Integral prediction * feat: stand plan field * feat: sub plan limit * perf: index * query extension * perf: share link push app name * perf: plan point unit * perf: get sub plan * perf: account page * feat 新增套餐详情弹窗代码 (#24) * merge 合并代码 * fix 新增套餐详情弹框 * fix 修复pr问题 * feat: change http node input to prompt editor (#21) * feat: change http node input to prompt editor * fix * split PromptEditor to HttpInput * Team plans (#25) * perf: pay check * perf: team plan test * plan limit check * replace sensitive text * perf: fix some null * collection null check * perf: plans modal * perf: http module * pacakge (#26) * individuation page and pay modal amount (#27) * feat: individuation page * team chat config * pay modal * plan count and replace invalid chars (#29) * fix: user oneapi * fix: training queue * fix: qa queue * perf: remove space chars * replace invalid chars * change httpinput dropdown menu (#28) * perf: http * reseet free plan * perf: plan code to packages * remove llm config to package * perf: code * perf: faq * fix: get team plan --------- Co-authored-by: yst <77910600+yu-and-liu@users.noreply.github.com> Co-authored-by: liuxingwan <liuxingwan.lxw@alibaba-inc.com> Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
@@ -19,14 +19,15 @@ export async function authOpenApiKey({ apikey }: { apikey: string }) {
|
||||
// auth limit
|
||||
// @ts-ignore
|
||||
if (global.feConfigs?.isPlus) {
|
||||
await POST('/support/openapi/authLimit', { openApi } as AuthOpenApiLimitProps);
|
||||
await POST('/support/openapi/authLimit', {
|
||||
openApi: openApi.toObject()
|
||||
} as AuthOpenApiLimitProps);
|
||||
}
|
||||
|
||||
updateApiKeyUsedTime(openApi._id);
|
||||
|
||||
return {
|
||||
apikey,
|
||||
userId: String(openApi.userId),
|
||||
teamId: String(openApi.teamId),
|
||||
tmbId: String(openApi.tmbId),
|
||||
appId: openApi.appId || ''
|
||||
|
@@ -1,8 +1,6 @@
|
||||
import { connectionMongo, type Model } from '../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import type { OpenApiSchema } from '@fastgpt/global/support/openapi/type';
|
||||
import { PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
|
||||
import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
@@ -10,10 +8,6 @@ import {
|
||||
|
||||
const OpenApiSchema = new Schema(
|
||||
{
|
||||
userId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'user'
|
||||
},
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamCollectionName,
|
||||
@@ -44,22 +38,17 @@ const OpenApiSchema = new Schema(
|
||||
type: String,
|
||||
default: 'Api Key'
|
||||
},
|
||||
usage: {
|
||||
// total usage. value from bill total
|
||||
usagePoints: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
get: (val: number) => formatStorePrice2Read(val)
|
||||
default: 0
|
||||
},
|
||||
limit: {
|
||||
expiredTime: {
|
||||
type: Date
|
||||
},
|
||||
credit: {
|
||||
// value from user settings
|
||||
maxUsagePoints: {
|
||||
type: Number,
|
||||
default: -1,
|
||||
set: (val: number) => val * PRICE_SCALE,
|
||||
get: (val: number) => formatStorePrice2Read(val)
|
||||
default: -1
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@@ -8,15 +8,21 @@ export function updateApiKeyUsedTime(id: string) {
|
||||
});
|
||||
}
|
||||
|
||||
export function updateApiKeyUsage({ apikey, usage }: { apikey: string; usage: number }) {
|
||||
export function updateApiKeyUsage({
|
||||
apikey,
|
||||
totalPoints
|
||||
}: {
|
||||
apikey: string;
|
||||
totalPoints: number;
|
||||
}) {
|
||||
MongoOpenApi.findOneAndUpdate(
|
||||
{ apiKey: apikey },
|
||||
{
|
||||
$inc: {
|
||||
usage
|
||||
usagePoints: totalPoints
|
||||
}
|
||||
}
|
||||
).catch((err) => {
|
||||
console.log('update apiKey usage error', err);
|
||||
console.log('update apiKey totalPoints error', err);
|
||||
});
|
||||
}
|
||||
|
@@ -35,8 +35,7 @@ const OutLinkSchema = new Schema({
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
total: {
|
||||
// total amount
|
||||
usagePoints: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
@@ -48,6 +47,10 @@ const OutLinkSchema = new Schema({
|
||||
default: false
|
||||
},
|
||||
limit: {
|
||||
maxUsagePoints: {
|
||||
type: Number,
|
||||
default: -1
|
||||
},
|
||||
expiredTime: {
|
||||
type: Date
|
||||
},
|
||||
@@ -55,16 +58,18 @@ const OutLinkSchema = new Schema({
|
||||
type: Number,
|
||||
default: 1000
|
||||
},
|
||||
credit: {
|
||||
type: Number,
|
||||
default: -1
|
||||
},
|
||||
hookUrl: {
|
||||
type: String
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
OutLinkSchema.index({ shareId: -1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoOutLink: Model<SchemaType> =
|
||||
models['outlinks'] || model('outlinks', OutLinkSchema);
|
||||
|
||||
|
@@ -1,18 +1,19 @@
|
||||
import axios from 'axios';
|
||||
import { MongoOutLink } from './schema';
|
||||
import { FastGPTProUrl } from '../../common/system/constants';
|
||||
import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type';
|
||||
|
||||
export const updateOutLinkUsage = async ({
|
||||
export const addOutLinkUsage = async ({
|
||||
shareId,
|
||||
total
|
||||
totalPoints
|
||||
}: {
|
||||
shareId: string;
|
||||
total: number;
|
||||
totalPoints: number;
|
||||
}) => {
|
||||
MongoOutLink.findOneAndUpdate(
|
||||
{ shareId },
|
||||
{
|
||||
$inc: { total },
|
||||
$inc: { usagePoints: totalPoints },
|
||||
lastTime: new Date()
|
||||
}
|
||||
).catch((err) => {
|
||||
@@ -23,11 +24,13 @@ export const updateOutLinkUsage = async ({
|
||||
export const pushResult2Remote = async ({
|
||||
outLinkUid,
|
||||
shareId,
|
||||
appName,
|
||||
responseData
|
||||
}: {
|
||||
outLinkUid?: string; // raw id, not parse
|
||||
shareId?: string;
|
||||
responseData?: any[];
|
||||
appName: string;
|
||||
responseData?: ChatHistoryItemResType[];
|
||||
}) => {
|
||||
if (!shareId || !outLinkUid || !FastGPTProUrl) return;
|
||||
try {
|
||||
@@ -42,6 +45,7 @@ export const pushResult2Remote = async ({
|
||||
url: '/shareAuth/finish',
|
||||
data: {
|
||||
token: outLinkUid,
|
||||
appName,
|
||||
responseData
|
||||
}
|
||||
});
|
||||
|
@@ -107,7 +107,7 @@ export async function authDatasetCollection({
|
||||
collection: CollectionWithDatasetType;
|
||||
}
|
||||
> {
|
||||
const { userId, teamId, tmbId } = await parseHeaderCert(props);
|
||||
const { teamId, tmbId } = await parseHeaderCert(props);
|
||||
const { role } = await getTmbInfoByTmbId({ tmbId });
|
||||
|
||||
const { collection, isOwner, canWrite } = await (async () => {
|
||||
@@ -143,7 +143,6 @@ export async function authDatasetCollection({
|
||||
})();
|
||||
|
||||
return {
|
||||
userId,
|
||||
teamId,
|
||||
tmbId,
|
||||
collection,
|
||||
@@ -163,7 +162,7 @@ export async function authDatasetFile({
|
||||
file: DatasetFileSchema;
|
||||
}
|
||||
> {
|
||||
const { userId, teamId, tmbId } = await parseHeaderCert(props);
|
||||
const { teamId, tmbId } = await parseHeaderCert(props);
|
||||
|
||||
const [file, collection] = await Promise.all([
|
||||
getFileById({ bucketName: BucketNameEnum.dataset, fileId }),
|
||||
@@ -190,7 +189,6 @@ export async function authDatasetFile({
|
||||
});
|
||||
|
||||
return {
|
||||
userId,
|
||||
teamId,
|
||||
tmbId,
|
||||
file,
|
||||
@@ -200,4 +198,4 @@ export async function authDatasetFile({
|
||||
} catch (error) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
||||
}
|
||||
}
|
||||
}
|
@@ -12,7 +12,7 @@ export async function authUserNotVisitor(props: AuthModeType): Promise<
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
}
|
||||
> {
|
||||
const { userId, teamId, tmbId } = await parseHeaderCert(props);
|
||||
const { teamId, tmbId } = await parseHeaderCert(props);
|
||||
const team = await getTmbInfoByTmbId({ tmbId });
|
||||
|
||||
if (team.role === TeamMemberRoleEnum.visitor) {
|
||||
@@ -20,7 +20,6 @@ export async function authUserNotVisitor(props: AuthModeType): Promise<
|
||||
}
|
||||
|
||||
return {
|
||||
userId,
|
||||
teamId,
|
||||
tmbId,
|
||||
team,
|
||||
|
@@ -94,10 +94,10 @@ export async function parseHeaderCert({
|
||||
})();
|
||||
|
||||
// auth apikey
|
||||
const { userId, teamId, tmbId, appId: apiKeyAppId = '' } = await authOpenApiKey({ apikey });
|
||||
const { teamId, tmbId, appId: apiKeyAppId = '' } = await authOpenApiKey({ apikey });
|
||||
|
||||
return {
|
||||
uid: userId,
|
||||
uid: '',
|
||||
teamId,
|
||||
tmbId,
|
||||
apikey,
|
||||
@@ -217,4 +217,4 @@ export const authFileToken = (token?: string) =>
|
||||
fileId: decoded.fileId
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@@ -1,23 +0,0 @@
|
||||
import { StandSubPlanLevelMapType } from '@fastgpt/global/support/wallet/sub/type';
|
||||
import { getVectorCountByTeamId } from '../../../common/vectorStore/controller';
|
||||
import { getTeamDatasetMaxSize } from '../../wallet/sub/utils';
|
||||
|
||||
export const checkDatasetLimit = async ({
|
||||
teamId,
|
||||
insertLen = 0,
|
||||
standardPlans
|
||||
}: {
|
||||
teamId: string;
|
||||
insertLen?: number;
|
||||
standardPlans?: StandSubPlanLevelMapType;
|
||||
}) => {
|
||||
const [{ maxSize }, usedSize] = await Promise.all([
|
||||
getTeamDatasetMaxSize({ teamId, standardPlans }),
|
||||
getVectorCountByTeamId(teamId)
|
||||
]);
|
||||
|
||||
if (usedSize + insertLen >= maxSize) {
|
||||
return Promise.reject(`数据库容量不足,无法继续添加。可以在账号页面进行扩容。`);
|
||||
}
|
||||
return;
|
||||
};
|
91
packages/service/support/permission/teamLimit.ts
Normal file
91
packages/service/support/permission/teamLimit.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { getVectorCountByTeamId } from '../../common/vectorStore/controller';
|
||||
import { getTeamPlanStatus, getTeamStandPlan } from '../../support/wallet/sub/utils';
|
||||
import { MongoApp } from '../../core/app/schema';
|
||||
import { MongoPlugin } from '../../core/plugin/schema';
|
||||
import { MongoDataset } from '../../core/dataset/schema';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
|
||||
export const checkDatasetLimit = async ({
|
||||
teamId,
|
||||
insertLen = 0
|
||||
}: {
|
||||
teamId: string;
|
||||
insertLen?: number;
|
||||
}) => {
|
||||
const [{ standardConstants, totalPoints, usedPoints, datasetMaxSize }, usedSize] =
|
||||
await Promise.all([getTeamPlanStatus({ teamId }), getVectorCountByTeamId(teamId)]);
|
||||
|
||||
if (!standardConstants) return;
|
||||
|
||||
if (usedSize + insertLen >= datasetMaxSize) {
|
||||
return Promise.reject(TeamErrEnum.datasetSizeNotEnough);
|
||||
}
|
||||
|
||||
if (usedPoints >= totalPoints) {
|
||||
return Promise.reject(TeamErrEnum.aiPointsNotEnough);
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
export const checkTeamAIPoints = async (teamId: string) => {
|
||||
const { standardConstants, totalPoints, usedPoints } = await getTeamPlanStatus({
|
||||
teamId
|
||||
});
|
||||
|
||||
if (!standardConstants) return;
|
||||
|
||||
if (usedPoints >= totalPoints) {
|
||||
return Promise.reject(TeamErrEnum.aiPointsNotEnough);
|
||||
}
|
||||
|
||||
return {
|
||||
totalPoints,
|
||||
usedPoints
|
||||
};
|
||||
};
|
||||
|
||||
export const checkTeamDatasetLimit = async (teamId: string) => {
|
||||
const [{ standardConstants }, datasetCount] = await Promise.all([
|
||||
getTeamStandPlan({ teamId }),
|
||||
MongoDataset.countDocuments({
|
||||
teamId,
|
||||
type: { $ne: DatasetTypeEnum.folder }
|
||||
})
|
||||
]);
|
||||
|
||||
if (standardConstants && datasetCount >= standardConstants.maxDatasetAmount) {
|
||||
return Promise.reject(TeamErrEnum.datasetAmountNotEnough);
|
||||
}
|
||||
};
|
||||
export const checkTeamAppLimit = async (teamId: string) => {
|
||||
const [{ standardConstants }, appCount] = await Promise.all([
|
||||
getTeamStandPlan({ teamId }),
|
||||
MongoApp.count({ teamId })
|
||||
]);
|
||||
|
||||
if (standardConstants && appCount >= standardConstants.maxAppAmount) {
|
||||
return Promise.reject(TeamErrEnum.appAmountNotEnough);
|
||||
}
|
||||
};
|
||||
export const checkTeamPluginLimit = async (teamId: string) => {
|
||||
const [{ standardConstants }, pluginCount] = await Promise.all([
|
||||
getTeamStandPlan({ teamId }),
|
||||
MongoPlugin.count({ teamId })
|
||||
]);
|
||||
|
||||
if (standardConstants && pluginCount >= standardConstants.maxAppAmount) {
|
||||
return Promise.reject(TeamErrEnum.pluginAmountNotEnough);
|
||||
}
|
||||
};
|
||||
|
||||
export const checkTeamReRankPermission = async (teamId: string) => {
|
||||
const { standardConstants } = await getTeamStandPlan({
|
||||
teamId
|
||||
});
|
||||
|
||||
if (standardConstants && !standardConstants?.permissionReRank) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
@@ -2,7 +2,6 @@ import { UserType } from '@fastgpt/global/support/user/type';
|
||||
import { MongoUser } from './schema';
|
||||
import { getTmbInfoByTmbId, getUserDefaultTeam } from './team/controller';
|
||||
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||
import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
|
||||
|
||||
export async function authUserExist({ userId, username }: { userId?: string; username?: string }) {
|
||||
if (userId) {
|
||||
@@ -47,22 +46,3 @@ export async function getUserDetail({
|
||||
team: tmb
|
||||
};
|
||||
}
|
||||
|
||||
export async function getUserAndAuthBalance({
|
||||
tmbId,
|
||||
minBalance
|
||||
}: {
|
||||
tmbId: string;
|
||||
minBalance?: number;
|
||||
}) {
|
||||
const user = await getUserDetail({ tmbId });
|
||||
|
||||
if (!user) {
|
||||
return Promise.reject(UserErrEnum.unAuthUser);
|
||||
}
|
||||
if (minBalance !== undefined && user.team.balance < minBalance) {
|
||||
return Promise.reject(UserErrEnum.balanceNotEnough);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { connectionMongo, type Model } from '../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
||||
import { PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import { PRICE_SCALE } from '@fastgpt/global/support/wallet/constants';
|
||||
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||
import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant';
|
||||
|
||||
@@ -63,6 +63,8 @@ const UserSchema = new Schema({
|
||||
});
|
||||
|
||||
try {
|
||||
// login
|
||||
UserSchema.index({ username: 1, password: 1 });
|
||||
UserSchema.index({ createTime: -1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
@@ -36,7 +36,7 @@ export async function getTmbInfoByTmbId({ tmbId }: { tmbId: string }) {
|
||||
return Promise.reject('tmbId or userId is required');
|
||||
}
|
||||
return getTeamMember({
|
||||
_id: new Types.ObjectId(tmbId),
|
||||
_id: new Types.ObjectId(String(tmbId)),
|
||||
status: notLeaveStatus
|
||||
});
|
||||
}
|
||||
|
@@ -27,7 +27,10 @@ const TeamSchema = new Schema({
|
||||
},
|
||||
maxSize: {
|
||||
type: Number,
|
||||
default: 3
|
||||
default: 1
|
||||
},
|
||||
tagsUrl: {
|
||||
type: String
|
||||
},
|
||||
limit: {
|
||||
lastExportDatasetTime: {
|
||||
|
35
packages/service/support/user/team/teamTagsSchema.ts
Normal file
35
packages/service/support/user/team/teamTagsSchema.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { connectionMongo, type Model } from '../../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { TeamTagsSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
|
||||
import {
|
||||
TeamCollectionName,
|
||||
TeamTagsCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
|
||||
const TeamTagsSchema = new Schema({
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamCollectionName,
|
||||
required: true
|
||||
},
|
||||
key: {
|
||||
type: String
|
||||
},
|
||||
createTime: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
TeamTagsSchema.index({ teamId: 1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoTeamTags: Model<TeamTagsSchemaType> =
|
||||
models[TeamTagsCollectionName] || model(TeamTagsCollectionName, TeamTagsSchema);
|
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
user sub plan
|
||||
1. type=standard: There will only be 1, and each team will have one
|
||||
2. type=extraDatasetSize/extraPoints: Can buy multiple
|
||||
*/
|
||||
import { connectionMongo, type Model } from '../../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||
@@ -23,25 +28,10 @@ const SubSchema = new Schema({
|
||||
required: true
|
||||
},
|
||||
status: {
|
||||
// active: continue sub; canceled: canceled sub;
|
||||
type: String,
|
||||
enum: Object.keys(subStatusMap),
|
||||
required: true
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
enum: Object.keys(subModeMap)
|
||||
},
|
||||
currentMode: {
|
||||
type: String,
|
||||
enum: Object.keys(subModeMap),
|
||||
required: true
|
||||
},
|
||||
nextMode: {
|
||||
type: String,
|
||||
enum: Object.keys(subModeMap),
|
||||
required: true
|
||||
},
|
||||
startTime: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
@@ -55,12 +45,16 @@ const SubSchema = new Schema({
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
pointPrice: {
|
||||
// stand level point total price
|
||||
type: Number
|
||||
},
|
||||
|
||||
// sub content
|
||||
// standard sub
|
||||
currentMode: {
|
||||
type: String,
|
||||
enum: Object.keys(subModeMap)
|
||||
},
|
||||
nextMode: {
|
||||
type: String,
|
||||
enum: Object.keys(subModeMap)
|
||||
},
|
||||
currentSubLevel: {
|
||||
type: String,
|
||||
enum: Object.keys(standardSubLevelMap)
|
||||
@@ -69,79 +63,32 @@ const SubSchema = new Schema({
|
||||
type: String,
|
||||
enum: Object.keys(standardSubLevelMap)
|
||||
},
|
||||
|
||||
// stand sub and extra points sub. Plan total points
|
||||
totalPoints: {
|
||||
type: Number
|
||||
},
|
||||
pointPrice: {
|
||||
// stand level point total price
|
||||
type: Number
|
||||
},
|
||||
surplusPoints: {
|
||||
// plan surplus points
|
||||
type: Number
|
||||
},
|
||||
|
||||
// extra dataset size
|
||||
currentExtraDatasetSize: {
|
||||
type: Number
|
||||
},
|
||||
nextExtraDatasetSize: {
|
||||
type: Number
|
||||
},
|
||||
|
||||
currentExtraPoints: {
|
||||
type: Number
|
||||
},
|
||||
nextExtraPoints: {
|
||||
type: Number
|
||||
},
|
||||
|
||||
// standard sub limit
|
||||
// maxTeamMember: {
|
||||
// type: Number
|
||||
// },
|
||||
// maxAppAmount: {
|
||||
// type: Number
|
||||
// },
|
||||
// maxDatasetAmount: {
|
||||
// type: Number
|
||||
// },
|
||||
// chatHistoryStoreDuration: {
|
||||
// // n day
|
||||
// type: Number
|
||||
// },
|
||||
// maxDatasetSize: {
|
||||
// type: Number
|
||||
// },
|
||||
// trainingWeight: {
|
||||
// // 0 1 2 3
|
||||
// type: Number
|
||||
// },
|
||||
// customApiKey: {
|
||||
// type: Boolean
|
||||
// },
|
||||
// customCopyright: {
|
||||
// type: Boolean
|
||||
// },
|
||||
// websiteSyncInterval: {
|
||||
// // hours
|
||||
// type: Number
|
||||
// },
|
||||
// reRankWeight: {
|
||||
// // 0 1 2 3
|
||||
// type: Number
|
||||
// },
|
||||
// totalPoints: {
|
||||
// // record standard sub points
|
||||
// type: Number
|
||||
// },
|
||||
|
||||
surplusPoints: {
|
||||
// standard sub / extra points sub
|
||||
type: Number
|
||||
},
|
||||
|
||||
// abandon
|
||||
renew: Boolean, //决定是否续费
|
||||
datasetStoreAmount: Number
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
SubSchema.index({ teamId: 1 });
|
||||
SubSchema.index({ status: 1 });
|
||||
SubSchema.index({ type: 1 });
|
||||
SubSchema.index({ expiredTime: -1 });
|
||||
// get team plan
|
||||
SubSchema.index({ teamId: 1, type: 1, expiredTime: -1 });
|
||||
|
||||
// timer task. check expired plan; update standard plan;
|
||||
SubSchema.index({ type: 1, currentSubLevel: 1, expiredTime: -1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
@@ -1,87 +1,147 @@
|
||||
import { SubTypeEnum } from '@fastgpt/global/support/wallet/sub/constants';
|
||||
import {
|
||||
StandardSubLevelEnum,
|
||||
SubModeEnum,
|
||||
SubStatusEnum,
|
||||
SubTypeEnum
|
||||
} from '@fastgpt/global/support/wallet/sub/constants';
|
||||
import { MongoTeamSub } from './schema';
|
||||
import { addHours } from 'date-fns';
|
||||
import { FeTeamSubType, StandSubPlanLevelMapType } from '@fastgpt/global/support/wallet/sub/type.d';
|
||||
import { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type.d';
|
||||
import { getVectorCountByTeamId } from '../../../common/vectorStore/controller';
|
||||
import dayjs from 'dayjs';
|
||||
import { ClientSession } from '../../../common/mongo';
|
||||
import { addMonths } from 'date-fns';
|
||||
|
||||
/* get team dataset max size */
|
||||
export const getTeamDatasetMaxSize = async ({
|
||||
teamId,
|
||||
standardPlans
|
||||
}: {
|
||||
teamId: string;
|
||||
standardPlans?: StandSubPlanLevelMapType;
|
||||
}) => {
|
||||
if (!standardPlans) {
|
||||
return {
|
||||
maxSize: Infinity,
|
||||
sub: null
|
||||
};
|
||||
}
|
||||
export const getStandardPlans = () => {
|
||||
return global?.subPlans?.standard;
|
||||
};
|
||||
export const getStandardPlan = (level: `${StandardSubLevelEnum}`) => {
|
||||
return global.subPlans?.standard?.[level];
|
||||
};
|
||||
|
||||
const plans = await MongoTeamSub.find({
|
||||
teamId,
|
||||
expiredTime: { $gte: addHours(new Date(), -3) }
|
||||
}).lean();
|
||||
|
||||
const standard = plans.find((plan) => plan.type === SubTypeEnum.standard);
|
||||
const extraDatasetSize = plans.find((plan) => plan.type === SubTypeEnum.extraDatasetSize);
|
||||
|
||||
const standardMaxDatasetSize =
|
||||
standard?.currentSubLevel && standardPlans
|
||||
? standardPlans[standard.currentSubLevel]?.maxDatasetSize || Infinity
|
||||
: Infinity;
|
||||
const totalDatasetSize =
|
||||
standardMaxDatasetSize + (extraDatasetSize?.currentExtraDatasetSize || 0);
|
||||
export const getTeamStandPlan = async ({ teamId }: { teamId: string }) => {
|
||||
const standardPlans = global.subPlans?.standard;
|
||||
const standard = await MongoTeamSub.findOne({ teamId, type: SubTypeEnum.standard }).lean();
|
||||
|
||||
return {
|
||||
maxSize: totalDatasetSize,
|
||||
sub: extraDatasetSize
|
||||
[SubTypeEnum.standard]: standard,
|
||||
standardConstants:
|
||||
standard?.currentSubLevel && standardPlans
|
||||
? standardPlans[standard.currentSubLevel]
|
||||
: undefined
|
||||
};
|
||||
};
|
||||
|
||||
export const getTeamSubPlanStatus = async ({
|
||||
export const initTeamStandardPlan2Free = async ({
|
||||
teamId,
|
||||
standardPlans
|
||||
session
|
||||
}: {
|
||||
teamId: string;
|
||||
standardPlans?: StandSubPlanLevelMapType;
|
||||
}): Promise<FeTeamSubType> => {
|
||||
session?: ClientSession;
|
||||
}) => {
|
||||
const freePoints = global?.subPlans?.standard?.free?.totalPoints || 100;
|
||||
|
||||
const teamStandardSub = await MongoTeamSub.findOne({ teamId, type: SubTypeEnum.standard });
|
||||
|
||||
if (teamStandardSub) {
|
||||
teamStandardSub.status = SubStatusEnum.active;
|
||||
teamStandardSub.currentMode = SubModeEnum.month;
|
||||
teamStandardSub.nextMode = SubModeEnum.month;
|
||||
teamStandardSub.startTime = new Date();
|
||||
teamStandardSub.expiredTime = addMonths(new Date(), 1);
|
||||
|
||||
teamStandardSub.currentSubLevel = StandardSubLevelEnum.free;
|
||||
teamStandardSub.nextSubLevel = StandardSubLevelEnum.free;
|
||||
|
||||
teamStandardSub.price = 0;
|
||||
teamStandardSub.pointPrice = 0;
|
||||
|
||||
teamStandardSub.totalPoints = freePoints;
|
||||
teamStandardSub.surplusPoints =
|
||||
teamStandardSub.surplusPoints && teamStandardSub.surplusPoints < 0
|
||||
? teamStandardSub.surplusPoints + freePoints
|
||||
: freePoints;
|
||||
return teamStandardSub.save({ session });
|
||||
}
|
||||
|
||||
return MongoTeamSub.create(
|
||||
[
|
||||
{
|
||||
teamId,
|
||||
type: SubTypeEnum.standard,
|
||||
status: SubStatusEnum.active,
|
||||
currentMode: SubModeEnum.month,
|
||||
nextMode: SubModeEnum.month,
|
||||
startTime: new Date(),
|
||||
expiredTime: addMonths(new Date(), 1),
|
||||
price: 0,
|
||||
pointPrice: 0,
|
||||
|
||||
currentSubLevel: StandardSubLevelEnum.free,
|
||||
nextSubLevel: StandardSubLevelEnum.free,
|
||||
|
||||
totalPoints: freePoints,
|
||||
surplusPoints: freePoints
|
||||
}
|
||||
],
|
||||
{ session }
|
||||
);
|
||||
};
|
||||
|
||||
export const getTeamPlanStatus = async ({
|
||||
teamId
|
||||
}: {
|
||||
teamId: string;
|
||||
}): Promise<FeTeamPlanStatusType> => {
|
||||
const standardPlans = global.subPlans?.standard;
|
||||
|
||||
const [plans, usedDatasetSize] = await Promise.all([
|
||||
MongoTeamSub.find({ teamId }).lean(),
|
||||
getVectorCountByTeamId(teamId)
|
||||
]);
|
||||
|
||||
const standard = plans.find((plan) => plan.type === SubTypeEnum.standard);
|
||||
const extraDatasetSize = plans.find((plan) => plan.type === SubTypeEnum.extraDatasetSize);
|
||||
const extraPoints = plans.find((plan) => plan.type === SubTypeEnum.extraPoints);
|
||||
const extraDatasetSize = plans.filter((plan) => plan.type === SubTypeEnum.extraDatasetSize);
|
||||
const extraPoints = plans.filter((plan) => plan.type === SubTypeEnum.extraPoints);
|
||||
|
||||
// Free user, first login after expiration. The free subscription plan will be reset
|
||||
if (
|
||||
standard &&
|
||||
standard.expiredTime &&
|
||||
standard.currentSubLevel === StandardSubLevelEnum.free &&
|
||||
dayjs(standard.expiredTime).isBefore(new Date())
|
||||
) {
|
||||
console.log('Init free stand plan', { teamId });
|
||||
await initTeamStandardPlan2Free({ teamId });
|
||||
return getTeamPlanStatus({ teamId });
|
||||
}
|
||||
|
||||
const totalPoints = standardPlans
|
||||
? (standard?.totalPoints || 0) +
|
||||
extraPoints.reduce((acc, cur) => acc + (cur.totalPoints || 0), 0)
|
||||
: Infinity;
|
||||
const surplusPoints =
|
||||
(standard?.surplusPoints || 0) +
|
||||
extraPoints.reduce((acc, cur) => acc + (cur.surplusPoints || 0), 0);
|
||||
|
||||
const standardMaxDatasetSize =
|
||||
standard?.currentSubLevel && standardPlans
|
||||
? standardPlans[standard.currentSubLevel]?.maxDatasetSize || Infinity
|
||||
: Infinity;
|
||||
const totalDatasetSize =
|
||||
standardMaxDatasetSize + (extraDatasetSize?.currentExtraDatasetSize || 0);
|
||||
|
||||
const standardMaxPoints =
|
||||
standard?.currentSubLevel && standardPlans
|
||||
? standardPlans[standard.currentSubLevel]?.totalPoints || Infinity
|
||||
: Infinity;
|
||||
const totalPoints = standardMaxPoints + (extraPoints?.currentExtraPoints || 0);
|
||||
|
||||
const surplusPoints = (standard?.surplusPoints || 0) + (extraPoints?.surplusPoints || 0);
|
||||
standardMaxDatasetSize +
|
||||
extraDatasetSize.reduce((acc, cur) => acc + (cur.currentExtraDatasetSize || 0), 0);
|
||||
|
||||
return {
|
||||
[SubTypeEnum.standard]: standard,
|
||||
[SubTypeEnum.extraDatasetSize]: extraDatasetSize,
|
||||
[SubTypeEnum.extraPoints]: extraPoints,
|
||||
standardConstants:
|
||||
standard?.currentSubLevel && standardPlans
|
||||
? standardPlans[standard.currentSubLevel]
|
||||
: undefined,
|
||||
|
||||
standardMaxDatasetSize,
|
||||
datasetMaxSize: totalDatasetSize,
|
||||
usedDatasetSize,
|
||||
|
||||
standardMaxPoints,
|
||||
totalPoints,
|
||||
usedPoints: totalPoints - surplusPoints
|
||||
usedPoints: totalPoints - surplusPoints,
|
||||
|
||||
datasetMaxSize: totalDatasetSize,
|
||||
usedDatasetSize
|
||||
};
|
||||
};
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import { MongoBill } from './schema';
|
||||
import { UsageSourceEnum } from '@fastgpt/global/support/wallet/usage/constants';
|
||||
import { MongoUsage } from './schema';
|
||||
import { ClientSession } from '../../../common/mongo';
|
||||
|
||||
export const createTrainingBill = async ({
|
||||
export const createTrainingUsage = async ({
|
||||
teamId,
|
||||
tmbId,
|
||||
appName,
|
||||
@@ -14,33 +14,33 @@ export const createTrainingBill = async ({
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
appName: string;
|
||||
billSource: `${BillSourceEnum}`;
|
||||
billSource: `${UsageSourceEnum}`;
|
||||
vectorModel: string;
|
||||
agentModel: string;
|
||||
session?: ClientSession;
|
||||
}) => {
|
||||
const [{ _id }] = await MongoBill.create(
|
||||
const [{ _id }] = await MongoUsage.create(
|
||||
[
|
||||
{
|
||||
teamId,
|
||||
tmbId,
|
||||
appName,
|
||||
source: billSource,
|
||||
totalPoints: 0,
|
||||
list: [
|
||||
{
|
||||
moduleName: 'wallet.moduleName.index',
|
||||
moduleName: 'support.wallet.moduleName.index',
|
||||
model: vectorModel,
|
||||
charsLength: 0,
|
||||
amount: 0
|
||||
},
|
||||
{
|
||||
moduleName: 'wallet.moduleName.qa',
|
||||
moduleName: 'support.wallet.moduleName.qa',
|
||||
model: agentModel,
|
||||
charsLength: 0,
|
||||
amount: 0
|
||||
}
|
||||
],
|
||||
total: 0
|
||||
]
|
||||
}
|
||||
],
|
||||
{ session }
|
@@ -1,13 +1,15 @@
|
||||
import { connectionMongo, type Model } from '../../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { BillSchema as BillType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import { BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import { UsageSchemaType } from '@fastgpt/global/support/wallet/usage/type';
|
||||
import { UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
|
||||
import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
|
||||
const BillSchema = new Schema({
|
||||
export const UsageCollectionName = 'usages';
|
||||
|
||||
const UsageSchema = new Schema({
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamCollectionName,
|
||||
@@ -18,6 +20,11 @@ const BillSchema = new Schema({
|
||||
ref: TeamMemberCollectionName,
|
||||
required: true
|
||||
},
|
||||
source: {
|
||||
type: String,
|
||||
enum: Object.keys(UsageSourceMap),
|
||||
required: true
|
||||
},
|
||||
appName: {
|
||||
type: String,
|
||||
default: ''
|
||||
@@ -31,16 +38,16 @@ const BillSchema = new Schema({
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
total: {
|
||||
// 1 * PRICE_SCALE
|
||||
totalPoints: {
|
||||
// total points
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
source: {
|
||||
type: String,
|
||||
enum: Object.keys(BillSourceMap),
|
||||
required: true
|
||||
},
|
||||
// total: {
|
||||
// // total points
|
||||
// type: Number,
|
||||
// required: true
|
||||
// },
|
||||
list: {
|
||||
type: Array,
|
||||
default: []
|
||||
@@ -48,11 +55,15 @@ const BillSchema = new Schema({
|
||||
});
|
||||
|
||||
try {
|
||||
BillSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 }, { background: true });
|
||||
BillSchema.index({ time: 1 }, { expireAfterSeconds: 180 * 24 * 60 * 60 });
|
||||
UsageSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 }, { background: true });
|
||||
// timer task. clear dead team
|
||||
UsageSchema.index({ teamId: 1, time: -1 }, { background: true });
|
||||
|
||||
UsageSchema.index({ time: 1 }, { expireAfterSeconds: 180 * 24 * 60 * 60 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoBill: Model<BillType> = models['bill'] || model('bill', BillSchema);
|
||||
MongoBill.syncIndexes();
|
||||
export const MongoUsage: Model<UsageSchemaType> =
|
||||
models[UsageCollectionName] || model(UsageCollectionName, UsageSchema);
|
||||
MongoUsage.syncIndexes();
|
Reference in New Issue
Block a user