import { Hono } from 'hono'; import { db } from '../db/index'; import { gitCommits, gitPRs, users } from '../db/schema'; import { eq, and, gte, desc } from 'drizzle-orm'; import { AppError } from '../middleware/error-handler'; import dayjs from 'dayjs'; export const gitRoutes = new Hono(); // GET /api/git/activity gitRoutes.get('/git/activity', async (c) => { const user = c.get('user'); const queryUserId = c.req.query('userId'); const weeks = parseInt(c.req.query('weeks') || '12'); if (user.role === 'viewer') { throw new AppError(40103, 'Insufficient permissions', 403); } let targetUserId: string | undefined; if (user.role === 'developer') { targetUserId = user.sub; } else if (queryUserId) { targetUserId = queryUserId; } const startDate = dayjs().subtract(weeks, 'week').startOf('week').toDate(); // 所有 commits const commitQuery = targetUserId ? db.select().from(gitCommits).where(and(eq(gitCommits.userId, targetUserId), gte(gitCommits.committedAt, startDate))) : db.select().from(gitCommits).where(gte(gitCommits.committedAt, startDate)); const commits = await commitQuery; // Heatmap(按天) const dayMap: Record = {}; const today = dayjs(); for (let d = dayjs(startDate); d.isBefore(today) || d.isSame(today, 'day'); d = d.add(1, 'day')) { dayMap[d.format('YYYY-MM-DD')] = { commits: 0, additions: 0, deletions: 0 }; } for (const commit of commits) { const day = dayjs(commit.committedAt).format('YYYY-MM-DD'); if (dayMap[day]) { dayMap[day].commits++; dayMap[day].additions += commit.additions || 0; dayMap[day].deletions += commit.deletions || 0; } } const heatmap = Object.entries(dayMap).map(([date, data]) => ({ date, ...data })); // 统计指标(替代原来的 PR 指标) const allCommits = await db.select().from(gitCommits); const thisMonthStart = dayjs().startOf('month').toDate(); const thisMonthCommits = allCommits.filter(c => dayjs(c.committedAt).isAfter(thisMonthStart)); const activeContributors = new Set(allCommits.filter(c => c.userId).map(c => c.userId)).size; const activeRepos = new Set(allCommits.map(c => c.repoName)).size; const stats = { totalCommits: allCommits.length, activeContributors, thisMonthCommits: thisMonthCommits.length, activeRepos, }; // 每周趋势(按人堆叠) const allUsers = await db.select().from(users); const userMap = new Map(allUsers.map(u => [u.id, u.displayName])); const weeklyTrend = []; for (let i = 0; i < weeks; i++) { const weekStart = dayjs().subtract(weeks - 1 - i, 'week').startOf('week'); const weekEnd = weekStart.add(7, 'day'); const weekCommits = commits.filter(c => { const d = dayjs(c.committedAt); return d.isAfter(weekStart) && d.isBefore(weekEnd); }); // 按人分组 const byUser: Record = {}; for (const c of weekCommits) { const uid = c.userId || 'unknown'; byUser[uid] = (byUser[uid] || 0) + 1; } weeklyTrend.push({ weekStart: weekStart.format('YYYY-MM-DD'), total: weekCommits.length, additions: weekCommits.reduce((sum, c) => sum + (c.additions || 0), 0), deletions: weekCommits.reduce((sum, c) => sum + (c.deletions || 0), 0), byUser: Object.entries(byUser).map(([userId, count]) => ({ userId, name: userMap.get(userId) || '未关联', commits: count, })), }); } return c.json({ code: 0, data: { heatmap, stats, weeklyTrend }, message: 'success', }); });