feat: set openai account

This commit is contained in:
archer 2023-07-28 12:02:23 +08:00
parent 7a56680935
commit dfda5285bd
No known key found for this signature in database
GPG Key ID: 569A5660D2379E28
21 changed files with 211 additions and 156 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

View File

@ -33,10 +33,12 @@
}, },
"user": { "user": {
"Bill Detail": "Bill Detail", "Bill Detail": "Bill Detail",
"Old password is error": "Old password is error",
"OpenAI Account Setting": "OpenAI Account Setting",
"Pay": "Pay", "Pay": "Pay",
"Set OpenAI Account Failed": "Set OpenAI account failed",
"Update Password": "Update Password", "Update Password": "Update Password",
"Update password failed": "Update password failed", "Update password failed": "Update password failed",
"Update password succseful": "Update password succseful", "Update password succseful": "Update password succseful"
"Old password is error": "Old password is error"
} }
} }

View File

@ -33,10 +33,12 @@
}, },
"user": { "user": {
"Bill Detail": "账单详情", "Bill Detail": "账单详情",
"Old password is error": "旧密码错误",
"OpenAI Account Setting": "OpenAI 账号配置",
"Pay": "充值", "Pay": "充值",
"Set OpenAI Account Failed": "设置 OpenAI 账号异常",
"Update Password": "修改密码", "Update Password": "修改密码",
"Update password failed": "修改密码异常", "Update password failed": "修改密码异常",
"Update password succseful": "修改密码成功", "Update password succseful": "修改密码成功"
"Old password is error": "旧密码错误"
} }
} }

View File

@ -1 +0,0 @@
import { GET, POST, DELETE } from './request';

View File

