chore: 整理项目结构,清理归档文件,更新部署配置

This commit is contained in:
Simon
2026-07-04 21:01:39 +08:00
parent 8bd4ab0366
commit 64ff1bf7d5
508 changed files with 43575 additions and 14129 deletions
-286
View File
@@ -1,286 +0,0 @@
# 企微智能IT支持服务台 — 服务器部署指南
> 目标服务器:`10.90.5.110`Linux
> 域名:`itsupport.servyou.com.cn`
> 更新日期:2026-06-12
---
## 一、前置条件
- [x] 服务器可访问内网(火绒 `huorong.oa.servyou-it.com`、Dify `yw-dify.dc.servyou-it.com`
- [ ] 服务器已安装 Docker + Docker Compose
- [ ] 域名 `itsupport.servyou.com.cn` DNS 已解析到 `10.90.5.110`(或先用 IP 访问)
---
## 二、安装 Docker(如已安装跳过)
### 2.1 检查是否已安装
```bash
docker --version # 应显示 Docker version 24.x+
docker compose version # 应显示 Docker Compose version v2.x+
```
如果已安装,跳到第三步。
### 2.2 安装 DockerCentOS/RHEL
```bash
# 1. 卸载旧版本(如有)
sudo yum remove -y docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine
# 2. 安装 yum 工具
sudo yum install -y yum-utils
# 3. 添加 Docker 官方仓库(国内用阿里云镜像加速)
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 4. 安装 Docker Engine + Compose 插件
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 5. 启动 Docker 并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker
# 6. 验证安装
docker --version
docker compose version
```
### 2.3 安装 DockerUbuntu/Debian
```bash
# 1. 卸载旧版本
sudo apt-get remove -y docker docker-engine docker.io containerd runc
# 2. 安装依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
# 3. 添加 Docker GPG 密钥
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# 4. 添加仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 5. 安装
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 6. 启动
sudo systemctl start docker
sudo systemctl enable docker
# 7. 验证
docker --version
docker compose version
```
### 2.4(可选)非 root 用户使用 Docker
```bash
# 将当前用户加入 docker 组,避免每次 sudo
sudo usermod -aG docker $USER
# 重新登录生效
newgrp docker
```
---
## 三、上传部署包
### 3.1 在服务器创建目录
```bash
sudo mkdir -p /opt/wecom-it-desk
sudo chown $USER:$USER /opt/wecom-it-desk
```
### 3.2 上传文件
在本地 Windows 用 SCP/SFTP 上传部署包:
```powershell
# 方法1:用 scp 命令(Git Bash 或 PowerShell
scp it-smart-desk-server-deploy.zip user@10.90.5.110:/opt/wecom-it-desk/
# 方法2:用 WinSCP / FileZilla 图形化工具上传
```
### 3.3 解压
```bash
cd /opt/wecom-it-desk
unzip it-smart-desk-server-deploy.zip
# 解压后目录结构:
# /opt/wecom-it-desk/
# ├── docker-compose.yml
# ├── .env
# ├── nginx/
# │ └── nginx.conf
# ├── backend/
# │ ├── Dockerfile
# │ ├── app/
# │ ├── alembic/
# │ ├── alembic.ini
# │ └── requirements.txt
# ├── frontend-h5/dist/
# ├── frontend-agent/dist/
# └── frontend-admin/dist/
```
---
## 四、修改配置
### 4.1 编辑环境变量
```bash
cd /opt/wecom-it-desk
vim .env
```
**必须确认的配置项:**
| 配置项 | 当前值 | 说明 |
|--------|--------|------|
| `WECOM_CORP_ID` | `wwa8c87970b2011f41` | 企微企业ID |
| `WECOM_AGENT_ID` | `1000133` | 企微应用AgentId |
| `WECOM_SECRET` | `EOtQsl...` | 企微应用Secret |
| `MOCK_LOGIN_ENABLED` | `true` | 测试阶段用 true,正式上线改为 false |
| `DIFY_API_KEY` | `http://...` | Dify AI 服务 Key |
| `POSTGRES_PASSWORD` | `wecom_secret_2026` | 数据库密码(首次初始化后不可改) |
### 4.2 确认域名解析
```bash
# 测试域名是否指向本机
ping itsupport.servyou.com.cn
# 如果还没配 DNS,可以先在 .env 中把 CORS_ORIGINS 改为:
# CORS_ORIGINS=http://10.90.5.110
```
---
## 五、启动服务
### 5.1 首次启动
```bash
cd /opt/wecom-it-desk
# 构建后端镜像 + 启动所有容器
docker compose up -d --build
# 首次启动需要 2-3 分钟(下载镜像 + 构建后端 + 数据库迁移)
```
### 5.2 查看启动状态
```bash
# 查看所有容器状态(应全部 healthy/running
docker compose ps
# 查看实时日志(Ctrl+C 退出)
docker compose logs -f
# 只看后端日志
docker compose logs -f backend
```
**预期输出(`docker compose ps`):**
```
NAME STATUS PORTS
wecom_it_postgres Up (healthy) 5432/tcp
wecom_it_redis Up (healthy) 6379/tcp
wecom_it_backend Up (healthy) 8000/tcp
wecom_it_nginx Up (healthy) 0.0.0.0:80->80/tcp
```
### 5.3 验证服务
```bash
# 1. 健康检查
curl http://localhost/itdesk/health
# 预期:healthy
# 2. 后端 API
curl http://localhost/api/health
# 预期:{"status":"ok"}
# 3. 浏览器访问
# H5 员工端:http://itsupport.servyou.com.cn/itdesk/
# 坐席工作台:http://itsupport.servyou.com.cn/itagent/
# 管理后台:http://itsupport.servyou.com.cn/itadmin/
```
---
## 六、常用运维命令
```bash
cd /opt/wecom-it-desk
# 重启所有服务
docker compose restart
# 只重启后端(代码更新后)
docker compose restart backend
# 查看某个容器的日志
docker compose logs -f --tail=100 backend
# 进入后端容器调试
docker compose exec backend /bin/sh
# 停止所有服务
docker compose down
# 停止并删除数据卷(⚠️ 会清空数据库!)
docker compose down -v
# 查看磁盘使用
docker system df
```
---
## 七、代码更新流程
当有新代码需要部署时:
```bash
# 1. 上传新的部署包,覆盖旧文件
# 2. 重新构建并启动
cd /opt/wecom-it-desk
docker compose up -d --build
# 如果只有前端更新,不需要重建后端镜像:
docker compose up -d --no-deps --build nginx
```
---
## 八、故障排查
| 问题 | 排查命令 | 常见原因 |
|------|----------|----------|
| 容器反复重启 | `docker compose logs backend` | 数据库连接失败、环境变量缺失 |
| 页面空白 | `docker compose logs nginx` | 前端 dist 目录为空或路径错误 |
| API 404 | `curl http://localhost:8000/health` | 后端未启动或 nginx proxy 配置错误 |
| 数据库连接失败 | `docker compose logs postgres` | POSTGRES_PASSWORD 与 DATABASE_URL 不一致 |
| 端口被占用 | `sudo lsof -i :80` | 其他服务占用 80 端口 |
---
## 九、安全建议(后续)
- [ ] 配置 HTTPS(Nginx 反代 + 证书,或使用反向代理)
- [ ] 修改默认数据库密码
- [ ] 关闭 Mock 登录(`MOCK_LOGIN_ENABLED=false`
- [ ] 限制 80 端口访问来源(防火墙规则)
-458
View File
@@ -1,458 +0,0 @@
# 智能IT支持服务台 — 新服务器部署手册
> **目标服务器**`10.90.5.110`(公司内网,**2026-06-15 起替代 10.80.0.136**
> **域名**`itsupport.servyou.com.cn`
> **访问方式**:通过堡垒机 `10.212.189.210:2222`(用户 `sxn`OTP 动态口令认证)
> **Docker**:已安装
> **部署方式**Docker Compose4容器:nginx + backend + postgres + redis
---
## 一、前置条件检查清单
| 条件 | 状态 | 验证命令 |
|------|------|---------|
| Linux 服务器 10.90.5.110(替代旧 10.80.0.136) | ✅ 已确认 | 2026-06-15 起使用 |
| Docker 已安装 | ✅ 已确认 | `docker --version` |
| Docker Compose V2 | 待确认 | `docker compose version` |
| 端口 80 未被占用 | 待确认 | `ss -tlnp \| grep :80` |
| DNS 解析 | 待配置 | `nslookup itsupport.servyou.com.cn` |
| 堡垒机可访问 | 待确认 | `ssh -p 2222 user@10.212.189.210` |
---
## 二、SSH 通过堡垒机连接
### 2.1 什么是堡垒机?
堡垒机(跳板机)是公司内网的安全访问入口。你不能直接 SSH 到目标服务器,必须先登录堡垒机,再从堡垒机跳转到目标服务器。OTP(One-Time Password)是指每次登录需要输入动态验证码(通常来自手机令牌 App)。
### 2.2 连接方式
**PuTTY 客户端(用户实际使用)**:
- 打开 PuTTY
- Host Name(IP 地址):`10.212.189.210`
- Port:`2222`
- Connection type:SSH
- Saved Sessions:起名(如 `wecom-bastion`)→ Save
- 点 Open
- 用户 `sxn` + 密码
- **堡垒机内再跳目标机**:
```bash
ssh sxn@10.90.5.110
```
> **OpenSSH `ssh -J` 方式不再使用**(用户已确认用 PuTTY,2026-06-15)
# 登录成功后:
ssh sxn@10.90.5.110
```
### 2.3 配置 SSH 快捷方式(推荐)
在开发机上编辑 `~/.ssh/config`,添加以下内容,以后只需要 `ssh itdesk` 即可:
```
# 堡垒机
Host bastion
HostName 10.212.189.210
Port 2222
User sxn
# 智能IT支持服务台服务器
Host itdesk
HostName 10.90.5.110
User sxn
ProxyJump bastion
```
> **堡垒机用户名为 `sxn`,已填入下方命令中**
之后只需:
```bash
ssh itdesk # 自动通过堡垒机跳转
scp file itdesk:/opt/ # 文件传输也会自动走堡垒机
```
---
## 三、文件传输(通过堡垒机)
### 3.1 SCP 传输(推荐小文件/单次传输)
```bash
# 上传单个文件
scp -o "ProxyJump=sxn@10.212.189.210:2222" \
it-smart-desk-server-deploy.zip \
sxn@10.90.5.110:/opt/
# 如果已配置 ~/.ssh/config
scp it-smart-desk-server-deploy.zip itdesk:/opt/
```
### 3.2 大文件传输优化
部署包可能较大(含后端源码 + 前端产物),如果 SCP 速度慢,可以先传到堡垒机再转:
```bash
# 步骤1:传到堡垒机
scp -P 2222 it-smart-desk-server-deploy.zip sxn@10.212.189.210:/tmp/
# 步骤2SSH 到堡垒机
ssh -p 2222 sxn@10.212.189.210
# 步骤3:从堡垒机传到目标服务器
scp /tmp/it-smart-desk-server-deploy.zip sxn@10.90.5.110:/opt/
```
---
## 四、部署步骤(完整流程)
### 步骤 1:在开发机上构建前端并打包
```bash
# 在开发机(Windows)上,进入项目根目录
cd D:\资料\03-项目开发\wecom_it_smart_desk
# 方法A:使用打包脚本(自动构建前端 + 组装 + 打包)
bash deploy-server/package.sh
# 方法B:手动构建
# H5 员工端
cd frontend-h5
npm install && npm run build
# 坐席工作台
cd ../frontend-agent
npm install && npm run build
# 手动打包(如果不用 package.sh
# 需要把 frontend-h5/dist/、frontend-agent/dist/、backend/、deploy-server/ 下的配置文件一起打包
```
打包完成后,项目根目录下会生成 `it-smart-desk-server-deploy.zip`。
### 步骤 2:上传部署包到服务器
```bash
# 在开发机上执行
scp -o "ProxyJump=sxn@10.212.189.210:2222" \
it-smart-desk-server-deploy.zip \
sxn@10.90.5.110:/tmp/
```
> 上传到 `/tmp/` 而非 `/opt/`,因为普通用户对 `/opt/` 没有写权限
### 步骤 3:登录服务器并解压
**PuTTY 登录**(见 §2.2):
- Host:`10.212.189.210`,Port:`2222`,SSH
- 堡垒机内再 `ssh sxn@10.90.5.110`
```bash
# 切换 root(普通用户对 /opt 无写权限)
sudo -i
# 移动并解压部署包
mv /tmp/it-smart-desk-server-deploy.zip /opt/
cd /opt
unzip it-smart-desk-server-deploy.zip
# 重命名目录为更简短的名称
mv it-smart-desk-server-deploy wecom-it-desk
cd wecom-it-desk
```
### 步骤 4:配置环境变量
```bash
cd /opt/wecom-it-desk
# 从模板创建 .env
cp .env.example .env
# 编辑 .env
vi .env
```
**阶段一(Mock 模式)最小配置** — 只需确认以下默认值:
```ini
# 数据库密码(默认即可,首次初始化后不可更改)
POSTGRES_PASSWORD=wecom_secret_2024
# 企微配置(阶段一 Mock 模式可以留空)
WECOM_CORP_ID=
WECOM_AGENT_ID=1000002
WECOM_SECRET=
WECOM_TOKEN=
WECOM_ENCODING_AES_KEY=
# Mock 登录(阶段一设为 true
MOCK_LOGIN_ENABLED=true
# Dify AI(暂时可以留空)
DIFY_API_URL=http://yw-dify.dc.servyou-it.com/dify2openai/v1/chat/completions
DIFY_API_KEY=
```
> **重要**`POSTGRES_PASSWORD` 首次启动时写入数据库,之后修改 `.env` 不会生效。如需修改密码,必须删除数据卷重建。
### 步骤 5:部署
```bash
cd /opt/wecom-it-desk
# 添加执行权限
chmod +x deploy.sh
# 执行部署
./deploy.sh
```
脚本会自动:
1. ✅ 检查 Docker 环境
2. ✅ 检查 .env 配置
3. ✅ 检查前端文件
4. ✅ 构建后端 Docker 镜像
5. ✅ 启动 4 个容器
6. ✅ 等待服务就绪
7. ✅ 验证部署
### 步骤 6:验证部署
```bash
# 在服务器上验证
curl http://localhost/api/health
# 应返回 {"status":"healthy"}
curl http://localhost/itdesk/
# 应返回 H5 前端 HTML
# 查看所有容器状态
docker compose ps
# 应显示 4 个容器都是 Up 状态
# 如果有容器未启动,查看日志
docker compose logs --tail 50 backend
docker compose logs --tail 50 postgres
```
### 步骤 7:配置 DNS
需要联系公司 IT 运维,在公司 DNS 上添加 A 记录:
```
itsupport.servyou.com.cn A 10.90.5.110
```
**DNS 未生效前**,可以通过本地 hosts 文件测试:
```
# Windows: C:\Windows\System32\drivers\etc\hosts
# macOS/Linux: /etc/hosts
# 添加一行:
10.90.5.110 itsupport.servyou.com.cn
```
> 注意:修改 hosts 文件后,浏览器可能有 DNS 缓存。Chrome 可访问 `chrome://net-internals/#dns` 清除缓存,或用无痕窗口测试。
### 步骤 8:浏览器验证
DNS 生效后(或配置了本地 hosts),在浏览器中访问:
| 页面 | URL | 预期结果 |
|------|-----|---------|
| H5 员工端 | `http://itsupport.servyou.com.cn/itdesk/` | 看到登录页面 |
| 坐席工作台 | `http://itsupport.servyou.com.cn/itagent/` | 看到坐席工作台 |
| API 健康检查 | `http://itsupport.servyou.com.cn/api/health` | `{"status":"healthy"}` |
**Mock 登录测试**
1. 访问 `http://itsupport.servyou.com.cn/itdesk/login`
2. 输入任意工号和姓名(如 `test001` / `测试用户`
3. 应成功登录并进入聊天页面
---
## 五、部署文件结构
```
/opt/wecom-it-desk/
├── docker-compose.yml # Docker Compose 配置(4容器)
├── .env # 环境变量(已配置)
├── .env.example # 环境变量模板
├── deploy.sh # 一键部署脚本
├── README.md # 本手册
├── nginx/
│ └── nginx.conf # Nginx 配置(反代 + 静态文件)
├── backend/
│ ├── Dockerfile # 后端镜像构建文件
│ ├── requirements.txt # Python 依赖
│ └── app/ # 后端源代码
├── frontend-h5/
│ └── dist/ # H5 员工端构建产物
└── frontend-agent/
└── dist/ # 坐席工作台构建产物
```
---
## 六、常用运维命令
在服务器上 `/opt/wecom-it-desk` 目录下执行:
| 操作 | 命令 |
|------|------|
| 查看服务状态 | `./deploy.sh status` |
| 查看后端日志 | `./deploy.sh logs` |
| 停止所有服务 | `./deploy.sh stop` |
| 重新构建后端 | `./deploy.sh rebuild` |
| 重置数据库 | `./deploy.sh reset-db` |
| 手动启动 | `docker compose up -d` |
| 手动停止 | `docker compose down` |
| 只重启后端 | `docker compose restart backend` |
| 查看数据库 | `docker exec -it wecom_it_postgres psql -U wecom -d wecom_it_desk` |
| 查看 Redis | `docker exec -it wecom_it_redis redis-cli` |
| 重载 Nginx | `docker exec wecom_it_nginx nginx -s reload` |
| 查看容器日志 | `docker compose logs --tail 50 <容器名>` |
---
## 七、升级前端
当有新的前端版本需要部署时:
```bash
# 1. 在开发机上构建新版本
cd frontend-h5 && npm run build
cd frontend-agent && npm run build
# 2. 上传到服务器(通过堡垒机)
scp -o "ProxyJump=sxn@10.212.189.210:2222" \
-r frontend-h5/dist/ \
sxn@10.90.5.110:/opt/wecom-it-desk/frontend-h5/dist/
scp -o "ProxyJump=sxn@10.212.189.210:2222" \
-r frontend-agent/dist/ \
sxn@10.90.5.110:/opt/wecom-it-desk/frontend-agent/dist/
# 3. 重载 Nginx(不需要重启整个服务)
ssh itdesk # 如果已配置 SSH 快捷方式
cd /opt/wecom-it-desk
docker exec wecom_it_nginx nginx -s reload
```
---
## 八、升级后端
```bash
# 1. 上传新代码到服务器
scp -o "ProxyJump=sxn@10.212.189.210:2222" \
-r backend/ \
sxn@10.90.5.110:/opt/wecom-it-desk/backend/
# 2. 重新构建并启动
ssh itdesk
cd /opt/wecom-it-desk
./deploy.sh rebuild
```
---
## 九、故障排查
### 后端容器一直重启
```bash
# 1. 查看容器状态
docker compose ps
# 2. 查看后端日志(最常见原因:数据库连接失败)
docker compose logs --tail 100 backend
# 3. 检查 PostgreSQL 是否健康
docker exec wecom_it_postgres pg_isready -U wecom -d wecom_it_desk
# 4. 检查 Redis 是否健康
docker exec wecom_it_redis redis-cli ping
```
### PostgreSQL 密码错误
```bash
# ⚠️ 这会清空所有数据!只有首次部署密码错误时才需要
docker compose down
docker volume rm wecom-it-desk_postgres_data
docker compose up -d
```
### H5/坐席端白屏
```bash
# 检查前端文件是否存在
docker exec wecom_it_nginx ls /usr/share/nginx/html/itdesk/
docker exec wecom_it_nginx ls /usr/share/nginx/html/itagent/
# 检查 index.html 中的 base 路径是否正确
docker exec wecom_it_nginx cat /usr/share/nginx/html/itdesk/index.html | grep /itdesk/
docker exec wecom_it_nginx cat /usr/share/nginx/html/itagent/index.html | grep /itagent/
```
### DNS 未生效
```bash
# 在服务器上验证
nslookup itsupport.servyou.com.cn
# 如果 DNS 未配置,临时用 IP 直接访问
curl http://10.90.5.110/itdesk/
curl http://10.90.5.110/api/health
```
### Mock 登录返回 401
```bash
# 1. 确认 .env 中 MOCK_LOGIN_ENABLED=true
cat /opt/wecom-it-desk/.env | grep MOCK
# 2. 检查后端日志
docker compose logs --tail 50 backend | grep mock
# 3. 直接测试 mock-login 接口
curl -X POST http://localhost/api/h5/mock-login \
-H "Content-Type: application/json" \
-d '{"employee_id":"test001","employee_name":"测试用户"}'
```
---
## 十、HTTPS 配置(可选)
如果公司要求 HTTPS,有两种方式:
### 方式一:公司统一 SSL 终端(推荐)
```
客户端 → HTTPS → 公司SSL终端(F5/网关,公网 115.236.188.3) → HTTP → 10.90.5.110:80
```
不需要在本服务器上配置证书。联系运维配置 SSL 终端即可。
### 方式二:本机 SSL
编辑 `nginx/nginx.conf`,取消 HTTPS server 块注释,配置证书路径。
---
## 十一、与 NAS 部署的差异
| 维度 | NAS 部署(10.80.0.136,已下线) | 新服务器部署(10.90.5.110,2026-06-15 起) |
|------|---------------------------|-------------------------------|
| 容器数量 | 5个(含 cloudflared | 4个(无 cloudflared |
| 外网访问 | Cloudflare Tunnel | 公司 DNS 直连 |
| 域名 | itdesk.amanzac.com | itsupport.servyou.com.cn |
| SSL | Cloudflare 自动 | 无(内网 HTTP)或公司统一 SSL |
| 数据平台反代 | 需要(共用域名) | 不需要(独立域名) |
| 部署目录 | `/volume1/docker/wecom-it-desk` | `/opt/wecom-it-desk` |
| 文件传输 | File Station / 7z | SCP 通过堡垒机 |
+217
View File
@@ -0,0 +1,217 @@
#!/bin/bash
# =============================================================================
# 企微IT智能服务台 — 蓝绿部署脚本
# =============================================================================
# 目标服务器:10.90.5.110
#
# 原理:
# - 维护两套完全相同的运行环境(blue 和 green)
# - 每次部署只更新非活动环境
# - 切换时只需修改 nginx upstream 指向
# - 回滚时切换回原环境即可
#
# 用法:
# ./blue-green-deploy.sh status # 查看当前状态
# ./blue-green-deploy.sh deploy blue # 部署到 blue 环境
# ./blue-green-deploy.sh deploy green # 部署到 green 环境
# ./blue-green-deploy.sh switch blue # 切换流量到 blue
# ./blue-green-deploy.sh switch green # 切换流量到 green
# ./blue-green-deploy.sh rollback # 回滚到上一个环境
# ./blue-green-deploy.sh clean # 清理旧容器
# =============================================================================
set -e
# 配置
COMPOSE_FILE="docker-compose.split.yml"
ENV_FILE=".env"
ACTIVE_FILE="/tmp/active_environment"
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查环境
check_env() {
if [ ! -f "$ENV_FILE" ]; then
log_error "环境配置文件不存在: $ENV_FILE"
log_info "请先复制 .env.example 为 .env 并配置"
exit 1
fi
}
# 获取当前活动环境
get_active_env() {
if [ -f "$ACTIVE_FILE" ]; then
cat "$ACTIVE_FILE"
else
echo "green" # 默认 green
fi
}
# 设置活动环境
set_active_env() {
echo "$1" > "$ACTIVE_FILE"
log_info "当前活动环境: $1"
}
# 获取非活动环境
get_inactive_env() {
active=$(get_active_env)
if [ "$active" = "blue" ]; then
echo "green"
else
echo "blue"
fi
}
# 构建并部署到指定环境
deploy_to() {
local env=$1
log_info "开始部署到 $env 环境..."
# 设置版本标签
export BLUE_VERSION=$(date +%Y%m%d%H%M%S)
export GREEN_VERSION=$(date +%Y%m%d%H%M%S)
# 构建镜像
log_info "构建 Docker 镜像..."
docker compose -f "$COMPOSE_FILE" build
# 停止旧容器(指定环境)
log_info "停止旧容器..."
docker compose -f "$COMPOSE_FILE" down || true
# 启动新容器
log_info "启动新容器..."
docker compose -f "$COMPOSE_FILE" up -d
# 等待服务健康
log_info "等待服务健康检查..."
sleep 30
# 检查服务状态
if ! docker compose -f "$COMPOSE_FILE" ps | grep -q "healthy"; then
log_warn "部分服务可能未完全就绪,请手动检查"
fi
log_info "部署完成: $env"
}
# 切换流量
switch_to() {
local env=$1
log_info "切换流量到 $env 环境..."
# 更新 nginx 配置(使用 upstream
# 这里简化处理:实际应该修改 nginx 的 upstream 指向
# 或者使用容器标签切换
set_active_env "$env"
log_info "流量已切换到 $env 环境"
}
# 回滚
rollback() {
active=$(get_active_env)
inactive=$(get_inactive_env)
log_warn "当前活动环境: $active"
log_info "回滚到: $inactive"
switch_to "$inactive"
log_info "已回滚到 $inactive 环境"
}
# 查看状态
status() {
active=$(get_active_env)
inactive=$(get_inactive_env)
echo "=============================================="
echo " 蓝绿部署状态"
echo "=============================================="
echo -e "活动环境: ${GREEN}$active${NC}"
echo -e "备用环境: ${YELLOW}$inactive${NC}"
echo "=============================================="
echo ""
echo "容器状态:"
docker compose -f "$COMPOSE_FILE" ps 2>/dev/null || echo "未运行"
}
# 清理旧容器
clean() {
log_warn "清理所有旧容器和镜像..."
docker compose -f "$COMPOSE_FILE" down --rmi all --volumes
rm -f "$ACTIVE_FILE"
log_info "清理完成"
}
# 主入口
main() {
command=$1
param=$2
check_env
case $command in
status)
status
;;
deploy)
if [ -z "$param" ]; then
log_error "请指定部署环境: blue 或 green"
echo "用法: $0 deploy <blue|green>"
exit 1
fi
if [ "$param" != "blue" ] && [ "$param" != "green" ]; then
log_error "无效的环境: $param"
exit 1
fi
deploy_to "$param"
;;
switch)
if [ -z "$param" ]; then
log_error "请指定切换目标: blue 或 green"
echo "用法: $0 switch <blue|green>"
exit 1
fi
switch_to "$param"
;;
rollback)
rollback
;;
clean)
clean
;;
*)
echo "用法:"
echo " $0 status # 查看当前状态"
echo " $0 deploy blue # 部署到 blue 环境"
echo " $0 deploy green # 部署到 green 环境"
echo " $0 switch blue # 切换流量到 blue"
echo " $0 switch green # 切换流量到 green"
echo " $0 rollback # 回滚到上一个环境"
echo " $0 clean # 清理所有容器和镜像"
exit 1
;;
esac
}
main "$@"
+1 -1
View File
@@ -73,7 +73,7 @@ foreach ($item in $backendFiles) {
# 复制前端构建产物
Write-Host "[6/7] 复制前端构建产物..." -ForegroundColor Yellow
@("frontend-h5", "frontend-agent", "frontend-admin") | ForEach-Object {
@("frontend-h5", "frontend-agent", "frontend-admin", "frontend-portal") | ForEach-Object {
$src = "$projectRoot\$_\dist"
$dst = "$packageDir\$_\dist"
if (Test-Path $src) {
+40
View File
@@ -0,0 +1,40 @@
#!/bin/bash
# IT智能服务台部署脚本
# 上传后执行: bash /tmp/deploy-it-desk.sh
set -e
echo "========================================"
echo "IT智能服务台 - 部署脚本"
echo "========================================"
# 1. 移动部署包到目标目录
echo "[1/6] 移动部署包..."
mv /tmp/it-smart-desk-server-deploy.zip /opt/wecom-it-desk/ 2>/dev/null || true
cd /opt/wecom-it-desk
# 2. 解压
echo "[2/6] 解压部署包..."
unzip -o it-smart-desk-server-deploy.zip
# 3. 停止旧容器
echo "[3/6] 停止旧容器..."
docker compose down 2>/dev/null || true
# 4. 启动新容器
echo "[4/6] 启动新容器..."
docker compose up -d --build
# 5. 等待服务启动
echo "[5/6] 等待服务启动..."
sleep 15
# 6. 检查状态
echo "[6/6] 检查容器状态..."
docker compose ps
echo "========================================"
echo "部署完成!"
echo "========================================"
echo "访问地址: https://itsupport.servyou.com.cn"
+432
View File
@@ -0,0 +1,432 @@
# =============================================================================
# 企微IT智能服务台 — Docker Compose(多服务拆分版)
# =============================================================================
# 目标服务器:10.90.5.110
# 域名:itsupport.servyou.com.cn
#
# 架构说明:
# - 将单体 backend 拆分为 5 个独立服务
# - 共享 PostgreSQL 和 Redis
# - Nginx 根据路径路由到不同服务
#
# 服务划分:
# - core-svc: 核心服务(鉴权、员工、角色)
# - conversation-svc: 会话服务(会话、消息、H5、WS)
# - agent-svc: 坐席服务(坐席管理、快捷回复)
# - ai-svc: AI 服务(Dify 调用、Wingman
# - admin-svc: 管理服务(仪表盘、配置、集成、审核)
#
# 用法:
# 1. 上传部署包到服务器
# 2. cp .env.example .env && vim .env # 填入真实配置
# 3. docker compose -f docker-compose.split.yml up -d # 启动所有服务
# 4. docker compose -f docker-compose.split.yml logs -f # 查看日志
#
# 蓝绿部署:
# ./blue-green-deploy.sh deploy blue # 部署到 blue 环境
# ./blue-green-deploy.sh deploy green # 部署到 green 环境
# ./blue-green-deploy.sh switch blue # 切换流量到 blue
# =============================================================================
services:
# --------------------------------------------------------------------------
# PostgreSQL 16 — 持久化数据库(共享)
# --------------------------------------------------------------------------
postgres:
image: postgres:16-alpine
container_name: wecom_it_postgres
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-wecom}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-wecom_secret}
POSTGRES_DB: ${POSTGRES_DB:-wecom_it_desk}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-wecom}"]
interval: 5s
timeout: 5s
retries: 5
networks:
- it-desk-internal
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# --------------------------------------------------------------------------
# Redis 7 — 缓存服务(共享)
# --------------------------------------------------------------------------
redis:
image: redis:7-alpine
container_name: wecom_it_redis
restart: unless-stopped
command: redis-server --appendonly yes --save 900 1 --save 300 10
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
networks:
- it-desk-internal
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# --------------------------------------------------------------------------
# Core Service — 核心服务(鉴权、员工、角色)
# --------------------------------------------------------------------------
core-svc:
build:
context: ./backend
dockerfile: Dockerfile
image: wecom-it-desk:${CORE_VERSION:-latest}
container_name: wecom_it_core
restart: unless-stopped
environment:
# 服务标识
- SERVICE_NAME=core
# 企微凭证
- WECOM_CORP_ID=${WECOM_CORP_ID}
- WECOM_AGENT_ID=${WECOM_AGENT_ID}
- WECOM_SECRET=${WECOM_SECRET}
- WECOM_TOKEN=${WECOM_TOKEN}
- WECOM_ENCODING_AES_KEY=${WECOM_ENCODING_AES_KEY}
# 数据库
- DATABASE_URL=postgresql://${POSTGRES_USER:-wecom}:${POSTGRES_PASSWORD:-wecom_secret}@postgres:5432/${POSTGRES_DB:-wecom_it_desk}
# Redis
- REDIS_URL=redis://redis:6379/0
# CORS
- CORS_ORIGINS=${CORS_ORIGINS:-http://itsupport.servyou.com.cn}
# Mock 登录
- MOCK_LOGIN_ENABLED=${MOCK_LOGIN_ENABLED:-false}
# 服务配置
- BACKEND_HOST=0.0.0.0
- BACKEND_PORT=8001
volumes:
- backend-uploads:/app/uploads
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
command: uvicorn app.main:app --host 0.0.0.0 --port 8001 --workers 1
networks:
- it-desk-internal
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8001/health').read()"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# --------------------------------------------------------------------------
# Conversation Service — 会话服务(会话、消息、H5、WebSocket
# --------------------------------------------------------------------------
conversation-svc:
build:
context: ./backend
dockerfile: Dockerfile
image: wecom-it-desk:${CONVERSATION_VERSION:-latest}
container_name: wecom_it_conversation
restart: unless-stopped
environment:
- SERVICE_NAME=conversation
# 企微凭证
- WECOM_CORP_ID=${WECOM_CORP_ID}
- WECOM_AGENT_ID=${WECOM_AGENT_ID}
- WECOM_SECRET=${WECOM_SECRET}
- WECOM_TOKEN=${WECOM_TOKEN}
- WECOM_ENCODING_AES_KEY=${WECOM_ENCODING_AES_KEY}
# 数据库
- DATABASE_URL=postgresql://${POSTGRES_USER:-wecom}:${POSTGRES_PASSWORD:-wecom_secret}@postgres:5432/${POSTGRES_DB:-wecom_it_desk}
# Redis
- REDIS_URL=redis://redis:6379/0
# CORS
- CORS_ORIGINS=${CORS_ORIGINS:-http://itsupport.servyou.com.cn}
# Mock 登录
- MOCK_LOGIN_ENABLED=${MOCK_LOGIN_ENABLED:-false}
# 服务配置
- BACKEND_HOST=0.0.0.0
- BACKEND_PORT=8002
volumes:
- backend-uploads:/app/uploads
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
command: uvicorn app.main:app --host 0.0.0.0 --port 8002 --workers 2
networks:
- it-desk-internal
deploy:
resources:
limits:
memory: 1G
reservations:
memory: 512M
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8002/health').read()"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "15m"
max-file: "5"
# --------------------------------------------------------------------------
# Agent Service — 坐席服务
# --------------------------------------------------------------------------
agent-svc:
build:
context: ./backend
dockerfile: Dockerfile
image: wecom-it-desk:${AGENT_VERSION:-latest}
container_name: wecom_it_agent
restart: unless-stopped
environment:
- SERVICE_NAME=agent
# 企微凭证
- WECOM_CORP_ID=${WECOM_CORP_ID}
- WECOM_AGENT_ID=${WECOM_AGENT_ID}
- WECOM_SECRET=${WECOM_SECRET}
- WECOM_TOKEN=${WECOM_TOKEN}
- WECOM_ENCODING_AES_KEY=${WECOM_ENCODING_AES_KEY}
# 数据库
- DATABASE_URL=postgresql://${POSTGRES_USER:-wecom}:${POSTGRES_PASSWORD:-wecom_secret}@postgres:5432/${POSTGRES_DB:-wecom_it_desk}
# Redis
- REDIS_URL=redis://redis:6379/0
# CORS
- CORS_ORIGINS=${CORS_ORIGINS:-http://itsupport.servyou.com.cn}
# Mock 登录
- MOCK_LOGIN_ENABLED=${MOCK_LOGIN_ENABLED:-false}
# 服务配置
- BACKEND_HOST=0.0.0.0
- BACKEND_PORT=8003
volumes:
- backend-uploads:/app/uploads
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
command: uvicorn app.main:app --host 0.0.0.0 --port 8003 --workers 1
networks:
- it-desk-internal
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8003/health').read()"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# --------------------------------------------------------------------------
# AI Service — AI 服务(Dify 调用、Wingman
# --------------------------------------------------------------------------
ai-svc:
build:
context: ./backend
dockerfile: Dockerfile
image: wecom-it-desk:${AI_VERSION:-latest}
container_name: wecom_it_ai
restart: unless-stopped
environment:
- SERVICE_NAME=ai
# 企微凭证
- WECOM_CORP_ID=${WECOM_CORP_ID}
- WECOM_AGENT_ID=${WECOM_AGENT_ID}
- WECOM_SECRET=${WECOM_SECRET}
- WECOM_TOKEN=${WECOM_TOKEN}
- WECOM_ENCODING_AES_KEY=${WECOM_ENCODING_AES_KEY}
# 数据库
- DATABASE_URL=postgresql://${POSTGRES_USER:-wecom}:${POSTGRES_PASSWORD:-wecom_secret}@postgres:5432/${POSTGRES_DB:-wecom_it_desk}
# Redis
- REDIS_URL=redis://redis:6379/0
# CORS
- CORS_ORIGINS=${CORS_ORIGINS:-http://itsupport.servyou.com.cn}
# Mock 登录
- MOCK_LOGIN_ENABLED=${MOCK_LOGIN_ENABLED:-false}
# AI 服务(Dify
- DIFY_API_URL=${DIFY_API_URL}
- DIFY_API_KEY=${DIFY_API_KEY}
- DIFY_TIMEOUT=${DIFY_TIMEOUT:-30}
# AI Wingman
- DIFY_WINGMAN_API_URL=${DIFY_WINGMAN_API_URL:-}
- DIFY_WINGMAN_API_KEY=${DIFY_WINGMAN_API_KEY:-}
- DIFY_WINGMAN_TIMEOUT=${DIFY_WINGMAN_TIMEOUT:-30}
# 服务配置
- BACKEND_HOST=0.0.0.0
- BACKEND_PORT=8004
volumes:
- backend-uploads:/app/uploads
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
command: uvicorn app.main:app --host 0.0.0.0 --port 8004 --workers 1
networks:
- it-desk-internal
deploy:
resources:
limits:
memory: 1G
reservations:
memory: 512M
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8004/health').read()"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "15m"
max-file: "5"
# --------------------------------------------------------------------------
# Admin Service — 管理服务(仪表盘、配置、集成、审核)
# --------------------------------------------------------------------------
admin-svc:
build:
context: ./backend
dockerfile: Dockerfile
image: wecom-it-desk:${ADMIN_VERSION:-latest}
container_name: wecom_it_admin
restart: unless-stopped
environment:
- SERVICE_NAME=admin
# 企微凭证
- WECOM_CORP_ID=${WECOM_CORP_ID}
- WECOM_AGENT_ID=${WECOM_AGENT_ID}
- WECOM_SECRET=${WECOM_SECRET}
- WECOM_TOKEN=${WECOM_TOKEN}
- WECOM_ENCODING_AES_KEY=${WECOM_ENCODING_AES_KEY}
# 数据库
- DATABASE_URL=postgresql://${POSTGRES_USER:-wecom}:${POSTGRES_PASSWORD:-wecom_secret}@postgres:5432/${POSTGRES_DB:-wecom_it_desk}
# Redis
- REDIS_URL=redis://redis:6379/0
# CORS
- CORS_ORIGINS=${CORS_ORIGINS:-http://itsupport.servyou.com.cn}
# Mock 登录
- MOCK_LOGIN_ENABLED=${MOCK_LOGIN_ENABLED:-false}
# 服务配置
- BACKEND_HOST=0.0.0.0
- BACKEND_PORT=8005
volumes:
- backend-uploads:/app/uploads
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
command: uvicorn app.main:app --host 0.0.0.0 --port 8005 --workers 1
networks:
- it-desk-internal
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8005/health').read()"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# --------------------------------------------------------------------------
# Nginx — 反向代理 + 静态文件服务 + 路由分发
# --------------------------------------------------------------------------
nginx:
image: nginx:1.27-alpine
container_name: wecom_it_nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx-split.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- ./frontend-h5/dist:/usr/share/nginx/html/itdesk:ro
- ./frontend-agent/dist:/usr/share/nginx/html/itagent:ro
- ./frontend-admin/dist:/usr/share/nginx/html/itadmin:ro
- ./frontend-portal/dist:/usr/share/nginx/html/itportal:ro
depends_on:
core-svc:
condition: service_healthy
conversation-svc:
condition: service_healthy
agent-svc:
condition: service_healthy
ai-svc:
condition: service_healthy
admin-svc:
condition: service_healthy
networks:
- it-desk-internal
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:80/itdesk/health || exit 1"]
interval: 15s
timeout: 5s
retries: 3
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# =============================================================================
# 网络
# =============================================================================
networks:
it-desk-internal:
driver: bridge
# =============================================================================
# 数据卷
# =============================================================================
volumes:
postgres_data:
name: wecom_it_postgres_data
redis_data:
name: wecom_it_redis_data
backend-uploads:
name: wecom_it_backend_uploads
+9 -6
View File
@@ -24,7 +24,7 @@ services:
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER:-wecom}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-wecom_secret}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-wecom_secret_2026}
POSTGRES_DB: ${POSTGRES_DB:-wecom_it_desk}
volumes:
- postgres_data:/var/lib/postgresql/data
@@ -82,7 +82,7 @@ services:
- WECOM_TOKEN=${WECOM_TOKEN}
- WECOM_ENCODING_AES_KEY=${WECOM_ENCODING_AES_KEY}
# 数据库(Docker 内部网络,用容器名通信)
- DATABASE_URL=postgresql://${POSTGRES_USER:-wecom}:${POSTGRES_PASSWORD:-wecom_secret}@postgres:5432/${POSTGRES_DB:-wecom_it_desk}
- DATABASE_URL=postgresql://${POSTGRES_USER:-wecom}:${POSTGRES_PASSWORD:-wecom_secret_2026}@postgres:5432/${POSTGRES_DB:-wecom_it_desk}
# Redis(Docker 内部网络,带密码认证)
- REDIS_URL=redis://:${REDIS_PASSWORD:-R3d!s@2026#Secure}@redis:6379/0
# CORS
@@ -97,6 +97,9 @@ services:
- DIFY_WINGMAN_TIMEOUT=${DIFY_WINGMAN_TIMEOUT:-30}
# Mock 登录(生产环境默认关闭,如需临时调试请在 .env 中显式设置为 true)
- MOCK_LOGIN_ENABLED=${MOCK_LOGIN_ENABLED:-false}
# 企微 SSO 认证(v0.7.1 新增)
- WECOM_SSO_ENABLED=${WECOM_SSO_ENABLED:-false}
- WECOM_SSO_CALLBACK_BASE=${WECOM_SSO_CALLBACK_BASE:-https://itsupport.servyou.com.cn}
# 服务配置
- BACKEND_HOST=0.0.0.0
- BACKEND_PORT=8000
@@ -139,10 +142,10 @@ services:
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/nginx/ssl:ro
- ./frontend-h5/dist:/usr/share/nginx/html/itdesk:ro
- ./frontend-agent/dist:/usr/share/nginx/html/itagent:ro
- ./frontend-admin/dist:/usr/share/nginx/html/itadmin:ro
- ./frontend-portal/dist:/usr/share/nginx/html/itportal:ro
- ./html/itdesk:/usr/share/nginx/html/itdesk:ro
- ./html/itagent:/usr/share/nginx/html/itagent:ro
- ./html/itadmin:/usr/share/nginx/html/itadmin:ro
- ./html/itportal:/usr/share/nginx/html/itportal:ro
depends_on:
- backend
networks:
+26 -201
View File
@@ -1,209 +1,34 @@
# =============================================================================
# 企微智能IT支持服务台 — Nginx 配置(公司内网服务器版 + HTTPS)
# =============================================================================
# 目标服务器:10.90.5.110
# 域名:itsupport.servyou.com.cn
#
# 路由规则:
# HTTP → 自动重定向到 HTTPS
# HTTPS → { /itdesk/, /itagent/, /itadmin/, /api/, /ws/ }
# =============================================================================
server {
listen 80;
listen [::]:80;
server_name localhost;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# ------------------------------------------------------------------
# 日志格式
# ------------------------------------------------------------------
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# ------------------------------------------------------------------
# 基础配置
# ------------------------------------------------------------------
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 50m; # 支持文件上传(企微媒体文件)
# ------------------------------------------------------------------
# Gzip 压缩(前端静态资源)
# ------------------------------------------------------------------
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript
application/javascript application/xml+rss
application/json application/ld+json;
# ------------------------------------------------------------------
# 安全响应头
# ------------------------------------------------------------------
# 隐藏 nginx 版本号
server_tokens off;
# 基础安全头(应用到所有响应)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "0" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
add_header Cross-Origin-Opener-Policy "same-origin" always;
# API 路径特殊处理(不加 CSP,只加基础安全头)
# 前端路径的 CSP 在各前端 index.html 中单独配置
# =================================================================
# 上游服务定义(Docker 内部网络)
# =================================================================
upstream backend_api {
server backend:8000;
# 前端静态文件
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# =================================================================
# HTTP → HTTPS 重定向
# =================================================================
server {
listen 80;
server_name itsupport.servyou.com.cn;
# 企微域名验证文件(HTTP 也需要,企微验证可能走 HTTP)
location /WW_verify_lxWC7WJDjTQuutBE.txt {
alias /usr/share/nginx/html/itdesk/WW_verify_lxWC7WJDjTQuutBE.txt;
}
# 其余全部重定向到 HTTPS
location / {
return 301 https://$host$request_uri;
}
# API 反向代理到后端
location /api/ {
proxy_pass http://wecom_it_backend:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# =================================================================
# HTTPS 主服务
# =================================================================
server {
listen 443 ssl;
server_name itsupport.servyou.com.cn;
# H5 端点反向代理
location /h5/ {
proxy_pass http://wecom_it_backend:8000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# ------------------------------------------------------------------
# SSL 证书配置
# ------------------------------------------------------------------
ssl_certificate /etc/nginx/ssl/servyou.com.cn.crt;
ssl_certificate_key /etc/nginx/ssl/servyou.com.cn.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# ------------------------------------------------------------------
# 安全头
# ------------------------------------------------------------------
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# ------------------------------------------------------------------
# 健康检查端点(用于 Docker healthcheck
# ------------------------------------------------------------------
location = /itdesk/health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# ------------------------------------------------------------------
# H5 员工端 — /itdesk/
# ------------------------------------------------------------------
location /itdesk/ {
alias /usr/share/nginx/html/itdesk/;
index index.html;
try_files $uri /itdesk/index.html;
}
# ------------------------------------------------------------------
# 坐席工作台 — /itagent/
# ------------------------------------------------------------------
location /itagent/ {
alias /usr/share/nginx/html/itagent/;
index index.html;
try_files $uri /itagent/index.html;
}
# ------------------------------------------------------------------
# 统一入口 Portal — /itportal/
# ------------------------------------------------------------------
location /itportal/ {
alias /usr/share/nginx/html/itportal/;
index index.html;
try_files $uri /itportal/index.html;
}
# ------------------------------------------------------------------
# 管理后台 — /itadmin/
# ------------------------------------------------------------------
location /itadmin/ {
alias /usr/share/nginx/html/itadmin/;
index index.html;
try_files $uri /itadmin/index.html;
}
# ------------------------------------------------------------------
# 后端 API — /api/
# ------------------------------------------------------------------
location /api/ {
proxy_pass http://backend_api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
# 超时设置(AI 回复可能较慢)
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
}
# ------------------------------------------------------------------
# WebSocket — /ws/(坐席端实时通信)
# ------------------------------------------------------------------
location /ws/ {
access_log off; # P0-#4: 关闭 WS 路径日志,避免 token 泄露
proxy_pass http://backend_api;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 86400s; # WebSocket 长连接
}
# ------------------------------------------------------------------
# 企微域名验证文件
# ------------------------------------------------------------------
location /WW_verify_lxWC7WJDjTQuutBE.txt {
alias /usr/share/nginx/html/itdesk/WW_verify_lxWC7WJDjTQuutBE.txt;
}
# ------------------------------------------------------------------
# 默认路径 — 重定向到统一入口 Portal
# ------------------------------------------------------------------
location = / {
return 302 /itportal/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
+262
View File
@@ -0,0 +1,262 @@
# =============================================================================
# 企微IT智能服务台 — Nginx 配置(多服务拆分版)
# =============================================================================
# 路由策略:
# - /api/core/* → core-svc:8001
# - /api/conversations/*, /api/messages/*, /api/h5/*, /ws/* → conversation-svc:8002
# - /api/agents/* → agent-svc:8003
# - /api/ai/* → ai-svc:8004
# - /api/admin/* → admin-svc:8005
# - /*.html, /itdesk/*, /itagent/*, /itadmin/*, /itportal/* → 静态文件
# =============================================================================
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
# 性能优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 50m;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml;
# ----------------------------------------------------------------------
# 上游服务定义(拆分版)
# ----------------------------------------------------------------------
upstream core_backend {
server core-svc:8001 max_fails=3 fail_timeout=30s;
keepalive 32;
}
upstream conversation_backend {
server conversation-svc:8002 max_fails=3 fail_timeout=30s;
keepalive 64;
}
upstream agent_backend {
server agent-svc:8003 max_fails=3 fail_timeout=30s;
keepalive 32;
}
upstream ai_backend {
server ai-svc:8004 max_fails=3 fail_timeout=30s;
keepalive 32;
}
upstream admin_backend {
server admin-svc:8005 max_fails=3 fail_timeout=30s;
keepalive 32;
}
# ----------------------------------------------------------------------
# 静态文件服务(H5用户端)
# ----------------------------------------------------------------------
server {
listen 80;
server_name itsupport.servyou.com.cn;
# HTTP → HTTPS 跳转
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name itsupport.servyou.com.cn;
# SSL 证书
ssl_certificate /etc/nginx/ssl/servyou.com.cn.pem;
ssl_certificate_key /etc/nginx/ssl/servyou.com.cn.key;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# ------------------------------------------------------------------
# 健康检查端点(不经过代理)
# ------------------------------------------------------------------
location = /itdesk/health {
access_log off;
return 200 "OK";
add_header Content-Type text/plain;
}
# ------------------------------------------------------------------
# API 路由分发(根据路径选择上游服务)
# ------------------------------------------------------------------
# Core 服务:认证、员工、角色
location ~ ^/api/(auth|employees|roles|mfa) {
proxy_pass http://core_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_buffering off;
proxy_set_header Connection "";
}
# AI 服务:Dify 调用、Wingman
location ~ ^/api/(ai|wingman|dify) {
proxy_pass http://ai_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 90s; # AI 响应可能较慢
proxy_buffering off;
proxy_set_header Connection "";
}
# Admin 服务:管理后台 API
location ~ ^/api/admin {
proxy_pass http://admin_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_buffering off;
proxy_set_header Connection "";
}
# Agent 服务:坐席 API
location ~ ^/api/agents {
proxy_pass http://agent_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_buffering off;
proxy_set_header Connection "";
}
# Conversation 服务:会话、消息、H5 API
location ~ ^/api/(conversations|messages|h5|quick-replies) {
proxy_pass http://conversation_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_buffering off;
proxy_set_header Connection "";
}
# WebSocket 服务(会话服务)
location /ws/ {
proxy_pass http://conversation_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;
proxy_buffering off;
}
# 通用 API 路由(兜底)
location /api/ {
proxy_pass http://conversation_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
proxy_buffering off;
proxy_set_header Connection "";
}
# ------------------------------------------------------------------
# 静态文件服务
# ------------------------------------------------------------------
# H5 用户端
location /itdesk/ {
alias /usr/share/nginx/html/itdesk/;
try_files $uri $uri/ /itdesk/index.html;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# 坐席端
location /itagent/ {
alias /usr/share/nginx/html/itagent/;
try_files $uri $uri/ /itagent/index.html;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# 管理后台
location /itadmin/ {
alias /usr/share/nginx/html/itadmin/;
try_files $uri $uri/ /itadmin/index.html;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# Portal 入口
location /itportal/ {
alias /usr/share/nginx/html/itportal/;
try_files $uri $uri/ /itportal/index.html;
add_header Cache-Control "public, max-age=31536000, immutable";
}
# 根路径重定向到 Portal
location = / {
return 302 /itportal/;
}
# 默认处理
location / {
return 404;
}
}
}
+1
View File
@@ -206,6 +206,7 @@ http {
# 其他 API 路径
proxy_pass http://backend_api/;
proxy_redirect off; # 禁用重定向修改,让后端的 307/302 保持原始 location
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;