diff --git a/admin/service/route/kb.js b/admin/service/route/kb.js index 3702e6cc5..706dcc574 100644 --- a/admin/service/route/kb.js +++ b/admin/service/route/kb.js @@ -10,7 +10,21 @@ export const useKbRoute = (app) => { const order = req.query._order === 'DESC' ? -1 : 1; const sort = req.query._sort || '_id'; const tag = req.query.tag || ''; - const where = { tags: { $elemMatch: { $regex: tag, $options: 'i' } } }; + const name = req.query.name || ''; + + const where = { + ...(name + ? { + name: { $regex: name, $options: 'i' } + } + : {}), + ...(tag + ? { + tags: { $elemMatch: { $regex: tag, $options: 'i' } } + } + : {}) + }; + console.log(where); const kbsRaw = await Kb.find(where) .skip(start) diff --git a/admin/service/route/user.js b/admin/service/route/user.js index f5e031e1b..4b02c2b22 100644 --- a/admin/service/route/user.js +++ b/admin/service/route/user.js @@ -9,6 +9,37 @@ const hashPassword = (psw) => { }; export const useUserRoute = (app) => { + // 统计近 30 天注册用户数量 + app.get('/users/data', auth(), async (req, res) => { + try { + const usersRaw = await User.aggregate([ + { $match: { createTime: { $gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) } } }, + { + $group: { + _id: { + year: { $year: '$createTime' }, + month: { $month: '$createTime' }, + day: { $dayOfMonth: '$createTime' } + }, + count: { $sum: 1 } + } + }, + { + $project: { + _id: 0, + date: { $dateFromParts: { year: '$_id.year', month: '$_id.month', day: '$_id.day' } }, + count: 1 + } + }, + { $sort: { date: 1 } } + ]); + + res.json(usersRaw); + } catch (err) { + console.log(`Error fetching users: ${err}`); + res.status(500).json({ error: 'Error fetching users' }); + } + }); // 获取用户列表 app.get('/users', auth(), async (req, res) => { try { diff --git a/admin/src/App.tsx b/admin/src/App.tsx index 86e95b14c..098cf80c7 100644 --- a/admin/src/App.tsx +++ b/admin/src/App.tsx @@ -74,6 +74,9 @@ function App() { list={ { const [userCount, setUserCount] = useState(0); //用户数量 const [kbCount, setkbCount] = useState(0); const [modelCount, setmodelCount] = useState(0); - useEffect(() => { - const fetchCounts = async () => { - const baseUrl = import.meta.env.VITE_PUBLIC_SERVER_URL; - const { token } = JSON.parse(window.localStorage.getItem(authStorageKey) ?? '{}'); + const [usersData, setUsersData] = useState([]); - const headers = { - 'Content-Type': 'application/json', - Authorization: `Bearer ${token}` - }; + useEffect(() => { + const baseUrl = import.meta.env.VITE_PUBLIC_SERVER_URL; + const { token } = JSON.parse(window.localStorage.getItem(authStorageKey) ?? '{}'); + const headers = { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + }; + + const fetchCounts = async () => { const userResponse = await fetch(`${baseUrl}/users?_end=1`, { headers }); @@ -31,7 +45,6 @@ export const Dashboard: React.FC = React.memo(() => { const userTotalCount = userResponse.headers.get('X-Total-Count'); const kbTotalCount = kbResponse.headers.get('X-Total-Count'); const modelTotalCount = modelResponse.headers.get('X-Total-Count'); - console.log(userTotalCount); if (userTotalCount) { setUserCount(Number(userTotalCount)); @@ -43,8 +56,20 @@ export const Dashboard: React.FC = React.memo(() => { setmodelCount(Number(modelTotalCount)); } }; + const fetchUserData = async () => { + const userResponse: UsersChartDataType = await fetch(`${baseUrl}/users/data`, { + headers + }).then((res) => res.json()); + setUsersData( + userResponse.map((item) => ({ + ...item, + date: dayjs(item.date).format('MM/DD') + })) + ); + }; fetchCounts(); + fetchUserData(); }, []); return ( @@ -76,6 +101,7 @@ export const Dashboard: React.FC = React.memo(() => { + @@ -141,3 +167,38 @@ const DataItem: React.FC<{ ); }); DataItem.displayName = 'DataItem'; + +const UserChart = ({ data }: { data: UsersChartDataType }) => { + return ( + + + + + + + + + + + + + + + + + + + + ); +};