From 2b2c70e53d641b716e351680a3fe23b8b08dad51 Mon Sep 17 00:00:00 2001 From: archer <545436317@qq.com> Date: Thu, 30 Mar 2023 01:04:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=A8=A1=E5=9E=8B=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=AF=BC=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/model.ts | 17 +- src/constants/model.ts | 5 +- src/constants/redis.ts | 2 + src/hooks/usePaging.ts | 3 +- src/pages/api/model/data/delModelDataById.ts | 8 +- ...pushModelData.ts => pushModelDataInput.ts} | 3 +- .../api/model/data/pushModelDataSelectData.ts | 57 ++++ src/pages/api/model/data/putModelData.ts | 12 +- src/pages/model/components/ModelDataCard.tsx | 268 +++++++++++++++++- src/pages/model/detail.tsx | 2 +- src/service/models/modelData.ts | 19 +- src/service/mongo.ts | 2 - src/service/redis.ts | 62 ++-- src/types/model.d.ts | 9 + src/types/mongoSchema.d.ts | 12 +- src/types/redis.d.ts | 10 + 16 files changed, 415 insertions(+), 76 deletions(-) create mode 100644 src/constants/redis.ts rename src/pages/api/model/data/{pushModelData.ts => pushModelDataInput.ts} (90%) create mode 100644 src/pages/api/model/data/pushModelDataSelectData.ts create mode 100644 src/types/redis.d.ts diff --git a/src/api/model.ts b/src/api/model.ts index b62d14922..f20ce46ee 100644 --- a/src/api/model.ts +++ b/src/api/model.ts @@ -1,5 +1,5 @@ import { GET, POST, DELETE, PUT } from './request'; -import type { ModelSchema } from '@/types/mongoSchema'; +import type { ModelSchema, ModelDataSchema } from '@/types/mongoSchema'; import { ModelUpdateParams } from '@/types/model'; import { TrainingItemType } from '../types/training'; import { PagingData } from '@/types'; @@ -39,10 +39,15 @@ type GetModelDataListProps = RequestPaging & { export const getModelDataList = (props: GetModelDataListProps) => GET(`/model/data/getModelData?${Obj2Query(props)}`); -export const postModelData = (data: { modelId: string; data: { q: string; a: string }[] }) => - POST(`/model/data/pushModelData`, data); +export const postModelDataInput = (data: { + modelId: string; + data: { text: ModelDataSchema['text']; q: ModelDataSchema['q'] }[]; +}) => POST(`/model/data/pushModelDataInput`, data); -export const putModelDataById = (data: { modelId: string; answer: string }) => +export const postModelDataSelect = (modelId: string, dataIds: string[]) => + POST(`/model/data/pushModelDataSelectData`, { modelId, dataIds }); + +export const putModelDataById = (data: { dataId: string; text: string }) => PUT('/model/data/putModelData', data); -export const DelOneModelData = (modelId: string) => - DELETE(`/model/data/delModelDataById?modelId=${modelId}`); +export const delOneModelData = (dataId: string) => + DELETE(`/model/data/delModelDataById?dataId=${dataId}`); diff --git a/src/constants/model.ts b/src/constants/model.ts index 2c37ee8b7..cb7e37239 100644 --- a/src/constants/model.ts +++ b/src/constants/model.ts @@ -75,10 +75,9 @@ export const formatModelStatus = { } }; -export const ModelDataStatusMap: Record = { +export const ModelDataStatusMap = { 0: '训练完成', - 1: '等待训练', - 2: '训练中' + 1: '训练中' }; export const defaultModel: ModelSchema = { diff --git a/src/constants/redis.ts b/src/constants/redis.ts new file mode 100644 index 000000000..6e8cf1b36 --- /dev/null +++ b/src/constants/redis.ts @@ -0,0 +1,2 @@ +export const ModelDataIndex = 'model:data'; +export const VecModelDataIndex = 'vec:model:data'; diff --git a/src/hooks/usePaging.ts b/src/hooks/usePaging.ts index 1733e4fa2..1bf8cdfe3 100644 --- a/src/hooks/usePaging.ts +++ b/src/hooks/usePaging.ts @@ -75,6 +75,7 @@ export const usePaging = ({ requesting, isLoadAll, nextPage, - initRequesting + initRequesting, + setData }; }; diff --git a/src/pages/api/model/data/delModelDataById.ts b/src/pages/api/model/data/delModelDataById.ts index 13c8ea07a..959a4baf9 100644 --- a/src/pages/api/model/data/delModelDataById.ts +++ b/src/pages/api/model/data/delModelDataById.ts @@ -5,8 +5,8 @@ import { authToken } from '@/service/utils/tools'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - let { modelId } = req.query as { - modelId: string; + let { dataId } = req.query as { + dataId: string; }; const { authorization } = req.headers; @@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< throw new Error('无权操作'); } - if (!modelId) { + if (!dataId) { throw new Error('缺少参数'); } @@ -24,7 +24,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< await connectToDatabase(); await ModelData.deleteOne({ - modelId, + _id: dataId, userId }); diff --git a/src/pages/api/model/data/pushModelData.ts b/src/pages/api/model/data/pushModelDataInput.ts similarity index 90% rename from src/pages/api/model/data/pushModelData.ts rename to src/pages/api/model/data/pushModelDataInput.ts index 426dfab69..6f3362eed 100644 --- a/src/pages/api/model/data/pushModelData.ts +++ b/src/pages/api/model/data/pushModelDataInput.ts @@ -2,12 +2,13 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; import { connectToDatabase, ModelData, Model } from '@/service/mongo'; import { authToken } from '@/service/utils/tools'; +import { ModelDataSchema } from '@/types/mongoSchema'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { const { modelId, data } = req.body as { modelId: string; - data: { q: string; a: string }[]; + data: { text: ModelDataSchema['text']; q: ModelDataSchema['q'] }[]; }; const { authorization } = req.headers; diff --git a/src/pages/api/model/data/pushModelDataSelectData.ts b/src/pages/api/model/data/pushModelDataSelectData.ts new file mode 100644 index 000000000..f7e01606e --- /dev/null +++ b/src/pages/api/model/data/pushModelDataSelectData.ts @@ -0,0 +1,57 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@/service/response'; +import { connectToDatabase, DataItem, ModelData } from '@/service/mongo'; +import { authToken } from '@/service/utils/tools'; +import { customAlphabet } from 'nanoid'; +const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12); + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + let { dataIds, modelId } = req.body as { dataIds: string[]; modelId: string }; + + if (!dataIds) { + throw new Error('参数错误'); + } + await connectToDatabase(); + + const { authorization } = req.headers; + + const userId = await authToken(authorization); + + const dataItems = ( + await Promise.all( + dataIds.map((dataId) => + DataItem.find<{ _id: string; result: { q: string }[]; text: string }>( + { + userId, + dataId + }, + 'result text' + ) + ) + ) + ).flat(); + + // push data + await ModelData.insertMany( + dataItems.map((item) => ({ + modelId: modelId, + userId, + text: item.text, + q: item.result.map((item) => ({ + id: nanoid(), + text: item.q + })) + })) + ); + + jsonRes(res, { + data: dataItems + }); + } catch (err) { + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/src/pages/api/model/data/putModelData.ts b/src/pages/api/model/data/putModelData.ts index 776865f40..2c13b8526 100644 --- a/src/pages/api/model/data/putModelData.ts +++ b/src/pages/api/model/data/putModelData.ts @@ -5,9 +5,9 @@ import { authToken } from '@/service/utils/tools'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - let { modelId, answer } = req.body as { - modelId: string; - answer: string; + let { dataId, text } = req.body as { + dataId: string; + text: string; }; const { authorization } = req.headers; @@ -15,7 +15,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< throw new Error('无权操作'); } - if (!modelId) { + if (!dataId) { throw new Error('缺少参数'); } @@ -26,11 +26,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< await ModelData.updateOne( { - modelId, + _id: dataId, userId }, { - a: answer + text } ); diff --git a/src/pages/model/components/ModelDataCard.tsx b/src/pages/model/components/ModelDataCard.tsx index 7ce60c8e4..2dcf1f1b1 100644 --- a/src/pages/model/components/ModelDataCard.tsx +++ b/src/pages/model/components/ModelDataCard.tsx @@ -10,30 +10,134 @@ import { Td, IconButton, Flex, - Button + Button, + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + Checkbox, + CheckboxGroup, + ModalCloseButton, + useDisclosure, + Input, + Textarea, + Stack } from '@chakra-ui/react'; import type { ModelSchema } from '@/types/mongoSchema'; import { ModelDataSchema } from '@/types/mongoSchema'; import { ModelDataStatusMap } from '@/constants/model'; import { usePaging } from '@/hooks/usePaging'; import ScrollData from '@/components/ScrollData'; -import { getModelDataList } from '@/api/model'; +import { + getModelDataList, + postModelDataInput, + postModelDataSelect, + delOneModelData, + putModelDataById +} from '@/api/model'; +import { getDataList } from '@/api/data'; import { DeleteIcon } from '@chakra-ui/icons'; +import { useForm, useFieldArray } from 'react-hook-form'; +import { useToast } from '@/hooks/useToast'; +import { useQuery } from '@tanstack/react-query'; +import { customAlphabet } from 'nanoid'; +const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12); + +type FormData = { text: string; q: { val: string }[] }; +type TabType = 'input' | 'select'; +const defaultValues = { + text: '', + q: [{ val: '' }] +}; const ModelDataCard = ({ model }: { model: ModelSchema }) => { const { nextPage, isLoadAll, requesting, - data: dataList, - total + data: modelDataList, + total, + setData, + getData } = usePaging({ api: getModelDataList, - pageSize: 10, + pageSize: 20, params: { modelId: model._id } }); + const { toast } = useToast(); + const { + isOpen: isOpenImportModal, + onOpen: onOpenImportModal, + onClose: onCloseImportModal + } = useDisclosure(); + const { register, handleSubmit, reset, control } = useForm({ + defaultValues + }); + const { + fields: inputQ, + append: appendQ, + remove: removeQ + } = useFieldArray({ + control, + name: 'q' + }); + + const importDataTypes: { id: TabType; label: string }[] = [ + { id: 'input', label: '手动输入' }, + { id: 'select', label: '数据集导入' } + ]; + const [importDataType, setImportDataType] = useState(importDataTypes[0].id); + const [importing, setImporting] = useState(false); + + const updateAnswer = useCallback(async (dataId: string, text: string) => { + putModelDataById({ + dataId, + text + }); + }, []); + + const { data: dataList = [] } = useQuery(['getDataList'], getDataList); + const [selectDataId, setSelectDataId] = useState([]); + + const sureImportData = useCallback( + async (e: FormData) => { + setImporting(true); + + try { + if (importDataType === 'input') { + await postModelDataInput({ + modelId: model._id, + data: [ + { + text: e.text, + q: e.q.map((item) => ({ + id: nanoid(), + text: item.val + })) + } + ] + }); + } else if (importDataType === 'select') { + const res = await postModelDataSelect(model._id, selectDataId); + console.log(res); + } + + toast({ + title: '导入数据成功,需要一段时间训练', + status: 'success' + }); + onCloseImportModal(); + getData(1, true); + reset(defaultValues); + } catch (err) { + console.log(err); + } + setImporting(false); + }, + [getData, importDataType, model._id, onCloseImportModal, reset, toast] + ); return ( <> @@ -41,18 +145,17 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => { 模型数据: {total}组 - + @@ -65,13 +168,55 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => { - {dataList.map((item) => ( + {modelDataList.map((item) => ( - - + + ))} @@ -79,6 +224,101 @@ const ModelDataCard = ({ model }: { model: ModelSchema }) => {
{item.q}{item.a} + {item.q.map((item, i) => ( + + Q{i + 1}: {item.text} + + ))} + + + {ModelDataStatusMap[item.status]} - } aria-label={'delete'} /> + } + variant={'outline'} + colorScheme={'gray'} + aria-label={'delete'} + size={'sm'} + onClick={async () => { + delOneModelData(item._id); + setData((state) => state.filter((data) => data._id !== item._id)); + }} + />
+ + + + + 选择数据导入 + + {importDataTypes.map((item) => ( + + ))} + + + + + {importDataType === 'input' && ( + <> + 知识点: +