fix colection create api (#766)

Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
This commit is contained in:
Archer
2024-01-23 09:01:24 +08:00
committed by GitHub
parent aab6ee51eb
commit 379673cae1
143 changed files with 40737 additions and 274 deletions

View File

@@ -5,6 +5,7 @@ import { type DateRange, DayPicker } from 'react-day-picker';
import MyIcon from '@fastgpt/web/components/common/Icon';
import 'react-day-picker/dist/style.css';
import zhCN from 'date-fns/locale/zh-CN';
import { useTranslation } from 'next-i18next';
const DateRangePicker = ({
onChange,
@@ -20,6 +21,7 @@ const DateRangePicker = ({
position?: 'bottom' | 'top';
defaultDate?: DateRange;
}) => {
const { t } = useTranslation();
const theme = useTheme();
const OutRangeRef = useRef(null);
const [range, setRange] = useState<DateRange | undefined>(defaultDate);
@@ -99,7 +101,7 @@ const DateRangePicker = ({
mr={2}
onClick={() => setShowSelected(false)}
>
{t('common.Close')}
</Button>
<Button
size={'sm'}
@@ -108,7 +110,7 @@ const DateRangePicker = ({
setShowSelected(false);
}}
>
{t('common.Confirm')}
</Button>
</Flex>
}

View File

@@ -1,6 +1,15 @@
import React, { useRef, forwardRef, useMemo } from 'react';
import { Menu, MenuList, MenuItem, Button, useDisclosure, MenuButton } from '@chakra-ui/react';
import type { ButtonProps } from '@chakra-ui/react';
import {
Menu,
MenuList,
MenuItem,
Button,
useDisclosure,
MenuButton,
Box,
css
} from '@chakra-ui/react';
import type { ButtonProps, MenuItemProps } from '@chakra-ui/react';
import { ChevronDownIcon } from '@chakra-ui/icons';
export type SelectProps = ButtonProps & {
@@ -19,88 +28,102 @@ const MySelect = (
selectRef: any
) => {
const ref = useRef<HTMLButtonElement>(null);
const menuItemStyles = {
const menuItemStyles: MenuItemProps = {
borderRadius: 'sm',
py: 2,
display: 'flex',
alignItems: 'center',
_hover: {
backgroundColor: 'myWhite.600'
},
_notLast: {
mb: 2
}
};
const { isOpen, onOpen, onClose } = useDisclosure();
const selectItem = useMemo(() => list.find((item) => item.value === value), [list, value]);
return (
<Menu
autoSelect={false}
isOpen={isOpen}
onOpen={onOpen}
onClose={onClose}
strategy={'fixed'}
matchWidth
<Box
css={css({
'& div': {
width: 'auto !important'
}
})}
>
<MenuButton
as={Button}
ref={ref}
width={width}
px={3}
rightIcon={<ChevronDownIcon />}
variant={'whitePrimary'}
textAlign={'left'}
_active={{
transform: 'none'
}}
{...(isOpen
? {
boxShadow: '0px 0px 4px #A8DBFF',
borderColor: 'primary.500'
}
: {})}
{...props}
<Menu
autoSelect={false}
isOpen={isOpen}
onOpen={onOpen}
onClose={onClose}
strategy={'fixed'}
matchWidth
>
{selectItem?.alias || selectItem?.label || placeholder}
</MenuButton>
<MenuList
minW={(() => {
const w = ref.current?.clientWidth;
if (w) {
return `${w}px !important`;
}
return Array.isArray(width)
? width.map((item) => `${item} !important`)
: `${width} !important`;
})()}
p={'6px'}
border={'1px solid #fff'}
boxShadow={'0px 2px 4px rgba(161, 167, 179, 0.25), 0px 0px 1px rgba(121, 141, 159, 0.25);'}
zIndex={99}
maxH={'40vh'}
overflowY={'auto'}
>
{list.map((item) => (
<MenuItem
key={item.value}
{...menuItemStyles}
{...(value === item.value
? {
color: 'primary.500',
bg: 'myWhite.300'
}
: {})}
onClick={() => {
if (onchange && value !== item.value) {
onchange(item.value);
<MenuButton
as={Button}
ref={ref}
width={width}
px={3}
rightIcon={<ChevronDownIcon />}
variant={'whitePrimary'}
textAlign={'left'}
_active={{
transform: 'none'
}}
{...(isOpen
? {
boxShadow: '0px 0px 4px #A8DBFF',
borderColor: 'primary.500'
}
}}
whiteSpace={'pre-wrap'}
>
{item.label}
</MenuItem>
))}
</MenuList>
</Menu>
: {})}
{...props}
>
{selectItem?.alias || selectItem?.label || placeholder}
</MenuButton>
<MenuList
minW={(() => {
const w = ref.current?.clientWidth;
if (w) {
return `${w}px !important`;
}
return Array.isArray(width)
? width.map((item) => `${item} !important`)
: `${width} !important`;
})()}
w={'auto'}
p={'6px'}
border={'1px solid #fff'}
boxShadow={
'0px 2px 4px rgba(161, 167, 179, 0.25), 0px 0px 1px rgba(121, 141, 159, 0.25);'
}
zIndex={99}
maxH={'40vh'}
overflowY={'auto'}
>
{list.map((item) => (
<MenuItem
key={item.value}
{...menuItemStyles}
{...(value === item.value
? {
color: 'primary.500',
bg: 'myWhite.300'
}
: {})}
onClick={() => {
if (onchange && value !== item.value) {
onchange(item.value);
}
}}
whiteSpace={'pre-wrap'}
>
{item.label}
</MenuItem>
))}
</MenuList>
</Menu>
</Box>
);
};

View File

@@ -1,5 +1,4 @@
import React, { useState } from 'react';
import MyModal from '@/components/MyModal';
import { useTranslation } from 'next-i18next';
import {
@@ -15,18 +14,30 @@ import {
Button
} from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import { getTeamDatasetValidSub, postExpandTeamDatasetSub } from '@/web/support/wallet/sub/api';
import {
getTeamDatasetValidSub,
posCheckTeamDatasetSizeSub,
postUpdateTeamDatasetSizeSub,
putTeamDatasetSubStatus
} from '@/web/support/wallet/sub/api';
import Markdown from '@/components/Markdown';
import MyTooltip from '@/components/MyTooltip';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { useConfirm } from '@/web/common/hooks/useConfirm';
import { getMonthRemainingDays } from '@fastgpt/global/common/math/date';
import { useRequest } from '@/web/common/hooks/useRequest';
import { useRouter } from 'next/router';
import { feConfigs } from '@/web/common/system/staticData';
import { useToast } from '@/web/common/hooks/useToast';
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
import MySelect from '@/components/Select';
import {
SubStatusEnum,
SubTypeEnum,
subSelectMap
} from '@fastgpt/global/support/wallet/sub/constants';
import { SubDatasetSizePreviewCheckResponse } from '@fastgpt/global/support/wallet/sub/api.d';
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/bill/tools';
import { useUserStore } from '@/web/support/user/useUserStore';
const SubDatasetModal = ({ onClose }: { onClose: () => void }) => {
const datasetStoreFreeSize = feConfigs?.subscription?.datasetStoreFreeSize || 0;
@@ -36,29 +47,93 @@ const SubDatasetModal = ({ onClose }: { onClose: () => void }) => {
const { toast } = useToast();
const router = useRouter();
const { ConfirmModal, openConfirm } = useConfirm({});
const { userInfo } = useUserStore();
const [datasetSize, setDatasetSize] = useState(0);
const [isRenew, setIsRenew] = useState('false');
const isExpand = datasetSize > 0;
const { data: datasetSub } = useQuery(['getTeamDatasetValidSub'], getTeamDatasetValidSub, {
onSuccess(res) {
setIsRenew(`${res?.sub?.renew}`);
setIsRenew(res?.sub?.status === SubStatusEnum.active ? 'true' : 'false');
setDatasetSize((res?.sub?.nextExtraDatasetSize || 0) / 1000);
}
});
const { mutate, isLoading } = useRequest({
mutationFn: () => postExpandTeamDatasetSub({ size: datasetSize, renew: isRenew === 'true' }),
onSuccess(res) {
if (isExpand) {
const { mutate: onClickUpdateSub, isLoading: isPaying } = useRequest({
mutationFn: () => postUpdateTeamDatasetSizeSub({ size: datasetSize }),
onSuccess() {
setTimeout(() => {
router.reload();
}, 100);
},
successToast: t('common.Update success'),
errorToast: t('common.error.Update error')
});
const { mutate: onClickPreviewCheck, isLoading: isFetchingPreviewCheck } = useRequest({
mutationFn: () =>
posCheckTeamDatasetSizeSub({
size: datasetSize
}),
onSuccess(res: SubDatasetSizePreviewCheckResponse) {
if (!res.payForNewSub) {
onClickUpdateSub('');
return;
} else {
onClose();
openConfirm(
() => {
if (!res.balanceEnough) return;
onClickUpdateSub('');
},
undefined,
<Box>
<Flex>
<Box flex={'0 0 100px'}>:</Box>
<Box>{datasetSub?.sub?.currentExtraDatasetSize || 0}</Box>
</Flex>
<Flex>
<Box flex={'0 0 100px'}>:</Box>
<Box>{res.newSubSize}</Box>
</Flex>
<Flex>
<Box flex={'0 0 100px'}>:</Box>
<Box>{formatStorePrice2Read(res.newPrice)}</Box>
</Flex>
<Flex>
<Box flex={'0 0 100px'}>:</Box>
<Box>{formatStorePrice2Read(res.payPrice)}</Box>
</Flex>
<Flex>
<Box flex={'0 0 100px'}>:</Box>
<Box>30</Box>
</Flex>
<Flex>
<Box flex={'0 0 100px'}>:</Box>
<Box>{formatStorePrice2Read(userInfo?.team?.balance).toFixed(3)}</Box>
</Flex>
{!res.balanceEnough && (
<Box mt={1} color={'red.600'}>
</Box>
)}
</Box>
)();
}
},
successToast: isExpand ? t('support.wallet.Pay success') : t('common.Update success'),
errorToast: isExpand ? t('support.wallet.Pay error') : t('common.error.Update error')
errorToast: t('common.error.Update error')
});
const { mutate: onUpdateStatus } = useRequest({
mutationFn: (e: 'true' | 'false') => {
setIsRenew(e);
return putTeamDatasetSubStatus({
status: subSelectMap[e],
type: SubTypeEnum.extraDatasetSize
});
},
successToast: t('common.Update success'),
errorToast: t('common.error.Update error')
});
const isLoading = isPaying || isFetchingPreviewCheck;
return (
<MyModal
@@ -83,21 +158,35 @@ const SubDatasetModal = ({ onClose }: { onClose: () => void }) => {
/>
</>
<Flex mt={4}>
<Box w={'100px'}>{t('support.wallet.subscription.Current dataset store')}: </Box>
<Box flex={'0 0 120px'}>{t('support.wallet.subscription.Current dataset store')}: </Box>
<Box ml={2} fontWeight={'bold'} flex={1}>
{datasetSub?.sub?.datasetStoreAmount || 0}
{datasetSub?.sub?.currentExtraDatasetSize || 0}
{t('core.dataset.data.unit')}
</Box>
</Flex>
{datasetSub?.sub?.expiredTime && (
{datasetSub?.sub?.nextExtraDatasetSize !== undefined && (
<Flex mt={4}>
<Box flex={'0 0 120px'}>{t('support.wallet.subscription.Next sub dataset size')}: </Box>
<Box ml={2} fontWeight={'bold'} flex={1}>
{datasetSub?.sub?.nextExtraDatasetSize || 0}
{t('core.dataset.data.unit')}
</Box>
</Flex>
)}
{!!datasetSub?.sub?.startTime && (
<Flex mt={3}>
<Box w={'100px'}>: </Box>
<Box flex={'0 0 120px'}>: </Box>
<Box ml={2}>{formatTime2YMDHM(datasetSub?.sub?.startTime)}</Box>
</Flex>
)}
{!!datasetSub?.sub?.expiredTime && (
<Flex mt={3}>
<Box flex={'0 0 120px'}>: </Box>
<Box ml={2}>{formatTime2YMDHM(datasetSub?.sub?.expiredTime)}</Box>
</Flex>
)}
<Flex mt={3} alignItems={'center'}>
<Box w={'100px'}>: </Box>
<Box flex={'0 0 120px'}>: </Box>
<MySelect
ml={2}
value={isRenew}
@@ -107,15 +196,16 @@ const SubDatasetModal = ({ onClose }: { onClose: () => void }) => {
{ label: '自动续费', value: 'true' },
{ label: '不自动续费', value: 'false' }
]}
onchange={setIsRenew}
onchange={onUpdateStatus}
/>
</Flex>
<Box mt={4}>
<Box>{t('support.wallet.subscription.Expand size')}</Box>
<Box>{t('support.wallet.subscription.Update extra dataset size')}</Box>
<Flex alignItems={'center'} mt={1}>
<NumberInput
flex={1}
min={0}
max={1000}
step={1}
value={datasetSize}
position={'relative'}
@@ -123,7 +213,7 @@ const SubDatasetModal = ({ onClose }: { onClose: () => void }) => {
setDatasetSize(Number(e));
}}
>
<NumberInputField value={datasetSize} step={1} min={0} />
<NumberInputField value={datasetSize} step={1} min={0} max={1000} />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
@@ -134,33 +224,14 @@ const SubDatasetModal = ({ onClose }: { onClose: () => void }) => {
</Box>
</ModalBody>
<ModalFooter>
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
<Button variant={'whiteBase'} onClick={onClose}>
{t('common.Close')}
</Button>
<Button
isLoading={isLoading}
onClick={() => {
if (isExpand) {
const currentMonthPrice = (
datasetSize *
datasetStorePrice *
(getMonthRemainingDays() / 30)
).toFixed(2);
const totalSize = (datasetSub?.sub?.datasetStoreAmount || 0) / 1000 + datasetSize;
openConfirm(
mutate,
undefined,
`本次扩容预估扣除 ${currentMonthPrice} 元。次月起,每月 1 号将会扣除 ${
totalSize * datasetStorePrice
} 元(共${totalSize * 1000}条)。请确保账号余额充足。`
)();
} else {
mutate('');
}
}}
>
{t('common.Confirm')}
</Button>
{datasetSize * 1000 !== datasetSub?.sub?.nextExtraDatasetSize && (
<Button ml={3} isLoading={isLoading} onClick={onClickPreviewCheck}>
{t('common.Confirm')}
</Button>
)}
</ModalFooter>
<ConfirmModal />

View File

@@ -32,7 +32,8 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
hasOutputTokens,
hasCharsLen,
hasDuration,
hasDataLen
hasDataLen,
hasDatasetSize
} = useMemo(() => {
let hasModel = false;
let hasTokens = false;
@@ -41,6 +42,7 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
let hasCharsLen = false;
let hasDuration = false;
let hasDataLen = false;
let hasDatasetSize = false;
bill.list.forEach((item) => {
if (item.model !== undefined) {
@@ -61,6 +63,9 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
if (typeof item.duration === 'number') {
hasDuration = true;
}
if (typeof item.datasetSize === 'number') {
hasDatasetSize = true;
}
});
return {
@@ -70,7 +75,8 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
hasOutputTokens,
hasCharsLen,
hasDuration,
hasDataLen
hasDataLen,
hasDatasetSize
};
}, [bill.list]);
@@ -83,10 +89,10 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
maxW={['90vw', '700px']}
>
<ModalBody>
<Flex alignItems={'center'} pb={4}>
{/* <Flex alignItems={'center'} pb={4}>
<Box flex={'0 0 80px'}>{t('wallet.bill.bill username')}:</Box>
<Box>{t(bill.memberName)}</Box>
</Flex>
</Flex> */}
<Flex alignItems={'center'} pb={4}>
<Box flex={'0 0 80px'}>{t('wallet.bill.Number')}:</Box>
<Box>{bill.id}</Box>
@@ -101,7 +107,7 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
</Flex>
<Flex alignItems={'center'} pb={4}>
<Box flex={'0 0 80px'}>{t('wallet.bill.Source')}:</Box>
<Box>{BillSourceMap[bill.source]}</Box>
<Box>{BillSourceMap[bill.source]?.label}</Box>
</Flex>
<Flex alignItems={'center'} pb={4}>
<Box flex={'0 0 80px'}>{t('wallet.bill.Total')}:</Box>
@@ -122,6 +128,9 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
{hasOutputTokens && <Th>{t('wallet.bill.Output Token Length')}</Th>}
{hasCharsLen && <Th>{t('wallet.bill.Text Length')}</Th>}
{hasDuration && <Th>{t('wallet.bill.Duration')}</Th>}
{hasDatasetSize && (
<Th>{t('support.user.team.subscription.type.extraDatasetSize')}</Th>
)}
<Th>()</Th>
</Tr>
</Thead>
@@ -135,6 +144,7 @@ const BillDetail = ({ bill, onClose }: { bill: BillItemType; onClose: () => void
{hasOutputTokens && <Td>{item.outputTokens ?? '-'}</Td>}
{hasCharsLen && <Td>{item.charsLength ?? '-'}</Td>}
{hasDuration && <Td>{item.duration ?? '-'}</Td>}
{hasDatasetSize && <Td>{item.datasetSize ?? '-'}</Td>}
<Td>{formatStorePrice2Read(item.amount)}</Td>
</Tr>
))}

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import {
Table,
Thead,
@@ -11,7 +11,7 @@ import {
Box,
Button
} from '@chakra-ui/react';
import { BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants';
import { BillSourceEnum, BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants';
import { getUserBills } from '@/web/support/wallet/bill/api';
import type { BillItemType } from '@fastgpt/global/support/wallet/bill/type';
import { usePagination } from '@/web/common/hooks/usePagination';
@@ -23,6 +23,11 @@ import { addDays } from 'date-fns';
import dynamic from 'next/dynamic';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useTranslation } from 'next-i18next';
import MySelect from '@/components/Select';
import { useQuery } from '@tanstack/react-query';
import { useUserStore } from '@/web/support/user/useUserStore';
import { getTeamMembers } from '@/web/support/user/team/api';
import Avatar from '@/components/Avatar';
const BillDetail = dynamic(() => import('./BillDetail'));
const BillTable = () => {
@@ -32,9 +37,39 @@ const BillTable = () => {
from: addDays(new Date(), -7),
to: new Date()
});
const [billSource, setBillSource] = useState<`${BillSourceEnum}` | ''>('');
const { isPc } = useSystemStore();
const { userInfo } = useUserStore();
const [billDetail, setBillDetail] = useState<BillItemType>();
const sourceList = useMemo(
() => [
{ label: t('common.All'), value: '' },
...Object.entries(BillSourceMap).map(([key, value]) => ({ label: value.label, value: key }))
],
[t]
);
const [selectTmbId, setSelectTmbId] = useState(userInfo?.team?.tmbId);
const { data: members = [] } = useQuery(['getMembers', userInfo?.team?.teamId], () => {
if (!userInfo?.team?.teamId) return [];
return getTeamMembers(userInfo.team.teamId);
});
const tmbList = useMemo(
() =>
members.map((item) => ({
label: (
<Flex alignItems={'center'}>
<Avatar src={item.avatar} w={'16px'} mr={1} />
{item.memberName}
</Flex>
),
value: item.tmbId
})),
[members]
);
console.log(members);
const {
data: bills,
isLoading,
@@ -45,19 +80,68 @@ const BillTable = () => {
pageSize: isPc ? 20 : 10,
params: {
dateStart: dateRange.from || new Date(),
dateEnd: addDays(dateRange.to || new Date(), 1)
}
dateEnd: addDays(dateRange.to || new Date(), 1),
source: billSource,
teamMemberId: selectTmbId
},
defaultRequest: false
});
useEffect(() => {
getData(1);
}, [billSource, selectTmbId]);
return (
<Flex flexDirection={'column'} py={[0, 5]} h={'100%'} position={'relative'}>
<Flex
flexDir={['column', 'row']}
gap={2}
w={'100%'}
px={[3, 8]}
alignItems={['flex-end', 'center']}
>
{tmbList.length > 1 && userInfo?.team?.canWrite && (
<Flex alignItems={'center'}>
<Box mr={2} flexShrink={0}>
{t('support.user.team.member')}
</Box>
<MySelect
size={'sm'}
minW={'100px'}
list={tmbList}
value={selectTmbId}
onchange={setSelectTmbId}
/>
</Flex>
)}
<Box flex={'1'} />
<Flex alignItems={'center'} gap={3}>
<DateRangePicker
defaultDate={dateRange}
position="bottom"
onChange={setDateRange}
onSuccess={() => getData(1)}
/>
<Pagination />
</Flex>
</Flex>
<TableContainer px={[3, 8]} position={'relative'} flex={'1 0 0'} h={0} overflowY={'auto'}>
<Table>
<Thead>
<Tr>
<Th>{t('user.team.Member Name')}</Th>
{/* <Th>{t('user.team.Member Name')}</Th> */}
<Th>{t('user.Time')}</Th>
<Th>{t('user.Source')}</Th>
<Th>
<MySelect
list={sourceList}
value={billSource}
size={'sm'}
onchange={(e) => {
setBillSource(e);
}}
w={'130px'}
></MySelect>
</Th>
<Th>{t('user.Application Name')}</Th>
<Th>{t('user.Total Amount')}</Th>
<Th></Th>
@@ -66,9 +150,9 @@ const BillTable = () => {
<Tbody fontSize={'sm'}>
{bills.map((item) => (
<Tr key={item.id}>
<Td>{item.memberName}</Td>
{/* <Td>{item.memberName}</Td> */}
<Td>{dayjs(item.time).format('YYYY/MM/DD HH:mm:ss')}</Td>
<Td>{BillSourceMap[item.source]}</Td>
<Td>{BillSourceMap[item.source]?.label}</Td>
<Td>{t(item.appName) || '-'}</Td>
<Td>{item.total}</Td>
<Td>
@@ -90,17 +174,7 @@ const BillTable = () => {
</Box>
</Flex>
)}
<Flex w={'100%'} mt={4} px={[3, 8]} alignItems={'center'} justifyContent={'flex-end'}>
<DateRangePicker
defaultDate={dateRange}
position="top"
onChange={setDateRange}
onSuccess={() => getData(1)}
/>
<Box ml={3}>
<Pagination />
</Box>
</Flex>
<Loading loading={isLoading} fixed={false} />
{!!billDetail && <BillDetail bill={billDetail} onClose={() => setBillDetail(undefined)} />}
</Flex>

View File

@@ -51,7 +51,16 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
}
]
: []),
...(feConfigs?.isPlus && feConfigs?.show_pay
...(feConfigs?.show_pay && userInfo?.team.canWrite
? [
{
icon: 'support/pay/payRecordLight',
label: t('user.Recharge Record'),
id: TabEnum.pay
}
]
: []),
...(feConfigs?.show_pay
? [
{
icon: 'support/pay/priceLight',
@@ -60,6 +69,7 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
}
]
: []),
...(feConfigs?.show_promotion
? [
{
@@ -69,15 +79,6 @@ const Account = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
}
]
: []),
...(feConfigs?.show_pay && userInfo?.team.canWrite
? [
{
icon: 'support/pay/payRecordLight',
label: t('user.Recharge Record'),
id: TabEnum.pay
}
]
: []),
...(userInfo?.team.canWrite
? [
{

View File

@@ -40,6 +40,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const [data, total] = await Promise.all([
MongoChat.aggregate([
{ $match: where },
{
$sort: {
userBadFeedbackCount: -1,
userGoodFeedbackCount: -1,
customFeedbacksCount: -1,
updateTime: -1
}
},
{ $skip: (pageNum - 1) * pageSize },
{ $limit: pageSize },
{
$lookup: {
from: ChatItemCollectionName,
@@ -107,16 +117,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
}
},
{
$sort: {
userBadFeedbackCount: -1,
userGoodFeedbackCount: -1,
customFeedbacksCount: -1,
updateTime: -1
}
},
{ $skip: (pageNum - 1) * pageSize },
{ $limit: pageSize },
{
$project: {
_id: 1,

View File

@@ -35,7 +35,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
]);
// auth chat permission
if (!app.canWrite && String(tmbId) !== String(chat?.tmbId)) {
if (chat && !app.canWrite && String(tmbId) !== String(chat?.tmbId)) {
throw new Error(ChatErrEnum.unAuthChat);
}

View File

@@ -0,0 +1,85 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { uploadFile } from '@fastgpt/service/common/file/gridfs/controller';
import { getUploadModel } from '@fastgpt/service/common/file/multer';
import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
import { FileCreateDatasetCollectionParams } from '@fastgpt/global/core/dataset/api';
import { removeFilesByPaths } from '@fastgpt/service/common/file/utils';
import { createOneCollection } from '@fastgpt/service/core/dataset/collection/controller';
import { DatasetCollectionTypeEnum } from '@fastgpt/global/core/dataset/constants';
/**
* Creates the multer uploader
*/
const upload = getUploadModel({
maxSize: 500 * 1024 * 1024
});
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
let filePaths: string[] = [];
const { datasetId } = req.query as { datasetId: string };
try {
await connectToDatabase();
const { teamId, tmbId } = await authDataset({
req,
authToken: true,
authApiKey: true,
per: 'w',
datasetId
});
const { file, bucketName, data } = await upload.doUpload<FileCreateDatasetCollectionParams>(
req,
res
);
filePaths = [file.path];
if (!file || !bucketName) {
throw new Error('file is empty');
}
const { fileMetadata, collectionMetadata, ...collectionData } = data;
// upload file and create collection
const fileId = await uploadFile({
teamId,
tmbId,
bucketName,
path: file.path,
filename: file.originalname,
contentType: file.mimetype,
metadata: fileMetadata
});
// create collection
const collectionId = await createOneCollection({
...collectionData,
metadata: collectionMetadata,
teamId,
tmbId,
type: DatasetCollectionTypeEnum.file,
fileId
});
jsonRes(res, {
data: collectionId
});
} catch (error) {
jsonRes(res, {
code: 500,
error
});
}
removeFilesByPaths(filePaths);
}
export const config = {
api: {
bodyParser: false
}
};

View File

@@ -78,16 +78,25 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
{
$match: match
},
{
$sort: { updateTime: -1 }
},
{
$skip: (pageNum - 1) * pageSize
},
{
$limit: pageSize
},
// count training data
{
$lookup: {
from: DatasetTrainingCollectionName,
let: { id: '$_id' },
let: { id: '$_id', team_id: match.teamId, dataset_id: match.datasetId },
pipeline: [
{
$match: {
$expr: {
$and: [{ $eq: ['$teamId', match.teamId] }, { $eq: ['$collectionId', '$$id'] }]
$and: [{ $eq: ['$teamId', '$$team_id'] }, { $eq: ['$collectionId', '$$id'] }]
}
}
},
@@ -100,14 +109,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
{
$lookup: {
from: DatasetDataCollectionName,
let: { id: '$_id' },
let: { id: '$_id', team_id: match.teamId, dataset_id: match.datasetId },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ['$teamId', match.teamId] },
{ $eq: ['$datasetId', match.datasetId] },
{ $eq: ['$teamId', '$$team_id'] },
{ $eq: ['$datasetId', '$$dataset_id'] },
{ $eq: ['$collectionId', '$$id'] }
]
}
@@ -136,15 +145,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
$ifNull: [{ $arrayElemAt: ['$trainingCount.count', 0] }, 0]
}
}
},
{
$sort: { updateTime: -1 }
},
{
$skip: (pageNum - 1) * pageSize
},
{
$limit: pageSize
}
]),
MongoDatasetCollection.countDocuments(match)

View File

@@ -27,22 +27,18 @@ export const fileCollectionCreate = ({
form.append('bucketName', BucketNameEnum.dataset);
form.append('file', file, encodeURIComponent(file.name));
return POST<string>(
`/proApi/core/dataset/collection/create/emptyFile?datasetId=${data.datasetId}`,
form,
{
timeout: 480000,
onUploadProgress: (e) => {
if (!e.total) return;
return POST<string>(`/core/dataset/collection/create/file?datasetId=${data.datasetId}`, form, {
timeout: 480000,
onUploadProgress: (e) => {
if (!e.total) return;
const percent = Math.round((e.loaded / e.total) * 100);
percentListen && percentListen(percent);
},
headers: {
'Content-Type': 'multipart/form-data; charset=utf-8'
}
const percent = Math.round((e.loaded / e.total) * 100);
percentListen && percentListen(percent);
},
headers: {
'Content-Type': 'multipart/form-data; charset=utf-8'
}
);
});
};
export async function chunksUpload({

View File

@@ -1,7 +1,16 @@
import { GET, POST, PUT, DELETE } from '@/web/common/api/request';
import { SubDatasetSizeParams } from '@fastgpt/global/support/wallet/sub/api';
import {
SubDatasetSizeParams,
SubDatasetSizePreviewCheckResponse
} from '@fastgpt/global/support/wallet/sub/api';
import { SubStatusEnum, SubTypeEnum } from '@fastgpt/global/support/wallet/sub/constants';
import { TeamSubSchema } from '@fastgpt/global/support/wallet/sub/type';
export const putTeamDatasetSubStatus = (data: {
status: `${SubStatusEnum}`;
type: `${SubTypeEnum}`;
}) => POST('/proApi/support/wallet/sub/updateStatus', data);
export const getTeamDatasetValidSub = () =>
GET<{
sub: TeamSubSchema;
@@ -9,5 +18,10 @@ export const getTeamDatasetValidSub = () =>
usedSize: number;
}>(`/support/wallet/sub/getDatasetSub`);
export const postExpandTeamDatasetSub = (data: SubDatasetSizeParams) =>
POST('/proApi/support/wallet/sub/datasetSize/expand', data);
export const posCheckTeamDatasetSizeSub = (data: SubDatasetSizeParams) =>
POST<SubDatasetSizePreviewCheckResponse>(
'/proApi/support/wallet/sub/extraDatasetSize/preCheck',
data
);
export const postUpdateTeamDatasetSizeSub = (data: SubDatasetSizeParams) =>
POST('/proApi/support/wallet/sub/extraDatasetSize/update', data);