import { Router } from 'express'; import { getDb } from '../db'; import { authMiddleware, AuthRequest } from '../middleware/auth'; const router = Router(); function withGroupNames(rows: any[]): any[] { return rows.map((r) => { const names: string[] = []; if (r._group_names_csv) { for (const s of String(r._group_names_csv).split(',')) { if (s && s.trim()) names.push(s.trim()); } } return { ...r, group_name: names.join('、') || null }; }); } router.post('/', authMiddleware, (req: AuthRequest, res) => { const { include_users, include_activities, include_hours, include_user_totals, include_user_activities, include_activity_matrix } = req.body || {}; const db = getDb(); const result: any = {}; if (include_users) { const rows = db.prepareCompat(` SELECT u.id, u.class, u.name, u.volunteer_no, GROUP_CONCAT(g.name) AS _group_names_csv 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 GROUP BY u.id ORDER BY u.class, u.name `).all(); result.users = withGroupNames(rows).map(({ _group_names_csv: _, ...rest }) => rest); } if (include_activities) { result.activities = db.prepareCompat(` SELECT a.*, GROUP_CONCAT(g.name) AS groups_name FROM activities a LEFT JOIN activity_groups ag ON a.id = ag.activity_id LEFT JOIN work_groups g ON ag.group_id = g.id GROUP BY a.id ORDER BY a.start_time DESC `).all(); } if (include_hours) { const rows = db.prepareCompat(` SELECT h.hours, h.remark, h.created_at, a.title AS activity_title, a.start_time, a.end_time, u.class, u.name, u.volunteer_no, (SELECT GROUP_CONCAT(g2.name) 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 FROM hours h JOIN activities a ON h.activity_id = a.id JOIN users u ON h.user_id = u.id ORDER BY a.start_time DESC, u.class, u.name `).all(); result.hours = withGroupNames(rows).map(({ _group_names_csv: _, ...rest }) => rest); } if (include_user_totals) { const rows = db.prepareCompat(` SELECT u.id, u.class, u.name, u.volunteer_no, (SELECT GROUP_CONCAT(g2.name) 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 FROM users u LEFT JOIN hours h ON h.user_id = u.id GROUP BY u.id ORDER BY total_hours DESC, u.class, u.name `).all(); result.user_totals = withGroupNames(rows).map(({ _group_names_csv: _, ...rest }) => rest); } if (include_user_activities) { const userRows = db.prepareCompat(` SELECT u.id, u.class, u.name, u.volunteer_no, (SELECT GROUP_CONCAT(g2.name) 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 FROM users u GROUP BY u.id ORDER BY u.class, u.name `).all() as any[]; const users = withGroupNames(userRows).map(({ _group_names_csv: _, ...rest }) => rest); result.user_activities = users.map((u) => { const acts = db.prepareCompat(` SELECT a.title, a.start_time, a.end_time, h.hours, h.remark FROM hours h JOIN activities a ON h.activity_id = a.id WHERE h.user_id = ? ORDER BY a.start_time DESC `).all(u.id); return { ...u, activities: acts }; }); } if (include_activity_matrix) { const activities = db.prepareCompat(` SELECT id, title, start_time FROM activities ORDER BY start_time DESC, id DESC `).all() as any[]; const userRows = db.prepareCompat(` SELECT u.id, u.class, u.name, u.volunteer_no, (SELECT GROUP_CONCAT(g2.name) 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 FROM users u GROUP BY u.id ORDER BY u.class, u.name `).all() as any[]; const users = withGroupNames(userRows); const hoursRows = db.prepareCompat(` SELECT user_id, activity_id, hours FROM hours `).all() as any[]; const matrix: Record = {}; for (const r of hoursRows) { const key = `${r.user_id}|${r.activity_id}`; matrix[key] = (matrix[key] || 0) + Number(r.hours || 0); } result.activity_matrix = { activities, users: users.map((u) => { const row: Record = { id: u.id, class: u.class, name: u.name, volunteer_no: u.volunteer_no, group_name: u.group_name, }; for (const a of activities) { row[String(a.id)] = Number((matrix[`${u.id}|${a.id}`] || 0).toFixed(2)); } row._total = Number( activities.reduce((s: number, a: any) => s + (Number(row[String(a.id)]) || 0), 0).toFixed(2) ); return row; }), }; } res.json(result); }); export default router;