From 09c67e8158ff4d7fbf94cae5ed8fd83164870a0d Mon Sep 17 00:00:00 2001 From: 537yaha <2930134478@qq.com> Date: Sun, 4 Jan 2026 19:26:20 +0800 Subject: [PATCH] Add Docker deployment support and GitHub Actions --- .dockerignore | 26 ++++ .env.example | 45 +++++++ .github/workflows/docker-build.yml | 16 +++ README.md | 177 ++++++++++++++++++++++++-- backend/.dockerignore | 35 +++++ backend/Dockerfile | 52 ++++++++ docker-compose.prod.yml | 75 +++++++++++ docker-compose.yml | 79 ++++++++++++ frontend/.dockerignore | 42 ++++++ frontend/Dockerfile | 50 ++++++++ frontend/README-配置说明.md | 65 ---------- frontend/README.md | 36 ------ frontend/app/layout.tsx | 5 + frontend/components/MatomoTracker.tsx | 33 +++++ 14 files changed, 626 insertions(+), 110 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .github/workflows/docker-build.yml create mode 100644 backend/.dockerignore create mode 100644 backend/Dockerfile create mode 100644 docker-compose.prod.yml create mode 100644 docker-compose.yml create mode 100644 frontend/.dockerignore create mode 100644 frontend/Dockerfile delete mode 100644 frontend/README-配置说明.md delete mode 100644 frontend/README.md create mode 100644 frontend/components/MatomoTracker.tsx diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..32900fa --- /dev/null +++ b/.dockerignore @@ -0,0 +1,26 @@ +# Git +.git +.gitignore + +# 环境变量文件 +.env +.env.local +.env.production + +# 文档 +doc/ +*.md +!README.md + +# IDE +.vscode +.idea +*.swp +*.swo + +# 日志 +*.log + +# 系统文件 +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..3ce6c16 --- /dev/null +++ b/.env.example @@ -0,0 +1,45 @@ +# ============================================ +# AI-CS 系统 Docker 部署配置模板 +# ============================================ +# 使用方法: +# 1. 复制此文件为 .env:cp .env.example .env +# 2. 编辑 .env 文件,修改以下配置 +# 3. .env 文件不要提交到 Git! + +# ============================================ +# MySQL 数据库配置 +# ============================================ +MYSQL_ROOT_PASSWORD=rootpassword +MYSQL_DATABASE=ai_cs +MYSQL_USER=ai_cs_user +MYSQL_PASSWORD=ai_cs_password +MYSQL_PORT=3306 + +# ============================================ +# 后端服务配置 +# ============================================ +BACKEND_PORT=8080 +ADMIN_USERNAME=admin +ADMIN_PASSWORD=admin123 +GIN_MODE=release + +# 加密密钥(用于加密 AI API Keys) +# 生成方式: +# - Linux/Mac: openssl rand -hex 32 +# - Windows PowerShell: -join ((48..57) + (97..102) | Get-Random -Count 64 | ForEach-Object {[char]$_}) +# - 在线工具: https://www.random.org/strings/?num=1&len=64&digits=on&upperalpha=off&loweralpha=on&unique=off&format=html&rnd=new +ENCRYPTION_KEY=changeme_please_use_random_hex_32_bytes_here + +# ============================================ +# 前端服务配置 +# ============================================ +FRONTEND_PORT=3000 +# 前端访问后端的地址(Docker 内部使用容器名,外部访问使用 localhost) +NEXT_PUBLIC_API_BASE_URL=http://localhost:8080 + +# ============================================ +# 生产版镜像配置(仅用于 docker-compose.prod.yml) +# ============================================ +# 如果使用预构建镜像,可以指定镜像名称(可选) +# BACKEND_IMAGE=2930134478/ai-cs-backend:latest +# FRONTEND_IMAGE=2930134478/ai-cs-frontend:latest \ No newline at end of file diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..09730fa --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,16 @@ +name: Build and Push Docker Image + +on: + push: + branches: [ main ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build and push + uses: docker/build-push-action@v4 + with: + push: true + tags: 537yaha/ai-cs:latest \ No newline at end of file diff --git a/README.md b/README.md index 8be3a7b..2963959 100644 --- a/README.md +++ b/README.md @@ -24,20 +24,179 @@ ## 🚀 快速开始 -### 环境要求 +### 方式一:预构建镜像一键部署(推荐,最简单)⭐ -- Go 1.21 或更高版本 -- Node.js 18+ 和 npm/yarn -- MySQL 8.0 或更高版本 +> **最简单快捷的方式**,直接使用预构建的 Docker 镜像,无需构建,一行命令启动。 -### 1. 克隆项目 +#### 前置要求 + +- Docker Desktop(Windows/Mac)或 Docker + Docker Compose(Linux) + +#### 部署步骤 + +1. **克隆项目并进入目录** ```bash git clone https://github.com/2930134478/AI-CS.git cd AI-CS ``` -### 2. 配置后端 +2. **配置环境变量** + +```bash +# 复制环境变量模板 +cp .env.example .env + +# 编辑 .env 文件,至少修改以下配置: +# - MYSQL_ROOT_PASSWORD: MySQL root 密码 +# - ADMIN_PASSWORD: 管理员密码(首次登录使用) +# - ENCRYPTION_KEY: 加密密钥(生成 64 位十六进制字符串) +``` + +生成加密密钥: + +```bash +# Linux/Mac +openssl rand -hex 32 + +# Windows PowerShell +-join ((48..57) + (97..102) | Get-Random -Count 64 | ForEach-Object {[char]$_}) +``` + +3. **一键启动** + +```bash +# 使用预构建镜像启动(自动从 Docker Hub 拉取镜像) +docker-compose -f docker-compose.prod.yml up -d +``` + +就这么简单!🎉 + +4. **访问应用** + +- **前端首页**: http://localhost:3000 +- **访客聊天**: http://localhost:3000/chat +- **客服登录**: http://localhost:3000/agent/login + - 用户名:`admin`(或 `.env` 中配置的 `ADMIN_USERNAME`) + - 密码:`.env` 中配置的 `ADMIN_PASSWORD` + +#### 常用命令 + +```bash +# 查看日志 +docker-compose -f docker-compose.prod.yml logs -f + +# 查看服务状态 +docker-compose -f docker-compose.prod.yml ps + +# 停止服务 +docker-compose -f docker-compose.prod.yml stop + +# 停止并删除容器(保留数据) +docker-compose -f docker-compose.prod.yml down + +# 完全重置(删除所有数据) +docker-compose -f docker-compose.prod.yml down -v +``` + +--- + +### 方式二:Docker 本地构建部署 + +> 适合需要自定义构建或网络无法访问 Docker Hub 的情况。 + +#### 前置要求 + +- Docker Desktop(Windows/Mac)或 Docker + Docker Compose(Linux) +- Git + +#### 部署步骤 + +1. **克隆项目** + +```bash +git clone https://github.com/2930134478/AI-CS.git +cd AI-CS +``` + +2. **配置环境变量** + +```bash +# 复制环境变量模板 +cp .env.example .env + +# 编辑 .env 文件,至少修改以下配置: +# - MYSQL_ROOT_PASSWORD: MySQL root 密码 +# - ADMIN_PASSWORD: 管理员密码(首次登录使用) +# - ENCRYPTION_KEY: 加密密钥(生成 64 位十六进制字符串) +``` + +生成加密密钥: + +```bash +# Linux/Mac +openssl rand -hex 32 + +# Windows PowerShell +-join ((48..57) + (97..102) | Get-Random -Count 64 | ForEach-Object {[char]$_}) +``` + +3. **构建并启动服务** + +```bash +# 构建并启动所有服务(首次构建需要一些时间) +docker-compose up -d --build + +# 查看日志 +docker-compose logs -f + +# 查看服务状态 +docker-compose ps +``` + +4. **访问应用** + +- **前端首页**: http://localhost:3000 +- **访客聊天**: http://localhost:3000/chat +- **客服登录**: http://localhost:3000/agent/login + - 用户名:`admin`(或 `.env` 中配置的 `ADMIN_USERNAME`) + - 密码:`.env` 中配置的 `ADMIN_PASSWORD` + +#### 常用命令 + +```bash +# 停止服务 +docker-compose stop + +# 停止并删除容器(保留数据) +docker-compose down + +# 完全重置(删除所有数据) +docker-compose down -v + +# 查看日志 +docker-compose logs -f backend +docker-compose logs -f frontend +``` + +--- + +### 方式三:传统部署(手动安装) + +#### 环境要求 + +- Go 1.24 或更高版本 +- Node.js 18+ 和 npm/yarn +- MySQL 8.0 或更高版本 + +#### 1. 克隆项目 + +```bash +git clone https://github.com/2930134478/AI-CS.git +cd AI-CS +``` + +#### 2. 配置后端 ```bash cd backend @@ -73,7 +232,7 @@ go run main.go > ⚠️ **重要**:`ADMIN_PASSWORD` 是必填项,如果不设置,系统不会创建默认管理员账号。 -### 3. 配置前端 +#### 3. 配置前端 ```bash cd frontend @@ -85,7 +244,7 @@ npm install npm run dev ``` -### 4. 访问应用 +#### 4. 访问应用 - **官网首页**: http://localhost:3000 - **访客聊天**: @@ -93,7 +252,7 @@ npm run dev - 或点击首页右下角的客服插件按钮 - **客服登录**: http://localhost:3000/agent/login -### 5. 默认管理员账号 +#### 5. 默认管理员账号 ⚠️ **重要说明**: diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..b26ab1f --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,35 @@ +# Git +.git +.gitignore + +# 编译产物 +backend.exe +backend +*.exe +*.dll +*.so + +# 环境变量 +.env +.env.local + +# 上传文件(保留目录结构但不包含文件) +uploads/* +!uploads/.gitkeep + +# 日志 +*.log + +# IDE +.vscode +.idea +*.swp + +# 系统文件 +.DS_Store +Thumbs.db + +# 其他 +node_modules +package.json +package-lock.json \ No newline at end of file diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..91c9aa7 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,52 @@ +# ============================================ +# 多阶段构建:构建阶段 +# ============================================ +FROM golang:1.24-alpine AS builder + +WORKDIR /app + +# 安装必要的构建工具 +RUN apk add --no-cache git ca-certificates tzdata + +# 复制 go mod 文件 +COPY go.mod go.sum ./ + +# 下载依赖 +RUN go mod download + +# 复制源代码 +COPY . . + +# 构建应用 +# CGO_ENABLED=0: 禁用 CGO,生成纯 Go 二进制文件 +# GOOS=linux: 目标操作系统 +# GOARCH=amd64: 目标架构 +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -o backend main.go + +# ============================================ +# 运行阶段 +# ============================================ +FROM alpine:latest + +WORKDIR /app + +# 安装必要的运行时依赖 +RUN apk --no-cache add ca-certificates tzdata + +# 从构建阶段复制二进制文件 +COPY --from=builder /app/backend . + +# 从构建阶段复制时区数据(可选) +COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo + +# 设置时区(可选,默认 UTC) +ENV TZ=Asia/Shanghai + +# 创建必要的目录 +RUN mkdir -p uploads/avatars uploads/messages + +# 暴露端口 +EXPOSE 8080 + +# 启动应用 +CMD ["./backend"] \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..5ed9e98 --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,75 @@ + + +services: + # MySQL 数据库 + mysql: + image: mysql:8.0 + container_name: ai-cs-mysql + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword} + MYSQL_DATABASE: ${MYSQL_DATABASE:-ai_cs} + MYSQL_USER: ${MYSQL_USER:-ai_cs_user} + MYSQL_PASSWORD: ${MYSQL_PASSWORD:-ai_cs_password} + ports: + - "${MYSQL_PORT:-3306}:3306" + volumes: + - mysql_data:/var/lib/mysql + command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-rootpassword}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + networks: + - ai-cs-network + restart: unless-stopped + + # 后端服务(使用预构建镜像) + backend: + image: ${BACKEND_IMAGE:-537yaha/ai-cs-backend:latest} + container_name: ai-cs-backend + environment: + DB_HOST: mysql + DB_PORT: 3306 + DB_USER: ${MYSQL_USER:-ai_cs_user} + DB_PASSWORD: ${MYSQL_PASSWORD:-ai_cs_password} + DB_NAME: ${MYSQL_DATABASE:-ai_cs} + ADMIN_USERNAME: ${ADMIN_USERNAME:-admin} + ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin123} + SERVER_HOST: 0.0.0.0 + SERVER_PORT: 8080 + GIN_MODE: ${GIN_MODE:-release} + ENCRYPTION_KEY: ${ENCRYPTION_KEY:-default-key} + ports: + - "${BACKEND_PORT:-8080}:8080" + volumes: + - ./backend/uploads:/app/uploads + depends_on: + mysql: + condition: service_healthy + networks: + - ai-cs-network + restart: unless-stopped + + # 前端服务(使用预构建镜像) + frontend: + image: ${FRONTEND_IMAGE:-537yaha/ai-cs-frontend:latest} + container_name: ai-cs-frontend + environment: + NEXT_PUBLIC_API_BASE_URL: ${NEXT_PUBLIC_API_BASE_URL:-http://localhost:8080} + ports: + - "${FRONTEND_PORT:-3000}:3000" + depends_on: + - backend + networks: + - ai-cs-network + restart: unless-stopped + +volumes: + mysql_data: + driver: local + +networks: + ai-cs-network: + driver: bridge \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..35f5d4e --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,79 @@ + + +services: + # MySQL 数据库 + mysql: + image: mysql:8.0 + container_name: ai-cs-mysql + environment: + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-rootpassword} + MYSQL_DATABASE: ${MYSQL_DATABASE:-ai_cs} + MYSQL_USER: ${MYSQL_USER:-ai_cs_user} + MYSQL_PASSWORD: ${MYSQL_PASSWORD:-ai_cs_password} + ports: + - "${MYSQL_PORT:-3306}:3306" + volumes: + - mysql_data:/var/lib/mysql + command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD:-rootpassword}"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + networks: + - ai-cs-network + restart: unless-stopped + + # 后端服务(本地构建) + backend: + build: + context: ./backend + dockerfile: Dockerfile + container_name: ai-cs-backend + environment: + DB_HOST: mysql + DB_PORT: 3306 + DB_USER: ${MYSQL_USER:-ai_cs_user} + DB_PASSWORD: ${MYSQL_PASSWORD:-ai_cs_password} + DB_NAME: ${MYSQL_DATABASE:-ai_cs} + ADMIN_USERNAME: ${ADMIN_USERNAME:-admin} + ADMIN_PASSWORD: ${ADMIN_PASSWORD:-admin123} + SERVER_HOST: 0.0.0.0 + SERVER_PORT: 8080 + GIN_MODE: ${GIN_MODE:-release} + ENCRYPTION_KEY: ${ENCRYPTION_KEY:-default-key} + ports: + - "${BACKEND_PORT:-8080}:8080" + volumes: + - ./backend/uploads:/app/uploads + depends_on: + mysql: + condition: service_healthy + networks: + - ai-cs-network + restart: unless-stopped + + # 前端服务(本地构建) + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + args: + NEXT_PUBLIC_API_BASE_URL: ${NEXT_PUBLIC_API_BASE_URL:-http://localhost:8080} + container_name: ai-cs-frontend + ports: + - "${FRONTEND_PORT:-3000}:3000" + depends_on: + - backend + networks: + - ai-cs-network + restart: unless-stopped + +volumes: + mysql_data: + driver: local + +networks: + ai-cs-network: + driver: bridge \ No newline at end of file diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..577957a --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,42 @@ +# Git +.git +.gitignore + +# 环境变量 +.env +.env.local +.env.production +.env.development + +# 构建产物 +.next +dist +out +build + +# 依赖 +node_modules + +# 日志 +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# IDE +.vscode +.idea +*.swp +*.swo + +# 测试 +coverage +.nyc_output + +# 系统文件 +.DS_Store +Thumbs.db + +# 其他 +*.tsbuildinfo +.eslintcache \ No newline at end of file diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..2ebb084 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,50 @@ +# ============================================ +# 多阶段构建:构建阶段 +# ============================================ +FROM node:18-alpine AS builder + +WORKDIR /app + +# 复制 package 文件 +COPY package*.json ./ + +# 安装依赖(使用 npm ci 确保依赖版本一致) +RUN npm ci + +# 复制源代码 +COPY . . + +# 构建参数(Next.js 环境变量必须在构建时传入) +ARG NEXT_PUBLIC_API_BASE_URL +ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL + +# 构建 Next.js 应用 +RUN npm run build + +# ============================================ +# 运行阶段 +# ============================================ +FROM node:18-alpine + +WORKDIR /app + +# 设置生产环境 +ENV NODE_ENV=production + +# 复制 package 文件(用于安装生产依赖) +COPY package*.json ./ + +# 只安装生产依赖 +RUN npm ci --only=production && npm cache clean --force + +# 从构建阶段复制必要的文件 +COPY --from=builder /app/next.config.ts ./ +COPY --from=builder /app/tsconfig.json ./ +COPY --from=builder /app/public ./public +COPY --from=builder /app/.next ./.next + +# 暴露端口 +EXPOSE 3000 + +# 启动 Next.js 生产服务器 +CMD ["npm", "start"] \ No newline at end of file diff --git a/frontend/README-配置说明.md b/frontend/README-配置说明.md deleted file mode 100644 index 464a3d5..0000000 --- a/frontend/README-配置说明.md +++ /dev/null @@ -1,65 +0,0 @@ -# 前端配置说明 - -## 真实设备测试配置 - -### 步骤 1:创建 `.env.local` 文件 - -在 `frontend` 目录下创建 `.env.local` 文件(如果不存在)。 - -### 步骤 2:配置 IP 地址 - -在 `.env.local` 文件中添加以下内容: - -```env -# 将 192.168.124.9 替换为你的电脑 IP 地址 -# 获取 IP 地址:在 PowerShell 中运行 ipconfig -NEXT_PUBLIC_API_BASE_URL=http://192.168.124.9:8080 -``` - -**你的 IP 地址**:根据 `ipconfig` 结果,你的 IP 地址是 **192.168.124.9** - -### 步骤 3:重启前端服务 - -```bash -# 停止当前服务(Ctrl+C) -# 重新启动 -npm run dev -``` - -### 步骤 4:在手机/平板上访问 - -1. 确保手机/平板连接**同一 WiFi** -2. 在手机浏览器输入:`http://192.168.124.9:3000` - ---- - -## 本地开发配置(默认) - -如果只想在本地开发,不需要配置 `.env.local` 文件,使用默认配置即可: - -- 前端:`http://localhost:3000` -- 后端:`http://127.0.0.1:8080` - ---- - -## 注意事项 - -1. ✅ **防火墙**:确保 Windows 防火墙允许端口 3000 和 8080 -2. ✅ **同一网络**:手机和电脑必须在同一 WiFi 网络 -3. ✅ **后端配置**:后端已配置为监听 `0.0.0.0:8080`,允许外部访问 - ---- - -## 快速配置 - -**Windows PowerShell**: - -```powershell -# 在 frontend 目录下运行 -@" -NEXT_PUBLIC_API_BASE_URL=http://192.168.124.9:8080 -"@ | Out-File -FilePath ".env.local" -Encoding utf8 -``` - -(记得将 `192.168.124.9` 替换为你的实际 IP 地址) - diff --git a/frontend/README.md b/frontend/README.md deleted file mode 100644 index e215bc4..0000000 --- a/frontend/README.md +++ /dev/null @@ -1,36 +0,0 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 916e913..f50a1de 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; +import MatomoTracker from "@/components/MatomoTracker"; const geistSans = Geist({ variable: "--font-geist-sans", @@ -17,6 +18,9 @@ export const metadata: Metadata = { description: "融合 AI 技术与人工客服,为企业提供高效、智能的客户服务解决方案", }; +// Matomo 容器 URL(格式:container_*.js) +const MATOMO_CONTAINER_URL = process.env.NEXT_PUBLIC_MATOMO_CONTAINER_URL || ''; + export default function RootLayout({ children, }: Readonly<{ @@ -28,6 +32,7 @@ export default function RootLayout({ className={`${geistSans.variable} ${geistMono.variable} antialiased`} > {children} + {MATOMO_CONTAINER_URL && } ); diff --git a/frontend/components/MatomoTracker.tsx b/frontend/components/MatomoTracker.tsx new file mode 100644 index 0000000..b1d19c0 --- /dev/null +++ b/frontend/components/MatomoTracker.tsx @@ -0,0 +1,33 @@ +'use client'; + +import { useEffect } from 'react'; + +interface MatomoTrackerProps { + containerUrl?: string; +} + +export default function MatomoTracker({ containerUrl }: MatomoTrackerProps) { + useEffect(() => { + if (!containerUrl) { + console.warn('Matomo 容器 URL 未配置'); + return; + } + + // 初始化 Matomo Tag Manager + const _mtm = (window._mtm = window._mtm || []); + _mtm.push({ 'mtm.startTime': new Date().getTime(), event: 'mtm.Start' }); + + const d = document; + const g = d.createElement('script'); + const s = d.getElementsByTagName('script')[0]; + + g.async = true; + g.src = containerUrl; + if (s.parentNode) { + s.parentNode.insertBefore(g, s); + } + }, [containerUrl]); + + return null; +} +