非常好的选择!这些功能将大大增强我们的分析系统。让我们逐步实现这些功能:

第三十二部分:用户行为跟踪、数据导出和实时分析

  1. 用户行为跟踪

首先,我们需要在客户端收集用户行为数据,然后将其发送到服务器。我们将使用 JavaScript 来实现这一点。

创建一个新文件 public/js/analytics.js

// 用于存储用户行为数据
let behaviorData = {
  pageViews: {},
  clickPath: [],
  totalTimeSpent: 0
};
 
// 记录页面访问
function logPageView() {
  const path = window.location.pathname;
  behaviorData.pageViews[path] = (behaviorData.pageViews[path] || 0) + 1;
}
 
// 记录点击
document.addEventListener('click', function(e) {
  behaviorData.clickPath.push({
    path: window.location.pathname,
    element: e.target.tagName,
    timestamp: new Date().toISOString()
  });
});
 
// 计算页面停留时间
let startTime = new Date();
window.addEventListener('beforeunload', function() {
  const endTime = new Date();
  behaviorData.totalTimeSpent += (endTime - startTime) / 1000; // 转换为秒
});
 
// 定期发送数据到服务器
setInterval(function() {
  fetch('/api/log-behavior', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(behaviorData)
  });
  // 重置数据
  behaviorData = {
    pageViews: {},
    clickPath: [],
    totalTimeSpent: 0
  };
}, 60000); // 每分钟发送一次
 
// 初始化
logPageView();
  1. src/views/layouts/main.eta 中引入这个 JavaScript 文件:
<script src="/js/analytics.js"></script>
  1. 创建一个新的路由来处理行为数据。在 src/routes/analytics.ts 中添加:
import { Elysia } from "elysia";
import { db } from "../db";
import { userBehaviors } from "../db/schema";
 
export const analyticsRoutes = new Elysia()
  // ... 保留现有的路由
  .post("/api/log-behavior", async ({ body }) => {
    const { pageViews, clickPath, totalTimeSpent } = body;
    await db.insert(userBehaviors).values({
      userId: session.user?.id, // 假设你有用户会话
      pageViews: JSON.stringify(pageViews),
      clickPath: JSON.stringify(clickPath),
      totalTimeSpent,
      timestamp: new Date()
    });
    return { success: true };
  });
  1. 更新 src/db/schema.ts 以添加新的 userBehaviors 表:
export const userBehaviors = sqliteTable("user_behaviors", {
  id: integer("id").primaryKey(),
  userId: integer("user_id").references(() => users.id),
  pageViews: text("page_views"),
  clickPath: text("click_path"),
  totalTimeSpent: real("total_time_spent"),
  timestamp: integer("timestamp", { mode: "timestamp" })
});
  1. 创建相应的迁移文件。

  2. 数据导出功能

src/routes/analytics.ts 中添加新的路由:

import { createObjectCsvStringifier } from "csv-writer";
 
export const analyticsRoutes = new Elysia()
  // ... 保留现有的路由
  .get("/export-data", async ({ query, set }) => {
    const { startDate, endDate, dataType } = query;
    let data;
 
    switch (dataType) {
      case 'user-growth':
        data = await db
          .select({
            date: sql<string>`date(${userRegistrations.registrationDate})`.as("date"),
            count: sql<number>`count(*)`.as("count"),
          })
          .from(userRegistrations)
          .where(sql`${userRegistrations.registrationDate} BETWEEN ${startDate} AND ${endDate}`)
          .groupBy(sql`date(${userRegistrations.registrationDate})`)
          .orderBy(sql`date(${userRegistrations.registrationDate})`)
          .all();
        break;
      // 添加其他数据类型的 case
    }
 
    const csvStringifier = createObjectCsvStringifier({
      header: Object.keys(data[0]).map(key => ({ id: key, title: key }))
    });
 
    set.headers['Content-Type'] = 'text/csv';
    set.headers['Content-Disposition'] = `attachment; filename="${dataType}-${startDate}-to-${endDate}.csv"`;
 
    return csvStringifier.getHeaderString() + csvStringifier.stringifyRecords(data);
  });
  1. 实时分析功能

首先,安装必要的依赖:

bun add @elysiajs/websocket

然后,更新 src/index.ts

import { Elysia } from "elysia";
import { websocket } from "@elysiajs/websocket";
 
const app = new Elysia()
  .use(websocket())
  .ws("/ws", {
    message(ws, message) {
      // 处理接收到的消息
    },
  })
  // ... 其他路由和中间件
  .listen(3000);

src/routes/analytics.ts 中,每当有新的分析数据时,发送 WebSocket 消息:

import { websocket } from "@elysiajs/websocket";
 
export const analyticsRoutes = new Elysia()
  .use(websocket())
  // ... 保留现有的路由
  .post("/api/log-behavior", async ({ body, server }) => {
    // ... 保存行为数据的代码
 
    // 发送实时更新
    server.websocket.clients.forEach(client => {
      client.send(JSON.stringify({
        type: 'behavior-update',
        data: body
      }));
    });
 
    return { success: true };
  });

在客户端(src/views/analytics.eta)中,添加 WebSocket 连接和更新逻辑:

<script>
  const socket = new WebSocket('ws://localhost:3000/ws');
 
  socket.onmessage = function(event) {
    const data = JSON.parse(event.data);
    if (data.type === 'behavior-update') {
      // 更新相关图表
      updateCharts(data.data);
    }
  };
 
  function updateCharts(data) {
    // 根据接收到的数据更新图表
    // 例如:
    // Plotly.extendTraces('user-growth-chart', { y: [[data.newUsers]] }, [0]);
    // Plotly.extendTraces('comment-activity-chart', { y: [[data.newComments]] }, [0]);
  }
</script>

这些更改实现了以下功能:

  1. 用户行为跟踪:

    • 记录页面访问次数
    • 跟踪用户点击路径
    • 计算页面停留时间
  2. 数据导出功能:

    • 允许管理员下载 CSV 格式的报告
    • 支持不同类型的数据导出(如用户增长数据)
  3. 实时分析功能:

    • 使用 WebSocket 实时更新仪表板
    • 当有新的行为数据时,立即更新相关图表

主要改进包括:

  • 更深入地了解用户行为,包括页面访问模式和停留时间
  • 提供数据导出功能,方便进行离线分析或报告生成
  • 实现实时更新,使管理员能够及时了解网站活动

接下来的步骤可以包括:

  1. 优化数据收集和存储方式,确保系统能够处理大量的行为数据
  2. 增加更多类型的数据导出选项
  3. 改进实时分析的可视化效果,如添加实时用户数量、当前活跃页面等指标
  4. 实现用户分群分析,基于收集到的行为数据
  5. 添加异常检测功能,当出现异常行为模式时发出警报

你希望进一步完善哪个方面,或者有其他想法吗?