diff --git a/client/src/pages/admin/Export.tsx b/client/src/pages/admin/Export.tsx index c12b96c..e5190a2 100644 --- a/client/src/pages/admin/Export.tsx +++ b/client/src/pages/admin/Export.tsx @@ -262,11 +262,19 @@ export default function Export() { const overviewCalc = (() => { if (!preview) return null; - const totalHours = (preview.hours || []).reduce((s: number, h: any) => s + Number(h.hours || 0), 0); const userCount = (preview.users || []).length; const activityCount = (preview.activities || []).length; const hourCount = (preview.hours || []).length; - const avgHours = userCount > 0 ? totalHours / userCount : 0; + let totalHours: number; + if (preview.user_totals && preview.user_totals.length > 0) { + totalHours = (preview.user_totals).reduce((s: number, u: any) => s + Number(u.total_hours || 0), 0); + } else if (preview.activity_matrix && Array.isArray(preview.activity_matrix.users)) { + totalHours = (preview.activity_matrix.users).reduce((s: number, u: any) => s + Number(u._total || 0), 0); + } else { + totalHours = (preview.hours || []).reduce((s: number, h: any) => s + Number(h.hours || 0), 0); + } + totalHours = Number(totalHours.toFixed(1)); + const avgHours = userCount > 0 ? Number((totalHours / userCount).toFixed(1)) : 0; return { totalHours, userCount, activityCount, hourCount, avgHours }; })(); diff --git a/client/src/pages/user/Dashboard.tsx b/client/src/pages/user/Dashboard.tsx index 8707fb5..576dc3a 100644 --- a/client/src/pages/user/Dashboard.tsx +++ b/client/src/pages/user/Dashboard.tsx @@ -68,7 +68,19 @@ export default function UserDashboard() {
累计义工时长
-
{Number(data.total_hours || 0).toFixed(1)} 小时
+
+ {Number(data.total_hours || 0).toFixed(1)} 小时 +
+ {data.activities.length > 0 && ( +
+ + 明细表合计: + + + {data.activities.reduce((s, r) => s + Number((r as any).hours || 0), 0).toFixed(1)} 小时 + +
+ )}
参与活动数
diff --git a/server/src/routes/export.ts b/server/src/routes/export.ts index 6390391..258d914 100644 --- a/server/src/routes/export.ts +++ b/server/src/routes/export.ts @@ -66,11 +66,17 @@ router.post('/', authMiddleware, (req: AuthRequest, res) => { FROM user_groups ug2 LEFT JOIN work_groups g2 ON ug2.group_id = g2.id WHERE ug2.user_id = u.id) AS _group_names_csv, - COALESCE(SUM(h.hours), 0) AS total_hours, - COUNT(DISTINCT h.activity_id) AS activity_count + COALESCE(vh.total_hours, 0) AS total_hours, + COALESCE(vh.activity_count, 0) AS activity_count FROM users u - LEFT JOIN hours h ON h.user_id = u.id - GROUP BY u.id + LEFT JOIN ( + SELECT user_id, + SUM(hours) AS total_hours, + COUNT(DISTINCT activity_id) AS activity_count + FROM hours h + WHERE EXISTS (SELECT 1 FROM activities a WHERE a.id = h.activity_id) + GROUP BY user_id + ) vh ON vh.user_id = u.id ORDER BY total_hours DESC, u.class, u.name `).all(); result.user_totals = withGroupNames(rows).map(({ _group_names_csv: _, ...rest }) => rest); @@ -112,7 +118,8 @@ router.post('/', authMiddleware, (req: AuthRequest, res) => { `).all() as any[]; const users = withGroupNames(userRows); const hoursRows = db.prepareCompat(` - SELECT user_id, activity_id, hours, remark FROM hours + SELECT user_id, activity_id, hours, remark FROM hours h + WHERE EXISTS (SELECT 1 FROM activities a WHERE a.id = h.activity_id) `).all() as any[]; const matrix: Record = {}; const remarkMatrix: Record = {}; diff --git a/server/src/routes/users.ts b/server/src/routes/users.ts index be319ca..9562a36 100644 --- a/server/src/routes/users.ts +++ b/server/src/routes/users.ts @@ -31,7 +31,11 @@ router.get('/', authMiddleware, (req: AuthRequest, res: Response) => { SELECT u.id, u.class, u.name, u.volunteer_no, GROUP_CONCAT(g.id) AS group_ids_csv, GROUP_CONCAT(g.name) AS group_names_csv, - COALESCE((SELECT SUM(h.hours) FROM hours h WHERE h.user_id = u.id), 0) AS total_hours + COALESCE(( + SELECT SUM(h.hours) FROM hours h + WHERE h.user_id = u.id + AND EXISTS (SELECT 1 FROM activities a WHERE a.id = h.activity_id) + ), 0) AS total_hours FROM users u LEFT JOIN user_groups ug ON u.id = ug.user_id LEFT JOIN work_groups g ON ug.group_id = g.id @@ -66,7 +70,11 @@ router.get('/:id', (req: Request, res: Response) => { SELECT u.id, u.class, u.name, u.volunteer_no, GROUP_CONCAT(g.id) AS group_ids_csv, GROUP_CONCAT(g.name) AS group_names_csv, - COALESCE((SELECT SUM(h.hours) FROM hours h WHERE h.user_id = u.id), 0) AS total_hours + COALESCE(( + SELECT SUM(h.hours) FROM hours h + WHERE h.user_id = u.id + AND EXISTS (SELECT 1 FROM activities a WHERE a.id = h.activity_id) + ), 0) AS total_hours_raw FROM users u LEFT JOIN user_groups ug ON u.id = ug.user_id LEFT JOIN work_groups g ON ug.group_id = g.id