签入版本

This commit is contained in:
小蚂蚁云
2026-10-20 21:53:26 +08:00
parent 5cc3da657c
commit b33799aa63
427 changed files with 32547 additions and 1 deletions
@@ -0,0 +1,85 @@
# 2.2 后端服务启动
:::tip 概述
后端为标准 GoFrame v2 工程,单入口 `main.go`,启动后监听 `:8000`。本节覆盖从编译到运行的完整步骤与常见启动问题。
:::
## 启动流程
```text
main.go
├─ 注册五数据库驱动(mysql/pgsql/mssql/oracle/sqlite blank import)
├─ 注册 Redis 适配器
└─ internal/cmd.Run(ctx)
├─ 1) utility/cache.Init Redis 探测,失败降级进程内存
├─ 2) wireDependencies 校验中间件依赖接线(缺失仅告警)
├─ 3) g.Server + router.Register 中间件链 + 路由绑定
├─ 4) DaemonInit(延时 2s) 定时任务调度器加载 status=1 任务
├─ 5) gcron 注册 CleanTemp 每日 03:30 清理上传临时目录
└─ 6) s.Run() 阻塞运行 HTTP 服务
```
## 编译与运行
```sh
# 在项目根目录执行
# 方式一:直接运行(开发)
go run main.go
# 方式二:编译后运行(推荐)
go build -o bin/admin-server.exe main.go
./bin/admin-server.exe
```
启动成功后控制台输出路由表与监听地址,服务监听 **:8000**。
## 启动前检查
1. **配置文件**:确认 `manifest/config/config.yaml` 中:
- `database.default.link` 数据库连接串(默认 MySQL root/root)
- `redis.default` 地址与密码
2. **数据库已初始化**:参见 [2.4 数据库初始化](./database-init)
3. **端口未占用**:
```sh
# Windows 查看端口占用
netstat -ano | findstr ":8000" | findstr LISTENING
```
## 验证启动成功
```sh
# 健康检查(免登录公开接口)
curl http://127.0.0.1:8000/api/health
# {"code":0,"ok":true,"msg":"操作成功","data":{"status":"up"}}
# OpenAPI 文档
# 浏览器打开 http://127.0.0.1:8000/swagger (本地 Redoc 页面)
# 原生 JSON:http://127.0.0.1:8000/api.json
```
## 服务重启(Windows)
```sh
netstat -ano | findstr ":8000" | findstr LISTENING
taskkill /F /PID <pid>
go build -o bin/admin-server.exe main.go && ./bin/admin-server.exe
```
服务已开启优雅重启(`server.graceful: true`),生产环境可使用进程管理器发 SIGHUSR2 热重启。
## 启动日志说明
| 日志 | 含义 |
|------|------|
| `IdentityLoader`/`TenantLoader`/`LogSink` 告警 | 中间件依赖未接线(不阻断启动),表现为不加载角色权限/跳过租户校验/日志不落库 |
| Redis 连接失败告警 + `memory fallback` | 已降级内存缓存,多实例共享能力退化 |
| 路由表输出(`server.dumpRouterMap`) | 配置为 true 时打印全部注册路由,调试路由问题用 |
## 常见启动失败原因
+ **端口占用**:8000 被其他进程占用,改 `server.address` 或释放端口
+ **数据库连不上**:检查 link 串、库是否存在、账号密码;库名含点需确认拼写
+ **配置文件缺失**:必须从项目根目录运行(相对路径读取 `manifest/config/config.yaml`)
+ **Go 版本过低**:go.mod 声明 1.27.2,低版本报 `go.mod requires go >= 1.27.2`
@@ -0,0 +1,104 @@
# 2.4 数据库初始化
:::tip 概述
项目以 MySQL 为规范基准数据库,五方言定稿脚本统一存放于 `document/{数据库}/easygoadmin.goframe.elevue.sql`。本节演示最短初始化路径与验证方法。
:::
## 定稿脚本目录
```text
document/
├── mysql/easygoadmin.goframe.elevue.sql # MySQL 定稿(规范基准,人工导入维护)
├── postgresql/easygoadmin.goframe.elevue.sql # PostgreSQL 定稿(由基准转换,待人工校对)
├── sqlserver/easygoadmin.goframe.elevue.sql # SQL Server 定稿(由基准转换,待人工校对)
├── oracle/easygoadmin.goframe.elevue.sql # Oracle 定稿(由基准转换,待人工校对)
└── sqlite/easygoadmin.goframe.elevue.sql # SQLite 定稿(由基准转换,待人工校对)
```
::: warning 定稿以 MySQL 为准
五份脚本中 **MySQL 版为规范基准**,后四份由基准转换生成,尚待人工校对。生产使用非 MySQL 库前,请人工核对后四份脚本(尤其是唯一索引 `uk_*` 与列注释枚举)。
:::
## 方式一:导入 MySQL 定稿脚本(推荐)
```sh
# 创建库(注意库名含点,utf8mb4)
mysql -uroot -p -e "CREATE DATABASE \`easygoadmin.goframe.elevue\` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"
# 导入定稿脚本(含 25 张表 + 索引 + 种子数据:超管账号/菜单/字典/参数/租户等)
mysql -uroot -p easygoadmin.goframe.elevue < document/mysql/easygoadmin.goframe.elevue.sql
```
导入后验证:
```sql
SHOW TABLES; -- 25 张 goframe_ 前缀表
SELECT username FROM goframe_user WHERE id=1; -- admin
```
## 其他数据库初始化
PostgreSQL 示例:
```sh
createdb -U postgres easygoadmin.goframe.elevue
psql -U postgres -d easygoadmin.goframe.elevue -f document/postgresql/easygoadmin.goframe.elevue.sql
```
SQLite 示例(文件库,无需建库):
```sh
sqlite3 easygoadmin.db < document/sqlite/easygoadmin.goframe.elevue.sql
```
SQL Server / Oracle 使用对应客户端工具执行 `document/sqlserver/`、`document/oracle/` 下脚本。
导入后修改 `manifest/config/config.yaml` 的 `database.default.type/link` 切换连接(五驱动已在 main.go 预注册,改配置即切换)。
## 从现有库迁移
已有 MySQL 数据要搬到其他方言,**不要手工导 SQL**,使用内置整库迁移器(结构 + 注释 + 索引 + 数据 + 序列一体搬运,五方言互迁):
```sh
go run ./cmd/migrator \
-src-type=mysql -src-host=127.0.0.1 -src-hostport=3306 \
-src-database=easygoadmin.goframe.elevue -src-username=root -src-password=root \
-dst-type=pgsql -dst-host=127.0.0.1 -dst-hostport=5432 \
-dst-database=easygoadmin.goframe.elevue -dst-username=postgres -dst-password=123456 \
-drop-target-first
```
详见 [7.6 数据库迁移工具](../tools/db-migrate) 与 [11.3 数据库迁移](../deploy/db-migrate)。仓库保留有实测日志:`document/数据迁移PostgreSQL版本日志.txt`、`数据迁移SqlServer版本日志.txt`、`数据迁移Sqlite版本日志.txt`。
## 表结构通用规范
全部 25 张表统一以下规范(建新表必须遵循,详见 [3.2 数据库建表](../guide/database)):
```sql
-- 统一审计列(25 表齐备)
id int NOT NULL AUTO_INCREMENT, -- 主键:数据库自增(禁用雪花)
create_user varchar(50), -- 创建人
create_time datetime DEFAULT (now()), -- 创建时间
update_user varchar(50), -- 更新人
update_time datetime DEFAULT (now()), -- 更新时间
del_flag int DEFAULT 0, -- 软删:0-正常 1-已删除
-- 租户级表(9 张)另有:
tenant_id int DEFAULT 0 -- 0=平台级
```
## 验证数据完整性
```sql
-- 种子超管与角色
SELECT u.username, r.code FROM goframe_user u
JOIN goframe_user_role ur ON ur.user_id=u.id
JOIN goframe_role r ON r.id=ur.role_id; -- admin / ROLE_SUPER_ADMIN
-- 字典种子
SELECT code FROM goframe_dict; -- role_data_scope 等
-- 唯一索引检查(13 个)
SELECT TABLE_NAME, INDEX_NAME FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA='easygoadmin.goframe.elevue' AND INDEX_NAME LIKE 'uk_%'
GROUP BY TABLE_NAME, INDEX_NAME;
```
@@ -0,0 +1,167 @@
# 2.7 Docker 快速体验
:::tip 概述
项目根目录已内置完整 Docker 化文件(`docker/` 目录 + 根目录 `.dockerignore`),一条命令拉起 后端 + MySQL + Redis 三容器全栈。生产部署细节见 [11.6 Docker 生产部署](../deploy/docker)。
:::
## 文件布局
```text
EasyGoAdmin_GoFrame_EleVue/
├── .dockerignore # 构建上下文排除清单(Docker 硬规则:必须在上下文根目录)
└── docker/
├── Dockerfile # 多阶段构建:golang:1.27-alpine 编译 → alpine 运行
├── docker-compose.yml # 三服务编排:后端 + MySQL8 + Redis7
└── .env.example # 环境变量模板(复制为 .env 使用,含明文密钥不入库)
```
:::warning 为什么 `.dockerignore` 不在 docker/ 里
`.dockerignore` 由**构建上下文根目录**决定,与 Dockerfile 位置无关。放 `docker/` 里会完全失效,`ui/node_modules`(数百 MB)会被打进构建上下文,构建极慢。而 `Dockerfile` / `docker-compose.yml` 位置无所谓,构建/启动时用 `-f` 指定即可。
:::
## 文件注释速览
**Dockerfile**(多阶段,关键设计):
```dockerfile
# 阶段一:builder —— 编译静态二进制
# 纯 Go 项目(sqlite 驱动为 modernc 系,无 CGO),CGO_ENABLED=0 静态编译
# 先拷 go.mod/go.sum 再 go mod download:源码变更但依赖未变时不重复下载
# -trimpath 去除本地路径,-s -w 去除符号表与调试信息
FROM golang:1.27-alpine AS builder
ENV GOPROXY=https://goproxy.cn,direct CGO_ENABLED=0 GOOS=linux
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build -trimpath -ldflags "-s -w" -o /build/bin/admin-server main.go
# 阶段二:runtime —— 最终业务镜像
# 运行时资产四件(缺一运行报错):
# manifest/config 配置文件
# manifest/data ip2region.xdb 归属地库
# resource 字体/swagger 静态资源
# templates 代码生成器模板(tool 域运行时读取)
# 先于二进制 COPY,业务代码变动不破坏资产层缓存
# 非 root 运行;tzdata 供 app.timezone=Asia/Shanghai 使用
FROM alpine:3.22 AS runtime
ENV TZ=Asia/Shanghai
RUN apk add --no-cache tzdata ca-certificates \
&& addgroup -g 1000 app && adduser -u 1000 -G app -s /sbin/nologin -D app
WORKDIR /opt/easygoadmin
COPY --chown=app:app manifest/config ./manifest/config
COPY --chown=app:app manifest/data ./manifest/data
COPY --chown=app:app resource ./resource
COPY --chown=app:app templates ./templates
COPY --chown=app:app --from=builder /build/bin/admin-server ./bin/admin-server
USER app
# 健康检查:/api/health 为公开白名单路径,不带 /v1
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
CMD wget -qO- "http://127.0.0.1:8000/api/health" || exit 1
ENTRYPOINT ["./bin/admin-server"]
```
**docker-compose.yml**(三服务编排,关键设计):
```yaml
services:
easygoadmin:
build:
context: .. # 上下文为项目根(compose 在 docker/ 目录)
dockerfile: docker/Dockerfile
# 敏感/环境相关配置一律 GF_ 前缀覆盖,不改镜像内 config.yaml
# (键名 = 配置路径大写下划线,如 file.uploadDir → GF_FILE_UPLOADDIR)
environment:
# 连接串由 .env 参数拼装:主机/端口/账号/密码/库名均可覆盖(连外部库改 .env 即可)
GF_DATABASE_DEFAULT_LINK: "mysql:${MYSQL_USER}:${MYSQL_PASSWORD}@tcp(${MYSQL_HOST}:${MYSQL_PORT})/${MYSQL_DATABASE}?..."
GF_REDIS_DEFAULT_ADDRESS: "${REDIS_HOST}:${REDIS_PORT}"
GF_FILE_UPLOADDIR: "/data/upload" # 配置默认是 Windows 绝对路径,容器内必须覆盖
GF_CAPTCHA_BYPASSCODE: "" # 生产清空万能验证码
GF_APP_ERRORMSG: "false" # 生产关闭详细错误信息
volumes:
- ../upload:/data/upload # 宿主路径相对 compose 文件目录,需 ../ 回到项目根
- ../log:/opt/easygoadmin/log
depends_on:
mysql: { condition: service_healthy } # 等 healthcheck 通过再启动
redis: { condition: service_healthy }
mysql:
image: mysql:8.0
volumes:
- mysql-data:/var/lib/mysql
# 首次初始化:导入定稿 SQL(仅空数据卷时执行一次)
- ../document/mysql/easygoadmin.goframe.elevue.sql:/docker-entrypoint-initdb.d/init.sql:ro
command: [--character-set-server=utf8mb4, --collation-server=utf8mb4_unicode_ci, --lower_case_table_names=1]
redis:
image: redis:7-alpine
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD:-123456}", "--appendonly", "yes"]
```
## 三步启动
```sh
cd docker
# 1. 首次:生成 .env 并按需修改密钥(JWT_SECRET / 数据库 / Redis 密码)
copy .env.example .env
# 2. 构建并启动全栈(首次 MySQL 自动导入定稿 SQL 建库)
docker compose up -d --build
# 3. 验证
curl http://127.0.0.1:8000/api/health
```
默认账号 `admin / 123456`。`.env` 变量说明:
| 变量 | 默认值 | 说明 |
|------|--------|------|
| APP_PORT | 8000 | 后端宿主端口 |
| JWT_SECRET | please-change-... | 令牌签名密钥,**生产必改** |
| MYSQL_HOST | mysql | **连接串内主机**:自带服务用服务名;连外部库填 IP/域名 |
| MYSQL_PORT | 3306 | **数据库监听端口**(进连接串;连外部库填外部库端口) |
| MYSQL_USER / MYSQL_PASSWORD | root / root | 数据库账号与密码 |
| MYSQL_DATABASE | easygoadmin.goframe.elevue | 库名(与定稿 SQL 一致,勿随意改) |
| REDIS_HOST | redis | 同 MYSQL_HOST 规则 |
| REDIS_PORT / REDIS_PASSWORD | 6379 / 123456 | Redis 监听端口(进连接串)与密码 |
> 自带 mysql/redis 服务的**宿主映射端口已在 compose 写死**(3306/6379,仅供宿主机客户端直连调试),不在 `.env` 中配置;占用冲突时直接改 compose 的 `ports`。
**连宿主机已有数据库/Redis**:`.env` 中把 `MYSQL_HOST` / `REDIS_HOST` 改为 `host.docker.internal`(Windows/Mac Docker Desktop;Linux 需 compose 加 `extra_hosts: host.docker.internal:host-gateway`),端口填宿主机上实际监听端口。
::: warning 容器内 127.0.0.1 不是宿主机
bridge 网络下每个容器有独立网络栈,`MYSQL_HOST=127.0.0.1` / `REDIS_HOST=127.0.0.1` 指向**容器自身**,连接必失败(Redis 会静默降级内存缓存,JWT 黑名单、验证码共享随之失效)。连宿主机一律用 `host.docker.internal` 或宿主机实际 IP。
:::
## 仅构建后端镜像(连宿主机已有 DB/Redis)
不启动 compose 里的 mysql/redis 服务时,在**项目根目录**执行:
```sh
docker build -f docker/Dockerfile -t easygoadmin:latest .
docker run -d --name easygoadmin -p 8000:8000 \
-e GF_DATABASE_DEFAULT_LINK="mysql:root:root@tcp(host.docker.internal:3306)/easygoadmin.goframe.elevue?charset=utf8mb4&parseTime=true&loc=Local" \
-e GF_REDIS_DEFAULT_ADDRESS="host.docker.internal:6379" \
-e GF_FILE_UPLOADDIR="/data/upload" \
-v ${PWD}/upload:/data/upload easygoadmin:latest
```
## 常见问题
::: warning 容器内注意
1. `file.uploadDir` 配置默认是 Windows 绝对路径,容器内**必须**用 `GF_FILE_UPLOADDIR` 覆盖并挂持久卷,否则重建丢文件。
2. 跨卷上传"复制后删源"逻辑依赖同一文件系统,upload 目录必须是独立卷。
3. 定稿 SQL 挂载到 `docker-entrypoint-initdb.d` **仅在 MySQL 数据卷为空时执行一次**;改表结构后需手动执行或 `docker volume rm` 清卷重建。
4. 代码生成器(tool 域)在容器内可用(`templates/` 已打入镜像),但生成产物写在容器文件系统里,**生产容器不建议使用**,开发期仍在宿主机跑 `go run ./cmd/generator`。
5. 连接串/Redis 地址在 bridge 网络下用**服务名**(`mysql`、`redis`)互访;连宿主机服务用 `host.docker.internal`;**容器内 127.0.0.1 指向容器自身**,填了连不上(详见上方变量表警示)。
:::
## 其他运行形态
| 形态 | 说明 |
|------|------|
| 二进制 + systemd | `go build` 产物 + systemd unit(见 [11.4 后端部署](../deploy/backend)) |
| 前端 Nginx 容器 | `ui/` 独立构建 dist 后由 Nginx 托管并反代 /api(见 [11.6](../deploy/docker)) |
| k8s | 按拓扑拆分 Deployment/Service,上传目录用 PV |
@@ -0,0 +1,142 @@
# 2.5 配置文件说明
:::tip 概述
本项目唯一运行时配置文件为 `manifest/config/config.yaml`,GoFrame gcfg 加载。环境变量覆盖规则:使用 `GF_` 前缀,例如 `GF_DATABASE_DEFAULT_LINK`。本节速览全部配置块,完整键值速查见[附录 C 配置项速查](../appendix/config-reference)。
:::
## 配置文件全景
```text
manifest/config/config.yaml
├─ server 服务端:监听地址、OpenAPI、优雅重启
├─ logger 日志:级别、输出、目录
├─ database 多数据库连接(type/link 切换五方言)
├─ redis 缓存连接(黑名单/验证码/字典/租户/身份缓存)
├─ jwt 双令牌:签名密钥、签发者、双有效期
├─ captcha 图形验证码:字符集、尺寸、干扰参数、万能码
├─ rateLimit 接口限流:窗口、普通阈值、认证入口阈值
├─ operationLog 操作日志:总开关、查询类记录开关
├─ job 定时任务:日志保留天数
├─ tenant 租户:超管新增的默认归属租户
├─ app 应用:时区、演示模式、错误信息开关
├─ api 接口:驼峰转换、受信代理列表
├─ cors 跨域:允许来源
├─ file 文件上传:目录、域名、白名单、大小、临时保留天数
├─ generator 代码生成器:父菜单 ID、覆盖开关
└─ ip2region 归属地库:xdb 文件路径
```
## 核心配置速览
### 服务端
```yaml
server:
address: ":8000" # 监听地址与端口
openapiPath: "/api.json" # OpenAPI 文档路径(原生自动生成)
swaggerPath: "" # Swagger UI(本地托管 resource/swagger/)
errorStack: true # 响应携带错误堆栈(生产建议 false)
graceful: true # 优雅重启
```
### 数据库与缓存
```yaml
database:
default:
type: "mysql"
link: "mysql:root:root@tcp(127.0.0.1:3306)/easygoadmin.goframe.elevue?charset=utf8mb4&parseTime=true&loc=Local"
debug: false # 调试期可开 SQL 日志,生产关闭
redis:
default:
address: "127.0.0.1:6379"
pass: "123456"
```
### JWT 与验证码
```yaml
jwt:
secret: "…" # 签名密钥(生产必须改)
issuer: "easygoadmin"
accessTokenExpire: 7200 # 访问令牌 2 小时
refreshTokenExpire: 604800 # 刷新令牌 7 天
captcha:
expire: 300
bypassCode: "618618" # 万能验证码(生产必须留空)
length: 6
```
### 限流与防护
```yaml
rateLimit:
enable: true
limit: 120 # 普通接口每窗口每 IP 每路径上限
windowSeconds: 60
authLimit: 10 # 登录/验证码/令牌兑换独立更严阈值
operationLog:
enable: true
logQuery: true
app:
demo: false # 演示模式:true 时拦截全部写操作
errorMsg: true # 详细错误信息(生产建议 false)
```
### 文件与生成器
```yaml
file:
uploadDir: "…" # 物理根目录(当前配置为开发机绝对路径,建议改相对项目根;
# 容器部署用 GF_FILE_UPLOADDIR 覆盖并挂持久卷)
domainUrl: "http://file.goframe.elevue" # 文件访问域名,空则 /upload/ 相对路径
imageExt: "gif,jpg,jpeg,png,bmp,webp,svg"
fileExt: "gif,jpg,…,csv,mp3,mp4,sql,js,css"
maxSize: 10 # 单文件上限 MB
tempExpireDays: 7 # 临时目录保留天数(0=不清理)
generator:
parentMenuId: 189 # 新模块菜单挂载父级 ID
overwrite: true # 覆盖开关(true 生成即覆盖;false=缺失补生成、存在跳过)
ip2region:
path: "manifest/data/ip2region.xdb"
```
## 环境变量覆盖
容器化/CI 部署时无需改镜像内配置文件,用环境变量覆盖任意键(12-Factor 标准,**环境变量 > 配置文件**):
```sh
GF_DATABASE_DEFAULT_LINK="mysql:root:secret@tcp(db:3306)/easygoadmin?charset=utf8mb4&parseTime=true"
GF_REDIS_DEFAULT_ADDRESS="redis:6379"
GF_APP_DEMO="false"
GF_FILE_UPLOADDIR="/data/upload"
```
**键名规则**:配置路径大写、点换下划线并加 `GF_` 前缀。
```text
配置键 database.default.link → 环境变量 GF_DATABASE_DEFAULT_LINK
配置键 file.uploadDir → 环境变量 GF_FILE_UPLOADDIR
配置键 jwt.secret → 环境变量 GF_JWT_SECRET
```
**生效语义**(实现见 `utility/config`,启动时经 `RegisterEnvOverride` 接线环境变量优先适配器):
```text
优先级 环境变量 > 配置文件(未设环境变量时行为与纯文件读取完全一致)
空值 环境变量为空串视为未设置,不会清空配置文件中的有效值
范围 仅覆盖配置文件中已存在的键(新增键须先在 config.yaml 声明)
整节点 读取 database 整节点(如框架初始化连接)时逐叶叠加覆盖,
连接串同样被环境变量接管
```
**Docker 部署的推荐姿势**:`.env` 用 `MYSQL_HOST` 等运维友好名,由 compose 的 `environment` 块映射为 `GF_` 规范键注入(`docker/docker-compose.yml` 即此模式);**不要**把友好名原样透传给应用——应用只认 `GF_` 前缀键。`.env` 值中含 `$` 须写成 `$$`(compose 插值会吞掉单 `$`)。
::: warning 生产环境必改项
1. `jwt.secret` 更换为随机长字符串
2. `captcha.bypassCode` 留空(万能验证码禁止上线)
3. `app.errorMsg: false`(不暴露错误堆栈)
4. `server.errorStack: false`
5. `database.default.debug: false`
6. `redis.default.pass` 设置强密码
:::
@@ -0,0 +1,110 @@
# 2.1 开发环境准备
:::tip 概述
本章指导您从零搭建开发环境,包括 Go、Node.js/pnpm、MySQL、Redis 的安装与配置。项目后端支持 MySQL、PostgreSQL、SQL Server、Oracle、SQLite 五种数据库,默认使用 MySQL。
:::
## 安装流程总览
```mermaid
graph TD
A[开始] --> B[安装 Go 1.27+]
B --> C[安装 MySQL 8.x]
C --> D[安装 Redis]
D --> E[安装 Node.js 18+ / pnpm]
E --> F[安装 GoFrame CLI gf]
F --> G[配置开发工具]
G --> H[环境就绪 ✅]
style A fill:#e8f5e9
style H fill:#e8f5e9
style C fill:#fff3e0
```
## Go 安装
:::warning 版本要求
项目 go.mod 声明 `go 1.27.2`,请安装 Go 1.27.2 及以上版本,过低版本会直接编译失败。
:::
### Windows
从 [https://go.dev/dl/](https://go.dev/dl/) 下载 `go1.27.2.windows-amd64.msi` 默认安装。验证:
```sh
go version # go version go1.27.2 windows/amd64
```
建议配置国内代理(下载依赖加速):
```sh
go env -w GOPROXY=https://goproxy.cn,direct
```
### GoFrame CLI(可选但推荐)
```sh
go install github.com/gogf/gf/cmd/gf/v2@latest
gf -v
```
`gf` 仅在表结构变更后刷新 dao 时使用(`gf gen dao -c hack/config.yaml`),日常开发不依赖。
## MySQL 安装
推荐 MySQL 8.x,安装后创建数据库(**注意库名含点**):
```sql
CREATE DATABASE `easygoadmin.goframe.elevue` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
```
默认连接账号 `root/root@127.0.0.1:3306`,如不同请同步修改 `manifest/config/config.yaml` 的 `database.default.link`。
::: warning 库名注意
库名 `easygoadmin.goframe.elevue` 含点号,DDL 语句中引用库名/表名时 MySQL 需要反引号包裹。MySQL 定稿导入脚本为 `document/mysql/easygoadmin.goframe.elevue.sql`(其余方言见 `document/{postgresql,sqlserver,oracle,sqlite}/`)。
:::
## Redis 安装
Redis 用于缓存、JWT 黑名单、验证码、在线用户、限流计数、任务执行锁。默认连接 `127.0.0.1:6379`,密码 `123456`(见 config.yaml `redis.default.pass`)。
```sh
# Windows 可使用 Memurai / WSL / Redis for Windows
# 验证
redis-cli -a 123456 ping # PONG
```
::: tip 降级说明
Redis 不可用时系统自动降级为进程内存缓存(`utility/cache`),服务可正常启动,但限流计数、任务锁、在线用户、黑名单等多实例共享能力退化为单机语义。生产环境 Redis 必须可用。
:::
## Node.js / pnpm 安装
前端要求 Node.js 18+,推荐 pnpm 包管理:
```sh
# 安装 Node.js(https://nodejs.org LTS 版)
node -v # v18+
# 安装 pnpm
npm install -g pnpm
pnpm -v
```
## 开发工具推荐
| 工具 | 用途 |
|------|------|
| GoLand / VS Code + Go 插件 | 后端开发 |
| VS Code + Volar 插件 | 前端开发(关闭 Vetur) |
| Navicat / DBeaver | 数据库管理 |
| RedisInsight / Another Redis Desktop | Redis 管理 |
| Apifox / Postman | 接口调试(也可直接用内置 /swagger) |
## 环境检查清单
+ [ ] `go version` 输出 1.27.2+
+ [ ] MySQL 已建库 `easygoadmin.goframe.elevue`(utf8mb4)
+ [ ] `redis-cli ping` 返回 PONG
+ [ ] `node -v` 输出 18+,`pnpm -v` 正常
+ [ ] `manifest/config/config.yaml` 中数据库/Redis 连接信息与实际一致
@@ -0,0 +1,98 @@
# 2.8 常见问题 FAQ
:::tip 概述
本节收录环境搭建与首次运行阶段最常遇到的问题。更多排障条目见 [附录 A 常见问题汇总](../appendix/faq) 与 [11.10 故障排查手册](../deploy/troubleshooting)。
:::
## 环境与编译
### Q:`go build` 报 `go.mod requires go >= 1.27.2`
go.mod 声明的版本高于本机 Go 版本。升级 Go 到 1.27.2+,或使用 go env 切换已安装的高版本。
### Q:依赖下载缓慢或失败
```sh
go env -w GOPROXY=https://goproxy.cn,direct
go mod tidy
```
### Q:前端 `pnpm install` 报引擎不匹配
检查 Node 版本(要求 18+)。部分依赖对 pnpm 版本敏感,可升级 pnpm 后重装:
```sh
npm install -g pnpm@latest
pnpm install
```
## 数据库
### Q:连接 MySQL 报错 `Unknown database`
库名是 `easygoadmin.goframe.elevue`(含点号),确认已建库且拼写一致;建库语句见 [2.4 数据库初始化](./database-init)。
### Q:切换数据库类型后启动报驱动错误
确认 `database.default.type` 与 `link` 前缀一致(如 `type: "pgsql"` 且 `link: "pgsql:..."`)。五驱动已预注册,无需改代码。
### Q:导入脚本后缺少唯一索引
`document/` 各方言脚本中 MySQL 版为规范基准,后四份待人工校对。以 MySQL 版核对 `uk_*` 唯一索引清单(应有 13 个),缺失需人工补齐——唯一索引是并发唯一性的最终保证。
## 启动与运行
### Q:启动报端口占用
```sh
netstat -ano | findstr ":8000" | findstr LISTENING
taskkill /F /PID <pid>
```
或修改 `manifest/config/config.yaml` 的 `server.address`。
### Q:启动日志出现 `IdentityLoader`/`LogSink` 告警
中间件依赖未接线(不阻断启动)。通常原因:`internal/cmd` 缺少对 `internal/logic` 的空白导入,或 init 注册被误删。表现为:不加载角色权限 / 跳过租户校验 / 操作日志不落库。
### Q:日志提示 Redis 连接失败并降级
服务以进程内存缓存运行(`UsingMemory()=true`)。限流计数、任务执行锁、在线用户、JWT 黑名单、验证码全部退化单机语义。检查 Redis 地址/密码,确认后可通过 `/api/cache/info` 验证 `mode` 是否恢复为 `redis`。
### Q:接口返回 `{"code":401}`
令牌缺失、过期或已被拉黑。重新登录获取新令牌;带 refresh_token 走 `POST /api/oauth2/token` 轮换。注意登录响应字段是 `access_token`(下划线)。
### Q:接口返回 `{"code":403}`
权限不足:确认 gmeta `permission` 标签对应节点已在「菜单管理」存在,且已在「角色管理」为当前角色勾选。超管(ROLE_SUPER_ADMIN)不受限。
### Q:接口返回 `{"code":429}`
触发限流(默认普通接口 120 次/分钟/IP/路径;登录/验证码/令牌兑换 10 次/分钟/IP)。等待窗口过期或调整 `rateLimit.*` 配置。
### Q:写操作被拒绝并提示"演示环境,禁止操作!"
`app.demo: true` 时拦截全部写操作(读操作与公开白名单放行)。本地开发改回 `false` 并重启。
### Q:验证码图片不显示
检查 `/api/captcha` 是否 200;字体文件 `resource/static/fonts/Vera.ttf` 缺失会降级 SVG 渲染(仍可用);若被限流(认证入口 10 次/分钟)等待窗口过期。
### Q:登录报"验证码错误"
开发环境可在验证码栏直接填配置中的 `captcha.bypassCode`(默认 618618)免校验;生产环境该值应留空。注意验证码为一次性校验(命中即删)。
## 前端
### Q:登录成功但刷新页面回到登录页
确认后端返回的令牌字段读取的是 `data.access_token`;检查浏览器 localStorage 是否被拦截清除;确认系统时间正常(令牌 exp 校验)。
### Q:页面菜单为空
菜单按角色权限动态生成。在「角色管理 → 权限」为角色勾选菜单节点;超管返回全量菜单。也可能是 `getUser` 接口 401——检查令牌。
### Q:接口 404
本项目全部路由统一 `/api` 前缀且**无 `/v1` 版本段**。`/api/v1/user/page` 形式会 404,正确为 `/api/user/page`。
@@ -0,0 +1,99 @@
# 2.6 第一个接口调试
:::tip 概述
本节通过三个最基础的接口(健康检查 → 登录 → 带令牌调用业务接口)带你完成第一次接口调试。项目内置 OpenAPI 文档(/api.json)与本地 Redoc 页面(/swagger),可在线查看全部接口并调试。
:::
## 接口调试三步走
### 第一步:健康检查(免登录)
```sh
curl http://127.0.0.1:8000/api/health
```
```json
{"code":0,"ok":true,"msg":"操作成功","data":{"status":"up"}}
```
### 第二步:获取验证码 + 登录
```sh
# 1. 获取图形验证码(公开接口)
curl http://127.0.0.1:8000/api/captcha
# 返回 {"key":"uuid...","captcha":"data:image/png;base64,..."}
# 2. 登录(公开接口;开发环境可携带万能验证码 bypassCode 免校验)
curl -X POST http://127.0.0.1:8000/api/login \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"123456","code":"618618","key":"上一步返回的key"}'
```
登录成功返回(**OAuth2 惯例下划线命名**):
```json
{
"code": 0, "ok": true, "msg": "操作成功",
"data": {
"access_token": "eyJhbGciOiJIUzI1NiIs…",
"refresh_token": "eyJhbGciOiJIUzI1NiIs…",
"expires_in": 7200,
"token_type": "Bearer"
}
}
```
### 第三步:带令牌调用业务接口
```sh
curl http://127.0.0.1:8000/api/user/page?pageNo=1&pageSize=3 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs…"
```
分页响应(统一信封 + PageData):
```json
{
"code": 0, "ok": true, "msg": "操作成功",
"data": {
"records": [ { "id": 1, "username": "admin", … } ],
"total": 1, "size": 3, "current": 1, "pages": 1
}
}
```
## 在线文档调试
浏览器打开:
| 地址 | 内容 |
|------|------|
| `http://127.0.0.1:8000/swagger` | 本地托管 Redoc UI(免登录) |
| `http://127.0.0.1:8000/api.json` | 原生 OpenAPI JSON(gmeta 补注册生成) |
gmeta 注解(method/path/summary/permission)会完整体现在文档中,接口与文档天然同步。
## 响应信封约定
| 字段 | 说明 |
|------|------|
| `code` | 0=成功;1=通用失败;401=未认证(HTTP 401);403=无权限;429=限流;其余业务码见 [8.2 错误码对照表](../api-spec/error-code) |
| `ok` | 布尔,与 code==0 等价 |
| `msg` | 提示信息,失败时前端弹出 |
| `data` | 业务数据,失败时为空对象 `{}` |
## 认证方式速记
```text
1. access_token 放 Header:Authorization: Bearer <token>
2. access_token 过期(401)→ POST /api/oauth2/token
{"grant_type":"refresh_token","refresh_token":"<refresh_token>"}
→ 拿到新令牌对(旧 refresh 拉黑防重放)
3. 登出:GET /api/logout(当前 jti 拉黑)
```
::: tip 调试技巧
+ 开发环境把 `captcha.bypassCode`(默认 618618)作为 code 值可跳过验证码校验。
+ 令牌接口返回字段是 `access_token`(下划线),业务接口出入参是驼峰,这是唯一的命名例外。
+ 每次登录签发新令牌对,旧 access 仍有效直到过期或被拉黑;强退会双令牌一并拉黑。
:::
@@ -0,0 +1,74 @@
# 2.3 前端服务启动
:::tip 概述
前端为 Vue3 + TypeScript + Vite + Element Plus 工程(`ui/` 目录),通过 pnpm 安装依赖后一条命令启动开发服务器,接口代理指向后端 `:8000`。
:::
## 安装依赖
```sh
cd ui
# 推荐 pnpm(仓库脚本按 pnpm 编写)
pnpm install
# 国内镜像加速(可选)
pnpm config set registry https://registry.npmmirror.com
```
## 启动开发服务器
```sh
pnpm dev
```
启动后终端输出本地地址(默认 Vite 端口),浏览器访问即可看到登录页。
## 代理与接口地址
开发环境下 Vite 把 `/api` 代理到后端 `http://127.0.0.1:8000`(见 `ui/vite.config.ts` 的 proxy 配置)。前端 axios baseURL 为 `/api`,模块路径与后端 gmeta path 逐条对齐。
::: tip URL 约定
全部接口统一 `/api` 前缀,**没有 `/v1` 版本段**(`api/v1` 只是 Go 包名)。例如用户分页是 `GET /api/user/page`。公开路由(登录/验证码/令牌兑换/健康检查)同样不带版本段。
:::
## 构建生产包
```sh
pnpm build # production 模式构建
pnpm build:prod # 显式 production
pnpm build:uat # uat 环境
pnpm build:test # test 环境
pnpm build:dev # dev 环境
```
构建产物经 `build/script/postBuild.ts` 后处理,输出 `dist/`,交给 Nginx 等静态服务器托管(部署见 [11.5 前端部署](../deploy/frontend))。
## 常用脚本
| 命令 | 说明 |
|------|------|
| `pnpm dev` | 启动开发服务器 |
| `pnpm build` | 生产构建 |
| `pnpm type:check` | vue-tsc 类型检查 |
| `pnpm lint:eslint` | ESLint 修复 |
| `pnpm lint:prettier` | Prettier 格式化 |
| `pnpm lint:stylelint` | Stylelint 样式检查 |
| `pnpm preview` | 构建并本地预览 |
## 本地测试账号
| 账号 | 密码 | 角色 |
|------|------|------|
| admin | 123456 | 超管(ROLE_SUPER_ADMIN,通配权限 `*:*:*`) |
::: warning 演示模式
若 `config.yaml` 中 `app.demo: true`,所有写操作会被拦截并提示"演示环境,禁止操作!"。本地开发请保持 `app.demo: false`。
:::
## 前端启动失败排查
+ **依赖安装失败**:切换 pnpm 镜像源;Node 版本低于 18 升级后再装
+ **接口 404/跨域**:确认后端已在 `:8000` 启动;开发代理仅在 `pnpm dev` 生效
+ **登录页验证码不显示**:后端未启动或 `/api/captcha` 被限流(认证入口默认 10 次/分钟/IP)
+ **页面白屏**:浏览器控制台看路由守卫报错;确认已登录或访问常量路由 `/login`