/** * 用户行为轨迹API * * 记录用户的所有行为: * - 查看章节 * - 购买行为 * - 匹配操作 * - 登录/注册 * - 分享行为 */ import { NextRequest, NextResponse } from 'next/server' import { query } from '@/lib/db' // 行为类型枚举 const ActionTypes = { VIEW_CHAPTER: 'view_chapter', // 查看章节 PURCHASE: 'purchase', // 购买 MATCH: 'match', // 匹配 LOGIN: 'login', // 登录 REGISTER: 'register', // 注册 SHARE: 'share', // 分享 BIND_PHONE: 'bind_phone', // 绑定手机 BIND_WECHAT: 'bind_wechat', // 绑定微信 WITHDRAW: 'withdraw', // 提现 REFERRAL_CLICK: 'referral_click', // 推荐链接点击 REFERRAL_BIND: 'referral_bind', // 推荐绑定 } /** * POST - 记录用户行为 */ export async function POST(request: NextRequest) { try { const body = await request.json() const { userId, phone, action, target, extraData } = body if (!userId && !phone) { return NextResponse.json({ success: false, error: '需要用户ID或手机号' }, { status: 400 }) } if (!action) { return NextResponse.json({ success: false, error: '行为类型不能为空' }, { status: 400 }) } // 如果只有手机号,查找用户ID let targetUserId = userId if (!userId && phone) { const users = await query('SELECT id FROM users WHERE phone = ?', [phone]) as any[] if (users.length > 0) { targetUserId = users[0].id } else { return NextResponse.json({ success: false, error: '用户不存在' }, { status: 404 }) } } // 记录行为轨迹 const trackId = 'track_' + Date.now().toString(36) + Math.random().toString(36).substr(2, 6) await query(` INSERT INTO user_tracks ( id, user_id, action, chapter_id, target, extra_data ) VALUES (?, ?, ?, ?, ?, ?) `, [ trackId, targetUserId, action, action === 'view_chapter' ? target : null, target || null, extraData ? JSON.stringify(extraData) : null ]) // 更新用户最后活跃时间 await query('UPDATE users SET updated_at = NOW() WHERE id = ?', [targetUserId]) return NextResponse.json({ success: true, trackId, message: '行为记录成功' }) } catch (error) { console.error('[User Track] POST Error:', error) return NextResponse.json({ success: false, error: '记录行为失败: ' + (error as Error).message }, { status: 500 }) } } /** * GET - 获取用户行为轨迹 */ export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url) const userId = searchParams.get('userId') const phone = searchParams.get('phone') const action = searchParams.get('action') const limit = parseInt(searchParams.get('limit') || '50') try { // 确定查询条件 let targetUserId = userId if (!userId && phone) { const users = await query('SELECT id FROM users WHERE phone = ?', [phone]) as any[] if (users.length > 0) { targetUserId = users[0].id } else { return NextResponse.json({ success: false, error: '用户不存在' }, { status: 404 }) } } if (!targetUserId) { return NextResponse.json({ success: false, error: '需要用户ID或手机号' }, { status: 400 }) } // 构建查询 let sql = ` SELECT ut.* FROM user_tracks ut WHERE ut.user_id = ? ` const params: any[] = [targetUserId] if (action) { sql += ' AND ut.action = ?' params.push(action) } sql += ' ORDER BY ut.created_at DESC LIMIT ?' params.push(limit) const tracks = await query(sql, params) as any[] // 格式化轨迹数据 const formattedTracks = tracks.map(t => ({ id: t.id, action: t.action, actionLabel: getActionLabel(t.action), target: t.target || t.chapter_id, chapterTitle: t.chapter_id || null, // 直接使用chapter_id作为标题 extraData: t.extra_data ? JSON.parse(t.extra_data) : null, createdAt: t.created_at, timeAgo: getTimeAgo(t.created_at) })) // 统计信息 const stats = await query(` SELECT action, COUNT(*) as count FROM user_tracks WHERE user_id = ? GROUP BY action `, [targetUserId]) as any[] return NextResponse.json({ success: true, tracks: formattedTracks, stats: stats.reduce((acc, s) => ({ ...acc, [s.action]: s.count }), {}), total: formattedTracks.length }) } catch (error) { console.error('[User Track] GET Error:', error) return NextResponse.json({ success: false, error: '获取行为轨迹失败: ' + (error as Error).message }, { status: 500 }) } } // 获取行为标签 function getActionLabel(action: string): string { const labels: Record = { 'view_chapter': '查看章节', 'purchase': '购买', 'match': '匹配伙伴', 'login': '登录', 'register': '注册', 'share': '分享', 'bind_phone': '绑定手机', 'bind_wechat': '绑定微信', 'withdraw': '提现', 'referral_click': '点击推荐链接', 'referral_bind': '推荐绑定', } return labels[action] || action } // 获取相对时间 function getTimeAgo(date: string | Date): string { const now = new Date() const then = new Date(date) const diffMs = now.getTime() - then.getTime() const diffMins = Math.floor(diffMs / 60000) const diffHours = Math.floor(diffMs / 3600000) const diffDays = Math.floor(diffMs / 86400000) if (diffMins < 1) return '刚刚' if (diffMins < 60) return `${diffMins}分钟前` if (diffHours < 24) return `${diffHours}小时前` if (diffDays < 7) return `${diffDays}天前` if (diffDays < 30) return `${Math.floor(diffDays / 7)}周前` return `${Math.floor(diffDays / 30)}个月前` }