feat: fulfillment modes, MQ, admin status, docs; scrub compose secrets

Made-with: Cursor
This commit is contained in:
2026-04-03 16:26:38 +08:00
parent 1f16966174
commit b4c5103fb3
56 changed files with 4088 additions and 3742 deletions

260
生产环境部署.md Normal file
View File

@@ -0,0 +1,260 @@
# 萌芽小店 · 生产环境部署指南
> 覆盖生产环境部署要点:后端目录为 **`mengyastore-backend-go`**,配置以 **环境变量** 为主(与本地 `.env` / `.env.production` 一致),无需再依赖 `config.json`。
---
## 目录
1. [环境要求](#1-环境要求)
2. [准备 MySQL](#2-准备-mysql)
3. [部署后端Docker](#3-部署后端docker)
4. [构建并部署前端](#4-构建并部署前端)
5. [Nginx 反向代理](#5-nginx-反向代理)
6. [验证部署](#6-验证部署)
7. [常见问题](#7-常见问题)
8. [升级与回滚](#8-升级与回滚)
---
## 1. 环境要求
| 组件 | 说明 |
|------|------|
| Linux 服务器 | Ubuntu 22.04 / Debian 12 等 |
| Docker 24+、Compose v2 | `docker compose version` |
| MySQL 8.x | 可内网独立实例 |
| Nginx 1.20+ | 静态资源 + `/api/` 反代 |
| Node.js 18+ | 仅前端 **构建** 时使用 |
可选:内网 **RabbitMQ**、**Redis**(与 `docker-compose` / 环境变量一致即可)。
---
## 2. 准备 MySQL
表结构由后端 **GORM AutoMigrate** 自动创建,只需 **建库 + 授权**
```sql
CREATE DATABASE IF NOT EXISTS `mengyastore`
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'mengyastore'@'%' IDENTIFIED BY 'your_strong_password';
GRANT ALL PRIVILEGES ON `mengyastore`.* TO 'mengyastore'@'%';
FLUSH PRIVILEGES;
```
也可参考仓库内 `mengyastore-backend-go/init.sql`
---
## 3. 部署后端Docker
### 3.1 代码路径
```bash
git clone https://github.com/shumengya/mengyastore.git # 或你的仓库
cd mengyastore/mengyastore-backend-go
```
> 若历史文档写成 `mengyastore-backend`,请改为 **`mengyastore-backend-go`**。
### 3.2 配置方式(推荐)
生产 **不要** 把真实口令写进 Git。任选其一
- **A.** 在宿主机导出环境变量后执行 `docker compose up`Compose 中 `${VAR:-默认值}` 会读取宿主机环境)。
- **B.** 在服务器的 `mengyastore-backend-go/` 放置 **`.env`**(勿提交),并在 `docker-compose.yml` 中增加 `env_file: .env`(若你自行加上该条目);当前仓库默认 compose **未挂载** `.env`,请通过 **environment****export** 注入。
**必填 / 常用变量(与 `internal/config/config.go` 一致):**
| 变量 | 说明 |
|------|------|
| `APP_ENV` | `production` |
| `DATABASE_DSN` | 生产 MySQL 连接串 |
| `ADMIN_TOKEN` | 管理后台令牌(强随机) |
| `AUTH_API_URL` | SproutGate 认证网关 Base URL |
| `RABBITMQ_ENABLED` | `true` / `false` |
| `RABBITMQ_URL` | 启用 MQ 时完整 `amqp://…`(或用各 `RABBITMQ_*` 拼装,见配置注释) |
| `REDIS_ENABLED` | `true` / `false` |
| `REDIS_ADDR``REDIS_PASSWORD``REDIS_DB` | 启用 Redis 探测/缓存时 |
| `HTTP_LISTEN_ADDR` | 可选,默认容器内 `:8080` |
| `PUBLIC_API_BASE_URL` | 可选,管理端「系统状态」展示的对外 API 基址(反代 HTTPS 时建议设) |
根据业务关闭不需要的中间件时,将 `RABBITMQ_ENABLED``REDIS_ENABLED` 设为 `false`,避免无实例仍去连接。
### 3.3 修改 `docker-compose.yml`
部署前请 **核对并本地化**
- `DATABASE_DSN` 指向你的库。
- `ports`(示例 `28081:8080`)是否与 Nginx `proxy_pass` 一致。
- **删除或替换** 仓库中带默认口令的示例值;通过宿主机环境注入生产密钥。
示例(**仅结构示意,口令用环境变量**
```yaml
services:
backend:
build: { context: . }
container_name: mengyastore-backend
ports:
- "28081:8080"
environment:
APP_ENV: production
GIN_MODE: release
TZ: Asia/Shanghai
DATABASE_DSN: ${DATABASE_DSN}
ADMIN_TOKEN: ${ADMIN_TOKEN}
AUTH_API_URL: ${AUTH_API_URL:-}
REDIS_ENABLED: ${REDIS_ENABLED:-false}
REDIS_ADDR: ${REDIS_ADDR:-}
REDIS_PASSWORD: ${REDIS_PASSWORD:-}
RABBITMQ_ENABLED: ${RABBITMQ_ENABLED:-false}
RABBITMQ_ENV: prod
RABBITMQ_URL: ${RABBITMQ_URL:-}
restart: unless-stopped
```
### 3.4 启动
```bash
cd /opt/mengyastore/mengyastore-backend-go # 你的实际路径
export DATABASE_DSN='...'
export ADMIN_TOKEN='...'
# … 其他变量按需 export
docker compose up -d --build
docker compose logs -f
```
预期日志含:`[DB] 数据库连接成功,表结构已同步``萌芽小店后端启动于 http://localhost:8080`
### 3.5 健康检查
```bash
curl -s http://127.0.0.1:28081/api/health
curl -s http://127.0.0.1:28081/api/products
```
---
## 4. 构建并部署前端
```bash
cd mengyastore-frontend
npm ci || npm install
```
**同源部署**Nginx 把 `/api/` 反代到后端):构建前可不设 `VITE_API_BASE_URL`,打包后请求会走当前站点域名。
**前后端不同域**:创建 `.env.production`
```env
VITE_API_BASE_URL=https://你的-api-域名
```
```bash
npm run build
```
`dist/` 同步到服务器,例如 `/var/www/mengyastore/`
```bash
rsync -avz --delete dist/ user@server:/var/www/mengyastore/
```
---
## 5. Nginx 反向代理
```nginx
server {
listen 80;
server_name your-domain.com;
root /var/www/mengyastore;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:28081;
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_read_timeout 60s;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2|webmanifest)$ {
expires 7d;
add_header Cache-Control "public, immutable";
}
}
```
HTTPS 可使用 `certbot --nginx` 签发 Let's Encrypt。
---
## 6. 验证部署
- [ ] 首页与商品列表正常。
- [ ] SproutGate 登录后收藏、聊天、我的订单可用。
- [ ] **Logo 连点 5 次** → 管理员验证 → 带 `?token=` 或使用 `X-Admin-Token` 访问后台。
- [ ] 后台 **系统状态**MySQL / Redis / RabbitMQ若启用显示合理。
- [ ] 商品:**卡密** 与 **固定内容** 两种类型可分别保存;固定内容商品前台库存显示「不限」。
- [ ] 下一单:结算页金额、收款码区、**复制订单号**、确认后内容与邮件/MQ 预期一致(依你的 SMTP/MQ 配置)。
- [ ] PWA 可安装(如需要)。
---
## 7. 常见问题
**CORS / 白屏**
确认 Nginx `location /api/` 指向后端端口,且容器 `docker compose ps` 为运行中。
**管理 Token 无效**
核对容器内实际生效的 `ADMIN_TOKEN`(与前端输入一致)。当前项目 **不再依赖** `config.json`
**RabbitMQ 报 504 / channel closed**
服务端已实现重连与消费重启;若仍异常,查 Broker 日志、网络与 `RABBITMQ_URL`/vhost/权限。
**数据库连不上**
在宿主机用 `mysql` 客户端测 `DATABASE_DSN` 同款主机端口账号Docker 网络须能访问 MySQL。
**站点设置 KV 查询**
后端对缺失的 `site_settings` 键使用不产生「record not found」噪声的查询方式首次保存 SMTP 等后会写入库表。
---
## 8. 升级与回滚
```bash
cd mengyastore-backend-go
git pull
docker compose up -d --build
docker compose logs -f --tail=100
```
回滚:保留上一版本的镜像 tag 或 git tag重新 `docker compose up` 指定旧镜像(视你实际镜像命名而定)。
---
## 附:目录结构示例
```
/opt/mengyastore/
mengyastore-backend-go/
docker-compose.yml
Dockerfile
.env ← 仅存服务器本地,勿提交 Git
...
/var/www/mengyastore/ ← 前端 dist
index.html
assets/
```
部署完成后访问你的域名即可。