@ -1,10 +1,8 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { Box, Flex, Button, useDisclosure, useTheme, ModalBody } from '@chakra-ui/react'; import { Box, Flex, Button, useDisclosure, useTheme, Divider } from '@chakra-ui/react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { UserUpdateParams } from '@/types/user'; import { UserUpdateParams } from '@/types/user';
import { putUserInfo } from '@/api/user';
import { useToast } from '@/hooks/useToast'; import { useToast } from '@/hooks/useToast';
import { useGlobalStore } from '@/store/global';
import { useUserStore } from '@/store/user'; import { useUserStore } from '@/store/user';
import { UserType } from '@/types/user'; import { UserType } from '@/types/user';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
@ -28,13 +26,16 @@ const UpdatePswModal = dynamic(() => import('./UpdatePswModal'), {
loading: () => <Loading fixed={false} />, loading: () => <Loading fixed={false} />,
ssr: false ssr: false
}); });
const OpenAIAccountModal = dynamic(() => import('./OpenAIAccountModal'), {
loading: () => <Loading fixed={false} />,
ssr: false
});
const UserInfo = () => { const UserInfo = () => {
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation(); const { t } = useTranslation();
const { userInfo, updateUserInfo, initUserInfo } = useUserStore(); const { userInfo, updateUserInfo, initUserInfo } = useUserStore();
const { setLoading } = useGlobalStore(); const { reset } = useForm<UserUpdateParams>({
const { reset, register } = useForm<UserUpdateParams>({
defaultValues: userInfo as UserType defaultValues: userInfo as UserType
}); });
@ -49,41 +50,32 @@ const UserInfo = () => {
onClose: onCloseUpdatePsw, onClose: onCloseUpdatePsw,
onOpen: onOpenUpdatePsw onOpen: onOpenUpdatePsw
} = useDisclosure(); } = useDisclosure();
const { isOpen: isOpenOpenai, onClose: onCloseOpenai, onOpen: onOpenOpenai } = useDisclosure();
const { File, onOpen: onOpenSelectFile } = useSelectFile({ const { File, onOpen: onOpenSelectFile } = useSelectFile({
fileType: '.jpg,.png', fileType: '.jpg,.png',
multiple: false multiple: false
}); });
const onclickSave = useCallback( const onclickSave = useCallback(
async (data: UserUpdateParams) => { async (data: UserType) => {
setLoading(true); await updateUserInfo({
try { avatar: data.avatar,
await putUserInfo({ openaiAccount: data.openaiAccount
avatar: data.avatar });
}); reset(data);
updateUserInfo({ toast({
avatar: data.avatar title: '更新数据成功',
}); status: 'success'
reset(data); });
toast({
title: '更新数据成功',
status: 'success'
});
} catch (error) {
toast({
title: getErrText(error),
status: 'error'
});
}
setLoading(false);
}, },
[reset, setLoading, toast, updateUserInfo] [reset, toast, updateUserInfo]
); );
const onSelectFile = useCallback( const onSelectFile = useCallback(
async (e: File[]) => { async (e: File[]) => {
const file = e[0]; const file = e[0];
if (!file) return; if (!file || !userInfo) return;
try { try {
const src = await compressImg({ const src = await compressImg({
file, file,
@ -110,6 +102,7 @@ const UserInfo = () => {
reset(res); reset(res);
} }
}); });
return ( return (
<Box display={['block', 'flex']} py={[2, 10]} justifyContent={'center'} fontSize={['lg', 'xl']}> <Box display={['block', 'flex']} py={[2, 10]} justifyContent={'center'} fontSize={['lg', 'xl']}>
<Flex <Flex
@ -155,22 +148,65 @@ const UserInfo = () => {
</Button> </Button>
</Flex> </Flex>
{feConfigs?.show_userDetail && ( {feConfigs?.show_userDetail && (
<Box mt={6} whiteSpace={'nowrap'} w={['85%', '300px']}> <>
<Flex alignItems={'center'}> <Box mt={6} whiteSpace={'nowrap'} w={['85%', '300px']}>
<Box flex={'0 0 50px'}>:</Box> <Flex alignItems={'center'}>
<Box flex={1}> <Box flex={'0 0 50px'}>:</Box>
<strong>{userInfo?.balance.toFixed(3)}</strong> <Box flex={1}>
</Box> <strong>{userInfo?.balance.toFixed(3)}</strong>
<Button size={['sm', 'md']} ml={5} onClick={onOpenPayModal}> </Box>
<Button size={['sm', 'md']} ml={5} onClick={onOpenPayModal}>
</Button>
</Flex> </Button>
</Box> </Flex>
</Box>
<Divider my={3} />
<MyTooltip label={'点击配置账号'}>
<Flex
w={['85%', '300px']}
py={3}
px={6}
border={theme.borders.sm}
borderWidth={'1.5px'}
borderRadius={'md'}
bg={'myWhite.300'}
alignItems={'center'}
cursor={'pointer'}
userSelect={'none'}
onClick={onOpenOpenai}
>
<Avatar src={'/imgs/openai.png'} w={'18px'} />
<Box ml={2} flex={1}>
OpenAI
</Box>
<Box
w={'9px'}
h={'9px'}
borderRadius={'50%'}
bg={userInfo?.openaiAccount?.key ? '#67c13b' : 'myGray.500'}
/>
</Flex>
</MyTooltip>
</>
)} )}
</Box> </Box>
{isOpenPayModal && <PayModal onClose={onClosePayModal} />} {isOpenPayModal && <PayModal onClose={onClosePayModal} />}
{isOpenUpdatePsw && <UpdatePswModal onClose={onCloseUpdatePsw} />} {isOpenUpdatePsw && <UpdatePswModal onClose={onCloseUpdatePsw} />}
{isOpenOpenai && userInfo && (
<OpenAIAccountModal
defaultData={userInfo?.openaiAccount}
onSuccess={(data) =>
onclickSave({
...userInfo,
openaiAccount: data
})
}
onClose={onCloseOpenai}
/>
)}
<File onSelect={onSelectFile} /> <File onSelect={onSelectFile} />
</Box> </Box>
); );

View File

@ -0,0 +1,62 @@
import React from 'react';
import { ModalBody, Box, Flex, Input, ModalFooter, Button } from '@chakra-ui/react';
import MyModal from '@/components/MyModal';
import { useTranslation } from 'react-i18next';
import { useForm } from 'react-hook-form';
import { useRequest } from '@/hooks/useRequest';
import { UserType } from '@/types/user';
const OpenAIAccountModal = ({
defaultData,
onSuccess,
onClose
}: {
defaultData: UserType['openaiAccount'];
onSuccess: (e: UserType['openaiAccount']) => Promise<any>;
onClose: () => void;
}) => {
const { t } = useTranslation();
const { register, handleSubmit } = useForm({
defaultValues: defaultData
});
const { mutate: onSubmit, isLoading } = useRequest({
mutationFn: async (data: UserType['openaiAccount']) => onSuccess(data),
onSuccess(res) {
onClose();
},
errorToast: t('user.Set OpenAI Account Failed')
});
return (
<MyModal isOpen onClose={onClose} title={t('user.OpenAI Account Setting')}>
<ModalBody>
<Box fontSize={'sm'} color={'myGray.500'}>
API
</Box>
<Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 65px'}>API Key:</Box>
<Input flex={1} {...register('key')}></Input>
</Flex>
<Flex alignItems={'center'} mt={5}>
<Box flex={'0 0 65px'}>BaseUrl:</Box>
<Input
flex={1}
{...register('baseUrl')}
placeholder={'中转地址,未自动补全 "v1"'}
></Input>
</Flex>
</ModalBody>
<ModalFooter>
<Button mr={3} variant={'base'} onClick={onClose}>
</Button>
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>
</Button>
</ModalFooter>
</MyModal>
);
};
export default OpenAIAccountModal;

View File

@ -1,62 +0,0 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response';
import { authUser } from '@/service/utils/auth';
import { connectToDatabase, TrainingData, User, promotionRecord } from '@/service/mongo';
import { TrainingModeEnum } from '@/constants/plugin';
import mongoose from 'mongoose';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await authUser({ req, authRoot: true });
const { amount, userId, type } = req.body as {
amount: number;
userId: number;
type: 'withdraw';
};
await connectToDatabase();
if (!userId || !amount || type !== 'withdraw' || amount <= 0) {
throw new Error('params is error');
}
// check promotion balance
const countResidue: { totalAmount: number }[] = await promotionRecord.aggregate([
{ $match: { userId: new mongoose.Types.ObjectId(userId) } },
{
$group: {
_id: null, // 分组条件,这里使用 null 表示不分组
totalAmount: { $sum: '$amount' } // 计算 amount 字段的总和
}
},
{
$project: {
_id: false, // 排除 _id 字段
totalAmount: true // 只返回 totalAmount 字段
}
}
]);
const balance = countResidue[0].totalAmount;
if (balance < amount) {
throw new Error('可提现余额不足');
}
// add record
await promotionRecord.create({
userId,
type,
amount: -amount
});
jsonRes(res, {
data: balance
});
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
}

View File

@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response'; import { jsonRes } from '@/service/response';
import { authBalanceByUid, authUser } from '@/service/utils/auth'; import { authBalanceByUid, authUser } from '@/service/utils/auth';
import { withNextCors } from '@/service/utils/tools'; import { withNextCors } from '@/service/utils/tools';
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai'; import { getAIChatApi, axiosConfig } from '@/service/ai/openai';
import { pushGenerateVectorBill } from '@/service/events/pushBill'; import { pushGenerateVectorBill } from '@/service/events/pushBill';
type Props = { type Props = {
@ -49,7 +49,7 @@ export async function getVector({
} }
// 获取 chatAPI // 获取 chatAPI
const chatAPI = getOpenAIApi(); const chatAPI = getAIChatApi();
// 把输入的内容转成向量 // 把输入的内容转成向量
const result = await chatAPI const result = await chatAPI

View File

@ -1,7 +1,7 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction // Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response'; import { jsonRes } from '@/service/response';
import { authUser, getSystemOpenAiKey } from '@/service/utils/auth'; import { authUser } from '@/service/utils/auth';
import axios from 'axios'; import axios from 'axios';
import { axiosConfig } from '@/service/ai/openai'; import { axiosConfig } from '@/service/ai/openai';

View File

@ -5,22 +5,44 @@ import { User } from '@/service/models/user';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase } from '@/service/mongo';
import { authUser } from '@/service/utils/auth'; import { authUser } from '@/service/utils/auth';
import { UserUpdateParams } from '@/types/user'; import { UserUpdateParams } from '@/types/user';
import { getAIChatApi, openaiBaseUrl } from '@/service/ai/openai';
/* 更新一些基本信息 */ /* 更新一些基本信息 */
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) { export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try { try {
const { avatar } = req.body as UserUpdateParams; let { avatar, openaiAccount } = req.body as UserUpdateParams;
const { userId } = await authUser({ req, authToken: true }); const { userId } = await authUser({ req, authToken: true });
await connectToDatabase(); await connectToDatabase();
// auth key
if (openaiAccount?.key) {
console.log('auth user openai key', openaiAccount?.key);
const chatAPI = getAIChatApi({
base: openaiAccount?.baseUrl || openaiBaseUrl,
apikey: openaiAccount?.key
});
const response = await chatAPI.createChatCompletion({
model: 'gpt-3.5-turbo',
max_tokens: 1,
messages: [{ role: 'user', content: 'hi' }]
});
if (!response?.data?.choices?.[0]?.message?.content) {
throw new Error(JSON.stringify(response?.data));
}
}
// 更新对应的记录 // 更新对应的记录
await User.updateOne( await User.updateOne(
{ {
_id: userId _id: userId
}, },
{ {
...(avatar && { avatar }) ...(avatar && { avatar }),
...(openaiAccount && { openaiAccount })
} }
); );

View File

@ -230,13 +230,13 @@ const OutLink = ({ appId }: { appId: string }) => {
title: '免登录窗口', title: '免登录窗口',
desc: '分享链接给其他用户,无需登录即可直接进行使用', desc: '分享链接给其他用户,无需登录即可直接进行使用',
value: LinkTypeEnum.share value: LinkTypeEnum.share
},
{
icon: 'outlink_iframe',
title: '网页嵌入',
desc: '嵌入到已有网页中,右下角会生成对话按键',
value: LinkTypeEnum.iframe
} }
// {
// icon: 'outlink_iframe',
// title: '网页嵌入',
// desc: '嵌入到已有网页中,右下角会生成对话按键',
// value: LinkTypeEnum.iframe
// }
]} ]}
value={linkType} value={linkType}
onChange={(e) => setLinkType(e as `${LinkTypeEnum}`)} onChange={(e) => setLinkType(e as `${LinkTypeEnum}`)}

View File

@ -1,27 +1,26 @@
import { Configuration, OpenAIApi } from 'openai'; import { Configuration, OpenAIApi } from 'openai';
const baseUrl = export const openaiBaseUrl = 'https://api.openai.com/v1';
process.env.ONEAPI_URL || process.env.OPENAI_BASE_URL || 'https://api.openai.com/v1'; export const baseUrl = process.env.ONEAPI_URL || process.env.OPENAI_BASE_URL || openaiBaseUrl;
export const getSystemOpenAiKey = () => { export const systemAIChatKey = process.env.ONEAPI_KEY || process.env.OPENAIKEY || '';
return process.env.ONEAPI_KEY || process.env.OPENAIKEY || '';
};
export const getOpenAIApi = () => { export const getAIChatApi = (props?: { base?: string; apikey?: string }) => {
return new OpenAIApi( return new OpenAIApi(
new Configuration({ new Configuration({
basePath: baseUrl basePath: props?.base || baseUrl,
apiKey: props?.apikey || systemAIChatKey
}) })
); );
}; };
/* openai axios config */ /* openai axios config */
export const axiosConfig = () => { export const axiosConfig = (props?: { base?: string; apikey?: string }) => {
return { return {
baseURL: baseUrl, // 此处仅对非 npm 模块有效 baseURL: props?.base || baseUrl, // 此处仅对非 npm 模块有效
httpsAgent: global.httpsAgent, httpsAgent: global.httpsAgent,
headers: { headers: {
Authorization: `Bearer ${getSystemOpenAiKey()}`, Authorization: `Bearer ${props?.apikey || systemAIChatKey}`,
auth: process.env.OPENAI_BASE_URL_AUTH || '' auth: process.env.OPENAI_BASE_URL_AUTH || ''
} }
}; };

View File

@ -5,7 +5,7 @@ import { TrainingModeEnum } from '@/constants/plugin';
import { ERROR_ENUM } from '../errorCode'; import { ERROR_ENUM } from '../errorCode';
import { sendInform } from '@/pages/api/user/inform/send'; import { sendInform } from '@/pages/api/user/inform/send';
import { authBalanceByUid } from '../utils/auth'; import { authBalanceByUid } from '../utils/auth';
import { axiosConfig, getOpenAIApi } from '../ai/openai'; import { axiosConfig, getAIChatApi } from '../ai/openai';
import { ChatCompletionRequestMessage } from 'openai'; import { ChatCompletionRequestMessage } from 'openai';
import { modelToolMap } from '@/utils/plugin'; import { modelToolMap } from '@/utils/plugin';
import { gptMessage2ChatType } from '@/utils/adapt'; import { gptMessage2ChatType } from '@/utils/adapt';
@ -55,7 +55,7 @@ export async function generateQA(): Promise<any> {
const startTime = Date.now(); const startTime = Date.now();
const chatAPI = getOpenAIApi(); const chatAPI = getAIChatApi();
// 请求 chatgpt 获取回答 // 请求 chatgpt 获取回答
const response = await Promise.all( const response = await Promise.all(

View File

@ -35,18 +35,17 @@ const UserSchema = new Schema({
type: Schema.Types.ObjectId, type: Schema.Types.ObjectId,
ref: 'user' ref: 'user'
}, },
promotion: {
rate: {
// 返现比例
type: Number,
default: 15
}
},
limit: { limit: {
exportKbTime: { exportKbTime: {
// Every half hour // Every half hour
type: Date type: Date
} }
},
openaiAccount: {
type: {
key: String,
baseUrl: String
}
} }
}); });

View File

@ -2,7 +2,7 @@ import { adaptChatItem_openAI } from '@/utils/plugin/openai';
import { ChatContextFilter } from '@/service/utils/chat/index'; import { ChatContextFilter } from '@/service/utils/chat/index';
import type { ChatHistoryItemResType, ChatItemType } from '@/types/chat'; import type { ChatHistoryItemResType, ChatItemType } from '@/types/chat';
import { ChatModuleEnum, ChatRoleEnum, TaskResponseKeyEnum } from '@/constants/chat'; import { ChatModuleEnum, ChatRoleEnum, TaskResponseKeyEnum } from '@/constants/chat';
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai'; import { getAIChatApi, axiosConfig } from '@/service/ai/openai';
import type { ClassifyQuestionAgentItemType } from '@/types/app'; import type { ClassifyQuestionAgentItemType } from '@/types/app';
import { countModelPrice } from '@/service/events/pushBill'; import { countModelPrice } from '@/service/events/pushBill';
@ -63,7 +63,7 @@ export const dispatchClassifyQuestion = async (props: Record<string, any>): Prom
required: ['type'] required: ['type']
} }
}; };
const chatAPI = getOpenAIApi(); const chatAPI = getAIChatApi();
const response = await chatAPI.createChatCompletion( const response = await chatAPI.createChatCompletion(
{ {

View File

@ -5,7 +5,7 @@ import { adaptChatItem_openAI } from '@/utils/plugin/openai';
import { ChatContextFilter } from '@/service/utils/chat/index'; import { ChatContextFilter } from '@/service/utils/chat/index';
import type { ChatItemType } from '@/types/chat'; import type { ChatItemType } from '@/types/chat';
import { ChatRoleEnum } from '@/constants/chat'; import { ChatRoleEnum } from '@/constants/chat';
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai'; import { getAIChatApi, axiosConfig } from '@/service/ai/openai';
import type { ClassifyQuestionAgentItemType } from '@/types/app'; import type { ClassifyQuestionAgentItemType } from '@/types/app';
import { authUser } from '@/service/utils/auth'; import { authUser } from '@/service/utils/auth';
@ -79,7 +79,7 @@ export async function extract({ agents, history = [], userChatInput, description
} }
}; };
const chatAPI = getOpenAIApi(); const chatAPI = getAIChatApi();
const response = await chatAPI.createChatCompletion( const response = await chatAPI.createChatCompletion(
{ {

View File

@ -9,7 +9,7 @@ import type { ChatHistoryItemResType } from '@/types/chat';
import { ChatModuleEnum, ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat'; import { ChatModuleEnum, ChatRoleEnum, sseResponseEventEnum } from '@/constants/chat';
import { SSEParseData, parseStreamChunk } from '@/utils/sse'; import { SSEParseData, parseStreamChunk } from '@/utils/sse';
import { textAdaptGptResponse } from '@/utils/adapt'; import { textAdaptGptResponse } from '@/utils/adapt';
import { getOpenAIApi, axiosConfig } from '@/service/ai/openai'; import { getAIChatApi, axiosConfig } from '@/service/ai/openai';
import { TaskResponseKeyEnum } from '@/constants/chat'; import { TaskResponseKeyEnum } from '@/constants/chat';
import { getChatModel } from '@/service/utils/data'; import { getChatModel } from '@/service/utils/data';
import { countModelPrice } from '@/service/events/pushBill'; import { countModelPrice } from '@/service/events/pushBill';
@ -77,7 +77,7 @@ export const dispatchChatCompletion = async (props: Record<string, any>): Promis
// FastGpt temperature range: 1~10 // FastGpt temperature range: 1~10
temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2); temperature = +(modelConstantsData.maxTemperature * (temperature / 10)).toFixed(2);
temperature = Math.max(temperature, 0.01); temperature = Math.max(temperature, 0.01);
const chatAPI = getOpenAIApi(); const chatAPI = getAIChatApi();
const response = await chatAPI.createChatCompletion( const response = await chatAPI.createChatCompletion(
{ {

View File

@ -162,11 +162,6 @@ export const authUser = async ({
}; };
}; };
/* random get openai api key */
export const getSystemOpenAiKey = () => {
return process.env.ONEAPI_KEY || process.env.OPENAIKEY || '';
};
// 模型使用权校验 // 模型使用权校验
export const authApp = async ({ export const authApp = async ({
appId, appId,

View File

@ -4,7 +4,7 @@ import { immer } from 'zustand/middleware/immer';
import type { UserType, UserUpdateParams } from '@/types/user'; import type { UserType, UserUpdateParams } from '@/types/user';
import { getMyModels, getModelById, putAppById } from '@/api/app'; import { getMyModels, getModelById, putAppById } from '@/api/app';
import { formatPrice } from '@/utils/user'; import { formatPrice } from '@/utils/user';
import { getTokenLogin } from '@/api/user'; import { getTokenLogin, putUserInfo } from '@/api/user';
import { defaultApp } from '@/constants/model'; import { defaultApp } from '@/constants/model';
import { AppListItemType, AppUpdateParams } from '@/types/app'; import { AppListItemType, AppUpdateParams } from '@/types/app';
import type { KbItemType, KbListItemType } from '@/types/plugin'; import type { KbItemType, KbListItemType } from '@/types/plugin';
@ -16,7 +16,7 @@ type State = {
userInfo: UserType | null; userInfo: UserType | null;
initUserInfo: () => Promise<UserType>; initUserInfo: () => Promise<UserType>;
setUserInfo: (user: UserType | null) => void; setUserInfo: (user: UserType | null) => void;
updateUserInfo: (user: UserUpdateParams) => void; updateUserInfo: (user: UserUpdateParams) => Promise<void>;
myApps: AppListItemType[]; myApps: AppListItemType[];
myCollectionApps: AppListItemType[]; myCollectionApps: AppListItemType[];
loadMyApps: (init?: boolean) => Promise<AppListItemType[]>; loadMyApps: (init?: boolean) => Promise<AppListItemType[]>;
@ -52,7 +52,7 @@ export const useUserStore = create<State>()(
: null; : null;
}); });
}, },
updateUserInfo(user: UserUpdateParams) { async updateUserInfo(user: UserUpdateParams) {
set((state) => { set((state) => {
if (!state.userInfo) return; if (!state.userInfo) return;
state.userInfo = { state.userInfo = {
@ -60,6 +60,7 @@ export const useUserStore = create<State>()(
...user ...user
}; };
}); });
await putUserInfo(user);
}, },
myApps: [], myApps: [],
myCollectionApps: [], myCollectionApps: [],

View File

@ -16,8 +16,9 @@ export interface UserModelSchema {
promotionAmount: number; promotionAmount: number;
openaiKey: string; openaiKey: string;
createTime: number; createTime: number;
promotion: { openaiAccount?: {
rate: number; key: string;
baseUrl: string;
}; };
limit: { limit: {
exportKbTime?: Date; exportKbTime?: Date;

View File

@ -1,18 +1,17 @@
import { BillSourceEnum } from '@/constants/user'; import { BillSourceEnum } from '@/constants/user';
import type { BillSchema } from './mongoSchema'; import type { BillSchema, UserModelSchema } from './mongoSchema';
export interface UserType { export interface UserType {
_id: string; _id: string;
username: string; username: string;
avatar: string; avatar: string;
balance: number; balance: number;
promotion: { openaiAccount: UserModelSchema['openaiAccount'];
rate: number;
};
} }
export interface UserUpdateParams { export interface UserUpdateParams {
balance?: number; balance?: number;
avatar?: string; avatar?: string;
openaiAccount?: UserModelSchema['openaiAccount'];
} }
export interface UserBillType { export interface UserBillType {