From 56f6e69bc705b124cb2fec735ebbb9e691bb3f0b Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Mon, 5 Aug 2024 00:29:14 +0800 Subject: [PATCH] System inform (#2263) * feat: Bind Notification Pipe (#2229) * chore: account page add bind notification modal * feat: timerlock schema and type * feat(fe): bind notification method modal * chore: fe adjust * feat: clean useless code * fix: cron lock * chore: adjust the code * chore: rename api * chore: remove unused code * chore: fe adjust * perf: bind inform ux * fix: time ts * chore: notification (#2251) * perf: send message code * perf: sub schema index * fix: timezone plugin * fix: format --------- Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com> --- packages/global/common/string/time.ts | 5 + packages/global/common/type/utils.ts | 9 ++ .../global/support/user/auth/constants.ts | 6 +- .../global/support/user/inform/constants.ts | 11 ++ packages/global/support/user/inform/type.d.ts | 11 +- packages/global/support/user/team/type.d.ts | 4 +- packages/global/support/user/type.d.ts | 6 +- .../common/system/timerLock/constants.ts | 25 ++-- .../service/common/system/timerLock/schema.ts | 8 +- .../service/common/system/timerLock/utils.ts | 3 +- packages/service/support/user/controller.ts | 4 +- packages/service/support/user/schema.ts | 10 +- .../service/support/user/team/controller.ts | 3 +- .../service/support/user/team/teamSchema.ts | 8 +- packages/service/support/wallet/sub/schema.ts | 14 +- .../service/support/wallet/usage/schema.ts | 2 +- packages/web/i18n/en/common.json | 2 + packages/web/i18n/en/user.json | 4 +- packages/web/i18n/zh/common.json | 4 + packages/web/i18n/zh/user.json | 7 +- .../app/src/pages/account/components/Info.tsx | 29 ++++- .../components/UpdateNotificationModal.tsx | 120 ++++++++++++++++++ .../support/user/account/loginByPassword.ts | 1 + .../api/support/user/account/tokenLogin.ts | 30 ++--- .../pages/api/support/user/account/update.ts | 102 ++++++++------- .../src/pages/app/list/components/List.tsx | 1 - projects/app/src/service/events/generateQA.ts | 2 +- .../app/src/service/events/generateVector.ts | 2 +- projects/app/src/service/events/utils.ts | 11 +- projects/app/src/web/support/user/api.ts | 3 + .../src/web/support/user/hooks/useSendCode.ts | 68 ++++------ 31 files changed, 344 insertions(+), 171 deletions(-) create mode 100644 packages/global/common/type/utils.ts create mode 100644 projects/app/src/pages/account/components/UpdateNotificationModal.tsx diff --git a/packages/global/common/string/time.ts b/packages/global/common/string/time.ts index ff8e095e1..407eff459 100644 --- a/packages/global/common/string/time.ts +++ b/packages/global/common/string/time.ts @@ -1,5 +1,10 @@ import dayjs from 'dayjs'; import cronParser from 'cron-parser'; +import utc from 'dayjs/plugin/utc'; +import timezone from 'dayjs/plugin/timezone'; + +dayjs.extend(utc); +dayjs.extend(timezone); export const formatTime2YMDHMW = (time?: Date) => dayjs(time).format('YYYY-MM-DD HH:mm:ss dddd'); export const formatTime2YMDHM = (time?: Date) => diff --git a/packages/global/common/type/utils.ts b/packages/global/common/type/utils.ts new file mode 100644 index 000000000..77a50456c --- /dev/null +++ b/packages/global/common/type/utils.ts @@ -0,0 +1,9 @@ +export type RequireAtLeastOne = Omit & + { + [K in Keys]-?: Required> & Partial>; + }[Keys]; + +export type RequireOnlyOne = Omit & + { + [K in Keys]-?: Required> & Partial, undefined>>; + }[Keys]; diff --git a/packages/global/support/user/auth/constants.ts b/packages/global/support/user/auth/constants.ts index 068203dcb..860ade109 100644 --- a/packages/global/support/user/auth/constants.ts +++ b/packages/global/support/user/auth/constants.ts @@ -1,11 +1,13 @@ export enum UserAuthTypeEnum { register = 'register', findPassword = 'findPassword', - wxLogin = 'wxLogin' + wxLogin = 'wxLogin', + bindNotification = 'bindNotification' } export const userAuthTypeMap = { [UserAuthTypeEnum.register]: 'register', [UserAuthTypeEnum.findPassword]: 'findPassword', - [UserAuthTypeEnum.wxLogin]: 'wxLogin' + [UserAuthTypeEnum.wxLogin]: 'wxLogin', + [UserAuthTypeEnum.bindNotification]: 'bindNotification' }; diff --git a/packages/global/support/user/inform/constants.ts b/packages/global/support/user/inform/constants.ts index 073e8805e..c4c628589 100644 --- a/packages/global/support/user/inform/constants.ts +++ b/packages/global/support/user/inform/constants.ts @@ -15,3 +15,14 @@ export const InformLevelMap = { label: '紧急' } }; + +export enum SendInformTemplateCodeEnum { + EXPIRE_SOON = 'EXPIRE_SOON', + EXPIRED = 'EXPIRED', + FREE_CLEAN = 'FREE_CLEAN', + REGISTER = 'REGISTER', + RESET_PASSWORD = 'RESET_PASSWORD', + BIND_NOTIFICATION = 'BIND_NOTIFICATION', + LACK_OF_POINTS = 'LACK_OF_POINTS', + CUSTOM = 'CUSTOM' +} diff --git a/packages/global/support/user/inform/type.d.ts b/packages/global/support/user/inform/type.d.ts index 04a66fe30..1fa0dd6b4 100644 --- a/packages/global/support/user/inform/type.d.ts +++ b/packages/global/support/user/inform/type.d.ts @@ -1,13 +1,16 @@ -import { InformLevelEnum } from './constants'; +import { InformLevelEnum, SendInformTemplateCodeEnum } from './constants'; export type SendInformProps = { - title: string; - content: string; level: `${InformLevelEnum}`; + templateCode: `${SendInformTemplateCodeEnum}`; + templateParam: Record; + customLockMinutes?: number; // custom lock minutes }; + export type SendInform2UserProps = SendInformProps & { - tmbId: string; + teamId: string; }; + export type SendInform2User = SendInformProps & { type: `${InformTypeEnum}`; tmbId: string; diff --git a/packages/global/support/user/team/type.d.ts b/packages/global/support/user/team/type.d.ts index 06db1fca7..80daa89ef 100644 --- a/packages/global/support/user/team/type.d.ts +++ b/packages/global/support/user/team/type.d.ts @@ -18,7 +18,9 @@ export type TeamSchema = { }; lafAccount: LafAccountType; defaultPermission: PermissionValueType; + notificationAccount?: string; }; + export type tagsType = { label: string; key: string; @@ -63,6 +65,7 @@ export type TeamTmbItemType = { role: `${TeamMemberRoleEnum}`; status: `${TeamMemberStatusEnum}`; lafAccount?: LafAccountType; + notificationAccount?: string; permission: TeamPermission; }; @@ -72,7 +75,6 @@ export type TeamMemberItemType = { teamId: string; memberName: string; avatar: string; - // TODO: this should be deprecated. role: `${TeamMemberRoleEnum}`; status: `${TeamMemberStatusEnum}`; permission: TeamPermission; diff --git a/packages/global/support/user/type.d.ts b/packages/global/support/user/type.d.ts index 7364b82f4..c3700b8e8 100644 --- a/packages/global/support/user/type.d.ts +++ b/packages/global/support/user/type.d.ts @@ -1,12 +1,10 @@ +import { TeamPermission } from '../permission/user/controller'; import { UserStatusEnum } from './constant'; import { TeamTmbItemType } from './team/type'; export type UserModelSchema = { _id: string; username: string; - email?: string; - phonePrefix?: number; - phone?: string; password: string; avatar: string; promotionRate: number; @@ -31,4 +29,6 @@ export type UserType = { openaiAccount: UserModelSchema['openaiAccount']; team: TeamTmbItemType; standardInfo?: standardInfoType; + notificationAccount?: string; + permission: TeamPermission; }; diff --git a/packages/service/common/system/timerLock/constants.ts b/packages/service/common/system/timerLock/constants.ts index 0f382c6af..1e79cb446 100644 --- a/packages/service/common/system/timerLock/constants.ts +++ b/packages/service/common/system/timerLock/constants.ts @@ -4,14 +4,21 @@ export enum TimerIdEnum { checkInvalidVector = 'checkInvalidVector', clearExpiredSubPlan = 'clearExpiredSubPlan', updateStandardPlan = 'updateStandardPlan', - scheduleTriggerApp = 'scheduleTriggerApp' + scheduleTriggerApp = 'scheduleTriggerApp', + notification = 'notification' } -export const timerIdMap = { - [TimerIdEnum.checkInValidDatasetFiles]: 'checkInValidDatasetFiles', - [TimerIdEnum.checkInvalidDatasetData]: 'checkInvalidDatasetData', - [TimerIdEnum.checkInvalidVector]: 'checkInvalidVector', - [TimerIdEnum.clearExpiredSubPlan]: 'clearExpiredSubPlan', - [TimerIdEnum.updateStandardPlan]: 'updateStandardPlan', - [TimerIdEnum.scheduleTriggerApp]: 'scheduleTriggerApp' -}; +export enum LockNotificationEnum { + NotificationExpire = 'notification_expire', + NotificationFreeClean = 'notification_free_clean', + NotificationLackOfPoints = 'notification_lack_of_points' +} + +export type LockType = `${LockNotificationEnum}`; + +// add a new type enum example: +// export enum ExampleLockEnum { +// ExampleLockType1 = 'example_lock_type1' +// } +// +// export type LockType = `${NotificationLockEnum}` | `${ExampleLockEnum}` diff --git a/packages/service/common/system/timerLock/schema.ts b/packages/service/common/system/timerLock/schema.ts index 318dc056a..a89bcfc97 100644 --- a/packages/service/common/system/timerLock/schema.ts +++ b/packages/service/common/system/timerLock/schema.ts @@ -1,6 +1,5 @@ -import { connectionMongo, getMongoModel, type Model } from '../../mongo'; -import { timerIdMap } from './constants'; -const { Schema, model, models } = connectionMongo; +import { connectionMongo, getMongoModel } from '../../mongo'; +const { Schema } = connectionMongo; import { TimerLockSchemaType } from './type.d'; export const collectionName = 'systemtimerlocks'; @@ -9,8 +8,7 @@ const TimerLockSchema = new Schema({ timerId: { type: String, required: true, - unique: true, - enum: Object.keys(timerIdMap) + unique: true }, expiredTime: { type: Date, diff --git a/packages/service/common/system/timerLock/utils.ts b/packages/service/common/system/timerLock/utils.ts index f50c484ed..9662096f8 100644 --- a/packages/service/common/system/timerLock/utils.ts +++ b/packages/service/common/system/timerLock/utils.ts @@ -1,4 +1,3 @@ -import { TimerIdEnum } from './constants'; import { MongoTimerLock } from './schema'; import { addMinutes } from 'date-fns'; @@ -9,7 +8,7 @@ export const checkTimerLock = async ({ timerId, lockMinuted }: { - timerId: TimerIdEnum; + timerId: string; lockMinuted: number; }) => { try { diff --git a/packages/service/support/user/controller.ts b/packages/service/support/user/controller.ts index 9510abfc4..3594cfe5e 100644 --- a/packages/service/support/user/controller.ts +++ b/packages/service/support/user/controller.ts @@ -45,6 +45,8 @@ export async function getUserDetail({ timezone: user.timezone, promotionRate: user.promotionRate, openaiAccount: user.openaiAccount, - team: tmb + team: tmb, + notificationAccount: tmb.notificationAccount, + permission: tmb.permission }; } diff --git a/packages/service/support/user/schema.ts b/packages/service/support/user/schema.ts index d59c5b90f..6d0c56ff1 100644 --- a/packages/service/support/user/schema.ts +++ b/packages/service/support/user/schema.ts @@ -1,5 +1,5 @@ -import { connectionMongo, getMongoModel, type Model } from '../../common/mongo'; -const { Schema, model, models } = connectionMongo; +import { connectionMongo, getMongoModel } from '../../common/mongo'; +const { Schema } = connectionMongo; import { hashStr } from '@fastgpt/global/common/string/tools'; import type { UserModelSchema } from '@fastgpt/global/support/user/type'; import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant'; @@ -18,15 +18,9 @@ const UserSchema = new Schema({ required: true, unique: true // 唯一 }, - email: { - type: String - }, phonePrefix: { type: Number }, - phone: { - type: String - }, password: { type: String, required: true, diff --git a/packages/service/support/user/team/controller.ts b/packages/service/support/user/team/controller.ts index 0d3cad678..cc3332e93 100644 --- a/packages/service/support/user/team/controller.ts +++ b/packages/service/support/user/team/controller.ts @@ -40,7 +40,8 @@ async function getTeamMember(match: Record): Promise import('./standardDetailModal')); const TeamMenu = dynamic(() => import('@/components/support/user/team/TeamMenu')); const PayModal = dynamic(() => import('./PayModal')); const UpdatePswModal = dynamic(() => import('./UpdatePswModal')); +const UpdateNotification = dynamic(() => import('./UpdateNotificationModal')); const OpenAIAccountModal = dynamic(() => import('./OpenAIAccountModal')); const LafAccountModal = dynamic(() => import('@/components/support/laf/LafAccountModal')); const CommunityModal = dynamic(() => import('@/components/CommunityModal')); @@ -113,6 +114,11 @@ const MyInfo = () => { onClose: onCloseUpdatePsw, onOpen: onOpenUpdatePsw } = useDisclosure(); + const { + isOpen: isOpenUpdateNotification, + onClose: onCloseUpdateNotification, + onOpen: onOpenUpdateNotification + } = useDisclosure(); const { File, onOpen: onOpenSelectFile } = useSelectFile({ fileType: '.jpg,.png', multiple: false @@ -225,7 +231,7 @@ const MyInfo = () => { )} - {feConfigs.isPlus && ( + {feConfigs?.isPlus && ( {t('common:user.Member Name')}:  { {t('common:user.Account')}:  {userInfo?.username} - {feConfigs.isPlus && ( + {feConfigs?.isPlus && ( {t('common:user.Password')}:  ***** @@ -258,13 +264,27 @@ const MyInfo = () => { )} + {feConfigs?.isPlus && ( + + {t('common:user.Notification Receive')}:  + + {userInfo?.team.notificationAccount || t('common:user.Notification Receive Bind')} + + + {userInfo?.permission.isOwner && ( + + )} + + )} {t('common:user.Team')}:  - {feConfigs.isPlus && ( + {feConfigs?.isPlus && ( {t('common:user.team.Balance')}:  @@ -282,6 +302,7 @@ const MyInfo = () => { {isOpenPayModal && } {isOpenUpdatePsw && } + {isOpenUpdateNotification && } ); @@ -579,7 +600,7 @@ const Other = () => { )} {feConfigs?.chatbotUrl && ( void }) => { + const { t } = useTranslation(); + const { initUserInfo } = useUserStore(); + const { register, handleSubmit, trigger, getValues, watch } = useForm({ + defaultValues: { + account: '', + verifyCode: '' + } + }); + const account = watch('account'); + const verifyCode = watch('verifyCode'); + + const { runAsync: onSubmit, loading: isLoading } = useRequest2( + (data: FormType) => { + return updateNotificationAccount(data); + }, + { + onSuccess() { + initUserInfo(); + onClose(); + }, + successToast: t('user:bind_inform_account_success'), + errorToast: t('user:bind_inform_account_error') + } + ); + + const { sendCodeText, sendCode, codeCountDown } = useSendCode(); + + const onclickSendCode = useCallback(async () => { + const check = await trigger('account'); + if (!check) return; + sendCode({ + username: getValues('account'), + type: 'bindNotification' + }); + }, [getValues, sendCode, trigger]); + + return ( + + + + + + {t('user:notification.Bind Notification Pipe Hint')} + + + {t('common:user.Account')} + + + + {t('common:support.user.Verify Code')} + + 0 + ? { + color: 'myGray.500' + } + : { + color: 'primary.700', + cursor: 'pointer', + onClick: onclickSendCode + })} + > + {sendCodeText} + + + + + + + + + + ); +}; + +export default UpdateNotificationModal; diff --git a/projects/app/src/pages/api/support/user/account/loginByPassword.ts b/projects/app/src/pages/api/support/user/account/loginByPassword.ts index 44917e3e1..d34272a86 100644 --- a/projects/app/src/pages/api/support/user/account/loginByPassword.ts +++ b/projects/app/src/pages/api/support/user/account/loginByPassword.ts @@ -6,6 +6,7 @@ import { connectToDatabase } from '@/service/mongo'; import { getUserDetail } from '@fastgpt/service/support/user/controller'; import type { PostLoginProps } from '@fastgpt/global/support/user/api.d'; import { UserStatusEnum } from '@fastgpt/global/support/user/constant'; +import { checkTeamAiPointsAndLock } from '@/service/events/utils'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { diff --git a/projects/app/src/pages/api/support/user/account/tokenLogin.ts b/projects/app/src/pages/api/support/user/account/tokenLogin.ts index fca3069b0..324b9d77b 100644 --- a/projects/app/src/pages/api/support/user/account/tokenLogin.ts +++ b/projects/app/src/pages/api/support/user/account/tokenLogin.ts @@ -1,21 +1,15 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { jsonRes } from '@fastgpt/service/common/response'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; -import { connectToDatabase } from '@/service/mongo'; import { getUserDetail } from '@fastgpt/service/support/user/controller'; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - await connectToDatabase(); - const { tmbId } = await authCert({ req, authToken: true }); - - jsonRes(res, { - data: await getUserDetail({ tmbId }) - }); - } catch (err) { - jsonRes(res, { - code: 500, - error: err - }); - } +import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; +import { NextAPI } from '@/service/middleware/entry'; +export type TokenLoginQuery = {}; +export type TokenLoginBody = {}; +export type TokenLoginResponse = {}; +async function handler( + req: ApiRequestProps, + _res: ApiResponseType +): Promise { + const { tmbId } = await authCert({ req, authToken: true }); + return getUserDetail({ tmbId }); } +export default NextAPI(handler); diff --git a/projects/app/src/pages/api/support/user/account/update.ts b/projects/app/src/pages/api/support/user/account/update.ts index e9f19fca5..0f45d0b08 100644 --- a/projects/app/src/pages/api/support/user/account/update.ts +++ b/projects/app/src/pages/api/support/user/account/update.ts @@ -1,62 +1,60 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { jsonRes } from '@fastgpt/service/common/response'; import { MongoUser } from '@fastgpt/service/support/user/schema'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { UserUpdateParams } from '@/types/user'; import { getAIApi, openaiBaseUrl } from '@fastgpt/service/core/ai/config'; -import { connectToDatabase } from '@/service/mongo'; import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema'; /* update user info */ -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - await connectToDatabase(); - const { avatar, timezone, openaiAccount, lafAccount } = req.body as UserUpdateParams; +import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next'; +import { NextAPI } from '@/service/middleware/entry'; +export type UserAccountUpdateQuery = {}; +export type UserAccountUpdateBody = UserUpdateParams; +export type UserAccountUpdateResponse = {}; +async function handler( + req: ApiRequestProps, + _res: ApiResponseType +): Promise { + const { avatar, timezone, openaiAccount, lafAccount } = req.body; - const { tmbId } = await authCert({ req, authToken: true }); - const tmb = await MongoTeamMember.findById(tmbId); - if (!tmb) { - throw new Error('can not find it'); - } - const userId = tmb.userId; - // auth key - if (openaiAccount?.key) { - console.log('auth user openai key', openaiAccount?.key); - const baseUrl = openaiAccount?.baseUrl || openaiBaseUrl; - openaiAccount.baseUrl = baseUrl; - - const ai = getAIApi({ - userKey: openaiAccount - }); - - const response = await ai.chat.completions.create({ - model: 'gpt-4o-mini', - max_tokens: 1, - messages: [{ role: 'user', content: 'hi' }] - }); - if (response?.choices?.[0]?.message?.content === undefined) { - throw new Error('Key response is empty'); - } - } - - // 更新对应的记录 - await MongoUser.updateOne( - { - _id: userId - }, - { - ...(avatar && { avatar }), - ...(timezone && { timezone }), - openaiAccount: openaiAccount?.key ? openaiAccount : null, - lafAccount: lafAccount?.token ? lafAccount : null - } - ); - - jsonRes(res); - } catch (err) { - jsonRes(res, { - code: 500, - error: err - }); + const { tmbId } = await authCert({ req, authToken: true }); + const tmb = await MongoTeamMember.findById(tmbId); + if (!tmb) { + throw new Error('can not find it'); } + const userId = tmb.userId; + // auth key + if (openaiAccount?.key) { + console.log('auth user openai key', openaiAccount?.key); + const baseUrl = openaiAccount?.baseUrl || openaiBaseUrl; + openaiAccount.baseUrl = baseUrl; + + const ai = getAIApi({ + userKey: openaiAccount + }); + + const response = await ai.chat.completions.create({ + model: 'gpt-4o-mini', + max_tokens: 1, + messages: [{ role: 'user', content: 'hi' }] + }); + if (response?.choices?.[0]?.message?.content === undefined) { + throw new Error('Key response is empty'); + } + } + + // 更新对应的记录 + await MongoUser.updateOne( + { + _id: userId + }, + { + ...(avatar && { avatar }), + ...(timezone && { timezone }), + openaiAccount: openaiAccount?.key ? openaiAccount : null, + lafAccount: lafAccount?.token ? lafAccount : null + } + ); + + return {}; } +export default NextAPI(handler); diff --git a/projects/app/src/pages/app/list/components/List.tsx b/projects/app/src/pages/app/list/components/List.tsx index 803f578ee..ffde09776 100644 --- a/projects/app/src/pages/app/list/components/List.tsx +++ b/projects/app/src/pages/app/list/components/List.tsx @@ -49,7 +49,6 @@ const ListItem = () => { const { parentId = null } = router.query; const { isPc } = useSystem(); const { lastChatAppId, setLastChatAppId } = useChatStore(); - const { myApps, loadMyApps, onUpdateApp, setMoveAppId, folderDetail } = useContextSelector( AppListContext, diff --git a/projects/app/src/service/events/generateQA.ts b/projects/app/src/service/events/generateQA.ts index 0ff74c1ad..248938037 100644 --- a/projects/app/src/service/events/generateQA.ts +++ b/projects/app/src/service/events/generateQA.ts @@ -88,7 +88,7 @@ export async function generateQA(): Promise { } // auth balance - if (!(await checkTeamAiPointsAndLock(data.teamId, data.tmbId))) { + if (!(await checkTeamAiPointsAndLock(data.teamId))) { reduceQueue(); return generateQA(); } diff --git a/projects/app/src/service/events/generateVector.ts b/projects/app/src/service/events/generateVector.ts index e72c51e87..264ab4e2f 100644 --- a/projects/app/src/service/events/generateVector.ts +++ b/projects/app/src/service/events/generateVector.ts @@ -89,7 +89,7 @@ export async function generateVector(): Promise { } // auth balance - if (!(await checkTeamAiPointsAndLock(data.teamId, data.tmbId))) { + if (!(await checkTeamAiPointsAndLock(data.teamId))) { reduceQueue(); return generateVector(); } diff --git a/projects/app/src/service/events/utils.ts b/projects/app/src/service/events/utils.ts index 1095f009d..4634d395b 100644 --- a/projects/app/src/service/events/utils.ts +++ b/projects/app/src/service/events/utils.ts @@ -4,7 +4,7 @@ import { sendOneInform } from '../support/user/inform/api'; import { lockTrainingDataByTeamId } from '@fastgpt/service/core/dataset/training/controller'; import { InformLevelEnum } from '@fastgpt/global/support/user/inform/constants'; -export const checkTeamAiPointsAndLock = async (teamId: string, tmbId: string) => { +export const checkTeamAiPointsAndLock = async (teamId: string) => { try { await checkTeamAIPoints(teamId); return true; @@ -13,11 +13,10 @@ export const checkTeamAiPointsAndLock = async (teamId: string, tmbId: string) => // send inform and lock data try { sendOneInform({ - level: InformLevelEnum.important, - title: '文本训练任务中止', - content: - '该团队账号AI积分不足,文本训练任务中止,重新充值后将会继续。暂停的任务将在 7 天后被删除。', - tmbId: tmbId + level: InformLevelEnum.emergency, + templateCode: 'LACK_OF_POINTS', + templateParam: {}, + teamId }); console.log('余额不足,暂停【向量】生成任务'); lockTrainingDataByTeamId(teamId); diff --git a/projects/app/src/web/support/user/api.ts b/projects/app/src/web/support/user/api.ts index 7abe534eb..ed68cd3a3 100644 --- a/projects/app/src/web/support/user/api.ts +++ b/projects/app/src/web/support/user/api.ts @@ -63,6 +63,9 @@ export const updatePasswordByOld = ({ oldPsw, newPsw }: { oldPsw: string; newPsw newPsw: hashStr(newPsw) }); +export const updateNotificationAccount = (data: { account: string; verifyCode: string }) => + PUT('/proApi/support/user/team/updateNotificationAccount', data); + export const postLogin = ({ password, ...props }: PostLoginProps) => POST('/support/user/account/loginByPassword', { ...props, diff --git a/projects/app/src/web/support/user/hooks/useSendCode.ts b/projects/app/src/web/support/user/hooks/useSendCode.ts index a4a10d9e7..10184ffc6 100644 --- a/projects/app/src/web/support/user/hooks/useSendCode.ts +++ b/projects/app/src/web/support/user/hooks/useSendCode.ts @@ -1,19 +1,40 @@ -import { useState, useMemo, useCallback } from 'react'; +import { useState, useMemo } from 'react'; import { sendAuthCode } from '@/web/support/user/api'; import { UserAuthTypeEnum } from '@fastgpt/global/support/user/auth/constants'; -import { useToast } from '@fastgpt/web/hooks/useToast'; -import { getErrText } from '@fastgpt/global/common/error/utils'; import { useTranslation } from 'next-i18next'; import { useSystemStore } from '@/web/common/system/useSystemStore'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; -let timer: any; +let timer: NodeJS.Timeout; export const useSendCode = () => { const { t } = useTranslation(); - const { toast } = useToast(); const { feConfigs } = useSystemStore(); - const [codeSending, setCodeSending] = useState(false); const [codeCountDown, setCodeCountDown] = useState(0); + + const { runAsync: sendCode, loading: codeSending } = useRequest2( + async ({ username, type }: { username: string; type: `${UserAuthTypeEnum}` }) => { + if (codeCountDown > 0) return; + const googleToken = await getClientToken(feConfigs.googleClientVerKey); + await sendAuthCode({ username, type, googleToken }); + setCodeCountDown(60); + + timer = setInterval(() => { + setCodeCountDown((val) => { + if (val <= 0) { + clearInterval(timer); + } + return val - 1; + }); + }, 1000); + }, + { + successToast: '验证码已发送', + errorToast: '验证码发送异常', + refreshDeps: [codeCountDown, feConfigs?.googleClientVerKey] + } + ); + const sendCodeText = useMemo(() => { if (codeSending) return t('common:support.user.auth.Sending Code'); if (codeCountDown >= 10) { @@ -25,41 +46,6 @@ export const useSendCode = () => { return '获取验证码'; }, [codeCountDown, codeSending, t]); - const sendCode = useCallback( - async ({ username, type }: { username: string; type: `${UserAuthTypeEnum}` }) => { - if (codeCountDown > 0) return; - setCodeSending(true); - try { - await sendAuthCode({ - username, - type, - googleToken: await getClientToken(feConfigs.googleClientVerKey) - }); - setCodeCountDown(60); - timer = setInterval(() => { - setCodeCountDown((val) => { - if (val <= 0) { - clearInterval(timer); - } - return val - 1; - }); - }, 1000); - toast({ - title: '验证码已发送', - status: 'success', - position: 'top' - }); - } catch (error: any) { - toast({ - title: getErrText(error, '验证码发送异常'), - status: 'error' - }); - } - setCodeSending(false); - }, - [codeCountDown, feConfigs?.googleClientVerKey, toast] - ); - return { codeSending, sendCode,