Add Docker deployment support and GitHub Actions

This commit is contained in:
537yaha
2026-01-04 19:26:20 +08:00
parent fcfe5aa417
commit 09c67e8158
14 changed files with 626 additions and 110 deletions
+26
View File
@@ -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
+45
View File
@@ -0,0 +1,45 @@
# ============================================
# AI-CS 系统 Docker 部署配置模板
# ============================================
# 使用方法:
# 1. 复制此文件为 .envcp .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
+16
View File
@@ -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
+168 -9
View File
@@ -24,20 +24,179 @@
## 🚀 快速开始 ## 🚀 快速开始
### 环境要求 ### 方式一:预构建镜像一键部署(推荐,最简单)⭐
- Go 1.21 或更高版本 > **最简单快捷的方式**,直接使用预构建的 Docker 镜像,无需构建,一行命令启动。
- Node.js 18+ 和 npm/yarn
- MySQL 8.0 或更高版本
### 1. 克隆项目 #### 前置要求
- Docker DesktopWindows/Mac)或 Docker + Docker ComposeLinux
#### 部署步骤
1. **克隆项目并进入目录**
```bash ```bash
git clone https://github.com/2930134478/AI-CS.git git clone https://github.com/2930134478/AI-CS.git
cd AI-CS 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 DesktopWindows/Mac)或 Docker + Docker ComposeLinux
- 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 ```bash
cd backend cd backend
@@ -73,7 +232,7 @@ go run main.go
> ⚠️ **重要**`ADMIN_PASSWORD` 是必填项,如果不设置,系统不会创建默认管理员账号。 > ⚠️ **重要**`ADMIN_PASSWORD` 是必填项,如果不设置,系统不会创建默认管理员账号。
### 3. 配置前端 #### 3. 配置前端
```bash ```bash
cd frontend cd frontend
@@ -85,7 +244,7 @@ npm install
npm run dev npm run dev
``` ```
### 4. 访问应用 #### 4. 访问应用
- **官网首页**: http://localhost:3000 - **官网首页**: http://localhost:3000
- **访客聊天**: - **访客聊天**:
@@ -93,7 +252,7 @@ npm run dev
- 或点击首页右下角的客服插件按钮 - 或点击首页右下角的客服插件按钮
- **客服登录**: http://localhost:3000/agent/login - **客服登录**: http://localhost:3000/agent/login
### 5. 默认管理员账号 #### 5. 默认管理员账号
⚠️ **重要说明** ⚠️ **重要说明**
+35
View File
@@ -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
+52
View File
@@ -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"]
+75
View File
@@ -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
+79
View File
@@ -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
+42
View File
@@ -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
+50
View File
@@ -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"]
-65
View File
@@ -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 地址)
-36
View File
@@ -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.
+5
View File
@@ -1,6 +1,7 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google"; import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css"; import "./globals.css";
import MatomoTracker from "@/components/MatomoTracker";
const geistSans = Geist({ const geistSans = Geist({
variable: "--font-geist-sans", variable: "--font-geist-sans",
@@ -17,6 +18,9 @@ export const metadata: Metadata = {
description: "融合 AI 技术与人工客服,为企业提供高效、智能的客户服务解决方案", description: "融合 AI 技术与人工客服,为企业提供高效、智能的客户服务解决方案",
}; };
// Matomo 容器 URL(格式:container_*.js
const MATOMO_CONTAINER_URL = process.env.NEXT_PUBLIC_MATOMO_CONTAINER_URL || '';
export default function RootLayout({ export default function RootLayout({
children, children,
}: Readonly<{ }: Readonly<{
@@ -28,6 +32,7 @@ export default function RootLayout({
className={`${geistSans.variable} ${geistMono.variable} antialiased`} className={`${geistSans.variable} ${geistMono.variable} antialiased`}
> >
{children} {children}
{MATOMO_CONTAINER_URL && <MatomoTracker containerUrl={MATOMO_CONTAINER_URL} />}
</body> </body>
</html> </html>
); );
+33
View File
@@ -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;
}