mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 13:03:50 +00:00
feat: 修改计费模式为tokens
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
"eventsource-parser": "^0.1.0",
|
"eventsource-parser": "^0.1.0",
|
||||||
"formidable": "^2.1.1",
|
"formidable": "^2.1.1",
|
||||||
"framer-motion": "^9.0.6",
|
"framer-motion": "^9.0.6",
|
||||||
|
"gpt-token-utils": "^1.2.0",
|
||||||
"hyperdown": "^2.4.29",
|
"hyperdown": "^2.4.29",
|
||||||
"immer": "^9.0.19",
|
"immer": "^9.0.19",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
|
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@@ -27,6 +27,7 @@ specifiers:
|
|||||||
eventsource-parser: ^0.1.0
|
eventsource-parser: ^0.1.0
|
||||||
formidable: ^2.1.1
|
formidable: ^2.1.1
|
||||||
framer-motion: ^9.0.6
|
framer-motion: ^9.0.6
|
||||||
|
gpt-token-utils: ^1.2.0
|
||||||
husky: ^8.0.3
|
husky: ^8.0.3
|
||||||
hyperdown: ^2.4.29
|
hyperdown: ^2.4.29
|
||||||
immer: ^9.0.19
|
immer: ^9.0.19
|
||||||
@@ -69,6 +70,7 @@ dependencies:
|
|||||||
eventsource-parser: registry.npmmirror.com/eventsource-parser/0.1.0
|
eventsource-parser: registry.npmmirror.com/eventsource-parser/0.1.0
|
||||||
formidable: registry.npmmirror.com/formidable/2.1.1
|
formidable: registry.npmmirror.com/formidable/2.1.1
|
||||||
framer-motion: registry.npmmirror.com/framer-motion/9.0.6_biqbaboplfbrettd7655fr4n2y
|
framer-motion: registry.npmmirror.com/framer-motion/9.0.6_biqbaboplfbrettd7655fr4n2y
|
||||||
|
gpt-token-utils: registry.npmmirror.com/gpt-token-utils/1.2.0
|
||||||
hyperdown: registry.npmmirror.com/hyperdown/2.4.29
|
hyperdown: registry.npmmirror.com/hyperdown/2.4.29
|
||||||
immer: registry.npmmirror.com/immer/9.0.19
|
immer: registry.npmmirror.com/immer/9.0.19
|
||||||
jsonwebtoken: registry.npmmirror.com/jsonwebtoken/9.0.0
|
jsonwebtoken: registry.npmmirror.com/jsonwebtoken/9.0.0
|
||||||
@@ -6964,6 +6966,12 @@ packages:
|
|||||||
get-intrinsic: registry.npmmirror.com/get-intrinsic/1.2.0
|
get-intrinsic: registry.npmmirror.com/get-intrinsic/1.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
registry.npmmirror.com/gpt-token-utils/1.2.0:
|
||||||
|
resolution: {integrity: sha512-s8twaU38UE2Vp65JhQEjz8qvWhWY8KZYvmvYHapxlPT03Ok35Clq+gm9eE27wQILdFisseMVRSiC5lJR9GBklA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/gpt-token-utils/-/gpt-token-utils-1.2.0.tgz}
|
||||||
|
name: gpt-token-utils
|
||||||
|
version: 1.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
registry.npmmirror.com/graceful-fs/4.2.10:
|
registry.npmmirror.com/graceful-fs/4.2.10:
|
||||||
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.10.tgz}
|
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.10.tgz}
|
||||||
name: graceful-fs
|
name: graceful-fs
|
||||||
|
@@ -2,15 +2,25 @@ import React, { useRef, useEffect, useMemo } from 'react';
|
|||||||
import type { BoxProps } from '@chakra-ui/react';
|
import type { BoxProps } from '@chakra-ui/react';
|
||||||
import { Box } from '@chakra-ui/react';
|
import { Box } from '@chakra-ui/react';
|
||||||
import { throttle } from 'lodash';
|
import { throttle } from 'lodash';
|
||||||
|
import { useLoading } from '@/hooks/useLoading';
|
||||||
|
|
||||||
interface Props extends BoxProps {
|
interface Props extends BoxProps {
|
||||||
nextPage: () => void;
|
nextPage: () => void;
|
||||||
isLoadAll: boolean;
|
isLoadAll: boolean;
|
||||||
requesting: boolean;
|
requesting: boolean;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
initRequesting?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ScrollData = ({ children, nextPage, isLoadAll, requesting, ...props }: Props) => {
|
const ScrollData = ({
|
||||||
|
children,
|
||||||
|
nextPage,
|
||||||
|
isLoadAll,
|
||||||
|
requesting,
|
||||||
|
initRequesting,
|
||||||
|
...props
|
||||||
|
}: Props) => {
|
||||||
|
const { Loading } = useLoading({ defaultLoading: true });
|
||||||
const elementRef = useRef<HTMLDivElement>(null);
|
const elementRef = useRef<HTMLDivElement>(null);
|
||||||
const loadText = useMemo(() => {
|
const loadText = useMemo(() => {
|
||||||
if (requesting) return '请求中……';
|
if (requesting) return '请求中……';
|
||||||
@@ -43,7 +53,7 @@ const ScrollData = ({ children, nextPage, isLoadAll, requesting, ...props }: Pro
|
|||||||
}, [elementRef, nextPage]);
|
}, [elementRef, nextPage]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box {...props} ref={elementRef} overflow={'auto'}>
|
<Box {...props} ref={elementRef} overflow={'auto'} position={'relative'}>
|
||||||
{children}
|
{children}
|
||||||
<Box
|
<Box
|
||||||
mt={2}
|
mt={2}
|
||||||
@@ -58,6 +68,7 @@ const ScrollData = ({ children, nextPage, isLoadAll, requesting, ...props }: Pro
|
|||||||
>
|
>
|
||||||
{loadText}
|
{loadText}
|
||||||
</Box>
|
</Box>
|
||||||
|
{initRequesting && <Loading fixed={false} />}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -13,7 +13,7 @@ export type ModelConstantsData = {
|
|||||||
trainName: string; // 空字符串代表不能训练
|
trainName: string; // 空字符串代表不能训练
|
||||||
maxToken: number;
|
maxToken: number;
|
||||||
maxTemperature: number;
|
maxTemperature: number;
|
||||||
price: number; // 多少钱 / 1字,单位: 0.00001元
|
price: number; // 多少钱 / 1token,单位: 0.00001元
|
||||||
};
|
};
|
||||||
|
|
||||||
export const modelList: ModelConstantsData[] = [
|
export const modelList: ModelConstantsData[] = [
|
||||||
@@ -21,10 +21,10 @@ export const modelList: ModelConstantsData[] = [
|
|||||||
serviceCompany: 'openai',
|
serviceCompany: 'openai',
|
||||||
name: 'chatGPT',
|
name: 'chatGPT',
|
||||||
model: ChatModelNameEnum.GPT35,
|
model: ChatModelNameEnum.GPT35,
|
||||||
trainName: 'turbo',
|
trainName: '',
|
||||||
maxToken: 4000,
|
maxToken: 4000,
|
||||||
maxTemperature: 2,
|
maxTemperature: 2,
|
||||||
price: 5
|
price: 3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
serviceCompany: 'openai',
|
serviceCompany: 'openai',
|
||||||
@@ -33,7 +33,7 @@ export const modelList: ModelConstantsData[] = [
|
|||||||
trainName: 'davinci',
|
trainName: 'davinci',
|
||||||
maxToken: 4000,
|
maxToken: 4000,
|
||||||
maxTemperature: 2,
|
maxTemperature: 2,
|
||||||
price: 50
|
price: 30
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@@ -18,11 +18,15 @@ export const usePaging = <T = any>({
|
|||||||
const [total, setTotal] = useState(0);
|
const [total, setTotal] = useState(0);
|
||||||
const [isLoadAll, setIsLoadAll] = useState(false);
|
const [isLoadAll, setIsLoadAll] = useState(false);
|
||||||
const [requesting, setRequesting] = useState(false);
|
const [requesting, setRequesting] = useState(false);
|
||||||
|
const [initRequesting, setInitRequesting] = useState(false);
|
||||||
|
|
||||||
const getData = useCallback(
|
const getData = useCallback(
|
||||||
async (num: number, init = false) => {
|
async (num: number, init = false) => {
|
||||||
if (requesting) return;
|
if (requesting) return;
|
||||||
if (!init && isLoadAll) return;
|
if (!init && isLoadAll) return;
|
||||||
|
if (init) {
|
||||||
|
setInitRequesting(true);
|
||||||
|
}
|
||||||
setRequesting(true);
|
setRequesting(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -49,6 +53,7 @@ export const usePaging = <T = any>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setRequesting(false);
|
setRequesting(false);
|
||||||
|
setInitRequesting(false);
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
[api, isLoadAll, pageSize, params, requesting, toast]
|
[api, isLoadAll, pageSize, params, requesting, toast]
|
||||||
@@ -66,6 +71,7 @@ export const usePaging = <T = any>({
|
|||||||
getData,
|
getData,
|
||||||
requesting,
|
requesting,
|
||||||
isLoadAll,
|
isLoadAll,
|
||||||
nextPage
|
nextPage,
|
||||||
|
initRequesting
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -143,15 +143,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
!stream.destroyed && stream.push(null);
|
!stream.destroyed && stream.push(null);
|
||||||
stream.destroy();
|
stream.destroy();
|
||||||
|
|
||||||
const promptsLen = formatPrompts.reduce((sum, item) => sum + item.content.length, 0);
|
const promptsContent = formatPrompts.map((item) => item.content).join('');
|
||||||
console.log(`responseLen: ${responseContent.length}`, `promptLen: ${promptsLen}`);
|
console.log(`responseLen: ${responseContent.length}`, `promptLen: ${promptsContent.length}`);
|
||||||
// 只有使用平台的 key 才计费
|
// 只有使用平台的 key 才计费
|
||||||
!userApiKey &&
|
!userApiKey &&
|
||||||
pushBill({
|
pushBill({
|
||||||
modelName: model.service.modelName,
|
modelName: model.service.modelName,
|
||||||
userId,
|
userId,
|
||||||
chatId,
|
chatId,
|
||||||
textLen: promptsLen + responseContent.length
|
text: promptsContent + responseContent
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (step === 1) {
|
if (step === 1) {
|
||||||
|
@@ -54,21 +54,20 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const responseMessage = response.data.choices[0]?.text || '';
|
const responseContent = response.data.choices[0]?.text || '';
|
||||||
|
|
||||||
const promptsLen = prompt.reduce((sum, item) => sum + item.value.length, 0);
|
console.log(`responseLen: ${responseContent.length}`, `promptLen: ${formatPrompts.length}`);
|
||||||
console.log(`responseLen: ${responseMessage.length}`, `promptLen: ${promptsLen}`);
|
|
||||||
// 只有使用平台的 key 才计费
|
// 只有使用平台的 key 才计费
|
||||||
!userApiKey &&
|
!userApiKey &&
|
||||||
pushBill({
|
pushBill({
|
||||||
modelName: model.service.modelName,
|
modelName: model.service.modelName,
|
||||||
userId,
|
userId,
|
||||||
chatId,
|
chatId,
|
||||||
textLen: promptsLen + responseMessage.length
|
text: formatPrompts + responseContent
|
||||||
});
|
});
|
||||||
|
|
||||||
jsonRes(res, {
|
jsonRes(res, {
|
||||||
data: responseMessage
|
data: responseContent
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
jsonRes(res, {
|
jsonRes(res, {
|
||||||
|
@@ -21,6 +21,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
// 根据 userId 获取模型信息
|
// 根据 userId 获取模型信息
|
||||||
const models = await Model.find({
|
const models = await Model.find({
|
||||||
userId
|
userId
|
||||||
|
}).sort({
|
||||||
|
_id: -1
|
||||||
});
|
});
|
||||||
|
|
||||||
jsonRes(res, {
|
jsonRes(res, {
|
||||||
|
@@ -12,12 +12,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
}
|
}
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
await Bill.updateMany(
|
const bills = await Bill.find({
|
||||||
{},
|
tokenLen: { $exists: false }
|
||||||
{
|
});
|
||||||
type: 'chat',
|
await Promise.all(
|
||||||
modelName: 'gpt-3.5-turbo'
|
bills.map((bill) =>
|
||||||
}
|
Bill.findByIdAndUpdate(bill._id, {
|
||||||
|
tokenLen: bill.textLen
|
||||||
|
})
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
jsonRes(res, {
|
jsonRes(res, {
|
||||||
|
@@ -5,6 +5,7 @@ import axios from 'axios';
|
|||||||
import { authToken } from '@/service/utils/tools';
|
import { authToken } from '@/service/utils/tools';
|
||||||
import { customAlphabet } from 'nanoid';
|
import { customAlphabet } from 'nanoid';
|
||||||
import { connectToDatabase, Pay } from '@/service/mongo';
|
import { connectToDatabase, Pay } from '@/service/mongo';
|
||||||
|
import { PRICE_SCALE } from '@/utils/user';
|
||||||
|
|
||||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 20);
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 20);
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
// 充值记录 + 1
|
// 充值记录 + 1
|
||||||
const payOrder = await Pay.create({
|
const payOrder = await Pay.create({
|
||||||
userId,
|
userId,
|
||||||
price: amount * 100000,
|
price: amount * PRICE_SCALE,
|
||||||
orderId: id
|
orderId: id
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -32,7 +32,8 @@ const DataList = () => {
|
|||||||
isLoadAll,
|
isLoadAll,
|
||||||
requesting,
|
requesting,
|
||||||
data: dataList,
|
data: dataList,
|
||||||
getData
|
getData,
|
||||||
|
initRequesting
|
||||||
} = usePaging<DataListItem>({
|
} = usePaging<DataListItem>({
|
||||||
api: getDataList,
|
api: getDataList,
|
||||||
pageSize: 20
|
pageSize: 20
|
||||||
@@ -76,7 +77,13 @@ const DataList = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
{/* 数据表 */}
|
{/* 数据表 */}
|
||||||
<Card mt={3} flex={'1 0 0'} h={['auto', '0']} px={6} py={4}>
|
<Card mt={3} flex={'1 0 0'} h={['auto', '0']} px={6} py={4}>
|
||||||
<ScrollData h={'100%'} nextPage={nextPage} isLoadAll={isLoadAll} requesting={requesting}>
|
<ScrollData
|
||||||
|
h={'100%'}
|
||||||
|
nextPage={nextPage}
|
||||||
|
isLoadAll={isLoadAll}
|
||||||
|
requesting={requesting}
|
||||||
|
initRequesting={initRequesting}
|
||||||
|
>
|
||||||
<TableContainer>
|
<TableContainer>
|
||||||
<Table>
|
<Table>
|
||||||
<Thead>
|
<Thead>
|
||||||
@@ -96,7 +103,7 @@ const DataList = () => {
|
|||||||
defaultValue={item.name}
|
defaultValue={item.name}
|
||||||
size={'sm'}
|
size={'sm'}
|
||||||
onBlur={(e) => {
|
onBlur={(e) => {
|
||||||
if (!e.target.value) return;
|
if (!e.target.value || e.target.value === item.name) return;
|
||||||
updateDataName(item._id, e.target.value);
|
updateDataName(item._id, e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@@ -112,7 +112,7 @@ const CreateModel = ({
|
|||||||
{formatPrice(
|
{formatPrice(
|
||||||
modelList.find((item) => item.model === getValues('serviceModelName'))?.price || 0
|
modelList.find((item) => item.model === getValues('serviceModelName'))?.price || 0
|
||||||
) * 1000}
|
) * 1000}
|
||||||
元/1000字(包括上下文和标点符号)
|
元/1K tokens(包括上下文和标点符号)
|
||||||
</Box>
|
</Box>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
|
||||||
|
@@ -195,7 +195,7 @@ const ModelDetail = ({ modelId }: { modelId: string }) => {
|
|||||||
return () => {
|
return () => {
|
||||||
window.onbeforeunload = null;
|
window.onbeforeunload = null;
|
||||||
};
|
};
|
||||||
}, []);
|
}, [router]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -246,7 +246,13 @@ const ModelDetail = ({ modelId }: { modelId: string }) => {
|
|||||||
</Card>
|
</Card>
|
||||||
<Grid mt={5} gridTemplateColumns={media('1fr 1fr', '1fr')} gridGap={5}>
|
<Grid mt={5} gridTemplateColumns={media('1fr 1fr', '1fr')} gridGap={5}>
|
||||||
<ModelEditForm formHooks={formHooks} />
|
<ModelEditForm formHooks={formHooks} />
|
||||||
<Card p={4}>{!!model && <Training model={model} />}</Card>
|
|
||||||
|
{canTrain && (
|
||||||
|
<Card p={4}>
|
||||||
|
<Training model={model} />
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
<Card p={4}>
|
<Card p={4}>
|
||||||
<Box fontWeight={'bold'} fontSize={'lg'}>
|
<Box fontWeight={'bold'} fontSize={'lg'}>
|
||||||
神奇操作
|
神奇操作
|
||||||
|
@@ -100,7 +100,7 @@ const PayModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
<Thead>
|
<Thead>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Th>模型类型</Th>
|
<Th>模型类型</Th>
|
||||||
<Th>价格(元/1000字符,包含所有上下文)</Th>
|
<Th>价格(元/1K tokens,包含所有上下文)</Th>
|
||||||
</Tr>
|
</Tr>
|
||||||
</Thead>
|
</Thead>
|
||||||
<Tbody>
|
<Tbody>
|
||||||
|
@@ -1,41 +1,49 @@
|
|||||||
import { connectToDatabase, Bill, User } from '../mongo';
|
import { connectToDatabase, Bill, User } from '../mongo';
|
||||||
import { modelList } from '@/constants/model';
|
import { modelList } from '@/constants/model';
|
||||||
|
import { encode } from 'gpt-token-utils';
|
||||||
|
import { formatPrice } from '@/utils/user';
|
||||||
|
|
||||||
export const pushBill = async ({
|
export const pushBill = async ({
|
||||||
modelName,
|
modelName,
|
||||||
userId,
|
userId,
|
||||||
chatId,
|
chatId,
|
||||||
textLen
|
text
|
||||||
}: {
|
}: {
|
||||||
modelName: string;
|
modelName: string;
|
||||||
userId: string;
|
userId: string;
|
||||||
chatId: string;
|
chatId: string;
|
||||||
textLen: number;
|
text: string;
|
||||||
}) => {
|
}) => {
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
let billId;
|
let billId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 获取模型单价格
|
||||||
const modelItem = modelList.find((item) => item.model === modelName);
|
const modelItem = modelList.find((item) => item.model === modelName);
|
||||||
|
const unitPrice = modelItem?.price || 5;
|
||||||
|
|
||||||
if (!modelItem) return;
|
// 计算 token 数量
|
||||||
|
const tokens = encode(text);
|
||||||
|
|
||||||
const price = modelItem.price * textLen;
|
// 计算价格
|
||||||
|
const price = unitPrice * tokens.length;
|
||||||
|
console.log('token len:', tokens.length, 'price: ', `${formatPrice(price)}元`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 插入 Bill 记录
|
// 插入 Bill 记录
|
||||||
const res = await Bill.create({
|
const res = await Bill.create({
|
||||||
userId,
|
userId,
|
||||||
type: 'chat',
|
type: 'chat',
|
||||||
modelName: modelItem.model,
|
modelName,
|
||||||
chatId,
|
chatId,
|
||||||
textLen,
|
textLen: text.length,
|
||||||
|
tokenLen: tokens.length,
|
||||||
price
|
price
|
||||||
});
|
});
|
||||||
billId = res._id;
|
billId = res._id;
|
||||||
|
|
||||||
// 扣费
|
// 账号扣费
|
||||||
await User.findByIdAndUpdate(userId, {
|
await User.findByIdAndUpdate(userId, {
|
||||||
$inc: { balance: -price }
|
$inc: { balance: -price }
|
||||||
});
|
});
|
||||||
|
@@ -31,6 +31,11 @@ const BillSchema = new Schema({
|
|||||||
type: Number,
|
type: Number,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
tokenLen: {
|
||||||
|
// 折算成 token 的数量
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
price: {
|
price: {
|
||||||
type: Number,
|
type: Number,
|
||||||
required: true
|
required: true
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { Schema, model, models } from 'mongoose';
|
import { Schema, model, models } from 'mongoose';
|
||||||
import { hashPassword } from '@/service/utils/tools';
|
import { hashPassword } from '@/service/utils/tools';
|
||||||
|
import { PRICE_SCALE } from '@/utils/user';
|
||||||
|
|
||||||
const UserSchema = new Schema({
|
const UserSchema = new Schema({
|
||||||
email: {
|
email: {
|
||||||
@@ -16,7 +17,7 @@ const UserSchema = new Schema({
|
|||||||
},
|
},
|
||||||
balance: {
|
balance: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 0.5 * 100000
|
default: 0.5 * PRICE_SCALE
|
||||||
},
|
},
|
||||||
accounts: [
|
accounts: [
|
||||||
{
|
{
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
const tokenKey = 'fast-gpt-token';
|
const tokenKey = 'fast-gpt-token';
|
||||||
|
export const PRICE_SCALE = 100000;
|
||||||
|
|
||||||
export const setToken = (val: string) => {
|
export const setToken = (val: string) => {
|
||||||
localStorage.setItem(tokenKey, val);
|
localStorage.setItem(tokenKey, val);
|
||||||
@@ -14,5 +15,5 @@ export const clearToken = () => {
|
|||||||
* 把数据库读取到的price,转化成元
|
* 把数据库读取到的price,转化成元
|
||||||
*/
|
*/
|
||||||
export const formatPrice = (val: number) => {
|
export const formatPrice = (val: number) => {
|
||||||
return val / 100000;
|
return val / PRICE_SCALE;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user