Skip to content

Commit 1367ba9

Browse files
committed
feat: admin
1 parent 62489ef commit 1367ba9

File tree

3 files changed

+111
-30
lines changed

3 files changed

+111
-30
lines changed

admin/service/route/kb.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export const useKbRoute = (app) => {
2424
}
2525
: {})
2626
};
27-
console.log(where);
2827

2928
const kbsRaw = await Kb.find(where)
3029
.skip(start)

admin/service/route/user.js

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ const hashPassword = (psw) => {
88
return crypto.createHash('sha256').update(psw).digest('hex');
99
};
1010

11+
const day = 60;
12+
1113
export const useUserRoute = (app) => {
1214
// 统计近 30 天注册用户数量
1315
app.get('/users/data', auth(), async (req, res) => {
1416
try {
15-
const day = 60;
1617
let startCount = await User.countDocuments({
1718
createTime: { $lt: new Date(Date.now() - day * 24 * 60 * 60 * 1000) }
1819
});
@@ -92,7 +93,6 @@ export const useUserRoute = (app) => {
9293
res.status(500).json({ error: 'Error fetching users' });
9394
}
9495
});
95-
9696
// 创建用户
9797
app.post('/users', auth(), async (req, res) => {
9898
try {
@@ -134,7 +134,6 @@ export const useUserRoute = (app) => {
134134
res.status(500).json({ error: 'Error updating user' });
135135
}
136136
});
137-
138137
// 新增: 获取 pays 列表
139138
app.get('/pays', auth(), async (req, res) => {
140139
try {
@@ -179,4 +178,52 @@ export const useUserRoute = (app) => {
179178
res.status(500).json({ error: 'Error fetching pays', details: err.message });
180179
}
181180
});
181+
// 获取本月账单
182+
app.get('/pays/data', auth(), async (req, res) => {
183+
try {
184+
let startCount = 0;
185+
186+
const paysRaw = await Pay.aggregate([
187+
{
188+
$match: {
189+
status: 'SUCCESS',
190+
createTime: {
191+
$gte: new Date(Date.now() - day * 24 * 60 * 60 * 1000)
192+
}
193+
}
194+
},
195+
{
196+
$group: {
197+
_id: {
198+
year: { $year: '$createTime' },
199+
month: { $month: '$createTime' },
200+
day: { $dayOfMonth: '$createTime' }
201+
},
202+
count: { $sum: '$price' }
203+
}
204+
},
205+
{
206+
$project: {
207+
_id: 0,
208+
date: { $dateFromParts: { year: '$_id.year', month: '$_id.month', day: '$_id.day' } },
209+
count: 1
210+
}
211+
},
212+
{ $sort: { date: 1 } }
213+
]);
214+
215+
const countResult = paysRaw.map((item) => {
216+
startCount += item.count;
217+
return {
218+
date: item.date,
219+
count: startCount
220+
};
221+
});
222+
223+
res.json(countResult);
224+
} catch (err) {
225+
console.log(`Error fetching users: ${err}`);
226+
res.status(500).json({ error: 'Error fetching users' });
227+
}
228+
});
182229
};

admin/src/Dashboard.tsx

Lines changed: 61 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,24 @@ import {
1414
import dayjs from 'dayjs';
1515

1616
const authStorageKey = 'tushan:auth';
17+
const PRICE_SCALE = 100000;
1718

18-
type UsersChartDataType = { count: number; date: string; increase: number; increaseRate: string };
19+
type fetchChatData = { count: number; date: string; increase?: number; increaseRate?: string };
20+
21+
type chatDataType = {
22+
date: string;
23+
userCount: number;
24+
userIncrease?: number;
25+
userIncreaseRate?: string;
26+
payCount: number;
27+
};
1928

2029
export const Dashboard: React.FC = React.memo(() => {
2130
const [userCount, setUserCount] = useState(0); //用户数量
2231
const [kbCount, setkbCount] = useState(0);
2332
const [modelCount, setmodelCount] = useState(0);
24-
const [usersData, setUsersData] = useState<UsersChartDataType[]>([]);
33+
34+
const [chatData, setChatData] = useState<chatDataType[]>([]);
2535

2636
useEffect(() => {
2737
const baseUrl = import.meta.env.VITE_PUBLIC_SERVER_URL;
@@ -56,20 +66,29 @@ export const Dashboard: React.FC = React.memo(() => {
5666
setmodelCount(Number(modelTotalCount));
5767
}
5868
};
59-
const fetchUserData = async () => {
60-
const userResponse: UsersChartDataType[] = await fetch(`${baseUrl}/users/data`, {
61-
headers
62-
}).then((res) => res.json());
63-
setUsersData(
64-
userResponse.map((item) => ({
65-
...item,
66-
date: dayjs(item.date).format('MM/DD')
67-
}))
68-
);
69+
70+
const fetchChatData = async () => {
71+
const [userResponse, payResponse]: fetchChatData[][] = await Promise.all([
72+
fetch(`${baseUrl}/users/data`, {
73+
headers
74+
}).then((res) => res.json()),
75+
fetch(`${baseUrl}/pays/data`, {
76+
headers
77+
}).then((res) => res.json())
78+
]);
79+
80+
const data = userResponse.map((item, i) => ({
81+
date: dayjs(item.date).format('MM/DD'),
82+
userCount: item.count,
83+
userIncrease: item.increase,
84+
userIncreaseRate: item.increaseRate,
85+
payCount: payResponse[i].count / PRICE_SCALE
86+
}));
87+
setChatData(data);
6988
};
7089

7190
fetchCounts();
72-
fetchUserData();
91+
fetchChatData();
7392
}, []);
7493

7594
return (
@@ -101,7 +120,13 @@ export const Dashboard: React.FC = React.memo(() => {
101120
</Grid.Row>
102121

103122
<Divider />
104-
<UserChart data={usersData} />
123+
124+
<div>
125+
<strong>用户数量 & 支付情况</strong>
126+
<UserChart data={chatData} />
127+
</div>
128+
129+
<Divider />
105130
</Card>
106131
</Space>
107132
</div>
@@ -162,7 +187,7 @@ const DataItem = React.memo((props: { icon: React.ReactElement; title: string; c
162187
DataItem.displayName = 'DataItem';
163188

164189
const CustomTooltip = ({ active, payload }: any) => {
165-
const data = payload?.[0]?.payload as UsersChartDataType;
190+
const data = payload?.[0]?.payload as chatDataType;
166191
if (active && data) {
167192
return (
168193
<div
@@ -174,21 +199,24 @@ const CustomTooltip = ({ active, payload }: any) => {
174199
}}
175200
>
176201
<p className="label">
177-
count: <strong>{data.count}</strong>
202+
用户总数: <strong>{data.userCount}</strong>
203+
</p>
204+
<p className="label">
205+
60天累计支付: <strong>{data.payCount}</strong>
178206
</p>
179207
<p className="label">
180-
increase: <strong>{data.increase}</strong>
208+
用户昨日增长数量: <strong>{data.userIncrease}</strong>
181209
</p>
182210
<p className="label">
183-
increaseRate: <strong>{data.increaseRate}</strong>
211+
用户昨日增长比例: <strong>{data.userIncreaseRate}</strong>
184212
</p>
185213
</div>
186214
);
187215
}
188216
return null;
189217
};
190218

191-
const UserChart = ({ data }: { data: UsersChartDataType[] }) => {
219+
const UserChart = ({ data }: { data: chatDataType[] }) => {
192220
return (
193221
<ResponsiveContainer width="100%" height={320}>
194222
<AreaChart
@@ -198,25 +226,32 @@ const UserChart = ({ data }: { data: UsersChartDataType[] }) => {
198226
margin={{ top: 10, right: 30, left: 0, bottom: 0 }}
199227
>
200228
<defs>
201-
<linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
202-
<stop offset="5%" stopColor="#8884d8" stopOpacity={0.8} />
203-
<stop offset="95%" stopColor="#8884d8" stopOpacity={0} />
204-
</linearGradient>
205-
<linearGradient id="colorPv" x1="0" y1="0" x2="0" y2="1">
229+
<linearGradient id="userCount" x1="0" y1="0" x2="0" y2="1">
206230
<stop offset="5%" stopColor="#82ca9d" stopOpacity={0.8} />
207231
<stop offset="95%" stopColor="#82ca9d" stopOpacity={0} />
208232
</linearGradient>
233+
<linearGradient id="payCount" x1="0" y1="0" x2="0" y2="1">
234+
<stop offset="5%" stopColor="#8884d8" stopOpacity={0.8} />
235+
<stop offset="95%" stopColor="#8884d8" stopOpacity={0} />
236+
</linearGradient>
209237
</defs>
210238
<XAxis dataKey="date" />
211239
<YAxis />
212240
<CartesianGrid strokeDasharray="3 3" />
213241
<Tooltip content={<CustomTooltip />} />
214242
<Area
215243
type="monotone"
216-
dataKey="count"
244+
dataKey="userCount"
217245
stroke="#82ca9d"
218246
fillOpacity={1}
219-
fill="url(#colorPv)"
247+
fill="url(#userCount)"
248+
/>
249+
<Area
250+
type="monotone"
251+
dataKey="payCount"
252+
stroke="#8884d8"
253+
fillOpacity={1}
254+
fill="url(#payCount)"
220255
/>
221256
</AreaChart>
222257
</ResponsiveContainer>

0 commit comments

Comments
 (0)